提交 6a2f66f2 编写于 作者: R Richasy

完善登录流程,添加获取个人信息的接口,添加Controller层

上级 8a3848df
......@@ -41,6 +41,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lib.Uwp", "src\Lib\Lib.Uwp\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lib.Interfaces", "src\Lib\Lib.Interfaces\Lib.Interfaces.csproj", "{CAE9B987-6DB2-42A8-B3F2-3BB700CC4DD8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Controller", "Controller", "{65F17D44-343F-4B87-A726-3BE3E48168B4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Controller.Uwp", "src\Controller\Controller.Uwp\Controller.Uwp.csproj", "{5213E830-43F5-491E-A8EF-D75083578EF3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Controller.Uwp.UnitTests", "src\Controller\Controller.Uwp.UnitTests\Controller.Uwp.UnitTests.csproj", "{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
......@@ -307,6 +313,44 @@ Global
{CAE9B987-6DB2-42A8-B3F2-3BB700CC4DD8}.Release|x64.Build.0 = Release|x64
{CAE9B987-6DB2-42A8-B3F2-3BB700CC4DD8}.Release|x86.ActiveCfg = Release|x86
{CAE9B987-6DB2-42A8-B3F2-3BB700CC4DD8}.Release|x86.Build.0 = Release|x86
{5213E830-43F5-491E-A8EF-D75083578EF3}.Debug|Any CPU.ActiveCfg = Debug|x86
{5213E830-43F5-491E-A8EF-D75083578EF3}.Debug|ARM.ActiveCfg = Debug|x86
{5213E830-43F5-491E-A8EF-D75083578EF3}.Debug|ARM64.ActiveCfg = Debug|ARM64
{5213E830-43F5-491E-A8EF-D75083578EF3}.Debug|ARM64.Build.0 = Debug|ARM64
{5213E830-43F5-491E-A8EF-D75083578EF3}.Debug|x64.ActiveCfg = Debug|x64
{5213E830-43F5-491E-A8EF-D75083578EF3}.Debug|x64.Build.0 = Debug|x64
{5213E830-43F5-491E-A8EF-D75083578EF3}.Debug|x86.ActiveCfg = Debug|x86
{5213E830-43F5-491E-A8EF-D75083578EF3}.Debug|x86.Build.0 = Debug|x86
{5213E830-43F5-491E-A8EF-D75083578EF3}.Release|Any CPU.ActiveCfg = Release|x86
{5213E830-43F5-491E-A8EF-D75083578EF3}.Release|ARM.ActiveCfg = Release|x86
{5213E830-43F5-491E-A8EF-D75083578EF3}.Release|ARM64.ActiveCfg = Release|ARM64
{5213E830-43F5-491E-A8EF-D75083578EF3}.Release|ARM64.Build.0 = Release|ARM64
{5213E830-43F5-491E-A8EF-D75083578EF3}.Release|x64.ActiveCfg = Release|x64
{5213E830-43F5-491E-A8EF-D75083578EF3}.Release|x64.Build.0 = Release|x64
{5213E830-43F5-491E-A8EF-D75083578EF3}.Release|x86.ActiveCfg = Release|x86
{5213E830-43F5-491E-A8EF-D75083578EF3}.Release|x86.Build.0 = Release|x86
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Debug|Any CPU.ActiveCfg = Debug|x86
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Debug|ARM.ActiveCfg = Debug|x86
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Debug|ARM64.ActiveCfg = Debug|ARM64
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Debug|ARM64.Build.0 = Debug|ARM64
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Debug|ARM64.Deploy.0 = Debug|ARM64
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Debug|x64.ActiveCfg = Debug|x64
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Debug|x64.Build.0 = Debug|x64
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Debug|x64.Deploy.0 = Debug|x64
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Debug|x86.ActiveCfg = Debug|x86
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Debug|x86.Build.0 = Debug|x86
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Debug|x86.Deploy.0 = Debug|x86
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Release|Any CPU.ActiveCfg = Release|x86
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Release|ARM.ActiveCfg = Release|x86
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Release|ARM64.ActiveCfg = Release|ARM64
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Release|ARM64.Build.0 = Release|ARM64
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Release|ARM64.Deploy.0 = Release|ARM64
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Release|x64.ActiveCfg = Release|x64
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Release|x64.Build.0 = Release|x64
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Release|x64.Deploy.0 = Release|x64
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Release|x86.ActiveCfg = Release|x86
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Release|x86.Build.0 = Release|x86
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}.Release|x86.Deploy.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......@@ -326,6 +370,8 @@ Global
{C492CCBB-CDFD-403C-A981-1BC5EAA934D1} = {82E6DD11-BBC1-4BFC-A244-593EE27BF250}
{C8AB398F-10C2-4B97-87F0-F09B922947D6} = {45B5DFF9-9F13-4BF3-B561-8ABBDFE5C237}
{CAE9B987-6DB2-42A8-B3F2-3BB700CC4DD8} = {45B5DFF9-9F13-4BF3-B561-8ABBDFE5C237}
{5213E830-43F5-491E-A8EF-D75083578EF3} = {65F17D44-343F-4B87-A726-3BE3E48168B4}
{3B66D3B1-4B4C-4AAF-8556-B3B51999F177} = {65F17D44-343F-4B87-A726-3BE3E48168B4}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {563225C5-4D56-446B-9ADE-09D3F0DE7963}
......
......@@ -17,6 +17,9 @@
<Compile Include="$(SolutionDir)\src\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Controls\Bili\AccountAvatar.xaml.cs">
<DependentUpon>AccountAvatar.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Bili\BannerItem.cs" />
<Compile Include="Controls\Bili\BannerView.xaml.cs">
<DependentUpon>BannerView.xaml</DependentUpon>
......@@ -183,6 +186,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\Bili\AccountAvatar.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\Bili\BannerView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
......@@ -336,6 +343,10 @@
<Content Include="Assets\Mock\PartitionList.json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Controller\Controller.Uwp\Controller.Uwp.csproj">
<Project>{5213E830-43F5-491E-A8EF-D75083578EF3}</Project>
<Name>Controller.Uwp</Name>
</ProjectReference>
<ProjectReference Include="..\Models\Models.App\Models.App.csproj">
<Project>{9e61cc56-43f9-489c-aaaf-bd5ec69367c4}</Project>
<Name>Models.App</Name>
......
......@@ -185,14 +185,13 @@
UpdateTextOnSelect="False" />
</Grid>
<muxc:PersonPicture
<local:AccountAvatar
x:Name="UserAvatar"
Grid.Column="4"
Width="28"
Height="28"
Margin="12,0"
VerticalAlignment="Center"
Tapped="OnUserAvatarTappedAsync" />
VerticalAlignment="Center" />
</Grid>
</Grid>
......
......@@ -49,10 +49,5 @@ namespace Richasy.Bili.App.Controls
ViewModel.SetMainContentId(ViewModel.CurrentMainContentId);
}
}
private async void OnUserAvatarTappedAsync(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
await UserViewModel.Instance.SignInAsync();
}
}
}
<UserControl
x:Class="Richasy.Bili.App.Controls.AccountAvatar"
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:local="using:Richasy.Bili.App.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Common">
<VisualState x:Name="NormalState" />
<VisualState x:Name="LoadingState">
<VisualState.Setters>
<Setter Target="UserAvatar.Visibility" Value="Collapsed" />
<Setter Target="LoadingContainer.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<local:UserAvatar
x:Name="UserAvatar"
Width="{x:Bind Width, Mode=OneWay}"
Height="{x:Bind Height, Mode=OneWay}"
Avatar="{x:Bind ViewModel.Avatar, Mode=OneWay}"
Tapped="OnUserAvatarTappedAsync"
UserName="{x:Bind ViewModel.DisplayName, Mode=OneWay}" />
<Viewbox x:Name="LoadingContainer" Visibility="Collapsed">
<muxc:ProgressRing x:Name="LoadingRing" Style="{StaticResource PageProgressRingStyle}" />
</Viewbox>
</Grid>
</UserControl>
// Copyright (c) Richasy. All rights reserved.
using System.ComponentModel;
using Richasy.Bili.Controller.Uwp;
using Richasy.Bili.ViewModels.Uwp;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace Richasy.Bili.App.Controls
{
/// <summary>
/// 账户管理中枢.
/// </summary>
public sealed partial class AccountAvatar : UserControl
{
/// <summary>
/// <see cref="ViewModel"/>的依赖属性.
/// </summary>
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register(nameof(ViewModel), typeof(AccountViewModel), typeof(AccountAvatar), new PropertyMetadata(AccountViewModel.Instance));
/// <summary>
/// Initializes a new instance of the <see cref="AccountAvatar"/> class.
/// </summary>
public AccountAvatar()
{
this.InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
/// <summary>
/// 账户视图模型.
/// </summary>
public AccountViewModel ViewModel
{
get { return (AccountViewModel)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
CheckStatus();
ViewModel.PropertyChanged += OnViewModelPropertyChanged;
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
ViewModel.PropertyChanged -= OnViewModelPropertyChanged;
}
private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ViewModel.Status))
{
CheckStatus();
}
}
private void CheckStatus()
{
switch (ViewModel.Status)
{
case AccountViewModelStatus.Logout:
case AccountViewModelStatus.Login:
VisualStateManager.GoToState(this, nameof(NormalState), false);
break;
case AccountViewModelStatus.Logging:
VisualStateManager.GoToState(this, nameof(LoadingState), false);
break;
default:
break;
}
}
private async void OnUserAvatarTappedAsync(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
if (ViewModel.Status == AccountViewModelStatus.Logout)
{
await ViewModel.TrySignInAsync();
}
}
}
}
......@@ -56,10 +56,8 @@ namespace Richasy.Bili.App.Controls
{
e.Handled = true;
var animationService = ConnectedAnimationService.GetForCurrentView();
var animate1 = animationService.PrepareToAnimate("PartitionLogoAnimate", this.PartitionLogo);
var animate2 = animationService.PrepareToAnimate("PartitionNameAnimate", this.PartitionName);
animate1.Configuration = new DirectConnectedAnimationConfiguration();
animate2.Configuration = new DirectConnectedAnimationConfiguration();
animationService.PrepareToAnimate("PartitionLogoAnimate", this.PartitionLogo);
animationService.PrepareToAnimate("PartitionNameAnimate", this.PartitionName);
ItemClick?.Invoke(this, Data);
}
}
......
// Copyright (c) Richasy. All rights reserved.
using Richasy.Bili.ViewModels.Uwp;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace Richasy.Bili.App.Pages
......@@ -15,6 +17,12 @@ namespace Richasy.Bili.App.Pages
public RootPage()
{
this.InitializeComponent();
this.Loaded += OnLoadedAsync;
}
private async void OnLoadedAsync(object sender, RoutedEventArgs e)
{
await AccountViewModel.Instance.TrySignInAsync(true);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(SolutionDir)\Uwp.UnitTests.props" />
<PropertyGroup>
<ProjectGuid>{3B66D3B1-4B4C-4AAF-8556-B3B51999F177}</ProjectGuid>
<OutputType>AppContainerExe</OutputType>
<RootNamespace>Controller.Uwp.UnitTests</RootNamespace>
<AssemblyName>Controller.Uwp.UnitTests</AssemblyName>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
</PropertyGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="$(SolutionDir)\src\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Content Include="Properties\UnitTestApp.rd.xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="UnitTestApp.xaml.cs">
<DependentUpon>UnitTestApp.xaml</DependentUpon>
</Compile>
<Compile Include="UwpAdapterTests.cs" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="UnitTestApp.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
</ItemGroup>
<ItemGroup>
<AppxManifest Include="Package.appxmanifest">
<SubType>Designer</SubType>
</AppxManifest>
</ItemGroup>
<ItemGroup>
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\SplashScreen.scale-200.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Assets\StoreLogo.png" />
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions">
<Version>5.10.3</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.2.12</Version>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.SDK">
<Version>16.7.1</Version>
</PackageReference>
<PackageReference Include="xunit">
<Version>2.4.1</Version>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio">
<Version>2.4.1</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Controller.Uwp\Controller.Uwp.csproj">
<Project>{5213e830-43f5-491e-a8ef-d75083578ef3}</Project>
<Name>Controller.Uwp</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
</Project>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
IgnorableNamespaces="uap mp">
<Identity Name="Controller.Uwp.UnitTests"
Publisher="CN=Richasy"
Version="1.0.0.0" />
<mp:PhoneIdentity PhoneProductId="3632018e-64a0-4f9f-a4b1-b03eca3096fe" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
<Properties>
<DisplayName>Controller.Uwp.UnitTests.UnitTests</DisplayName>
<PublisherDisplayName>Richasy</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate" />
</Resources>
<Applications>
<Application Id="vstest.executionengine.universal.App"
Executable="Controller.Uwp.UnitTests.exe"
EntryPoint="Controller.Uwp.UnitTests.App">
<uap:VisualElements
DisplayName="Controller.Uwp.UnitTests"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png"
Description="Controller.Uwp.UnitTests"
BackgroundColor="transparent">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png"/>
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<Capability Name="internetClientServer" />
<Capability Name="privateNetworkClientServer" />
</Capabilities>
</Package>
\ No newline at end of file
// Copyright (c) Richasy. All rights reserved.
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Controller.Uwp.UnitTests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Richasy")]
[assembly: AssemblyProduct("Controller.Uwp.UnitTests")]
[assembly: AssemblyCopyright("Copyright © Richasy 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyMetadata("TargetPlatform", "UAP")]
[assembly: ComVisible(false)]
<!--
This file contains Runtime Directives used by .NET Native. The defaults here are suitable for most
developers. However, you can modify these parameters to modify the behavior of the .NET Native
optimizer.
Runtime Directives are documented at https://go.microsoft.com/fwlink/?LinkID=391919
To fully enable reflection for App1.MyClass and all of its public/private members
<Type Name="App1.MyClass" Dynamic="Required All"/>
To enable dynamic creation of the specific instantiation of AppClass<T> over System.Int32
<TypeInstantiation Name="App1.AppClass" Arguments="System.Int32" Activate="Required Public" />
Using the Namespace directive to apply reflection policy to all the types in a particular namespace
<Namespace Name="DataClasses.ViewModels" Serialize="All" />
-->
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Application>
<!--
An Assembly element with Name="*Application*" applies to all assemblies in
the application package. The asterisks are not wildcards.
-->
<Assembly Name="*Application*" Dynamic="Required All" />
<!-- Add your application specific runtime directives here. -->
</Application>
</Directives>
\ No newline at end of file
<Application
x:Class="Controller.Uwp.UnitTests.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Controller.Uwp.UnitTests.UnitTests">
</Application>
// Copyright (c) Richasy. All rights reserved.
using System;
using Windows.ApplicationModel.Activation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace Controller.Uwp.UnitTests
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public sealed partial class App : Application
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App() => InitializeComponent();
/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
/// </summary>
/// <param name="e">Details about the launch request and process.</param>
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active.
if (!(Window.Current.Content is Frame rootFrame))
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.CreateDefaultUI();
// Ensure the current window is active
Window.Current.Activate();
Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.Run(e.Arguments);
}
/// <summary>
/// Invoked when Navigation to a certain page fails.
/// </summary>
/// <param name="sender">The Frame which failed navigation.</param>
/// <param name="e">Details about the navigation failure.</param>
private void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
=> throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
}
// Copyright (c) Richasy. All rights reserved.
using FluentAssertions;
using Xunit;
namespace Controller.Uwp.UnitTests
{
public class UwpAdapterTests
{
[Fact]
public void SampleTest()
{
true.Should().BeFalse();
}
}
}
// Copyright (c) Richasy. All rights reserved.
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Richasy.Bili.Models.BiliBili;
namespace Richasy.Bili.Controller.Uwp
{
/// <summary>
/// 控制器的账户部分.
/// </summary>
public partial class BiliController
{
private MyInfo _myInfo;
/// <summary>
/// 已登录用户的账户数据.
/// </summary>
public MyInfo MyInfo
{
get => _myInfo;
set
{
_myInfo = value;
AccountChanged?.Invoke(this, _myInfo);
}
}
/// <summary>
/// 获取我的资料.
/// </summary>
/// <returns><see cref="Task"/>.</returns>
public async Task GetMyProfileAsync()
{
if (await _authorizeProvider.IsTokenValidAsync() && IsNetworkAvailable)
{
try
{
var profile = await _accountProvider.GetMyInformationAsync();
this.MyInfo = profile;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
}
}
}
// Copyright (c) Richasy. All rights reserved.
using System;
using System.Threading.Tasks;
using Richasy.Bili.Models.App.Args;
using Richasy.Bili.Models.Enums;
namespace Richasy.Bili.Controller.Uwp
{
/// <summary>
/// 授权验证部分.
/// </summary>
public partial class BiliController
{
private bool _isRequestLogout = false;
/// <summary>
/// 尝试启动用户登录流程.
/// </summary>
/// <param name="isSlientOnly">是否只静默登录.</param>
/// <returns><see cref="Task"/>.</returns>
public async Task TrySignInAsync(bool isSlientOnly = false)
{
try
{
var isTokenValid = await _authorizeProvider.IsTokenValidAsync(true);
if (isTokenValid)
{
Logged?.Invoke(this, EventArgs.Empty);
}
else if (IsNetworkAvailable && !isSlientOnly)
{
await _authorizeProvider.SignInAsync();
}
else
{
LoggedFailed?.Invoke(this, new Exception());
}
}
catch (Exception ex)
{
LoggedFailed?.Invoke(this, ex);
}
}
/// <summary>
/// 用户登出.
/// </summary>
/// <returns><see cref="Task"/>.</returns>
public async Task LogoutAsync()
{
this._isRequestLogout = true;
await _authorizeProvider.SignOutAsync();
}
private void OnAuthenticationStateChanged(object sender, AuthorizeStateChangedEventArgs e)
{
if (e.NewState == AuthorizeState.SignedOut && !_isRequestLogout)
{
LoggedFailed?.Invoke(this, new Exception("请求失败"));
}
switch (e.NewState)
{
case AuthorizeState.SignedIn:
Logged?.Invoke(this, EventArgs.Empty);
break;
case AuthorizeState.SignedOut:
LoggedOut?.Invoke(this, EventArgs.Empty);
this._isRequestLogout = false;
break;
default:
break;
}
}
}
}
// Copyright (c) Richasy. All rights reserved.
using System;
using Microsoft.Extensions.DependencyInjection;
using Richasy.Bili.Controller.Uwp.Interfaces;
using Richasy.Bili.Controller.Uwp.Modules;
using Richasy.Bili.Lib.Interfaces;
using Richasy.Bili.Lib.Uwp;
using Richasy.Bili.Locator.Uwp;
using Richasy.Bili.Models.BiliBili;
using Richasy.Bili.Toolkit.Interfaces;
using Richasy.Bili.Toolkit.Uwp;
namespace Richasy.Bili.Controller.Uwp
{
/// <summary>
/// 应用控制器,连接Lib层与ViewModel层的中间计算层.
/// </summary>
public partial class BiliController
{
private readonly ISettingsToolkit _settingsToolkit;
private readonly IAuthorizeProvider _authorizeProvider;
private readonly IAccountProvider _accountProvider;
private readonly INetworkModule _networkModule;
/// <summary>
/// Initializes a new instance of the <see cref="BiliController"/> class.
/// </summary>
internal BiliController()
{
RegisterToolkitServices();
ServiceLocator.Instance.LoadService(out _settingsToolkit)
.LoadService(out _networkModule)
.LoadService(out _authorizeProvider)
.LoadService(out _accountProvider);
RegisterEvents();
}
/// <summary>
/// Triggered when the user successfully logs in
/// </summary>
public event EventHandler Logged;
/// <summary>
/// Triggered when the user successfully logs out
/// </summary>
public event EventHandler LoggedOut;
/// <summary>
/// Triggered when user login fails
/// </summary>
public event EventHandler<Exception> LoggedFailed;
/// <summary>
/// Triggered when the user changes
/// </summary>
public event EventHandler<MyInfo> AccountChanged;
/// <summary>
/// 控制器实例.
/// </summary>
public static BiliController Instance { get; } = new Lazy<BiliController>(() => new BiliController()).Value;
/// <summary>
/// 当前网络是否正常.
/// </summary>
public bool IsNetworkAvailable => _networkModule.IsNetworkAvaliable;
private void RegisterEvents()
{
this._authorizeProvider.StateChanged += OnAuthenticationStateChanged;
// this._networkModule.NetworkChanged += OnNetworkChangedAsync;
}
private void RegisterToolkitServices()
{
var serviceCollection = new ServiceCollection()
.AddSingleton<IAppToolkit, AppToolkit>()
.AddSingleton<IFileToolkit, FileToolkit>()
.AddSingleton<IResourceToolkit, ResourceToolkit>()
.AddSingleton<INumberToolkit, NumberToolkit>()
.AddSingleton<ISettingsToolkit, SettingsToolkit>()
.AddSingleton<IMD5Toolkit, MD5Toolkit>()
.AddSingleton<INetworkModule, NetworkModule>()
.AddSingleton<IAuthorizeProvider, AuthorizeProvider>()
.AddSingleton<IHttpProvider, HttpProvider>()
.AddSingleton<IAccountProvider, AccountProvider>();
_ = new ServiceLocator(serviceCollection);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(SolutionDir)\Uwp.props" />
<PropertyGroup>
<ProjectGuid>{5213E830-43F5-491E-A8EF-D75083578EF3}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>Richasy.Bili.Controller.Uwp</RootNamespace>
<AssemblyName>Richasy.Bili.Controller.Uwp</AssemblyName>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
</PropertyGroup>
<ItemGroup>
<Compile Include="BiliController.Auth.cs" />
<Compile Include="BiliController.cs" />
<Compile Include="BiliController.Account.cs" />
<Compile Include="Interfaces\INetworkModule.cs" />
<Compile Include="Modules\NetworkModule.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="$(SolutionDir)\src\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Utilities\ConnectionInformation.cs" />
<Content Include="Properties\Controller.Uwp.rd.xml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.2.12</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Lib\Lib.Interfaces\Lib.Interfaces.csproj">
<Project>{cae9b987-6db2-42a8-b3f2-3bb700cc4dd8}</Project>
<Name>Lib.Interfaces</Name>
</ProjectReference>
<ProjectReference Include="..\..\Lib\Lib.Uwp\Lib.Uwp.csproj">
<Project>{c8ab398f-10c2-4b97-87f0-f09b922947d6}</Project>
<Name>Lib.Uwp</Name>
</ProjectReference>
<ProjectReference Include="..\..\Models\Models.App\Models.App.csproj">
<Project>{9e61cc56-43f9-489c-aaaf-bd5ec69367c4}</Project>
<Name>Models.App</Name>
</ProjectReference>
<ProjectReference Include="..\..\Models\Models.BiliBili\Models.BiliBili.csproj">
<Project>{8776a0bd-dbd1-4f11-a022-400d044ff618}</Project>
<Name>Models.BiliBili</Name>
</ProjectReference>
<ProjectReference Include="..\..\Models\Models.Enums\Models.Enums.csproj">
<Project>{88314412-b020-415b-aeab-57adc43b273e}</Project>
<Name>Models.Enums</Name>
</ProjectReference>
<ProjectReference Include="..\..\Utilities\Locator\Locator.Uwp\Locator.Uwp.csproj">
<Project>{793ec923-d704-4c6f-9506-eb6a32bfbb8d}</Project>
<Name>Locator.Uwp</Name>
</ProjectReference>
<ProjectReference Include="..\..\Utilities\Toolkit\Toolkit.Interfaces\Toolkit.Interfaces.csproj">
<Project>{ca4fe39c-2379-4966-9940-de738d94e982}</Project>
<Name>Toolkit.Interfaces</Name>
</ProjectReference>
<ProjectReference Include="..\..\Utilities\Toolkit\Toolkit.Uwp\Toolkit.Uwp.csproj">
<Project>{07b6d3b7-0ac0-489b-bf27-588ab6226b1c}</Project>
<Name>Toolkit.Uwp</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
</Project>
\ No newline at end of file
// Copyright (c) Richasy. All rights reserved.
using System;
using Richasy.Bili.Controller.Uwp.Utilities;
namespace Richasy.Bili.Controller.Uwp.Interfaces
{
/// <summary>
/// Definition of network module.
/// </summary>
public interface INetworkModule
{
/// <summary>
/// Event raised when the network changes.
/// </summary>
event EventHandler NetworkChanged;
/// <summary>
/// Gets instance of <see cref="Utilities.ConnectionInformation"/>.
/// </summary>
ConnectionInformation ConnectionInformation { get; }
/// <summary>
/// Gets a value indicating whether internet is available across all connections.
/// </summary>
bool IsNetworkAvaliable { get; }
}
}
// Copyright (c) Richasy. All rights reserved.
using System;
using Richasy.Bili.Controller.Uwp.Interfaces;
using Richasy.Bili.Controller.Uwp.Utilities;
using Windows.Networking.Connectivity;
namespace Richasy.Bili.Controller.Uwp.Modules
{
/// <summary>
/// Module used to detect whether the current network is available.
/// </summary>
public class NetworkModule : INetworkModule
{
/// <summary>
/// Initializes a new instance of the <see cref="NetworkModule"/> class.
/// </summary>
public NetworkModule()
{
ConnectionInformation = new ConnectionInformation();
UpdateConnectionInformation();
NetworkInformation.NetworkStatusChanged += OnNetworkStatusChanged;
}
/// <summary>
/// Finalizes an instance of the <see cref="NetworkModule"/> class.
/// </summary>
~NetworkModule()
{
NetworkInformation.NetworkStatusChanged -= OnNetworkStatusChanged;
}
/// <summary>
/// Event raised when the network changes.
/// </summary>
public event EventHandler NetworkChanged;
/// <summary>
/// Gets instance of <see cref="ConnectionInformation"/>.
/// </summary>
public ConnectionInformation ConnectionInformation { get; }
/// <inheritdoc/>
public bool IsNetworkAvaliable => ConnectionInformation?.IsInternetAvailable ?? false;
/// <summary>
/// Checks the current connection information and raises <see cref="NetworkChanged"/> if needed.
/// </summary>
private void UpdateConnectionInformation()
{
lock (ConnectionInformation)
{
try
{
ConnectionInformation.UpdateConnectionInformation(NetworkInformation.GetInternetConnectionProfile());
NetworkChanged?.Invoke(this, EventArgs.Empty);
}
catch
{
ConnectionInformation.Reset();
}
}
}
/// <summary>
/// Invokes <see cref="UpdateConnectionInformation"/> when the current network status changes.
/// </summary>
private void OnNetworkStatusChanged(object sender)
{
UpdateConnectionInformation();
}
}
}
// Copyright (c) Richasy. All rights reserved.
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Controller.Uwp")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Richasy")]
[assembly: AssemblyProduct("Controller.Uwp")]
[assembly: AssemblyCopyright("Copyright © Richasy 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
<?xml version="1.0" encoding="utf-8"?>
<!--
This file contains Runtime Directives, specifications about types your application accesses
through reflection and other dynamic code patterns. Runtime Directives are used to control the
.NET Native optimizer and ensure that it does not remove code accessed by your library. If your
library does not do any reflection, then you generally do not need to edit this file. However,
if your library reflects over types, especially types passed to it or derived from its types,
then you should write Runtime Directives.
The most common use of reflection in libraries is to discover information about types passed
to the library. Runtime Directives have three ways to express requirements on types passed to
your library.
1. Parameter, GenericParameter, TypeParameter, TypeEnumerableParameter
Use these directives to reflect over types passed as a parameter.
2. SubTypes
Use a SubTypes directive to reflect over types derived from another type.
3. AttributeImplies
Use an AttributeImplies directive to indicate that your library needs to reflect over
types or methods decorated with an attribute.
For more information on writing Runtime Directives for libraries, please visit https://go.microsoft.com/fwlink/?LinkID=391919
-->
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Library Name="Controller.Uwp">
<!-- add directives for your library here -->
</Library>
</Directives>
// Copyright (c) Richasy. All rights reserved.
using System.Collections.Generic;
using Windows.Networking.Connectivity;
namespace Richasy.Bili.Controller.Uwp.Utilities
{
/// <summary>
/// This class exposes information about the network connectivity.
/// </summary>
public class ConnectionInformation
{
private readonly List<string> networkNames = new List<string>();
/// <summary>
/// Gets a value indicating whether if the current internet connection is metered.
/// </summary>
public bool IsInternetOnMeteredConnection
{
get
{
return ConnectionCost != null && ConnectionCost.NetworkCostType != NetworkCostType.Unrestricted;
}
}
/// <summary>
/// Gets a value indicating whether internet is available across all connections.
/// </summary>
/// <returns>True if internet can be reached.</returns>
public bool IsInternetAvailable { get; private set; }
/// <summary>
/// Gets connectivity level for the current Internet Connection Profile.
/// </summary>
/// <returns>value of <see cref="NetworkConnectivityLevel"/>.</returns>
public NetworkConnectivityLevel ConnectivityLevel { get; private set; }
/// <summary>
/// Gets connection cost for the current Internet Connection Profile.
/// </summary>
/// <returns>value of <see cref="NetworkConnectivityLevel"/>.</returns>
public ConnectionCost ConnectionCost { get; private set; }
/// <summary>
/// Gets signal strength for the current Internet Connection Profile.
/// </summary>
/// <returns>value of <see cref="NetworkConnectivityLevel"/>.</returns>
public byte? SignalStrength { get; private set; }
/// <summary>
/// Gets signal strength for the current Internet Connection Profile.
/// </summary>
/// <returns>value of <see cref="NetworkConnectivityLevel"/>.</returns>
public IReadOnlyList<string> NetworkNames
{
get
{
return networkNames.AsReadOnly();
}
}
/// <summary>
/// Updates the current object based on profile passed.
/// </summary>
/// <param name="profile">instance of <see cref="ConnectionProfile"/>.</param>
public void UpdateConnectionInformation(ConnectionProfile profile)
{
if (profile == null)
{
Reset();
return;
}
networkNames.Clear();
var names = profile.GetNetworkNames();
if (names?.Count > 0)
{
networkNames.AddRange(names);
}
ConnectivityLevel = profile.GetNetworkConnectivityLevel();
switch (ConnectivityLevel)
{
case NetworkConnectivityLevel.None:
case NetworkConnectivityLevel.LocalAccess:
IsInternetAvailable = false;
break;
default:
IsInternetAvailable = true;
break;
}
ConnectionCost = profile.GetConnectionCost();
SignalStrength = profile.GetSignalBars();
}
/// <summary>
/// Resets the current object to default values.
/// </summary>
internal void Reset()
{
networkNames.Clear();
ConnectivityLevel = NetworkConnectivityLevel.None;
IsInternetAvailable = false;
ConnectionCost = null;
SignalStrength = null;
}
}
}
// Copyright (c) Richasy. All rights reserved.
using System.Threading.Tasks;
using Richasy.Bili.Models.BiliBili;
namespace Richasy.Bili.Lib.Interfaces
{
/// <summary>
/// 提供已登录账户相关的操作和功能.
/// </summary>
public interface IAccountProvider
{
/// <summary>
/// 获取已登录用户的个人资料.
/// </summary>
/// <returns>个人资料.</returns>
Task<MyInfo> GetMyInformationAsync();
}
}
......@@ -44,9 +44,8 @@ namespace Richasy.Bili.Lib.Interfaces
/// <summary>
/// 获取当前登录用户的访问令牌.
/// </summary>
/// <param name="isSlientOnly">是否保持静默登录.</param>
/// <returns>账户授权的令牌.</returns>
Task<string> GetTokenAsync(bool isSlientOnly = false);
Task<string> GetTokenAsync();
/// <summary>
/// 用户登录.
......@@ -60,12 +59,6 @@ namespace Richasy.Bili.Lib.Interfaces
/// <returns><see cref="Task"/>.</returns>
Task SignOutAsync();
/// <summary>
/// 尝试静默登录.
/// </summary>
/// <returns>静默登录的结果.</returns>
Task<bool> TrySilentSignInAsync();
/// <summary>
/// 当前的访问令牌是否有效.
/// </summary>
......
......@@ -120,6 +120,7 @@
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<ItemGroup>
<Compile Include="IAccountProvider.cs" />
<Compile Include="IAuthorizeProvider.cs" />
<Compile Include="IHttpProvider.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
......@@ -135,6 +136,10 @@
<Project>{9E61CC56-43F9-489C-AAAF-BD5EC69367C4}</Project>
<Name>Models.App</Name>
</ProjectReference>
<ProjectReference Include="..\..\Models\Models.BiliBili\Models.BiliBili.csproj">
<Project>{8776A0BD-DBD1-4F11-A022-400D044FF618}</Project>
<Name>Models.BiliBili</Name>
</ProjectReference>
<ProjectReference Include="..\..\Models\Models.Enums\Models.Enums.csproj">
<Project>{88314412-B020-415B-AEAB-57ADC43B273E}</Project>
<Name>Models.Enums</Name>
......
// Copyright (c) Richasy. All rights reserved.
using Richasy.Bili.Lib.Interfaces;
namespace Richasy.Bili.Lib.Uwp
{
/// <summary>
/// 提供已登录用户相关的数据操作.
/// </summary>
public partial class AccountProvider
{
private readonly IHttpProvider _httpProvider;
private int _mid;
}
}
// Copyright (c) Richasy. All rights reserved.
using System.Net.Http;
using System.Threading.Tasks;
using Richasy.Bili.Lib.Interfaces;
using Richasy.Bili.Models.BiliBili;
using static Richasy.Bili.Models.App.Constants.ServiceConstants;
namespace Richasy.Bili.Lib.Uwp
{
/// <summary>
/// 提供已登录用户的数据操作.
/// </summary>
public partial class AccountProvider : IAccountProvider
{
/// <summary>
/// Initializes a new instance of the <see cref="AccountProvider"/> class.
/// </summary>
/// <param name="httpProvider">网络操作工具.</param>
public AccountProvider(IHttpProvider httpProvider)
{
_httpProvider = httpProvider;
}
/// <inheritdoc/>
public async Task<MyInfo> GetMyInformationAsync()
{
var request = await _httpProvider.GetRequestMessageAsync(HttpMethod.Get, Api.Account.MyInfo, type: Models.Enums.RequestClientType.IOS);
var response = await _httpProvider.SendAsync(request);
var result = await _httpProvider.ParseAsync<ServerResponse<MyInfo>>(response);
_mid = result.Data.Mid;
return result.Data;
}
}
}
......@@ -76,7 +76,7 @@
x:Name="QRLoginContainer"
Grid.Row="2"
Margin="0,8,0,0"
RowSpacing="12">
RowSpacing="4">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
......
......@@ -330,6 +330,7 @@ namespace Richasy.Bili.Lib.Uwp
ApplicationData.Current.LocalSettings.Values[Settings.AuthResultKey] = compositeValue;
_lastAuthorizeTime = saveTime;
_tokenInfo = result;
State = AuthorizeState.SignedIn;
}
}
......
......@@ -118,7 +118,7 @@ namespace Richasy.Bili.Lib.Uwp
}
/// <inheritdoc/>
public async Task<string> GetTokenAsync(bool silentOnly = false)
public async Task<string> GetTokenAsync()
{
var internetConnectionProfile = NetworkInformation.GetInternetConnectionProfile();
if (internetConnectionProfile == null)
......@@ -131,7 +131,7 @@ namespace Richasy.Bili.Lib.Uwp
{
if (_tokenInfo != null)
{
if (await IsTokenValidAsync() && !silentOnly)
if (await IsTokenValidAsync())
{
return _tokenInfo.AccessToken;
}
......@@ -195,32 +195,6 @@ namespace Richasy.Bili.Lib.Uwp
return Task.CompletedTask;
}
/// <inheritdoc/>
public async Task<bool> TrySilentSignInAsync()
{
if (await IsTokenValidAsync(true))
{
if (State != AuthorizeState.SignedIn)
{
State = AuthorizeState.SignedIn;
}
return true;
}
State = AuthorizeState.Loading;
var token = await GetTokenAsync(true);
if (token == null)
{
State = AuthorizeState.SignedOut;
return false;
}
return true;
}
/// <inheritdoc/>
public async Task<bool> IsTokenValidAsync(bool isNetworkVerify = false)
{
......
......@@ -9,6 +9,8 @@
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
</PropertyGroup>
<ItemGroup>
<Compile Include="AccountProvider\AccountProvider.cs" />
<Compile Include="AccountProvider\AccountProvider.Extension.cs" />
<Compile Include="AuthorizeProvider\AccountLoginDialog.xaml.cs">
<DependentUpon>AccountLoginDialog.xaml</DependentUpon>
</Compile>
......
......@@ -113,6 +113,14 @@ namespace Richasy.Bili.Models.App.Constants
/// </summary>
public const string QRCodeCheck = _passBase + "/x/passport-tv-login/qrcode/poll";
}
public static class Account
{
/// <summary>
/// 我的信息.
/// </summary>
public const string MyInfo = _appBase + "/x/v2/account/myinfo";
}
}
#pragma warning restore SA1401 // Fields should be private
#pragma warning restore SA1600 // Elements should be documented
......
using System.Collections.Generic;
// Copyright (c) Richasy. All rights reserved.
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Richasy.Bili.Models.BiliBili
......
// Copyright (c) Richasy. All rights reserved.
using Newtonsoft.Json;
namespace Richasy.Bili.Models.BiliBili
{
/// <summary>
/// 我的信息.
/// </summary>
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class MyInfo
{
/// <summary>
/// 用户ID.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "mid", Required = Required.Default)]
public int Mid { get; set; }
/// <summary>
/// 用户名.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "name", Required = Required.Default)]
public string Name { get; set; }
/// <summary>
/// 用户签名.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "sign", Required = Required.Default)]
public string Sign { get; set; }
/// <summary>
/// 硬币数.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "coins", Required = Required.Default)]
public double Coins { get; set; }
/// <summary>
/// 生日,格式为YYYY-MM-DD.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "birthday", Required = Required.Default)]
public string Birthday { get; set; }
/// <summary>
/// 头像.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "face", Required = Required.Default)]
public string Avatar { get; set; }
/// <summary>
/// 性别,0-保密,1-男性,2-女性.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "sex", Required = Required.Default)]
public int Sex { get; set; }
/// <summary>
/// 账户等级.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "level", Required = Required.Default)]
public int Level { get; set; }
/// <summary>
/// 封禁状态,0-正常,1-被封.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "silence", Required = Required.Default)]
public int IsBlocking { get; set; }
/// <summary>
/// 大会员信息.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "vip", Required = Required.Default)]
public Vip VIP { get; set; }
}
/// <summary>
/// 大会员信息.
/// </summary>
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class Vip
{
/// <summary>
/// 大会员类型,0-非会员,1-月度大会员,2-年度及以上大会员.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "type", Required = Required.Default)]
public int Type { get; set; }
/// <summary>
/// 会员状态,0-无,1-有.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "status", Required = Required.Default)]
public int Status { get; set; }
/// <summary>
/// 会员过期时间(毫秒Unix时间戳).
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "due_date", Required = Required.Default)]
public long DueDate { get; set; }
}
}
// Copyright (c) Richasy. All rights reserved.
using System;
using ReactiveUI.Fody.Helpers;
using Richasy.Bili.Controller.Uwp;
namespace Richasy.Bili.ViewModels.Uwp
{
/// <summary>
/// 当前用户视图模型状态.
/// </summary>
public enum AccountViewModelStatus
{
/// <summary>
/// 用户已登出.
/// </summary>
Logout,
/// <summary>
/// 用户已登录.
/// </summary>
Login,
/// <summary>
/// 用户正在登录.
/// </summary>
Logging,
}
/// <summary>
/// 用户试图模型属性集.
/// </summary>
public partial class AccountViewModel
{
private readonly BiliController _controller;
/// <summary>
/// <see cref="AccountViewModel"/>的实例.
/// </summary>
public static AccountViewModel Instance { get; } = new Lazy<AccountViewModel>(() => new AccountViewModel()).Value;
/// <summary>
/// 当前视图模型状态.
/// </summary>
[Reactive]
public AccountViewModelStatus Status { get; set; }
/// <summary>
/// 头像.
/// </summary>
[Reactive]
public string Avatar { get; set; }
/// <summary>
/// 显示名称.
/// </summary>
[Reactive]
public string DisplayName { get; set; }
}
}
// Copyright (c) Richasy. All rights reserved.
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Richasy.Bili.Controller.Uwp;
using Richasy.Bili.Models.BiliBili;
namespace Richasy.Bili.ViewModels.Uwp
{
/// <summary>
/// 用户视图模型.
/// </summary>
public partial class AccountViewModel : ViewModelBase
{
/// <summary>
/// Initializes a new instance of the <see cref="AccountViewModel"/> class.
/// </summary>
internal AccountViewModel()
{
_controller = BiliController.Instance;
_controller.Logged += OnLoggedAsync;
_controller.LoggedFailed += OnLoggedFailed;
_controller.LoggedOut += OnLoggedOut;
_controller.AccountChanged += OnAccountChanged;
Status = AccountViewModelStatus.Logout;
}
/// <summary>
/// 尝试登录.
/// </summary>
/// <param name="isSlientOnly">是否只进行静默登录.</param>
/// <returns><see cref="Task"/>.</returns>
public async Task TrySignInAsync(bool isSlientOnly = false)
{
this.Status = AccountViewModelStatus.Logging;
await _controller.TrySignInAsync(isSlientOnly);
}
/// <summary>
/// 获取我的账户资料.
/// </summary>
/// <returns><see cref="Task"/>.</returns>
public async Task GetMyProfileAsync()
{
try
{
await _controller.GetMyProfileAsync();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
Status = AccountViewModelStatus.Logout;
}
}
private void OnLoggedOut(object sender, EventArgs e)
{
this.Status = AccountViewModelStatus.Logout;
}
private void OnLoggedFailed(object sender, Exception e)
{
Debug.WriteLine($"Login failed: {e.Message}");
// 它仅在用户未登录时触发.
if (this.Status != AccountViewModelStatus.Login)
{
this.Status = AccountViewModelStatus.Logout;
}
}
private async void OnLoggedAsync(object sender, EventArgs e)
{
if (this.Status != AccountViewModelStatus.Login)
{
this.Status = AccountViewModelStatus.Login;
await GetMyProfileAsync();
}
}
private void OnAccountChanged(object sender, MyInfo e)
{
if (e != null)
{
Avatar = e.Avatar;
DisplayName = e.Name;
}
}
}
}
// Copyright (c) Richasy. All rights reserved.
using System;
using Richasy.Bili.Lib.Interfaces;
namespace Richasy.Bili.ViewModels.Uwp
{
/// <summary>
/// 用户试图模型属性集.
/// </summary>
public partial class UserViewModel
{
private readonly IAuthorizeProvider _authorizeProvider;
/// <summary>
/// <see cref="UserViewModel"/>的实例.
/// </summary>
public static UserViewModel Instance { get; } = new Lazy<UserViewModel>(() => new UserViewModel()).Value;
}
}
// Copyright (c) Richasy. All rights reserved.
using System.Threading.Tasks;
using Richasy.Bili.Locator.Uwp;
namespace Richasy.Bili.ViewModels.Uwp
{
/// <summary>
/// 用户视图模型.
/// </summary>
public partial class UserViewModel : ViewModelBase
{
/// <summary>
/// Initializes a new instance of the <see cref="UserViewModel"/> class.
/// </summary>
internal UserViewModel()
{
ServiceLocator.Instance.LoadService(out _authorizeProvider);
}
/// <summary>
/// 登录账户.
/// </summary>
/// <returns><see cref="Task"/>.</returns>
public async Task SignInAsync()
{
if (await _authorizeProvider.IsTokenValidAsync())
{
await _authorizeProvider.TrySilentSignInAsync();
}
else
{
await _authorizeProvider.SignInAsync();
}
}
}
}
......@@ -17,8 +17,8 @@
<Compile Include="PartitionViewModel\PartitionViewModel.Properties.cs" />
<Compile Include="SubPartitionViewModel\SubPartitionViewModel.cs" />
<Compile Include="SubPartitionViewModel\SubPartitionViewModel.Properties.cs" />
<Compile Include="UserViewModel.cs" />
<Compile Include="UserViewModel.Properties.cs" />
<Compile Include="AccountViewModel.cs" />
<Compile Include="AccountViewModel.Properties.cs" />
<Compile Include="VideoViewModel\VideoViewModel.cs" />
<Compile Include="VideoViewModel\VideoViewModel.Properties.cs" />
<Compile Include="ViewModelBase.cs" />
......@@ -43,6 +43,10 @@
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Controller\Controller.Uwp\Controller.Uwp.csproj">
<Project>{5213e830-43f5-491e-a8ef-d75083578ef3}</Project>
<Name>Controller.Uwp</Name>
</ProjectReference>
<ProjectReference Include="..\..\Lib\Lib.Interfaces\Lib.Interfaces.csproj">
<Project>{cae9b987-6db2-42a8-b3f2-3bb700cc4dd8}</Project>
<Name>Lib.Interfaces</Name>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册