Supports protocol activation for illustrations

This commit is contained in:
Dylech30th 2021-10-17 02:52:13 +08:00
parent 69fefc84b6
commit 3f221021b7
24 changed files with 445 additions and 77 deletions

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31515.178
# Visual Studio Version 17
VisualStudioVersion = 17.0.31808.319
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pixeval", "src\Pixeval\Pixeval.csproj", "{97004D5A-21BE-4137-A529-A9E150AEB3CE}"
EndProject

View File

@ -0,0 +1,26 @@
using System.Collections.Generic;
using Windows.ApplicationModel.Activation;
using Microsoft.Windows.AppLifecycle;
namespace Pixeval.Activation
{
public static class ActivationRegistrar
{
static ActivationRegistrar()
{
FeatureHandlers[IllustrationActivationFragment] = new IllustrationAppActivationHandler();
}
public static readonly Dictionary<string, IAppActivationHandler> FeatureHandlers = new();
public const string IllustrationActivationFragment = "illust";
public static void Dispatch(AppActivationArguments args)
{
if (args.Kind == ExtendedActivationKind.Protocol && args.Data is IProtocolActivatedEventArgs { Uri: var activationUri } && FeatureHandlers.TryGetValue(activationUri.Host, out var handler))
{
handler.Execute(activationUri.PathAndQuery[1..]);
}
}
}
}

