Skip to content

Commit

Permalink
添加微软登录
Browse files Browse the repository at this point in the history
  • Loading branch information
YangSpring114 committed Nov 26, 2024
1 parent 7122faf commit 0d0dfe0
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 6 deletions.
3 changes: 2 additions & 1 deletion WonderLab/App.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ private static IHost ConfigureIoC(out IHost host) {
builder.Services.AddSingleton<MainWindowViewModel>();

//Configure Dialog
builder.Services.AddTransient<OfflineAuthDialogViewMdoel>();
builder.Services.AddTransient<OfflineAuthDialogViewModel>();
builder.Services.AddTransient<MicrosoftAuthDialogViewModel>();
builder.Services.AddTransient<YggdrasilAuthDialogViewModel>();
builder.Services.AddTransient<ChooseAccountTypeDialogViewModel>();

Expand Down
19 changes: 19 additions & 0 deletions WonderLab/Services/Account/AccountService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace WonderLab.Services.Accounts;

public sealed class AccountService {
private const string CLIENT_ID = "9fd44410-8ed7-4eb3-a160-9f1cc62c824c";

private readonly ConfigService _configService;
private readonly MicrosoftAuthenticator _microsoftAuthenticator;

private ObservableCollection<Account> _accounts;

public event EventHandler CollectionChanged;
Expand All @@ -18,6 +23,7 @@ public sealed class AccountService {

public AccountService(ConfigService configService) {
_configService = configService;
_microsoftAuthenticator = new(CLIENT_ID);
}

public void Initialize() {
Expand Down Expand Up @@ -46,4 +52,17 @@ public async ValueTask<IEnumerable<YggdrasilAccount>> CreateYggdrasilAccounts(st
CollectionChanged?.Invoke(this, EventArgs.Empty);
return accounts;
}

public async Task<MicrosoftAccount> CreateMicrosoftAccount(Action<DeviceCodeResponse> action, CancellationTokenSource cancellationTokenSource) {
var resultOAuth2 = await _microsoftAuthenticator
.DeviceFlowAuthAsync(action, cancellationTokenSource);

var account = await _microsoftAuthenticator.AuthenticateAsync();

_accounts.Add(account);
_configService.Entries.Accounts.Add(account);

CollectionChanged?.Invoke(this, EventArgs.Empty);
return account;
}
}
74 changes: 74 additions & 0 deletions WonderLab/ViewModels/Dialog/Auth/MicrosoftAuthDialogViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Notifications;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using DialogHostAvalonia;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using WonderLab.Infrastructure.Models.Messaging;
using WonderLab.Services.Accounts;

namespace WonderLab.ViewModels.Dialog.Auth;

public sealed partial class MicrosoftAuthDialogViewModel : ObservableObject {
private readonly AccountService _accountService;

private CancellationTokenSource _cancellationTokenSource;

Check warning on line 20 in WonderLab/ViewModels/Dialog/Auth/MicrosoftAuthDialogViewModel.cs

View workflow job for this annotation

GitHub Actions / test_build

Field 'MicrosoftAuthDialogViewModel._cancellationTokenSource' is never assigned to, and will always have its default value null

Check warning on line 20 in WonderLab/ViewModels/Dialog/Auth/MicrosoftAuthDialogViewModel.cs

View workflow job for this annotation

GitHub Actions / build_MacOS

Field 'MicrosoftAuthDialogViewModel._cancellationTokenSource' is never assigned to, and will always have its default value null

Check warning on line 20 in WonderLab/ViewModels/Dialog/Auth/MicrosoftAuthDialogViewModel.cs

View workflow job for this annotation

GitHub Actions / build_Windows

Field 'MicrosoftAuthDialogViewModel._cancellationTokenSource' is never assigned to, and will always have its default value null

Check warning on line 20 in WonderLab/ViewModels/Dialog/Auth/MicrosoftAuthDialogViewModel.cs

View workflow job for this annotation

GitHub Actions / build_Linux (x64)

Field 'MicrosoftAuthDialogViewModel._cancellationTokenSource' is never assigned to, and will always have its default value null

Check warning on line 20 in WonderLab/ViewModels/Dialog/Auth/MicrosoftAuthDialogViewModel.cs

View workflow job for this annotation

GitHub Actions / build_Linux (arm)

Field 'MicrosoftAuthDialogViewModel._cancellationTokenSource' is never assigned to, and will always have its default value null

Check warning on line 20 in WonderLab/ViewModels/Dialog/Auth/MicrosoftAuthDialogViewModel.cs

View workflow job for this annotation

GitHub Actions / build_Linux (arm64)

Field 'MicrosoftAuthDialogViewModel._cancellationTokenSource' is never assigned to, and will always have its default value null

[ObservableProperty] private string _verificationUrl;

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsCodeLoaded))]
private string _userCode;

public bool IsCodeLoaded => !string.IsNullOrEmpty(UserCode);

public MicrosoftAuthDialogViewModel(AccountService accountService) {
_accountService = accountService;
}

private bool CanCopy() => !string.IsNullOrEmpty(UserCode);

[RelayCommand]
private Task OnLoaded() => Task.Run(async () => {
var account = await _accountService.CreateMicrosoftAccount(x => {
UserCode = x.UserCode;
VerificationUrl = x.VerificationUrl;
}, _cancellationTokenSource);

Close();
WeakReferenceMessenger.Default.Send(new NotificationMessage($"已将微软账户\"{account.Name}\"添加至 WonderLab!", NotificationType.Warning));
});

[RelayCommand]
private void Close() => Dispatcher.UIThread.InvokeAsync(() => {
DialogHost.Close("PART_DialogHost");

try {
_cancellationTokenSource.Cancel();
} catch (System.Exception) {}
});

[RelayCommand]
private void OpenUrl() => Dispatcher.UIThread.InvokeAsync(() => {
Process.Start(new ProcessStartInfo(VerificationUrl) {
UseShellExecute = true,
Verb = "open"
}).Dispose();
});

[RelayCommand]
private void CopyCode() => Dispatcher.UIThread.InvokeAsync(async () => {
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) {
var clipboard = desktop.MainWindow?.Clipboard;

if (clipboard != null) {
await clipboard.SetTextAsync(UserCode);
}
}
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@

namespace WonderLab.ViewModels.Dialog.Auth;

public sealed partial class OfflineAuthDialogViewMdoel : ObservableObject {
public sealed partial class OfflineAuthDialogViewModel : ObservableObject {
private readonly AccountService _accountService;

[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(SaveCommand))]
private string _userName;

public OfflineAuthDialogViewMdoel(AccountService accountService) {
public OfflineAuthDialogViewModel(AccountService accountService) {
_accountService = accountService;
}

Expand Down
13 changes: 12 additions & 1 deletion WonderLab/ViewModels/Dialog/ChooseAccountTypeDialogViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ private Task GoToOfflineAuth() => Dispatcher.UIThread.InvokeAsync(async () => {
Close();

OfflineAuthDialog dialog = new() {
DataContext = App.Get<OfflineAuthDialogViewMdoel>()
DataContext = App.Get<OfflineAuthDialogViewModel>()
};

await DialogHost.Show(dialog, "PART_DialogHost");
Expand All @@ -35,4 +35,15 @@ private Task GoToYggdrasilAuth() => Dispatcher.UIThread.InvokeAsync(async () =>

await DialogHost.Show(dialog, "PART_DialogHost");
});

[RelayCommand]
private Task GoToMicrosoftAuth() => Dispatcher.UIThread.InvokeAsync(async () => {
Close();

MicrosoftAuthDialog dialog = new() {
DataContext = App.Get<MicrosoftAuthDialogViewModel>()
};

await DialogHost.Show(dialog, "PART_DialogHost");
});
}
1 change: 0 additions & 1 deletion WonderLab/ViewModels/Page/HomePageViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
using WonderLab.Assets.Language;
using WonderLab.Infrastructure.Models.Launch;
using WonderLab.Infrastructure.Models.Messaging;
using WonderLab.Services;
Expand Down
76 changes: 76 additions & 0 deletions WonderLab/Views/Dialog/Auth/MicrosoftAuthDialog.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<UserControl xmlns="https://github.com/avaloniaui"
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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
xmlns:wc="using:WonderLab.Controls"
x:Class="WonderLab.Views.Dialog.Auth.MicrosoftAuthDialog">
<Interaction.Behaviors>
<EventTriggerBehavior EventName="Loaded">
<InvokeCommandAction Command="{Binding LoadedCommand}"/>
</EventTriggerBehavior>
</Interaction.Behaviors>

<Grid Margin="16 24 16 0"
RowDefinitions="Auto, Auto, Auto">
<TextBlock Classes="Subtitle"
Text="创建新账户"/>

<wc:ProgressRing Width="45"
Height="45"
Grid.Row="1"
Margin="0 16 0 24"
BorderThickness="3"
IsIndeterminate="True"
Background="Transparent"
VerticalAlignment="Center"
HorizontalAlignment="Center"
IsVisible="{Binding IsCodeLoaded, Converter={StaticResource BooleanReverseConverter}}"/>

<StackPanel Grid.Row="1"
Spacing="8"
Margin="0 16 0 24"
HorizontalAlignment="Center"
IsVisible="{Binding IsCodeLoaded}">
<TextBlock Classes="Body"
Text="使用一次性代码"
TextAlignment="Center"/>

<Button HorizontalAlignment="Center"
Command="{Binding CopyCodeCommand}">
<TextBlock Classes="BodyStrong"
TextAlignment="Center"
Text="{Binding UserCode}"/>
</Button>

<TextBlock Classes="Body"
Text="访问验证网址以继续下一步验证操作"
TextAlignment="Center"/>

<TextBlock Classes="Caption"
Text="网址访问速度可能比较慢,但请务必在规定的时间内完成验证操作,否则此验证码将失效!"
TextAlignment="Center"/>

<ProgressBar IsIndeterminate="True"/>
</StackPanel>

<Border Grid.Row="2"
Padding="16"
Margin="-16 0"
CornerRadius="0 0 12 12"
BorderThickness="0 1.5 0 0"
Background="{DynamicResource ContentColor2}"
BorderBrush="{DynamicResource ContentColor3}">
<Grid ColumnDefinitions="Auto, 1*, Auto">
<Button Grid.Column="0"
Content="打开验证网页"
ToolTip.Tip="{Binding UserCode}"
Command="{Binding OpenUrlCommand}"/>

<Button Grid.Column="2"
Content="取消"
Command="{Binding CloseCommand}"/>
</Grid>
</Border>
</Grid>
</UserControl>
9 changes: 9 additions & 0 deletions WonderLab/Views/Dialog/Auth/MicrosoftAuthDialog.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Avalonia.Controls;

namespace WonderLab.Views.Dialog.Auth;

public partial class MicrosoftAuthDialog : UserControl {
public MicrosoftAuthDialog() {
InitializeComponent();
}
}
3 changes: 2 additions & 1 deletion WonderLab/Views/Dialog/ChooseAccountTypeDialog.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
Command="{Binding GoToYggdrasilAuthCommand}"/>

<Button Grid.Column="2"
Content="微软"/>
Content="微软"
Command="{Binding GoToMicrosoftAuthCommand}"/>

<Button Grid.Column="4"
Content="关闭"
Expand Down

0 comments on commit 0d0dfe0

Please sign in to comment.