提交 c4c5ed2b 编写于 作者: R RMBGAME

🚧 EditAppInfo

上级 4e1249cb
...@@ -327,11 +327,11 @@ ...@@ -327,11 +327,11 @@
"InterfaceFilePath": "..\\src\\BD.WTTS.Client.Plugins.GameAccount\\Settings\\Abstractions", "InterfaceFilePath": "..\\src\\BD.WTTS.Client.Plugins.GameAccount\\Settings\\Abstractions",
"Properties": [ "Properties": [
{ {
"TypeName": "ConcurrentDictionary<long, string?>", "TypeName": "ConcurrentDictionary<string, string?>",
"PropertyName": "AccountRemarks", "PropertyName": "AccountRemarks",
"DefaultValue": "null", "DefaultValue": "new()",
"DefaultValueIsConst": true, "DefaultValueIsConst": false,
"Summary": "Steam 账号备注字典", "Summary": "账号备注字典",
"IsRegionOrEndregion": null, "IsRegionOrEndregion": null,
"IsValueType": null "IsValueType": null
}, },
......
Subproject commit b3fb184c76a72236f61b67f4a54121f4318905df Subproject commit 74cbe2535ce8ffada2781e0b1c7032c6f3d56637
...@@ -103,7 +103,7 @@ sealed class AvaloniaWindowManagerImpl : IWindowManagerImpl ...@@ -103,7 +103,7 @@ sealed class AvaloniaWindowManagerImpl : IWindowManagerImpl
object? pageContent = null, object? pageContent = null,
string? okButtonText = null, string? okButtonText = null,
Func<bool>? cancelCloseAction = null) Func<bool>? cancelCloseAction = null)
where TPageViewModel : ViewModelBase, new() where TPageViewModel : ViewModelBase
{ {
var td = new TaskDialogEx var td = new TaskDialogEx
{ {
......
...@@ -52,6 +52,7 @@ ...@@ -52,6 +52,7 @@
<StyleInclude Source="/UI/Styling/Controls/TabStrip.axaml" /> <StyleInclude Source="/UI/Styling/Controls/TabStrip.axaml" />
<StyleInclude Source="/UI/Styling/Controls/AutoCompleteBox.axaml" /> <StyleInclude Source="/UI/Styling/Controls/AutoCompleteBox.axaml" />
<StyleInclude Source="/UI/Styling/Controls/IconElement.axaml" /> <StyleInclude Source="/UI/Styling/Controls/IconElement.axaml" />
<StyleInclude Source="/UI/Styling/Controls/CustomFilePicker.axaml" />
<!--<Style Selector=":is(Window):transparent"> <!--<Style Selector=":is(Window):transparent">
<Setter Property="Background" Value="{DynamicResource SolidBackgroundFillColorBaseBrush}"> <Setter Property="Background" Value="{DynamicResource SolidBackgroundFillColorBaseBrush}">
......
<Styles
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="https://steampp.net/services"
xmlns:spp="https://steampp.net/ui"
xmlns:ui="using:FluentAvalonia.UI.Controls">
<Design.PreviewWith>
<Border Padding="40">
<StackPanel Spacing="10">
<spp:CustomFilePicker>
<Image
Width="150"
Height="150"
Source="avares://BD.WTTS.Client.Avalonia/UI/Assets/ApplicationIcon.ico"
Stretch="UniformToFill" />
</spp:CustomFilePicker>
</StackPanel>
</Border>
</Design.PreviewWith>
<Style Selector="spp|CustomFilePicker">
<Setter Property="Background" Value="Transparent" />
<Setter Property="DragDrop.AllowDrop" Value="True" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="MinHeight" Value="65" />
<Setter Property="Template">
<ControlTemplate>
<Border
BorderBrush="{DynamicResource ButtonBorderBrush}"
BorderThickness="1"
ClipToBounds="True"
CornerRadius="5">
<Panel>
<ContentPresenter
Name="ContentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}" />
<Panel
Name="FilePickerTipBorder"
MaxWidth="{ReflectionBinding $parent.Bounds.Width}"
MaxHeight="{ReflectionBinding $parent.Bounds.Height}">
<Border Background="{DynamicResource CardBackgroundFillColorDefaultBrush}">
<StackPanel
Margin="10,5"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<ui:SymbolIcon FontSize="30" Symbol="Download" />
<TextBlock
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
Text="{ReflectionBinding Path=Res.CustomFilePicker_Tip,
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}"
TextAlignment="Center"
Theme="{StaticResource BodyStrongTextBlockStyle}" />
</StackPanel>
</Border>
<Rectangle
Margin="5"
RadiusX="2"
RadiusY="2"
Stroke="{DynamicResource CircleElevationBorderBrush}"
StrokeDashArray="5,5"
StrokeThickness="1.5" />
</Panel>
</Panel>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="spp|CustomFilePicker /template/ Panel#FilePickerTipBorder">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="spp|CustomFilePicker:pointerover /template/ Panel#FilePickerTipBorder">
<Setter Property="IsVisible" Value="True" />
</Style>
</Styles>
\ No newline at end of file
<Styles <Styles
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="https://steampp.net/services"> xmlns:s="https://steampp.net/services"
xmlns:ui="using:FluentAvalonia.UI.Controls">
<Design.PreviewWith> <Design.PreviewWith>
<Border Padding="20"> <Border Padding="20">
<StackPanel Spacing="5"> <StackPanel Spacing="5">
...@@ -56,6 +57,7 @@ ...@@ -56,6 +57,7 @@
InputGesture="Ctrl + A" InputGesture="Ctrl + A"
IsEnabled="{Binding $parent[TextBox].Text, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" /> IsEnabled="{Binding $parent[TextBox].Text, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
</MenuFlyout> </MenuFlyout>
</Styles.Resources> </Styles.Resources>
<Style Selector="PathIcon.TextBoxIcon"> <Style Selector="PathIcon.TextBoxIcon">
...@@ -94,6 +96,24 @@ ...@@ -94,6 +96,24 @@
</Setter> </Setter>
</Style> </Style>
<Style Selector="TextBox.clearButton[AcceptsReturn=False][IsReadOnly=False]:focus:not(TextBox:empty)">
<Setter Property="InnerRightContent">
<Template>
<Button
Width="30"
Padding="{StaticResource HelperButtonThemePadding}"
VerticalAlignment="Stretch"
BorderThickness="{TemplateBinding BorderThickness}"
Command="{Binding $parent[TextBox].Clear}"
CornerRadius="{DynamicResource ControlCornerRadius}"
Focusable="False"
FontSize="{TemplateBinding FontSize}"
IsTabStop="False"
Theme="{StaticResource TextBoxDeleteButtonStyle}" />
</Template>
</Setter>
</Style>
<Style Selector="TextBox.readonlybox"> <Style Selector="TextBox.readonlybox">
<Setter Property="Focusable" Value="False" /> <Setter Property="Focusable" Value="False" />
......
...@@ -59,6 +59,12 @@ ...@@ -59,6 +59,12 @@
<StreamGeometry x:Key="EADesktop"> <StreamGeometry x:Key="EADesktop">
M709.76 262.912l-252.928 400.085333H180.906667l64.341333-98.133333h171.690667l62.890666-99.626667H96.597333L33.706667 564.864h91.989333L0 761.173333h515.072l194.688-309.717333 70.485333 113.493333h-59.776l-62.848 98.090667h185.472l62.848 98.133333H1024z m-489.002667 4.565333L157.866667 367.104l406.186666-1.493333 62.890667-98.133334z M709.76 262.912l-252.928 400.085333H180.906667l64.341333-98.133333h171.690667l62.890666-99.626667H96.597333L33.706667 564.864h91.989333L0 761.173333h515.072l194.688-309.717333 70.485333 113.493333h-59.776l-62.848 98.090667h185.472l62.848 98.133333H1024z m-489.002667 4.565333L157.866667 367.104l406.186666-1.493333 62.890667-98.133334z
</StreamGeometry> </StreamGeometry>
<StreamGeometry x:Key="Rockstar">
M887.466667 0A136.533333 136.533333 0 0 1 1024 136.533333v750.933334a136.533333 136.533333 0 0 1-136.533333 136.533333H136.533333A136.533333 136.533333 0 0 1 0 887.466667V136.533333A136.533333 136.533333 0 0 1 136.533333 0z m-363.690667 163.242667l-11.093333 0.597333H284.672L187.733333 630.784h121.514667L341.333333 461.482667h83.285334c40.277333 0 60.074667 20.48 60.074666 62.805333 0 20.48-3.413333 47.104-3.413333 79.872-1.28 13.056 1.578667 26.197333 8.192 37.546667l88.064 94.890666-75.093333 165.888 161.109333-97.621333 120.149333 93.525333-22.528-155.648 137.898667-100.352h-153.6l-23.893333-157.013333L631.466667 641.706667h-17.066667a121.514667 121.514667 0 0 1-11.605333-61.44v-86.698667c0-43.690667-15.701333-69.632-47.104-77.824l8.618666-0.896a115.370667 115.370667 0 0 0 97.877334-129.493333 109.226667 109.226667 0 0 0-43.008-98.986667 190.464 190.464 0 0 0-106.496-22.528z m182.101333 410.197333l15.018667 97.621333h87.381333l-78.506666 55.978667 13.653333 98.304-75.093333-59.392-96.256 57.344 44.373333-96.256-53.930667-57.344h88.064l55.296-96.256zM477.866667 261.461333c43.008 0 68.266667 6.144 68.266666 48.469334 0 42.325333-30.72 64.170667-84.650666 64.170666H364.544l23.210667-112.64z
</StreamGeometry>
<StreamGeometry x:Key="BattleNet">
M850.496 458.88s73.76 3.744 73.76-39.616c0-56.64-98.24-107.648-98.24-107.648s15.36-32.64 24.96-50.88c9.664-18.24 36.64-89.248 39.04-105.472 2.976-20.512-1.632-26.88-1.632-26.88-6.624 43.744-77.888 169.728-83.648 173.984-69.472-32.48-164.992-41.6-164.992-41.6S546.24 64 458.24 64c-87.232 0-86.72 168.512-86.72 168.512s-24.64-47.776-55.648-47.776c-45.248 0-60.128 68.256-60.128 142.4-89.248 0-164.384 20-171.104 21.856-6.656 1.888-27.776 17.28-18.24 15.36 19.584-6.24 111.36-20.48 191.616-13.472 4.48 70.4 45.6 162.112 45.6 162.112s-88.256 127.744-88.256 218.88c0 24 10.528 67.872 73.76 67.872 53.12 0 112.768-31.872 123.872-38.24-9.728 13.856-16.992 40.48-16.992 52.736 0 10.016 6.016 38.4 46.88 38.4 52.48 0 111.232-40.256 111.232-40.256s55.52 92 102.88 134.112c12.768 11.392 25.024 13.504 25.024 13.504s-47.136-45.248-109.12-162.016c57.6-35.488 117.6-119.488 117.6-119.488s7.136 0.256 61.888 0.256c85.76 0 207.488-18.016 207.488-86.112 0.128-70.272-109.376-133.76-109.376-133.76z m9.504-42.368c0 24.864-23.616 24.64-23.616 24.64l-18.016 1.088s-34.112-17.856-54.88-26.368c0 0 32.128-49.376 39.648-63.136 5.6 3.392 56.864 35.392 56.864 63.776zM501.12 163.136c40.384 0 97.888 94.976 97.888 94.976s-89.76-8-163.648 35.392c2.016-68.384 25.024-130.368 65.76-130.368z m-159.616 76.992c12.736 0 25.248 15.616 30.496 28.736 0 8.768 4.48 59.776 4.48 59.776l-73.984-2.88c0-66.624 26.144-85.632 39.008-85.632z m-7.744 463.136c-40.512 0-48.768-22.528-48.768-42.752 0-45.888 36.64-110.144 36.64-110.144s41.12 86.4 112.736 122.752c-35.52 20.864-64.864 30.144-100.608 30.144z m131.36 90.368c-28.384 0-31.872-18.368-31.872-22.624 0-13.12 10.368-28.768 10.368-28.768s47.648-32.128 50.624-35.616l35.264 65.76s-36 21.248-64.384 21.248z m88.512-35.744c-17.28-30.144-30.016-61.632-30.016-61.632s70.88 4.48 109.024-34.752c-23.776 10.624-61.632 24.128-105.632 20 92-80.992 145.728-139.744 191.104-200.384-3.84-4.736-24.64-19.232-29.76-21.632-27.36 33.024-133.984 146.88-232.736 203.264-124.992-68.128-151.232-268.48-153.856-310.112l68.256 6.496s-25.632 45.504-25.632 78.976c0 33.376 4 35.136 4 35.136s-0.896-58.24 35.104-103.232c27.52 145.984 56.128 220.736 78.4 265.344 11.36-4.736 32.48-14.08 32.48-14.08s-63.008-181.664-59.52-304.544c28.64-15.232 71.04-30.976 118.784-30.976 125.76 0 226.88 53.984 226.88 53.984l-39.52 55.264s-35.232-63.744-85.12-75.136c26.24 19.52 55.744 45.376 71.008 82.496-104.256-40.64-230.016-62.112-270.4-66.88-3.488 14.88-2.976 36.16-2.976 36.16s168.64 31.104 291.36 101.216c-0.864 153.504-168.128 271.36-191.232 285.024z m159.744-114.752s52.384-68.64 51.52-159.648c0 0 84.608 52.384 84.608 103.52 0 56.992-136.128 56.128-136.128 56.128z
</StreamGeometry>
<StreamGeometry x:Key="QQ"> <StreamGeometry x:Key="QQ">
M909.937778 664.803556c-18.432-110.876444-95.829333-183.523556-95.829333-183.523556 11.064889-100.664889-29.496889-118.528-29.496889-118.528C776.106667 51.313778 517.432889 56.775111 512 56.917333c-5.432889-0.142222-264.135111-5.603556-272.611556 305.863111 0 0-40.561778 17.863111-29.496889 118.528 0 0-77.397333 72.647111-95.829333 183.523556 0 0-9.841778 187.335111 88.462222 22.954667 0 0 22.129778 62.435556 62.663111 118.528 0 0-72.504889 25.486222-66.332444 91.761778 0 0-2.474667 73.898667 154.823111 68.835556 0 0 110.563556-8.903111 143.758222-57.344l29.240889 0c33.166222 48.440889 143.758222 57.344 143.758222 57.344 157.240889 5.091556 154.794667-68.835556 154.794667-68.835556 6.115556-66.247111-66.332444-91.761778-66.332444-91.761778 40.533333-56.092444 62.663111-118.528 62.663111-118.528C919.751111 852.167111 909.937778 664.803556 909.937778 664.803556L909.937778 664.803556zM909.937778 664.803556 M909.937778 664.803556c-18.432-110.876444-95.829333-183.523556-95.829333-183.523556 11.064889-100.664889-29.496889-118.528-29.496889-118.528C776.106667 51.313778 517.432889 56.775111 512 56.917333c-5.432889-0.142222-264.135111-5.603556-272.611556 305.863111 0 0-40.561778 17.863111-29.496889 118.528 0 0-77.397333 72.647111-95.829333 183.523556 0 0-9.841778 187.335111 88.462222 22.954667 0 0 22.129778 62.435556 62.663111 118.528 0 0-72.504889 25.486222-66.332444 91.761778 0 0-2.474667 73.898667 154.823111 68.835556 0 0 110.563556-8.903111 143.758222-57.344l29.240889 0c33.166222 48.440889 143.758222 57.344 143.758222 57.344 157.240889 5.091556 154.794667-68.835556 154.794667-68.835556 6.115556-66.247111-66.332444-91.761778-66.332444-91.761778 40.533333-56.092444 62.663111-118.528 62.663111-118.528C919.751111 852.167111 909.937778 664.803556 909.937778 664.803556L909.937778 664.803556zM909.937778 664.803556
</StreamGeometry> </StreamGeometry>
......
using Avalonia.Input;
namespace BD.WTTS.UI.Views.Controls;
public sealed class CustomFilePicker : ContentControl
{
public static readonly StyledProperty<string?> FileNameProperty = AvaloniaProperty.Register<CustomFilePicker, string?>(nameof(FileName), null);
public static readonly StyledProperty<Stream?> FileStreamProperty = AvaloniaProperty.Register<CustomFilePicker, Stream?>(nameof(FileStream), null);
public static readonly StyledProperty<string?> FileExtensionsProperty = AvaloniaProperty.Register<CustomFilePicker, string?>(nameof(FileExtensions), "*");
public Stream? FileStream
{
get => GetValue(FileStreamProperty);
set => SetValue(FileStreamProperty, value);
}
public string? FileName
{
get => GetValue(FileNameProperty);
set
{
FileStream = IOPath.OpenRead(value);
SetValue(FileNameProperty, value);
}
}
public string? FileExtensions
{
get => GetValue(FileExtensionsProperty);
set => SetValue(FileExtensionsProperty, value);
}
public CustomFilePicker()
{
#region 启用拖拽文件的效果
//Windows管理员权限运行无法在低权限的资源管理中拖拽文件到高权限的程序上
//https://docs.microsoft.com/zh-cn/archive/blogs/patricka/q-why-doesnt-drag-and-drop-work-when-my-application-is-running-elevated-a-mandatory-integrity-control-and-uipi?tdsourcetag=s_pctim_aiomsg
void DragOver(object? sender, DragEventArgs e)
{
// Only allow Copy or Link as Drop Operations.
e.DragEffects = e.DragEffects & (DragDropEffects.Copy | DragDropEffects.Link);
// Only allow if the dragged data contains text or filenames.
if (!e.Data.Contains(DataFormats.Text) && !e.Data.Contains(DataFormats.Files))
e.DragEffects = DragDropEffects.None;
}
void Drop(object? sender, DragEventArgs e)
{
if (e.Data.Contains(DataFormats.Text))
{
FileName = e.Data.GetText();
}
else if (e.Data.Contains(DataFormats.Files))
{
var files = e.Data.GetFiles()?
.Select(s => s.TryGetLocalPath())
.Where(p => !string.IsNullOrEmpty(p))
.OfType<string>();
if (files.Any_Nullable())
FileName = string.Join(Environment.NewLine, files);
}
}
AddHandler(DragDrop.DropEvent, Drop);
AddHandler(DragDrop.DragOverEvent, DragOver);
#endregion
this.Tapped += CustomFilePicker_Tapped;
}
private async void CustomFilePicker_Tapped(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
//if (string.IsNullOrEmpty(FileExtensions) || FileExtensions == "*")
//{
// fileTypes = new ValueTuple<string, string[]>[] { ("All Files", new[] { "*" }) };
//}
//else
//{
// fileTypes = new ValueTuple<string, string[]>[] {
// ("Required Files", FileExtensions.Split(",")),
// ("All Files", new[] { "*" }),
// };
//}
var result = await FilePicker2.PickAsync(PickOptions.Images);
if (result != null)
{
FileName = result.FullPath;
}
else
{
Toast.Show(ToastIcon.Warning, Strings.PleaseSelect);
}
}
}
<UserControl xmlns="https://github.com/avaloniaui" <UserControl
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="BD.WTTS.UI.Views.Pages.TextInputDialogPage"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns="https://github.com/avaloniaui"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:spp="https://steampp.net/ui" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="BD.WTTS.UI.Views.Pages.TextInputDialogPage" xmlns:spp="https://steampp.net/ui"
x:DataType="spp:TextBoxWindowViewModel" d:DesignHeight="450"
x:CompileBindings="True"> d:DesignWidth="800"
<TextBox AcceptsReturn="True" x:CompileBindings="True"
MinWidth="450" x:DataType="spp:TextBoxWindowViewModel"
VerticalAlignment="Stretch" mc:Ignorable="d">
HorizontalAlignment="Stretch" <TextBox
Watermark="{Binding Placeholder}" Name="PasswordBox"
Text="{Binding Value}" MinWidth="450"
MaxLength="{Binding MaxLength}" HorizontalAlignment="Stretch"
Name="PasswordBox" VerticalAlignment="Stretch"
Classes="revealPasswordButton clearButton" AcceptsReturn="False"
PasswordChar="{StaticResource PasswordChar}"/> Classes="revealPasswordButton clearButton"
MaxLength="{Binding MaxLength}"
PasswordChar="{StaticResource PasswordChar}"
Text="{Binding Value}"
Watermark="{Binding Placeholder}" />
</UserControl> </UserControl>
using Avalonia.Markup.Xaml.MarkupExtensions;
namespace BD.WTTS.UI.Views.Pages; namespace BD.WTTS.UI.Views.Pages;
public partial class TextInputDialogPage : ReactiveUserControl<TextBoxWindowViewModel> public partial class TextInputDialogPage : ReactiveUserControl<TextBoxWindowViewModel>
...@@ -19,13 +21,15 @@ public partial class TextInputDialogPage : ReactiveUserControl<TextBoxWindowView ...@@ -19,13 +21,15 @@ public partial class TextInputDialogPage : ReactiveUserControl<TextBoxWindowView
{ {
case TextBoxWindowViewModel.TextBoxInputType.Password: case TextBoxWindowViewModel.TextBoxInputType.Password:
PasswordBox.IsVisible = true; PasswordBox.IsVisible = true;
PasswordBox.PasswordChar = '*'; PasswordBox.PasswordChar = App.Instance.FindResource("PasswordChar") as char? ?? '*';
PasswordBox.Classes.Set("revealPasswordButton", true); PasswordBox.Classes.Set("revealPasswordButton", true);
PasswordBox.Classes.Set("clearButton", false);
break; break;
case TextBoxWindowViewModel.TextBoxInputType.TextBox: case TextBoxWindowViewModel.TextBoxInputType.TextBox:
PasswordBox.IsVisible = true; PasswordBox.IsVisible = true;
PasswordBox.PasswordChar = default; PasswordBox.PasswordChar = default;
PasswordBox.Classes.Set("revealPasswordButton", false); PasswordBox.Classes.Set("revealPasswordButton", false);
PasswordBox.Classes.Set("clearButton", true);
break; break;
case TextBoxWindowViewModel.TextBoxInputType.ReadOnlyText: case TextBoxWindowViewModel.TextBoxInputType.ReadOnlyText:
PasswordBox.IsVisible = false; PasswordBox.IsVisible = false;
......
...@@ -4,12 +4,14 @@ public sealed class BasicAccount : ReactiveObject, IAccount ...@@ -4,12 +4,14 @@ public sealed class BasicAccount : ReactiveObject, IAccount
{ {
public string? DisplayName => string.IsNullOrEmpty(AliasName) ? AccountName : AliasName; public string? DisplayName => string.IsNullOrEmpty(AliasName) ? AccountName : AliasName;
[Reactive]
public string? AliasName { get; set; } public string? AliasName { get; set; }
public string? AccountName { get; set; } public string? AccountName { get; set; }
public string? AccountId { get; set; } public string? AccountId { get; set; }
[Reactive]
public DateTime? LastLoginTime { get; set; } public DateTime? LastLoginTime { get; set; }
public string? ImagePath { get; set; } public string? ImagePath { get; set; }
...@@ -18,5 +20,6 @@ public sealed class BasicAccount : ReactiveObject, IAccount ...@@ -18,5 +20,6 @@ public sealed class BasicAccount : ReactiveObject, IAccount
public string? PlatformName { get; init; } public string? PlatformName { get; init; }
[Reactive]
public bool MostRecent { get; set; } public bool MostRecent { get; set; }
} }
...@@ -55,6 +55,19 @@ public sealed partial class PlatformAccount ...@@ -55,6 +55,19 @@ public sealed partial class PlatformAccount
} }
}, fileTypes); }, fileTypes);
}); });
EditRemarkCommand = ReactiveCommand.Create<IAccount>(async acc =>
{
var text = await TextBoxWindowViewModel.ShowDialogAsync(new()
{
Value = acc.AliasName,
Title = AppResources.UserChange_EditRemark,
});
//可将名字设置为空字符串重置
if (text == null)
return;
acc.AliasName = text;
platformSwitcher.ChangeUserRemark(acc);
});
CopyCommand = ReactiveCommand.Create<string>(async text => await Clipboard2.SetTextAsync(text)); CopyCommand = ReactiveCommand.Create<string>(async text => await Clipboard2.SetTextAsync(text));
......
...@@ -81,6 +81,8 @@ public sealed partial class PlatformAccount : ReactiveObject ...@@ -81,6 +81,8 @@ public sealed partial class PlatformAccount : ReactiveObject
public ICommand SetAccountAvatarCommand { get; } public ICommand SetAccountAvatarCommand { get; }
public ICommand EditRemarkCommand { get; }
public ICommand CopyCommand { get; } public ICommand CopyCommand { get; }
public ICommand OpenLinkCommand { get; } public ICommand OpenLinkCommand { get; }
......
...@@ -19,11 +19,8 @@ public sealed class SteamAccount : ReactiveObject, IAccount ...@@ -19,11 +19,8 @@ public sealed class SteamAccount : ReactiveObject, IAccount
set => this.SteamUser.SteamId64 = Convert.ToInt64(value); set => this.SteamUser.SteamId64 = Convert.ToInt64(value);
} }
public string? AliasName [Reactive]
{ public string? AliasName { get; set; }
get => this.SteamUser.SteamID;
set => this.SteamUser.SteamID = value;
}
public string? AccountName public string? AccountName
{ {
......
...@@ -530,11 +530,6 @@ public sealed class BasicPlatformSwitcher : IPlatformSwitcher ...@@ -530,11 +530,6 @@ public sealed class BasicPlatformSwitcher : IPlatformSwitcher
return uniqueId; return uniqueId;
} }
public void ChangeUserRemark()
{
}
public Task<IEnumerable<IAccount>?> GetUsers(PlatformAccount platform) public Task<IEnumerable<IAccount>?> GetUsers(PlatformAccount platform)
{ {
List<BasicAccount>? accounts = null; List<BasicAccount>? accounts = null;
......
...@@ -90,11 +90,6 @@ public sealed class SteamPlatformSwitcher : IPlatformSwitcher ...@@ -90,11 +90,6 @@ public sealed class SteamPlatformSwitcher : IPlatformSwitcher
return ""; return "";
} }
public void ChangeUserRemark()
{
}
public async Task<IEnumerable<IAccount>?> GetUsers(PlatformAccount platform) public async Task<IEnumerable<IAccount>?> GetUsers(PlatformAccount platform)
{ {
var users = steamService.GetRememberUserList(); var users = steamService.GetRememberUserList();
......
...@@ -16,7 +16,13 @@ public interface IPlatformSwitcher ...@@ -16,7 +16,13 @@ public interface IPlatformSwitcher
string GetCurrentAccountId(PlatformAccount platform); string GetCurrentAccountId(PlatformAccount platform);
void ChangeUserRemark(); void ChangeUserRemark(IAccount account)
{
if (!string.IsNullOrEmpty(account.AccountId))
GameAccountSettings.AccountRemarks.Add($"{account.PlatformName}-{account.AccountId}", account.AliasName);
else
Toast.Show(ToastIcon.Error, "账号 Id 为空");
}
bool SetPlatformPath(PlatformAccount platform); bool SetPlatformPath(PlatformAccount platform);
......
...@@ -21,14 +21,14 @@ public partial interface IGameAccountSettings ...@@ -21,14 +21,14 @@ public partial interface IGameAccountSettings
=> Ioc.Get_Nullable<IOptionsMonitor<IGameAccountSettings>>()?.CurrentValue; => Ioc.Get_Nullable<IOptionsMonitor<IGameAccountSettings>>()?.CurrentValue;
/// <summary> /// <summary>
/// Steam 账号备注字典 /// 账号备注字典
/// </summary> /// </summary>
ConcurrentDictionary<long, string?>? AccountRemarks { get; set; } ConcurrentDictionary<string, string?>? AccountRemarks { get; set; }
/// <summary> /// <summary>
/// Steam 账号备注字典的默认值 /// 账号备注字典的默认值
/// </summary> /// </summary>
const ConcurrentDictionary<long, string?>? DefaultAccountRemarks = null; static readonly ConcurrentDictionary<string, string?> DefaultAccountRemarks = new();
/// <summary> /// <summary>
/// Steam 家庭共享临时禁用 /// Steam 家庭共享临时禁用
......
...@@ -44,10 +44,10 @@ public sealed partial class GameAccountSettings_ : IGameAccountSettings, ISettin ...@@ -44,10 +44,10 @@ public sealed partial class GameAccountSettings_ : IGameAccountSettings, ISettin
=> GameAccountSettingsContext.Instance.GameAccountSettings_; => GameAccountSettingsContext.Instance.GameAccountSettings_;
/// <summary> /// <summary>
/// Steam 账号备注字典 /// 账号备注字典
/// </summary> /// </summary>
[MPKey(0), MP2Key(0), JsonPropertyOrder(0)] [MPKey(0), MP2Key(0), JsonPropertyOrder(0)]
public ConcurrentDictionary<long, string?>? AccountRemarks { get; set; } public ConcurrentDictionary<string, string?>? AccountRemarks { get; set; }
/// <summary> /// <summary>
/// Steam 家庭共享临时禁用 /// Steam 家庭共享临时禁用
...@@ -72,9 +72,9 @@ public sealed partial class GameAccountSettings_ : IGameAccountSettings, ISettin ...@@ -72,9 +72,9 @@ public sealed partial class GameAccountSettings_ : IGameAccountSettings, ISettin
public static partial class GameAccountSettings public static partial class GameAccountSettings
{ {
/// <summary> /// <summary>
/// Steam 账号备注字典 /// 账号备注字典
/// </summary> /// </summary>
public static SettingsProperty<long, string?, ConcurrentDictionary<long, string?>, GameAccountSettings_> AccountRemarks { get; } public static SettingsProperty<string, string?, ConcurrentDictionary<string, string?>, GameAccountSettings_> AccountRemarks { get; }
= new(DefaultAccountRemarks); = new(DefaultAccountRemarks);
/// <summary> /// <summary>
......
...@@ -6,11 +6,6 @@ public class PlatformSettingsPageViewModel : ViewModelBase ...@@ -6,11 +6,6 @@ public class PlatformSettingsPageViewModel : ViewModelBase
public PlatformAccount? Platform { get; init; } public PlatformAccount? Platform { get; init; }
public PlatformSettingsPageViewModel()
{
PlatformSettings = new();
}
public PlatformSettingsPageViewModel(PlatformAccount platform) public PlatformSettingsPageViewModel(PlatformAccount platform)
{ {
Platform = platform; Platform = platform;
......
...@@ -63,7 +63,7 @@ public sealed class SteamFamilyShareManagePageViewModel : WindowViewModel ...@@ -63,7 +63,7 @@ public sealed class SteamFamilyShareManagePageViewModel : WindowViewModel
public async Task Refresh_Cash() public async Task Refresh_Cash()
{ {
IReadOnlyDictionary<long, string?>? accountRemarks = GameAccountSettings.AccountRemarks.Value; var accountRemarks = GameAccountSettings.AccountRemarks.Value;
foreach (var item in _AuthorizedSourceList.Items) foreach (var item in _AuthorizedSourceList.Items)
{ {
...@@ -79,7 +79,7 @@ public sealed class SteamFamilyShareManagePageViewModel : WindowViewModel ...@@ -79,7 +79,7 @@ public sealed class SteamFamilyShareManagePageViewModel : WindowViewModel
item.AvatarMedium = item.MiniProfile.AnimatedAvatar; item.AvatarMedium = item.MiniProfile.AnimatedAvatar;
} }
if (accountRemarks?.TryGetValue(item.SteamId64_Int, out var remark) == true && if (accountRemarks?.TryGetValue("Steam-" + item.SteamId64_Int, out var remark) == true &&
!string.IsNullOrEmpty(remark)) !string.IsNullOrEmpty(remark))
item.Remark = remark; item.Remark = remark;
} }
......
...@@ -81,7 +81,10 @@ ...@@ -81,7 +81,10 @@
<ui:MenuFlyoutItem Text="Backpack.tf" /> <ui:MenuFlyoutItem Text="Backpack.tf" />
</ui:MenuFlyoutSubItem> </ui:MenuFlyoutSubItem>
<ui:MenuFlyoutSeparator IsVisible="{ReflectionBinding #u.DataContext.IsSteamPlatform}" /> <ui:MenuFlyoutSeparator IsVisible="{ReflectionBinding #u.DataContext.IsSteamPlatform}" />
<ui:MenuFlyoutItem Text="修改备注名" /> <ui:MenuFlyoutItem
Command="{ReflectionBinding #u.DataContext.EditRemarkCommand}"
CommandParameter="{Binding}"
Text="修改备注名" />
<ui:MenuFlyoutItem <ui:MenuFlyoutItem
Command="{ReflectionBinding #u.DataContext.SetAccountAvatarCommand}" Command="{ReflectionBinding #u.DataContext.SetAccountAvatarCommand}"
CommandParameter="{Binding}" CommandParameter="{Binding}"
...@@ -126,7 +129,8 @@ ...@@ -126,7 +129,8 @@
MoreFlyout="{StaticResource SharedMenuFlyout}" MoreFlyout="{StaticResource SharedMenuFlyout}"
Status="{ReflectionBinding Path=Res.UserChange_RecentLogin, Status="{ReflectionBinding Path=Res.UserChange_RecentLogin,
Mode=OneWay, Mode=OneWay,
Source={x:Static s:ResourceService.Current}}"> Source={x:Static s:ResourceService.Current}}"
Tags="{Binding AliasName}">
<spp:AppItem.Status> <spp:AppItem.Status>
<Border Classes="Status" IsVisible="{Binding MostRecent}"> <Border Classes="Status" IsVisible="{Binding MostRecent}">
<TextBlock Text="{ReflectionBinding Path=Res.UserChange_RecentLogin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" /> <TextBlock Text="{ReflectionBinding Path=Res.UserChange_RecentLogin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
......
using BD.SteamClient.Constants;
using BD.SteamClient.Enums.SteamGridDB;
using BD.SteamClient.Models.SteamGridDB;
namespace BD.WTTS.UI.ViewModels;
public sealed class EditAppInfoPageViewModel : WindowViewModel
{
readonly SourceCache<SteamGridItem, long> _SteamGridItemSourceList;
readonly ReadOnlyObservableCollection<SteamGridItem>? _SteamGridItems;
public static string DisplayName => Strings.GameList_EditAppInfo;
public ReadOnlyObservableCollection<SteamGridItem>? SteamGridItems => _SteamGridItems;
[Reactive]
public SteamApp App { get; set; }
[Reactive]
public SteamGridItem? SelectGrid { get; set; }
[Reactive]
public bool IsLoadingSteamGrid { get; set; }
public bool IsSteamGridEmpty => !IsLoadingSteamGrid && !SteamGridItems.Any_Nullable();
public EditAppInfoPageViewModel(SteamApp app)
{
App = app;
App.RefreshEditImage();
if (App.SaveFiles.Any_Nullable())
foreach (var file in App.SaveFiles)
{
file.FormatPathGenerate();
}
_SteamGridItemSourceList = new SourceCache<SteamGridItem, long>(t => t.Id);
_SteamGridItemSourceList
.Connect()
.ObserveOn(RxApp.MainThreadScheduler)
.Sort(SortExpressionComparer<SteamGridItem>.Ascending(x => x.Id))
.Bind(out _SteamGridItems)
.Subscribe(_ => this.RaisePropertyChanged(nameof(IsSteamGridEmpty)));
}
public void AddLaunchItem()
{
if (App.LaunchItems == null)
{
App.LaunchItems = new ObservableCollection<SteamAppLaunchItem> { new() };
}
else
{
App.LaunchItems.Add(new());
}
}
public void UpLaunchItem(SteamAppLaunchItem item)
{
MoveLaunchItem(item, true);
}
public void DownLaunchItem(SteamAppLaunchItem item)
{
MoveLaunchItem(item, false);
}
private void MoveLaunchItem(in SteamAppLaunchItem item, bool isUp)
{
if (App.LaunchItems != null)
{
var oldindex = App.LaunchItems.IndexOf(item);
if (isUp ? oldindex == 0 : oldindex == App.LaunchItems.Count - 1)
{
return;
}
App.LaunchItems.Move(oldindex, isUp ? oldindex - 1 : oldindex + 1);
}
}
public void DeleteLaunchItem(SteamAppLaunchItem item)
{
if (App.LaunchItems != null)
{
App.LaunchItems.Remove(item);
}
}
bool CheckCurrentSteamUserStats(in SteamUser? user)
{
if (user == null)
{
Toast.Show(ToastIcon.Error, Strings.SaveEditedAppInfo_SteamUserNullTip);
return false;
}
return true;
}
public async void SaveEditAppInfo()
{
#region 自定义图片保存
var mostRecentUser = ISteamService.Instance.GetRememberUserList().Where(s => s.MostRecent).FirstOrDefault();
if (!(App.EditHeaderLogoStream is FileStream fs3))
{
if (!CheckCurrentSteamUserStats(mostRecentUser))
return;
if (await ISteamService.Instance.SaveAppImageToSteamFile(App.EditHeaderLogoStream,
mostRecentUser!, App.AppId, SteamGridItemType.Header) == false)
{
Toast.Show(ToastIcon.Error, string.Format(Strings.SaveImageFileFailed, nameof(SteamGridItemType.Logo)));
}
}
if (!(App.EditLibraryLogoStream is FileStream fs))
{
if (!CheckCurrentSteamUserStats(mostRecentUser))
return;
if (await ISteamService.Instance.SaveAppImageToSteamFile(App.EditLibraryLogoStream,
mostRecentUser!, App.AppId, SteamGridItemType.Logo) == false)
{
Toast.Show(ToastIcon.Error, string.Format(Strings.SaveImageFileFailed, nameof(SteamGridItemType.Logo)));
}
}
if (!(App.EditLibraryHeroStream is FileStream fs1))
{
if (!CheckCurrentSteamUserStats(mostRecentUser))
return;
if (await ISteamService.Instance.SaveAppImageToSteamFile(App.EditLibraryHeroStream,
mostRecentUser!, App.AppId, SteamGridItemType.Hero) == false)
{
Toast.Show(ToastIcon.Error, string.Format(Strings.SaveImageFileFailed, nameof(SteamGridItemType.Hero)));
}
}
if (!(App.EditLibraryGridStream is FileStream fs2))
{
if (!CheckCurrentSteamUserStats(mostRecentUser))
return;
if (await ISteamService.Instance.SaveAppImageToSteamFile(App.EditLibraryGridStream,
mostRecentUser!, App.AppId, SteamGridItemType.Grid) == false)
{
Toast.Show(ToastIcon.Error, string.Format(Strings.SaveImageFileFailed, nameof(SteamGridItemType.Grid)));
}
}
#endregion
App.IsEdited = true;
SteamConnectService.Current.SteamApps.AddOrUpdate(App);
//await MessageBox.ShowAsync("保存成功但还不会直接写入Steam文件, 请打开[保存Steam游戏自定义信息窗口]保存所有更改信息到Steam文件中。",
// ThisAssembly.AssemblyTrademark, MessageBox.Button.OK, MessageBox.Image.None, MessageBox.DontPromptType.SaveEditAppInfo);
this.Close();
}
public void CancelEditAppInfo()
{
App.RefreshEditImage();
this.Close();
}
public void ResetEditAppInfo()
{
//if (await MessageBox.ShowAsync("确定要重置当前App所有更改吗?(不会重置自定义图片)", ThisAssembly.AssemblyTrademark, MessageBox.Button.OKCancel) == MessageBox.Result.OK)
//{
App.RefreshEditImage();
if (App.OriginalData != null)
{
using BinaryReader reader = new BinaryReader(new MemoryStream(App.OriginalData));
reader.BaseStream.Seek(40L, SeekOrigin.Current);
var table = reader.ReadPropertyTable();
App.ExtractReaderProperty(table);
App.IsEdited = true;
SteamConnectService.Current.SteamApps.AddOrUpdate(App);
}
//}
}
public async void RefreshSteamGridItemList(SteamGridItemType type = SteamGridItemType.Grid)
{
IsLoadingSteamGrid = true;
_SteamGridItemSourceList.Clear();
var grid = await ISteamGridDBWebApiServiceImpl.Instance.GetSteamGridAppBySteamAppId(App.AppId);
if (grid != null)
{
var items = await ISteamGridDBWebApiServiceImpl.Instance.GetSteamGridItemsByGameId(grid.Id, type);
if (items.Any_Nullable())
{
_SteamGridItemSourceList.AddOrUpdate(items);
}
}
IsLoadingSteamGrid = false;
}
public async void ApplyCustomImageToApp(SteamGridItemType type)
{
if (SelectGrid == null)
{
Toast.Show(Strings.SaveEditedAppInfo_SelectImageFailed);
return;
}
var imageHttpClientService = Ioc.Get<IImageHttpClientService>();
var stream = await imageHttpClientService.GetImageMemoryStreamAsync(SelectGrid.Url, default);
switch (type)
{
case SteamGridItemType.Header:
App.EditHeaderLogoStream = stream;
break;
case SteamGridItemType.Grid:
App.EditLibraryGridStream = stream;
break;
case SteamGridItemType.Hero:
App.EditLibraryHeroStream = stream;
break;
case SteamGridItemType.Logo:
App.EditLibraryLogoStream = stream;
break;
}
}
public override void OnClosing(object? sender, CancelEventArgs e)
{
if (App != null && !App.IsEdited)
{
App.EditLibraryGridStream = null;
App.EditLibraryHeroStream = null;
App.EditLibraryLogoStream = null;
App.EditHeaderLogoStream = null;
}
_SteamGridItemSourceList.Dispose();
}
public async void OpenSteamGridDBImageUrl(SteamGridItem item)
{
await Browser2.OpenAsync(item.Url);
}
public async void OpenSteamGridDBAppUrl(SteamGridItem item)
{
var url = item.GridType switch
{
SteamGridItemType.Grid => string.Format(SteamGridDBApiUrls.SteamGridDBUrl_Grid, item.Id),
SteamGridItemType.Header => string.Format(SteamGridDBApiUrls.SteamGridDBUrl_Grid, item.Id),
SteamGridItemType.Hero => string.Format(SteamGridDBApiUrls.SteamGridDBUrl_Hero, item.Id),
SteamGridItemType.Icon => string.Format(SteamGridDBApiUrls.SteamGridDBUrl_Icon, item.Id),
SteamGridItemType.Logo => string.Format(SteamGridDBApiUrls.SteamGridDBUrl_Logo, item.Id),
_ => string.Format(SteamGridDBApiUrls.SteamGridDBUrl_Grid, item.Id),
};
await Browser2.OpenAsync(url);
}
public async void OpenSteamGridDBAuthorUrl(SteamGridItem item)
{
await Browser2.OpenAsync(string.Format(SteamGridDBApiUrls.SteamGridDB_Author_URL, item.Author.Steam64));
}
public void OpenFolder(string path)
{
if (!string.IsNullOrEmpty(path))
IPlatformService.Instance.OpenFolder(path);
}
public void ManageCloudArchive_Click()
{
GameListPageViewModel.ManageCloudArchive_Click(App);
}
}
...@@ -190,10 +190,21 @@ public sealed partial class GameListPageViewModel : TabItemViewModel ...@@ -190,10 +190,21 @@ public sealed partial class GameListPageViewModel : TabItemViewModel
Process2.Start(url, useShellExecute: true); Process2.Start(url, useShellExecute: true);
} }
public static void EditAppInfoClick(SteamApp app) public static async void EditAppInfoClick(SteamApp app)
{ {
if (app == null) return; if (app == null) return;
//IWindowManager.Instance.Show(CustomWindow.EditAppInfo, new EditAppInfoWindowViewModel(app), string.Empty, default); var vm = new EditAppInfoPageViewModel(app);
var result = await IWindowManager.Instance.ShowTaskDialogAsync(vm, Strings.GameList_EditAppInfo,
pageContent: new EditAppInfoPage(), okButtonText: Strings.Save, isCancelButton: true);
if (result)
{
vm.SaveEditAppInfo();
}
else
{
vm.CancelEditAppInfo();
}
} }
public static void ManageCloudArchive_Click(SteamApp app) public static void ManageCloudArchive_Click(SteamApp app)
......
using Avalonia.Controls;
using Avalonia.ReactiveUI;
using BD.SteamClient.Enums.SteamGridDB;
using FluentAvalonia.UI.Controls;
namespace BD.WTTS.UI.Views.Pages;
public partial class EditAppInfoPage : ReactiveUserControl<EditAppInfoPageViewModel>
{
public EditAppInfoPage()
{
InitializeComponent();
}
public async void ShowGridDialog_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
if (SteamGridDBDialog != null && ViewModel != null)
{
var type = (SteamGridItemType?)(sender as Control)?.Tag ?? SteamGridItemType.Grid;
ViewModel.RefreshSteamGridItemList(type);
var r = await SteamGridDBDialog.ShowAsync();
if (r == ContentDialogResult.Primary)
{
ViewModel.ApplyCustomImageToApp(type);
}
}
}
}
...@@ -503,14 +503,14 @@ public sealed class SteamConnectService ...@@ -503,14 +503,14 @@ public sealed class SteamConnectService
#region 加载备注信息和 JumpList #region 加载备注信息和 JumpList
IReadOnlyDictionary<long, string?>? accountRemarks = Ioc.Get<IPartialGameAccountSettings>()?.AccountRemarks; var accountRemarks = Ioc.Get<IPartialGameAccountSettings>()?.AccountRemarks;
#if WINDOWS #if WINDOWS
List<(string title, string applicationPath, string iconResourcePath, string arguments, string description, string customCategory)>? jumplistData = new(); List<(string title, string applicationPath, string iconResourcePath, string arguments, string description, string customCategory)>? jumplistData = new();
#endif #endif
foreach (var user in SteamUsers.Items) foreach (var user in SteamUsers.Items)
{ {
if (accountRemarks?.TryGetValue(user.SteamId64, out var remark) == true && if (accountRemarks?.TryGetValue("Steam-" + user.SteamId64, out var remark) == true &&
!string.IsNullOrEmpty(remark)) !string.IsNullOrEmpty(remark))
user.Remark = remark; user.Remark = remark;
......
...@@ -32,7 +32,7 @@ public interface IWindowManager ...@@ -32,7 +32,7 @@ public interface IWindowManager
object? pageContent = null, object? pageContent = null,
string? okButtonText = null, string? okButtonText = null,
Func<bool>? cancelCloseAction = null) Func<bool>? cancelCloseAction = null)
where TPageViewModel : ViewModelBase, new(); where TPageViewModel : ViewModelBase;
/// <summary> /// <summary>
/// 显示一个窗口 /// 显示一个窗口
......
...@@ -12,9 +12,9 @@ public partial interface IPartialGameAccountSettings ...@@ -12,9 +12,9 @@ public partial interface IPartialGameAccountSettings
=> Ioc.Get_Nullable<IPartialGameAccountSettings>(); => Ioc.Get_Nullable<IPartialGameAccountSettings>();
/// <summary> /// <summary>
/// Steam 账号备注字典 /// 账号备注字典
/// </summary> /// </summary>
ConcurrentDictionary<long, string?>? AccountRemarks { get; set; } ConcurrentDictionary<string, string?>? AccountRemarks { get; set; }
/// <summary> /// <summary>
/// Steam 家庭共享临时禁用 /// Steam 家庭共享临时禁用
......
...@@ -250,12 +250,12 @@ partial class Startup // 自定义控制台命令参数 ...@@ -250,12 +250,12 @@ partial class Startup // 自定义控制台命令参数
var steamService = ISteamService.Instance; var steamService = ISteamService.Instance;
var users = steamService.GetRememberUserList(); var users = steamService.GetRememberUserList();
IReadOnlyDictionary<long, string?>? accountRemarks = var accountRemarks =
Ioc.Get<IPartialGameAccountSettings>()?.AccountRemarks; Ioc.Get<IPartialGameAccountSettings>()?.AccountRemarks;
var sUsers = users.Select(s => var sUsers = users.Select(s =>
{ {
if (accountRemarks?.TryGetValue(s.SteamId64, out var remark) == true && if (accountRemarks?.TryGetValue("Steam-" + s.SteamId64, out var remark) == true &&
!string.IsNullOrEmpty(remark)) !string.IsNullOrEmpty(remark))
s.Remark = remark; s.Remark = remark;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册