View File

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Pixeval.Activation
{
public interface IAppActivationHandler
{
Task Execute(string id);
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Toolkit.Mvvm.Messaging;
using Microsoft.UI.Xaml.Media.Animation;
using Pixeval.Messages;
using Pixeval.Pages.IllustrationViewer;
using Pixeval.Util.UI;
using Pixeval.Utilities;
using Pixeval.ViewModel;
namespace Pixeval.Activation
{
public class IllustrationAppActivationHandler : IAppActivationHandler
{
public Task Execute(string id)
{
WeakReferenceMessenger.Default.Send(new MainPageFrameSetConnectedAnimationTargetMessage(App.AppViewModel.AppWindowRootFrame));
return App.AppViewModel.DispatchTaskAsync(async () =>
{
App.AppViewModel.PrepareForActivation();
try
{
var viewModels = new IllustrationViewModel(await App.AppViewModel.MakoClient.GetIllustrationFromIdAsync(id))
.GetMangaIllustrationViewModels()
.ToArray();
ConnectedAnimationService.GetForCurrentView().PrepareToAnimate("ForwardConnectedAnimation", App.AppViewModel.AppWindowRootFrame);
App.AppViewModel.RootFrameNavigate(typeof(IllustrationViewerPage), new IllustrationViewerPageViewModel(viewModels), new SuppressNavigationTransitionInfo());
}
catch (Exception e)
{
UIHelper.ShowTextToastNotification(
ActivationsResources.IllustrationActivationFailedTitle,
ActivationsResources.IllustrationActivationFailedContentFormatted.Format(e.Message));
}
finally
{
App.AppViewModel.ActivationProcessed();
}
});
}
}
}

View File

@ -1,8 +1,13 @@
using Microsoft.UI.Xaml;
using System;
using System.Linq;
using Windows.ApplicationModel.Activation;
using Microsoft.UI.Xaml.Media;
using Microsoft.Windows.AppLifecycle;
using Pixeval.Activation;
using Pixeval.Util.UI;
using Pixeval.ViewModel;
using ApplicationTheme = Pixeval.Options.ApplicationTheme;
using LaunchActivatedEventArgs = Microsoft.UI.Xaml.LaunchActivatedEventArgs;
namespace Pixeval
{
@ -11,7 +16,7 @@ namespace Pixeval
private const string ApplicationWideFontKey = "ContentControlThemeFontFamily";
public static AppViewModel AppViewModel { get; private set; } = null!;
public App()
{
// The theme can only be changed in ctor
@ -22,13 +27,24 @@ namespace Pixeval
ApplicationTheme.Light => Microsoft.UI.Xaml.ApplicationTheme.Light,
_ => RequestedTheme
};
AppInstance.GetCurrent().Activated += (_, arguments) => ActivationRegistrar.Dispatch(arguments);
InitializeComponent();
}
protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
var isProtocolActivated = AppInstance.GetCurrent().GetActivatedEventArgs() is { Kind: ExtendedActivationKind.Protocol };
if (isProtocolActivated && AppInstance.GetInstances().Count > 1)
{
var notCurrent = AppInstance.GetInstances().First(ins => !ins.IsCurrent);
await notCurrent.RedirectActivationToAsync(AppInstance.GetCurrent().GetActivatedEventArgs());
return;
}
Current.Resources[ApplicationWideFontKey] = new FontFamily(AppViewModel.AppSetting.AppFontFamilyName);
await AppViewModel.InitializeAsync();
await AppViewModel.InitializeAsync(isProtocolActivated);
}
/// <summary>

View File

@ -5,15 +5,31 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Closed="MainWindow_OnClosed"
mc:Ignorable="d">
<Frame x:Name="PixevalAppRootFrame"
x:FieldModifier="public"
Loaded="PixevalAppRootFrame_OnLoaded"
Navigating="PixevalAppRootFrame_OnNavigating"
NavigationFailed="PixevalAppRootFrame_OnNavigationFailed">
<Frame.ContentTransitions>
<TransitionCollection>
<NavigationThemeTransition DefaultNavigationTransitionInfo="{x:Bind DefaultNavigationTransitionInfo}" />
</TransitionCollection>
</Frame.ContentTransitions>
</Frame>
<Grid>
<Frame x:Name="PixevalAppRootFrame"
x:FieldModifier="public"
Loaded="PixevalAppRootFrame_OnLoaded"
Navigating="PixevalAppRootFrame_OnNavigating"
NavigationFailed="PixevalAppRootFrame_OnNavigationFailed">
<Frame.ContentTransitions>
<TransitionCollection>
<NavigationThemeTransition DefaultNavigationTransitionInfo="{x:Bind DefaultNavigationTransitionInfo}" />
</TransitionCollection>
</Frame.ContentTransitions>
</Frame>
<Grid x:Name="ProcessingActivation"
Width="100"
Height="100"
HorizontalAlignment="Center"
VerticalAlignment="Center"
CornerRadius="15"
Visibility="Collapsed">
<Rectangle Fill="{ThemeResource PixevalAppAcrylicBrush}" />
<ProgressRing Width="40"
Height="40"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsIndeterminate="True" />
</Grid>
</Grid>
</Window>

View File

@ -46,5 +46,15 @@ namespace Pixeval
{
NavigationMode = e.NavigationMode;
}
public void ShowActivationProgressRing()
{
ProcessingActivation.Visibility = Visibility.Visible;
}
public void HideActivationProgressRing()
{
ProcessingActivation.Visibility = Visibility.Collapsed;
}
}
}

View File

@ -9,5 +9,5 @@ namespace Pixeval.Messages
/// <see cref="MainPage"/>, the parameter contains the item that the <see cref="IllustrationViewerPage"/>
/// is currently browsing
/// </summary>
public record NavigatingBackToMainPageMessage(IllustrationViewModel IllustrationViewModel);
public record NavigatingBackToMainPageMessage(IllustrationViewModel? IllustrationViewModel);
}

View File

@ -49,6 +49,13 @@
<uap:Rotation Preference="landscape"/>
<uap:Rotation Preference="portrait"/></uap:InitialRotationPreference>
</uap:VisualElements>
<Extensions>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="pixeval">
<uap:DisplayName>Pixeval App Protocol</uap:DisplayName>
</uap:Protocol>
</uap:Extension>
</Extensions>
</Application>
</Applications>

View File

@ -61,7 +61,7 @@ namespace Pixeval.Pages.IllustrationViewer
IllustrationImageShowcaseFrame.Navigate(typeof(ImageViewerPage), _viewModel.Current);
WeakReferenceMessenger.Default.Send(new MainPageFrameSetConnectedAnimationTargetMessage(_viewModel.IllustrationGrid.GetItemContainer(_viewModel.IllustrationViewModelInTheGridView)));
WeakReferenceMessenger.Default.Send(new MainPageFrameSetConnectedAnimationTargetMessage(_viewModel.IllustrationGrid?.GetItemContainer(_viewModel.IllustrationViewModelInTheGridView!) ?? App.AppViewModel.AppWindowRootFrame));
WeakReferenceMessenger.Default.Register<IllustrationViewerPage, CommentRepliesHyperlinkButtonTappedMessage>(this, CommentRepliesHyperlinkButtonTapped);
}
@ -133,13 +133,13 @@ namespace Pixeval.Pages.IllustrationViewer
Effect = SlideNavigationTransitionEffect.FromLeft
});
}
private void NextIllustration()
{
var illustrationViewModel = (IllustrationViewModel) _viewModel.ContainerGridViewModel.IllustrationsView[_viewModel.IllustrationIndex + 1];
var illustrationViewModel = (IllustrationViewModel) _viewModel.ContainerGridViewModel!.IllustrationsView[_viewModel.IllustrationIndex!.Value + 1];
var viewModel = illustrationViewModel.GetMangaIllustrationViewModels().ToArray();
App.AppViewModel.RootFrameNavigate(typeof(IllustrationViewerPage), new IllustrationViewerPageViewModel(_viewModel.IllustrationGrid, viewModel), new SlideNavigationTransitionInfo
App.AppViewModel.RootFrameNavigate(typeof(IllustrationViewerPage), new IllustrationViewerPageViewModel(_viewModel.IllustrationGrid!, viewModel), new SlideNavigationTransitionInfo
{
Effect = SlideNavigationTransitionEffect.FromRight
});
@ -147,10 +147,10 @@ namespace Pixeval.Pages.IllustrationViewer
private void PrevIllustration()
{
var illustrationViewModel = (IllustrationViewModel)_viewModel.ContainerGridViewModel.IllustrationsView[_viewModel.IllustrationIndex - 1];
var illustrationViewModel = (IllustrationViewModel) _viewModel.ContainerGridViewModel!.IllustrationsView[_viewModel.IllustrationIndex!.Value - 1];
var viewModel = illustrationViewModel.GetMangaIllustrationViewModels().ToArray();
App.AppViewModel.RootFrameNavigate(typeof(IllustrationViewerPage), new IllustrationViewerPageViewModel(_viewModel.IllustrationGrid, viewModel), new SlideNavigationTransitionInfo
App.AppViewModel.RootFrameNavigate(typeof(IllustrationViewerPage), new IllustrationViewerPageViewModel(_viewModel.IllustrationGrid!, viewModel), new SlideNavigationTransitionInfo
{
Effect = SlideNavigationTransitionEffect.FromLeft
});

View File

@ -83,9 +83,11 @@
Icon="{ui:FontIcon Glyph=SearchAndAppsE773}" />
<NavigationViewItemHeader x:Uid="/MainPage/DownloadAndHistoriesTab" />
<NavigationViewItem x:Uid="/MainPage/HistoriesTab"
Icon="{ui:FontIcon Glyph=HistoryE81C}" />
Icon="{ui:FontIcon Glyph=HistoryE81C}"
Tag="{x:Bind _histories}" />
<NavigationViewItem x:Uid="/MainPage/DownloadListTab"
Icon="{ui:FontIcon Glyph=CheckListE9D5}" />
Icon="{ui:FontIcon Glyph=CheckListE9D5}"
Tag="{x:Bind _downloads}" />
</NavigationView.MenuItems>
<NavigationView.FooterMenuItems>
<NavigationViewItem x:Uid="/MainPage/HelpTab"

View File

@ -2,6 +2,7 @@
using System.Linq;
using System.Runtime;
using System.Threading.Tasks;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Microsoft.Toolkit.Mvvm.Messaging;
using Microsoft.UI.Xaml;
@ -9,6 +10,8 @@ using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media.Animation;
using Microsoft.UI.Xaml.Navigation;
using Microsoft.Windows.AppLifecycle;
using Pixeval.Activation;
using Pixeval.CommunityToolkit;
using Pixeval.CommunityToolkit.AdaptiveGridView;
using Pixeval.CoreApi.Global.Enum;
@ -36,6 +39,10 @@ namespace Pixeval.Pages
private readonly NavigationViewTag _settings = new(typeof(SettingsPage), App.AppViewModel.MakoClient.Configuration);
private readonly NavigationViewTag _downloads = new(typeof(DownloadListPage), null);
private readonly NavigationViewTag _histories = new(typeof(BrowsingHistoryPage), null);
private static UIElement? _connectedAnimationTarget;
// This field contains the view model that the illustration viewer is
@ -58,6 +65,12 @@ namespace Pixeval.Pages
// little dirty tricks
((NavigationViewItem) MainPageRootNavigationView.MenuItems[(int) App.AppViewModel.AppSetting.DefaultSelectedTabItem]).IsSelected = true;
if (App.AppViewModel.ConsumeProtocolActivation())
{
ActivationRegistrar.Dispatch(AppInstance.GetCurrent().GetActivatedEventArgs());
}
WeakReferenceMessenger.Default.Register<MainPage, MainPageFrameSetConnectedAnimationTargetMessage>(this, (_, message) => _connectedAnimationTarget = message.Sender);
WeakReferenceMessenger.Default.Register<MainPage, NavigatingBackToMainPageMessage>(this, (_, message) => _illustrationViewerContent = message.IllustrationViewModel);
WeakReferenceMessenger.Default.Register<MainPage, IllustrationTagClickedMessage>(this, (_, message) => PerformSearch(message.Tag));

View File

@ -0,0 +1,14 @@
<Page
x:Class="Pixeval.Pages.Misc.BrowsingHistoryPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Pixeval.Pages.Misc"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
</Grid>
</Page>

View File

@ -0,0 +1,31 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Pixeval.Pages.Misc
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class BrowsingHistoryPage : Page
{
public BrowsingHistoryPage()
{
this.InitializeComponent();
}
}
}

View File

@ -1,16 +0,0 @@
<Page x:Class="Pixeval.Pages.Misc.DownloadAndHistoryListPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<NavigationView PaneDisplayMode="Top">
<NavigationView.MenuItems>
<NavigationViewItem x:Uid="/DownloadAndHistoryListPage/DownloadingItemsTab" />
<NavigationViewItem x:Uid="/DownloadAndHistoryListPage/DownloadedItemsTab" />
<NavigationViewItem x:Uid="/DownloadAndHistoryListPage/BrowsingHistoryTab" />
</NavigationView.MenuItems>
</NavigationView>
</Page>

View File

@ -0,0 +1,7 @@
<Page x:Class="Pixeval.Pages.Misc.DownloadListPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d" />

View File

@ -1,8 +1,8 @@
namespace Pixeval.Pages.Misc
{
public sealed partial class DownloadAndHistoryListPage
public sealed partial class DownloadListPage
{
public DownloadAndHistoryListPage()
public DownloadListPage()
{
this.InitializeComponent();
}

View File

@ -44,7 +44,6 @@
<ItemGroup>
<Content Remove="Assets\Images\logo-no-caption.png" />
<Content Remove="Strings\zh-CN\BookmarksPage.resw" />
<Content Remove="Strings\zh-CN\DownloadAndHistoryListPage.resw" />
<Content Remove="Strings\zh-CN\IllustrationGridCommandBar.resw" />
<Content Remove="Strings\zh-CN\IllustrationInfoPage.resw" />
<Content Remove="Strings\zh-CN\IllustrationViewerPage.resw" />
@ -80,12 +79,14 @@
<None Remove="Pages\IllustrationViewer\CommentRepliesPage.xaml" />
<None Remove="Pages\IllustrationViewer\PixivReplyEmojiListPage.xaml" />
<None Remove="Pages\IllustrationViewer\PixivReplyStickerListPage.xaml" />
<None Remove="Pages\Misc\BrowsingHistoryPage.xaml" />
<None Remove="Popups\IllustrationResultFilterPopup.xaml" />
<None Remove="Popups\IllustrationResultFilter\IllustrationResultFilterFunctionEntry.xaml" />
<None Remove="Strings\zh-CN\Activations.resw" />
<None Remove="Strings\zh-CN\BookmarksPage.resw" />
<None Remove="Strings\zh-CN\CommentBlock.resw" />
<None Remove="Strings\zh-CN\CommentRepliesBlock.resw" />
<None Remove="Strings\zh-CN\DownloadAndHistoryListPage.resw" />
<None Remove="Strings\zh-CN\DownloadListPage.resw" />
<None Remove="Strings\zh-CN\IllustrationGridCommandBar.resw" />
<None Remove="Strings\zh-CN\IllustrationInfoPage.resw" />
<None Remove="Strings\zh-CN\IllustrationResultFilterPopupContent.resw" />
@ -98,7 +99,6 @@
<None Remove="Package.appxmanifest" />
<None Remove="Pages\BookMarksPage.xaml" />
<None Remove="Pages\Capability\NavigationPage.xaml" />
<None Remove="Pages\DownloadAndHistoryListPage.xaml" />
<None Remove="Pages\IllustrationGridPage.xaml" />
<None Remove="Pages\IllustrationViewerPage.xaml" />
<None Remove="Pages\IllustrationViewer\CommentsPage.xaml" />
@ -186,7 +186,7 @@
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Pages\Misc\DownloadAndHistoryListPage.xaml">
<Page Update="Pages\Misc\DownloadListPage.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
@ -232,10 +232,11 @@
<Folder Include="CommunityToolkit\Markdown\Parsers\Markdown\Helpers\" />
</ItemGroup>
<ItemGroup>
<PRIResource Include="Strings\zh-CN\Activations.resw" />
<PRIResource Include="Strings\zh-CN\BookmarksPage.resw" />
<PRIResource Include="Strings\zh-CN\CommentBlock.resw" />
<PRIResource Include="Strings\zh-CN\CommentRepliesBlock.resw" />
<PRIResource Include="Strings\zh-CN\DownloadAndHistoryListPage.resw" />
<PRIResource Include="Strings\zh-CN\DownloadListPage.resw" />
<PRIResource Include="Strings\zh-CN\IllustrationGridCommandBar.resw" />
<PRIResource Include="Strings\zh-CN\IllustrationInfoPage.resw" />
<PRIResource Include="Strings\zh-CN\IllustrationResultFilterPopupContent.resw" />
@ -280,6 +281,9 @@
<None Update="Pages\Capability\SearchResultsPage.xaml">
<Generator>MSBuild:Compile</Generator>
</None>
<Page Update="Pages\Misc\BrowsingHistoryPage.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Update="UserControls\TokenInput\TokenInput.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>

View File

@ -2,7 +2,6 @@
"profiles": {
"Pixeval": {
"commandName": "MsixPackage",
"commandLineArgs": "",
"alwaysReinstallApp": "false",
"remoteDebugEnabled": false,
"allowLocalNetworkLoopbackProperty": true,

View File

@ -117,13 +117,10 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="BrowsingHistoryTab.Content" xml:space="preserve">
<value>历史记录</value>
<data name="IllustrationActivationFailedContentFormatted" xml:space="preserve">
<value>错误详情: {0}</value>
</data>
<data name="DownloadedItemsTab.Content" xml:space="preserve">
<value>已下载</value>
</data>
<data name="DownloadingItemsTab.Content" xml:space="preserve">
<value>下载中</value>
<data name="IllustrationActivationFailedTitle" xml:space="preserve">
<value>启动失败</value>
</data>
</root>

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -68,12 +68,12 @@ namespace Pixeval.UserControls
e.Handled = true;
WeakReferenceMessenger.Default.Send(new MainPageFrameSetConnectedAnimationTargetMessage(sender as UIElement));
var viewModel = sender.GetDataContext<IllustrationViewModel>()
var viewModels = sender.GetDataContext<IllustrationViewModel>()
.GetMangaIllustrationViewModels()
.ToArray();
ConnectedAnimationService.GetForCurrentView().PrepareToAnimate("ForwardConnectedAnimation", (UIElement) sender);
App.AppViewModel.RootFrameNavigate(typeof(IllustrationViewerPage), new IllustrationViewerPageViewModel(this, viewModel), new SuppressNavigationTransitionInfo());
App.AppViewModel.RootFrameNavigate(typeof(IllustrationViewerPage), new IllustrationViewerPageViewModel(this, viewModels), new SuppressNavigationTransitionInfo());
}
private void IllustrationThumbnailContainerItem_OnTapped(object sender, TappedRoutedEventArgs e)

View File

@ -5,6 +5,7 @@ using Windows.Foundation;
using Windows.Graphics;
using Microsoft.Toolkit.Mvvm.Messaging;
using Microsoft.UI;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@ -30,6 +31,8 @@ namespace Pixeval.ViewModel
App = app;
}
private bool _activatedByProtocol;
public App App { get; }
public MainWindow Window = null!;
@ -116,21 +119,31 @@ namespace Pixeval.ViewModel
};
#if DEBUG
// ReSharper disable once UnusedParameter.Local
static Task UncaughtExceptionHandler(Exception e)
{
Debugger.Break();
return Task.CompletedTask;
}
#elif RELEASE
static async Task UncaughtExceptionHandler(Exception e)
async Task UncaughtExceptionHandler(Exception e)
{
await MessageDialogBuilder.CreateAcknowledgement(Window, MiscResources.ExceptionEncountered, e.ToString()).ShowAsync();
ExitWithPushedNotification();
}
#endif
}
public void DispatchTask(DispatcherQueueHandler action)
{
Window.DispatcherQueue.TryEnqueue(action);
}
public Task DispatchTaskAsync(Func<Task> action)
{
return Window.DispatcherQueue.EnqueueAsync(action);
}
/// <summary>
/// Exit the notification after pushing an <see cref="ApplicationExitingMessage"/>
@ -143,12 +156,13 @@ namespace Pixeval.ViewModel
Application.Current.Exit();
}
public async Task InitializeAsync()
public async Task InitializeAsync(bool activatedByProtocol)
{
_activatedByProtocol = activatedByProtocol;
RegisterUnhandledExceptionHandler();
await AppContext.WriteLogoIcoIfNotExist();
Window = new MainWindow();
Window = new MainWindow();
AppWindow = AppWindow.GetFromWindowId(Win32Interop.GetWindowIdFromWindow(GetMainWindowHandle()));
AppWindow.Title = AppContext.AppIdentifier;
AppWindow.Resize(new SizeInt32(AppSetting.WindowWidth, AppSetting.WindowHeight));
@ -188,5 +202,26 @@ namespace Pixeval.ViewModel
{
AppContext.SaveContext();
}
public void PrepareForActivation()
{
Window.ShowActivationProgressRing();
}
public void ActivationProcessed()
{
Window.HideActivationProgressRing();
}
/// <summary>
/// Gets and resets the <see cref="_activatedByProtocol"/> field, used for one-time activation process
/// during the app start
/// </summary>
public bool ConsumeProtocolActivation()
{
var original = _activatedByProtocol;
_activatedByProtocol = false;
return original;
}
}
}

View File

@ -25,12 +25,23 @@ namespace Pixeval.ViewModel
/// <summary>
/// The view model of the GridView that the <see cref="ImageViewerPageViewModels"/> comes from
/// </summary>
public IllustrationGridViewModel ContainerGridViewModel { get; }
public IllustrationGridViewModel? ContainerGridViewModel { get; }
/// <summary>
/// The <see cref="IllustrationGrid"/> that owns <see cref="ContainerGridViewModel"/>
/// </summary>
public IllustrationGrid IllustrationGrid { get; }
public IllustrationGrid? IllustrationGrid { get; }
/// <summary>
/// The <see cref="IllustrationViewModelInTheGridView"/> in <see cref="IllustrationGrid"/> that corresponds to current
/// <see cref="IllustrationViewerPageViewModel"/>
/// </summary>
public IllustrationViewModel? IllustrationViewModelInTheGridView { get; }
/// <summary>
/// The index of current illustration in <see cref="IllustrationGrid"/>
/// </summary>
public int? IllustrationIndex => ContainerGridViewModel?.IllustrationsView.IndexOf(IllustrationViewModelInTheGridView);
private ImageSource? _qrCodeSource;
@ -158,6 +169,14 @@ namespace Pixeval.ViewModel
_ = LoadUserProfileImage();
}
public IllustrationViewerPageViewModel(params IllustrationViewModel[] illustrations)
{
ImageViewerPageViewModels = illustrations.Select(i => new ImageViewerPageViewModel(this, i)).ToArray();
Current = ImageViewerPageViewModels[CurrentIndex];
InitializeCommands();
_ = LoadUserProfileImage();
}
public void UpdateCommandCanExecute()
{
PlayGifCommand.NotifyCanExecuteChanged();
@ -379,17 +398,6 @@ namespace Pixeval.ViewModel
private ImageSource? _userProfileImageSource;
/// <summary>
/// The <see cref="IllustrationViewModelInTheGridView"/> in <see cref="IllustrationGrid"/> that corresponds to current
/// <see cref="IllustrationViewerPageViewModel"/>
/// </summary>
public IllustrationViewModel IllustrationViewModelInTheGridView { get; }
/// <summary>
/// The index of current illustration in <see cref="IllustrationGrid"/>
/// </summary>
public int IllustrationIndex => ContainerGridViewModel.IllustrationsView.IndexOf(IllustrationViewModelInTheGridView);
// Remarks:
// The reason why we don't put UserProfileImageSource into IllustrationViewModel
// is because the whole array of Illustrations is just representing the same
@ -450,13 +458,21 @@ namespace Pixeval.ViewModel
{
// changes the IsBookmarked property of the item that of in the thumbnail list
// so the thumbnail item will also receives state update
IllustrationViewModelInTheGridView.IsBookmarked = true;
if (IllustrationViewModelInTheGridView is not null)
{
IllustrationViewModelInTheGridView.IsBookmarked = true;
}
return FirstIllustrationViewModel.PostPublicBookmarkAsync();
}
public Task RemoveBookmarkAsync()
{
IllustrationViewModelInTheGridView.IsBookmarked = false;
if (IllustrationViewModelInTheGridView is not null)
{
IllustrationViewModelInTheGridView.IsBookmarked = false;
}
return FirstIllustrationViewModel.RemoveBookmarkAsync();
}
@ -482,16 +498,28 @@ namespace Pixeval.ViewModel
#region Helper Functions
public Visibility CalculateNextImageButtonVisibility(int index)
{
if (IllustrationGrid is null)
{
return Visibility.Collapsed;
}
return index < ImageViewerPageViewModels.Length - 1 ? Visibility.Visible : Visibility.Collapsed;
}
public Visibility CalculatePrevImageButtonVisibility(int index)
{
if (IllustrationGrid is null)
{
return Visibility.Collapsed;
}
return index > 0 ? Visibility.Visible : Visibility.Collapsed;
}
public Visibility CalculateNextIllustrationButtonVisibility(int index)
{
if (ContainerGridViewModel is null)
{
return Visibility.Collapsed;
}
return ContainerGridViewModel.IllustrationsView.Count > IllustrationIndex + 1
? CalculateNextImageButtonVisibility(index).Inverse()
: Visibility.Collapsed;
@ -499,6 +527,10 @@ namespace Pixeval.ViewModel
public Visibility CalculatePrevIllustrationButtonVisibility(int index)
{
if (ContainerGridViewModel is null)
{
return Visibility.Collapsed;
}
return IllustrationIndex > 0
? CalculatePrevImageButtonVisibility(index).Inverse()
: Visibility.Collapsed;