Compare commits

...
23 Commits
Author SHA1 Message Date
Aigio Liu 9a78f387cf 🔖 3.0.0-rc.7 2024-03-16 19:20:17 +08:00
Aigio Liu 8a091cee0c 🐛 tabItems 2024-03-16 19:01:32 +08:00
RMBGAME 28f9dde2a4 Revert "🐛 PluginCount"
This reverts commit 01e8478ce0.
2024-03-16 18:44:34 +08:00
RMBGAME aa88832d09 🐛 账号切换备注 ToolTip 显示 2024-03-16 18:44:17 +08:00
ff m 3da18b39ac 🐛 HttpUtility.UrlEncode 特定日期下无法格式 下午 这个中文
2024%2f4%2f15+下午4%3a13%3a52+%2b00%3a00
2024-03-16 14:54:33 +08:00
Aigio Liu 01e8478ce0 🐛 PluginCount 2024-03-16 14:45:04 +08:00
RMBGAME 0de026257a 🐛 fix 登录窗口登录成功后未正常关闭 2024-03-15 22:33:08 +08:00
Aigio Liu 5ff097d7d6 🐛 UISettings.StartDefaultPageName 在最小化启动时无效 x2 2024-03-15 19:14:48 +08:00
Aigio Liu 59effc34a3 🐛 UISettings.StartDefaultPageName 在最小化启动时无效 2024-03-15 17:25:36 +08:00
Aigio Liu afd21a3285 ✨ 加速页面 Tab 选项框下标记住选择 2024-03-15 14:14:04 +08:00
Aigio Liu c7ffeb6046 🔖 3.0.0-rc.6 2024-03-15 13:12:02 +08:00
Aigio Liu e659e3d502 ✨ Visual Studio App Center Retirement
https://learn.microsoft.com/en-us/appcenter/retirement
2024-03-15 11:44:02 +08:00
Aigio Liu 077a333d49 🎨 改进主窗口数据加载 2024-03-15 11:32:50 +08:00
Aigio Liu 056a73e3f0 🐛 起始页数据加载 2024-03-15 11:32:50 +08:00
RMBGAME db5095c205 🐛 显示加速模式与脚本状态 2024-03-13 18:11:55 +08:00
RMBGAME a1789cec16 🐛 fix挂时长在游戏列表未加载完成时进入错误的标注了游戏所属 2024-03-13 17:44:28 +08:00
Aigio Liu 9a234ae84f 🐛 MvvmService thread safe 2024-03-13 16:41:15 +08:00
Aigio Liu 8692eaef7c 🐛 ProxyService thread safe 2024-03-13 16:09:30 +08:00
lxy829 cb6516ce53 🎨 EpicSwitch 2024-03-13 11:35:20 +08:00
RMBGAME d8362ce9ea ✨ 支持设置启动时默认页(仍有一些问题) 2024-03-13 05:17:01 +08:00
RMBGAME 0cfdff80cd 🐛 修复加速项目勾选三级菜单启动加速没生效问题
修复账号切换首次打开没有读取缓存依然加载问题
2024-03-12 20:43:11 +08:00
RMBGAME 15c67bcd52 🐛 fix Steam账号切换删除时缓存没有正常清理问题 2024-03-10 21:42:12 +08:00
RMBGAME 3ebe7de3d7 💄 Misc 2024-03-10 21:12:57 +08:00
35 changed files with 409 additions and 159 deletions
+9
View File
@@ -212,6 +212,15 @@
"Summary": "菜单排序",
"IsRegionOrEndregion": null,
"IsValueType": null
},
{
"TypeName": "string",
"PropertyName": "StartDefaultPageName",
"DefaultValue": "null",
"DefaultValueIsConst": true,
"Summary": "程序启动时默认页",
"IsRegionOrEndregion": null,
"IsValueType": null
}
],
"Usings": ""
+1 -1
View File
@@ -21,7 +21,7 @@ public static partial class AssemblyInfo
/// <summary>
/// RC 版本号,范围 0~9,只增不减
/// </summary>
const string ver_for_rc = "5";
const string ver_for_rc = "7";
public const string FileVersion = $"{Version2}.{ver_for_rc}0{ver_for_preview}.0";
@@ -2,11 +2,18 @@ namespace BD.WTTS.UI;
public static class ControlExtensions
{
public static void SetViewModel<T>(this Control c) where T : ViewModelBase, new()
public static void SetViewModel<T>(this Control c, bool useCache = false) where T : ViewModelBase, new()
{
if (c.DataContext == null || c.DataContext.GetType() != typeof(T))
{
c.DataContext = new T();
T cx;
if (useCache)
cx = IViewModelManager.Instance.Get<T>();
else
cx = new T();
c.DataContext = cx;
}
}
}
@@ -113,7 +113,6 @@ public sealed class AvaloniaWindowManagerImpl : IWindowManagerImpl
if (viewModel != null)
{
if (viewModel is IWindowViewModel window)
{
window.Close = b => td?.Hide(b ? TaskDialogStandardResult.OK : TaskDialogStandardResult.Close);
@@ -7,7 +7,7 @@ public partial class HomePage : ReactiveUserControl<HomePageViewModel>
public HomePage()
{
InitializeComponent();
this.SetViewModel<HomePageViewModel>();
this.SetViewModel<HomePageViewModel>(useCache: true);
//ShopsScrollViewer.PointerWheelChanged += ShopsScrollViewer_PointerWheelChanged;
}
@@ -22,6 +22,26 @@
Text="{Binding Path=Res.Settings_UI, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Theme="{StaticResource SubtitleTextBlockStyle}" />
<Border IsEnabled="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}">
<ui:SettingsExpander
Description="{Binding Path=Res.Advertisement_NoShow_Tip, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Header="{Binding Path=Res.Settings_UI_StartDefaultPage, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
IsEnabled="{Binding Path=User.UserType, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource EnumEqualValueConverter}, ConverterParameter={x:Static enum:UserType.Sponsor}}">
<ui:SettingsExpander.IconSource>
<ui:FontIconSource FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="{StaticResource HeartBroken}" />
</ui:SettingsExpander.IconSource>
<ui:SettingsExpander.Footer>
<ComboBox ItemsSource="{Binding Path=MainWindow2.TabItems, Mode=OneWay, Source={x:Static s:IViewModelManager.Instance}}" SelectedItem="{Binding SelectDefaultPage, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate DataType="spp:TabItemViewModel">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</ui:SettingsExpander.Footer>
</ui:SettingsExpander>
</Border>
<Border IsEnabled="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}">
<ui:SettingsExpander
Description="{Binding Path=Res.Advertisement_NoShow_Tip, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
@@ -37,7 +37,36 @@ public sealed partial class MainWindow : ReactiveAppWindow<MainWindowViewModel>
}
}
protected override void OnOpened(EventArgs e)
DateTime lastOpenedTime;
internal bool SetStartDefaultPageName()
{
if (NavigationService.Instance.CurrnetPage != null)
return false;
var mw2 = IViewModelManager.Instance.MainWindow2;
if (mw2 == null)
return false;
Type? pageType = null;
if (!string.IsNullOrEmpty(UISettings.StartDefaultPageName.Value))
{
var tabItems = mw2?.TabItems;
if (tabItems == null)
return false;
var page = tabItems.OfType<MenuTabItemViewModel>()
.FirstOrDefault(s => s.Id == UISettings.StartDefaultPageName.Value);
if (page != null)
{
pageType = page.PageType;
}
}
INavigationService.Instance.Navigate(pageType ?? typeof(HomePage));
return true;
}
protected override async void OnOpened(EventArgs e)
{
base.OnOpened(e);
if (Steamworks.SteamClient.IsValid)
@@ -46,24 +75,23 @@ public sealed partial class MainWindow : ReactiveAppWindow<MainWindowViewModel>
}
if (AppSplashScreen.IsInitialized)
{
if (NavigationService.Instance.CurrnetPage == null)
{
INavigationService.Instance.Navigate(typeof(HomePage));
}
Task2.InBackground(async () =>
{
IViewModelManager.Instance.Get<HomePageViewModel>().GetServerContent();
await AdvertiseService.Current.RefrshAdvertiseAsync();
NoticeService.Current.GetNewsAsync();
});
try // 在主窗口显示时调用此函数检查是否需要显示新版本通知窗口
{
Ioc.Get_Nullable<IAppUpdateService>()?.OnMainOpenTryShowNewVersionWindow();
}
catch
{
SetStartDefaultPageName();
}
if (lastOpenedTime == default ||
(DateTime.Now - lastOpenedTime) > TimeSpan.FromMinutes(30))
{
lastOpenedTime = DateTime.Now;
await IViewModelManager.Instance.Get<HomePageViewModel>().GetServerContent();
await AdvertiseService.Current.RefrshAdvertiseAsync();
await NoticeService.Current.GetNewsAsync();
}
try // 在主窗口显示时调用此函数检查是否需要显示新版本通知窗口
{
Ioc.Get_Nullable<IAppUpdateService>()?.OnMainOpenTryShowNewVersionWindow();
}
catch
{
}
}
}
}
@@ -72,7 +100,7 @@ public sealed class AppSplashScreen : IApplicationSplashScreen
{
public static bool IsInitialized = false;
public WindowViewModel? ViewModel { get; }
//public WindowViewModel? ViewModel { get; }
public string? AppName { get; }
@@ -162,7 +190,7 @@ public sealed class AppSplashScreen : IApplicationSplashScreen
});
AdvertiseService.Current.InitAdvertise();
NoticeService.Current.GetNewsAsync();
await NoticeService.Current.GetNewsAsync();
var mainWindow = App.Instance.MainWindow;
mainWindow.ThrowIsNull();
@@ -205,8 +233,10 @@ public sealed class AppSplashScreen : IApplicationSplashScreen
{
mainWindow.DataContext = IViewModelManager.Instance.MainWindow;
s.InitSettingSubscribe();
INavigationService.Instance.Navigate(typeof(HomePage));
if (mainWindow is MainWindow mainWindow1)
{
mainWindow1.SetStartDefaultPageName();
}
});
#if STARTUP_WATCH_TRACE || DEBUG
WatchTrace.Record("InitMainWindowViewModel");
@@ -151,7 +151,7 @@ public sealed class Plugin : PluginBase<Plugin>, IPlugin
//if (ResourceService.IsChineseSimplified)
//{
await ProxyService.Current.InitializeAsync();
await MainThread2.InvokeOnMainThreadAsync(ProxyService.Current.InitializeAsync);
//}
}
}
@@ -13,9 +13,9 @@ public sealed partial class GameAcceleratorService
: ReactiveObject
#endif
{
static GameAcceleratorService? mCurrent;
static readonly Lazy<GameAcceleratorService> mCurrent = new(() => new(), LazyThreadSafetyMode.ExecutionAndPublication);
public static GameAcceleratorService Current => mCurrent ??= new();
public static GameAcceleratorService Current => mCurrent.Value;
public SourceCache<XunYouGameViewModel, int> Games { get; }
@@ -10,9 +10,9 @@ public sealed partial class ProxyService
: ReactiveObject, IProxyService
#endif
{
static ProxyService? mCurrent;
static readonly Lazy<ProxyService> mCurrent = new(() => new(), LazyThreadSafetyMode.ExecutionAndPublication);
public static ProxyService Current => mCurrent ??= new();
public static ProxyService Current => mCurrent.Value;
readonly IReverseProxyService reverseProxyService = IReverseProxyService.Constants.Instance;
readonly IScriptManager scriptManager = IScriptManager.Instance;
@@ -62,6 +62,7 @@ public sealed partial class ProxyService
});
this.WhenAnyValue(v => v.ProxyDomainsList)
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(domain => domain?
.ToObservableChangeSet()
.AutoRefresh(x => x.ObservableItems)
@@ -75,6 +76,7 @@ public sealed partial class ProxyService
}));
this.WhenAnyValue(v => v.ProxyScripts)
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(script => script?
.Connect()
.AutoRefresh(x => x.Disable)
@@ -117,7 +119,7 @@ public sealed partial class ProxyService
return null;
var data = ProxyDomains.Items
.Where(x => x.Items != null)
.SelectMany(s => s.Items!.Where(w => w.Checked));
.SelectMany(s => s.Items!.Where(w => w.ThreeStateEnable != false));
//return data.Concat(data.SelectMany(s => GetProxyDomainsItems(s)));
return data;
}
@@ -197,6 +197,12 @@ public sealed partial class ProxySettings_ : IProxySettings, ISettings, ISetting
[MPKey(22), MP2Key(22), JsonPropertyOrder(22)]
public string? CustomDohAddres { get; set; } = IProxySettings.DefaultCustomDohAddres;
/// <summary>
/// 加速页面 Tab 选项卡选中下标
/// </summary>
[MPKey(23), MP2Key(23), JsonPropertyOrder(23)]
public int AcceleratorTabsSelectedIndex { get; set; }
}
public static partial class ProxySettings
@@ -359,4 +365,9 @@ public static partial class ProxySettings
public static SettingsProperty<string, ProxySettings_> CustomDohAddres { get; }
= new(DefaultCustomDohAddres);
/// <summary>
/// 加速页面 Tab 选项卡选中下标
/// </summary>
public static SettingsStructProperty<int, ProxySettings_> AcceleratorTabsSelectedIndex { get; }
= new();
}
@@ -7,6 +7,6 @@ public partial class AcceleratorPage : PageBase<AcceleratorPageViewModel>
public AcceleratorPage()
{
InitializeComponent();
this.SetViewModel<AcceleratorPageViewModel>();
this.SetViewModel<AcceleratorPageViewModel>(true);
}
}
@@ -353,10 +353,10 @@
VerticalAlignment="Top"
ItemContainerTheme="{StaticResource TabItemStyle}"
Theme="{StaticResource TabStripStyle}">
<TabStripItem Name="GameAccTab" Content="游戏加速" />
<TabStripItem Content="平台加速 (免费)" IsVisible="{Binding Source={x:Static s:ProxyService.Current}, Path=!ProxyStatus, Mode=OneWay}" />
<TabStripItem Content="{x:Static spp:Strings.CommunityFix_FlowStatistics}" IsVisible="{Binding Source={x:Static s:ProxyService.Current}, Path=ProxyStatus, Mode=OneWay}" />
<TabStripItem Content="{x:Static spp:Strings.CommunityFix_Services}" IsVisible="{Binding Source={x:Static s:ProxyService.Current}, Path=ProxyStatus, Mode=OneWay}" />
<TabStripItem Name="GameAccTab" Content="游戏加速" />
</TabStrip>
<Carousel
Grid.Row="1"
@@ -364,66 +364,6 @@
Classes="AnimeSlide"
SelectedIndex="{Binding #AcceleratorTabs.SelectedIndex}">
<!-- 游戏加速 -->
<Panel x:DataType="spp:GameAcceleratorPageViewModel">
<Panel.DataContext>
<spp:GameAcceleratorPageViewModel />
</Panel.DataContext>
<spp:ContentLoader
IsLoading="{Binding Source={x:Static s:GameAcceleratorService.Current}, Path=IsLoadingGames, Mode=OneWay}"
IsShowNoResultText="{Binding !!!Games.Count}"
IsVisible="{Binding Source={x:Static s:GameAcceleratorService.Current}, Path=!CurrentAcceleratorGame.IsAccelerated}"
NoResultMessage="{x:Static spp:Strings.GameAccelerator_GamesEmptyTip}}">
<ScrollViewer
Name="GameScrollViewer"
Padding="0,0,10,0"
HorizontalScrollBarVisibility="Disabled"
IsVisible="{Binding !!Games.Count}"
VerticalScrollBarVisibility="Auto">
<StackPanel>
<ItemsRepeater ItemsSource="{Binding Games}" VerticalCacheLength="50">
<ItemsRepeater.Layout>
<UniformGridLayout
ItemsJustification="Start"
ItemsStretch="Uniform"
MinColumnSpacing="10"
MinItemHeight="180"
MinItemWidth="200"
MinRowSpacing="10" />
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate DataType="spp:XunYouGameViewModel">
<spp:GameAppItem ButtonCommand="{Binding GameAcceleratorCommand, Source={x:Static s:GameAcceleratorService.Current}}" />
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
<StackPanel
Margin="0,30"
HorizontalAlignment="Center"
Spacing="10">
<spp:Image2
Width="100"
Height="100"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RenderOptions.BitmapInterpolationMode="HighQuality"
Source="avares://BD.WTTS.Client.Avalonia/UI/Assets/Icons/empty.png"
Stretch="UniformToFill" />
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="已经到底啦! 没有找到想加速的游戏的话可以搜索添加哦~" />
</StackPanel>
</StackPanel>
</ScrollViewer>
</spp:ContentLoader>
<spp:GameDetail
Margin="0,0,10,0"
Content="{Binding Source={x:Static s:GameAcceleratorService.Current}, Path=CurrentAcceleratorGame}"
IsVisible="{Binding Source={x:Static s:GameAcceleratorService.Current}, Path=CurrentAcceleratorGame.IsAccelerated}" />
</Panel>
<!-- 平台加速 -->
<ScrollViewer
Padding="0,0,10,0"
@@ -544,6 +484,67 @@
</Border>
</Grid>
<!-- 游戏加速 -->
<Panel x:DataType="spp:GameAcceleratorPageViewModel">
<Panel.DataContext>
<spp:GameAcceleratorPageViewModel />
</Panel.DataContext>
<spp:ContentLoader
IsLoading="{Binding Source={x:Static s:GameAcceleratorService.Current}, Path=IsLoadingGames, Mode=OneWay}"
IsShowNoResultText="{Binding !!!Games.Count}"
IsVisible="{Binding Source={x:Static s:GameAcceleratorService.Current}, Path=!CurrentAcceleratorGame.IsAccelerated}"
NoResultMessage="{x:Static spp:Strings.GameAccelerator_GamesEmptyTip}}">
<ScrollViewer
Name="GameScrollViewer"
Padding="0,0,10,0"
HorizontalScrollBarVisibility="Disabled"
IsVisible="{Binding !!Games.Count}"
VerticalScrollBarVisibility="Auto">
<StackPanel>
<ItemsRepeater ItemsSource="{Binding Games}" VerticalCacheLength="50">
<ItemsRepeater.Layout>
<UniformGridLayout
ItemsJustification="Start"
ItemsStretch="Uniform"
MinColumnSpacing="10"
MinItemHeight="180"
MinItemWidth="200"
MinRowSpacing="10" />
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate DataType="spp:XunYouGameViewModel">
<spp:GameAppItem ButtonCommand="{Binding GameAcceleratorCommand, Source={x:Static s:GameAcceleratorService.Current}}" />
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
<StackPanel
Margin="0,30"
HorizontalAlignment="Center"
Spacing="10">
<spp:Image2
Width="100"
Height="100"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RenderOptions.BitmapInterpolationMode="HighQuality"
Source="avares://BD.WTTS.Client.Avalonia/UI/Assets/Icons/empty.png"
Stretch="UniformToFill" />
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="已经到底啦! 没有找到想加速的游戏的话可以搜索添加哦~" />
</StackPanel>
</StackPanel>
</ScrollViewer>
</spp:ContentLoader>
<spp:GameDetail
Margin="0,0,10,0"
Content="{Binding Source={x:Static s:GameAcceleratorService.Current}, Path=CurrentAcceleratorGame}"
IsVisible="{Binding Source={x:Static s:GameAcceleratorService.Current}, Path=CurrentAcceleratorGame.IsAccelerated}" />
</Panel>
<!--<TabItem Header="加速日志">
</TabItem>-->
@@ -600,12 +601,21 @@
</AutoCompleteBox.ItemTemplate>
</AutoCompleteBox>
<!--<StackPanel
<StackPanel
Grid.ColumnSpan="2"
HorizontalAlignment="Right"
VerticalAlignment="Center"
IsVisible="{Binding Source={x:Static s:ProxyService.Current}, Path=ProxyStatus, Mode=OneWay}"
Spacing="8">
<StackPanel.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<CompiledBinding
Mode="OneWay"
Path="ProxyStatus"
Source="{x:Static s:ProxyService.Current}" />
<Binding Path="!#GameAccTab.IsSelected" />
</MultiBinding>
</StackPanel.IsVisible>
<StackPanel.Styles>
<Style Selector="TextBlock">
<Setter Property="VerticalAlignment" Value="Center" />
@@ -620,7 +630,7 @@
<Run Text="{x:Static spp:Strings.CommunityFix_EnableScriptService}" />
<Run Text="{Binding Source={x:Static s:ProxyService.Current}, Path=IsEnableScript, Converter={StaticResource StringFormatConverter}}" />
</TextBlock>
</StackPanel>-->
</StackPanel>
<spp:AdControl
Grid.Row="1"
@@ -1,6 +1,7 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using static System.Net.Mime.MediaTypeNames;
namespace BD.WTTS.UI.Views.Pages;
@@ -12,6 +13,8 @@ public partial class AcceleratorPage2 : PageBase<AcceleratorPageViewModel>
{
readonly Dictionary<string, string[]> dictPinYinArray = new();
List<int> acceleratorTabsSelectedIndexs = new();
/// <summary>
/// Initializes a new instance of the <see cref="AcceleratorPage2"/> class.
/// </summary>
@@ -21,7 +24,27 @@ public partial class AcceleratorPage2 : PageBase<AcceleratorPageViewModel>
var mode = ProxySettings.ProxyMode.Value;
InitializeComponent();
this.SetViewModel<AcceleratorPageViewModel>();
this.SetViewModel<AcceleratorPageViewModel>(true);
for (int i = 0; i < AcceleratorTabs.Items.Count; i++)
{
var item = AcceleratorTabs.Items[i];
if (item is Visual visual && visual.IsVisible)
{
acceleratorTabsSelectedIndexs.Add(i);
}
}
var itemCount = AcceleratorTabs.ItemCount;
var acceleratorTabsSelectedIndex = ProxySettings.AcceleratorTabsSelectedIndex.Value;
if (acceleratorTabsSelectedIndex >= 0 && acceleratorTabsSelectedIndex < itemCount)
{
if (acceleratorTabsSelectedIndexs.Contains(acceleratorTabsSelectedIndex))
{
AcceleratorTabs.SelectedIndex = acceleratorTabsSelectedIndex;
}
}
AcceleratorTabs.SelectionChanged += AcceleratorTabs_SelectionChanged;
//重置回保存的值
ProxySettings.ProxyMode.Value = mode;
@@ -31,7 +54,7 @@ public partial class AcceleratorPage2 : PageBase<AcceleratorPageViewModel>
ProxyService.Current.WhenValueChanged(x => x.ProxyStatus, false)
.Subscribe(x =>
{
AcceleratorTabs.SelectedIndex = x ? 2 : 1;
AcceleratorTabs.SelectedIndex = x ? 1 : 0;
}));
if (XunYouSDK.IsSupported)
@@ -48,15 +71,31 @@ public partial class AcceleratorPage2 : PageBase<AcceleratorPageViewModel>
else
{
GameAccTab.IsVisible = false;
AcceleratorTabs.SelectedIndex = 1;
AcceleratorTabs.SelectedIndex = 0;
}
});
SearchGameBox.DropDownClosed += SearchGameBox_DropDownClosed;
SearchGameBox.TextSelector = (_, _) => null;
SearchGameBox.TextSelector = (_, _) => null!;
SearchGameBox.TextFilter = GameContains;
}
void AcceleratorTabs_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
var value = AcceleratorTabs.SelectedIndex;
if (acceleratorTabsSelectedIndexs.Contains(value))
{
ProxySettings.AcceleratorTabsSelectedIndex.Value = value;
}
}
protected override void OnLoaded(RoutedEventArgs e)
{
base.OnLoaded(e);
if (AcceleratorTabs.SelectedIndex == default && ProxyService.Current.ProxyStatus)
AcceleratorTabs.SelectedIndex = 1;
}
private void SearchGameBox_DropDownClosed(object? sender, EventArgs e)
{
if (SearchGameBox.SelectedItem is XunYouGame xunYouGame && xunYouGame is not null)
@@ -98,17 +137,17 @@ public partial class AcceleratorPage2 : PageBase<AcceleratorPageViewModel>
}
/// <inheritdoc/>
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
//protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
//{
// base.OnDetachedFromVisualTree(e);
//try
//{
// ISettingsLoadService.Current.ForceSave<GameAcceleratorSettingsModel>();
//}
//catch (Exception ex)
//{
// Log.Error(nameof(AcceleratorPage), ex, "ForceSave fail.");
//}
}
// //try
// //{
// // ISettingsLoadService.Current.ForceSave<GameAcceleratorSettingsModel>();
// //}
// //catch (Exception ex)
// //{
// // Log.Error(nameof(AcceleratorPage), ex, "ForceSave fail.");
// //}
//}
}
@@ -7,9 +7,9 @@ namespace BD.WTTS.Services;
public sealed class ASFService : ReactiveObject
{
static ASFService? mCurrent;
static readonly Lazy<ASFService> mCurrent = new(() => new(), LazyThreadSafetyMode.ExecutionAndPublication);
public static ASFService Current => mCurrent ??= new();
public static ASFService Current => mCurrent.Value;
readonly IArchiSteamFarmService archiSteamFarmService = IArchiSteamFarmService.Instance;
@@ -13,7 +13,7 @@ public sealed class BasicPlatformSwitcher : IPlatformSwitcher
this.platformService = platformService;
}
bool BasicCopyInAccount(string accId, PlatformAccount platform)
async ValueTask<bool> BasicCopyInAccount(string accId, PlatformAccount platform)
{
var allIds = JTokenHelper.ReadDict(platform.IdsJsonPath);
var accName = allIds[accId];
@@ -63,10 +63,17 @@ public sealed class BasicPlatformSwitcher : IPlatformSwitcher
var regValue = regJson[accFile] ?? "";
if (!Registry2.SetRegistryKey(accFile[4..], regValue)) // Remove "REG:" and read data
if (DesktopBridge.IsRunningAsUwp)
{
Toast.Show(ToastIcon.Error, AppResources.Error_WriteRegistryFailed);
return false;
await SetEpicCurrentUserAsync(regValue);
}
else
{
if (!Registry2.SetRegistryKey(accFile[4..], regValue)) // Remove "REG:" and read data
{
Toast.Show(ToastIcon.Error, AppResources.Error_WriteRegistryFailed);
return false;
}
}
continue;
}
@@ -131,7 +138,7 @@ public sealed class BasicPlatformSwitcher : IPlatformSwitcher
// Copy saved files in
if (!string.IsNullOrEmpty(account?.AccountId))
{
if (!BasicCopyInAccount(account.AccountId, platform)) return;
if (!await BasicCopyInAccount(account.AccountId, platform)) return;
//Globals.AddTrayUser(platform.SafeName, $"+{platform.PrimaryId}:" + accId, accName, BasicSettings.TrayAccNumber); // Add to Tray list, using first Identifier
}
@@ -156,17 +163,21 @@ public sealed class BasicPlatformSwitcher : IPlatformSwitcher
//}
}
public ValueTask<bool> ClearCurrentLoginUser(PlatformAccount platform)
public async ValueTask<bool> ClearCurrentLoginUser(PlatformAccount platform)
{
var result = ClearCurrentLoginUserCore(platform);
return ValueTask.FromResult(result);
var result = await ClearCurrentLoginUserCore(platform);
return result;
}
bool ClearCurrentLoginUserCore(PlatformAccount platform)
async ValueTask<bool> ClearCurrentLoginUserCore(PlatformAccount platform)
{
// Foreach file/folder/reg in Platform.PathListToClear
if (platform.ClearPaths.Any_Nullable(accFile => !DeleteFileOrFolder(accFile, platform)))
return false;
foreach (var accFile in platform.ClearPaths!)
{
if (!await DeleteFileOrFolder(accFile, platform))
return false;
}
var uniqueIdFile = IOPath.ExpandEnvironmentVariables(platform.UniqueIdPath, platform.FolderPath);
@@ -185,7 +196,22 @@ public sealed class BasicPlatformSwitcher : IPlatformSwitcher
return true;
}
bool DeleteFileOrFolder(string accFile, PlatformAccount platform)
async ValueTask SetEpicCurrentUserAsync(string userName)
{
#if WINDOWS
string contents =
$"""
Windows Registry Editor Version 5.00
; {AssemblyInfo.Trademark} BD.WTTS.Services.Implementation.BasicPlatformSwitcher.SetEpicCurrentUserAsync
[HKEY_CURRENT_USER\Software\Epic Games\Unreal Engine\Identifiers]
"AccountId"="{userName}"
""";
var regpath = IOPath.GetCacheFilePath(WindowsPlatformServiceImpl.CacheTempDirName, "SwitchEpicUser", FileEx.Reg);
await WindowsPlatformServiceImpl.StartProcessRegeditAsync(regpath, contents);
#endif
}
async ValueTask<bool> DeleteFileOrFolder(string accFile, PlatformAccount platform)
{
// The "file" is a registry key
#if WINDOWS
@@ -198,7 +224,14 @@ public sealed class BasicPlatformSwitcher : IPlatformSwitcher
}
else
{
if (Registry2.SetRegistryKey(accFile[4..])) return true;
if (DesktopBridge.IsRunningAsUwp)
{
await SetEpicCurrentUserAsync(string.Empty);
return true;
}
else
if (Registry2.SetRegistryKey(accFile[4..])) return true;
}
Toast.Show(ToastIcon.Error, AppResources.Error_WriteRegistryFailed);
return false;
@@ -210,9 +243,10 @@ public sealed class BasicPlatformSwitcher : IPlatformSwitcher
{
if (accFile.StartsWith("JSON_SELECT"))
{
var path = accFile.Split("::")[1];
var path = IOPath.ExpandEnvironmentVariables(accFile.Split("::")[1]);
var selector = accFile.Split("::")[2];
JTokenHelper.ReplaceVarInJsonFile(path, selector, "");
return true;
}
}
@@ -479,7 +513,7 @@ public sealed class BasicPlatformSwitcher : IPlatformSwitcher
// Handle special args in username
//var hadSpecialProperties = ProcessSpecialAccName(specialString, accName, uniqueId);
var regJson = platform.UniqueIdPath.StartsWith("REG:") ? JTokenHelper.ReadRegJson(platform.RegJsonPath(name)) : new Dictionary<string, string>();
var regJson = platform.UniqueIdPath.StartsWith("REG:") ? JTokenHelper.ReadDict(platform.RegJsonPath(name)) : new Dictionary<string, string>();
foreach (var (accFile, savedFile) in platform.LoginFiles)
{
@@ -532,7 +566,7 @@ public sealed class BasicPlatformSwitcher : IPlatformSwitcher
var originalValueString = (string)originalValue;
originalValueString = IOPath.CleanPathIlegalCharacter(firstResult ? originalValueString.Split(delimiter).First() : originalValueString.Split(delimiter).Last());
if (!Directory.Exists(localCachePath)) Directory.CreateDirectory(localCachePath);
IOPath.DirCreateByNotExists(localCachePath);
JTokenHelper.SaveJsonFile(Path.Combine(localCachePath, savedFile), originalValueString);
continue;
}
@@ -217,6 +217,13 @@
MoreFlyout="{StaticResource SharedMenuFlyout}"
Status="{Binding Path=Res.UserChange_RecentLogin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Tags="{Binding AliasName}">
<ToolTip.Tip>
<TextBlock>
<Run FontSize="16" Text="{Binding DisplayName}" />
<LineBreak />
<Run Text="{Binding AliasName}" />
</TextBlock>
</ToolTip.Tip>
<spp:AppItem.Status>
<Border Classes="Status" IsVisible="{Binding MostRecent}">
<TextBlock Text="{Binding Path=Res.UserChange_RecentLogin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
@@ -11,7 +11,7 @@ public partial class GameAccountPage : PageBase<GameAccountPageViewModel>
public GameAccountPage()
{
InitializeComponent();
this.SetViewModel<GameAccountPageViewModel>();
this.SetViewModel<GameAccountPageViewModel>(true);
//DataContext ??= new GameAccountPageViewModel();
//this.WhenActivated(disposable =>
@@ -18,7 +18,7 @@ public sealed class IdleAppsPageViewModel : ViewModelBase
public IdleAppsPageViewModel()
{
CompositeDisposable.Add(GameLibrarySettings.AFKAppList.Subscribe(_ => Refresh_Click()));
CompositeDisposable.Add(GameLibrarySettings.AFKAppList.Subscribe(_ => Refresh_Click(), false));
RunStopBtnCommand = ReactiveCommand.Create<SteamApp>(RunStopBtn_Click);
DeleteButtonCommand = ReactiveCommand.Create<SteamApp>(DeleteButton_Click);
@@ -206,12 +206,18 @@ public sealed class IdleAppsPageViewModel : ViewModelBase
}
}
public void Refresh_Click()
public async void Refresh_Click()
{
var list = new ObservableCollection<SteamApp>();
if (GameLibrarySettings.AFKAppList.Any_Nullable())
{
int runOtherAppCount = 0;
while (SteamConnectService.Current.IsLoadingGameList)
{
await Task.Delay(500);
}
foreach (var item in GameLibrarySettings.AFKAppList.Value!)
{
var appInfo = SteamConnectService.Current.SteamApps.Items.FirstOrDefault(x => x.AppId == item.Key);
@@ -224,7 +230,7 @@ public sealed class IdleAppsPageViewModel : ViewModelBase
if (!runState.Process.HasExited)
{
appInfo.Process = runState.Process;
appInfo.Process.Exited += (object? _, EventArgs _) =>
appInfo.Process.Exited += (_, _) =>
{
SteamConnectService.Current.RuningSteamApps.TryRemove(appInfo.AppId, out runState);
};
@@ -4,7 +4,7 @@
"commandName": "Project",
"commandLineArgs": "run --rids win-x64 --hsm-sign --stm-upload",
"commandLineArgs_release": "run --rids win-x64 --hsm-sign --stm-upload",
"commandLineArgs_nsis": "nsis --rids win-x64 --hsm-sign --t 240306_1151276715468",
"commandLineArgs_nsis": "nsis --rids win-x64 --hsm-sign --t 240314_1523131679333",
"commandLineArgs_debug": "run --rids win-x64 --hsm-sign --stm-upload --debug",
"commandLineArgs_GenerateBridgeContentXml": "csproj --rids win-x64"
}
+9
View File
@@ -6486,6 +6486,15 @@ namespace BD.WTTS.Client.Resources {
}
}
/// <summary>
/// 查找类似 启动时默认打开页面 的本地化字符串。
/// </summary>
public static string Settings_UI_StartDefaultPage {
get {
return ResourceManager.GetString("Settings_UI_StartDefaultPage", resourceCulture);
}
}
/// <summary>
/// 查找类似 跟随系统 的本地化字符串。
/// </summary>
@@ -2927,5 +2927,8 @@
</data>
<data name="GameAccelerator_ChangeArea" xml:space="preserve">
<value>切换加速区服</value>
</data>
<data name="Settings_UI_StartDefaultPage" xml:space="preserve">
<value>启动时默认打开页面</value>
</data>
</root>
@@ -6,9 +6,9 @@ namespace BD.WTTS.Services;
/// </summary>
public sealed class AdvertiseService : ReactiveObject
{
static AdvertiseService? mCurrent;
static readonly Lazy<AdvertiseService> mCurrent = new(() => new(), LazyThreadSafetyMode.ExecutionAndPublication);
public static AdvertiseService Current => mCurrent ??= new();
public static AdvertiseService Current => mCurrent.Value;
[Reactive]
public ObservableCollection<AdvertisementDTO>? HorizontalBannerAdvertisements { get; set; }
@@ -6,9 +6,9 @@ namespace BD.WTTS.Services;
public sealed class UserService : ReactiveObject
{
static UserService? mCurrent;
static readonly Lazy<UserService> mCurrent = new(() => new(), LazyThreadSafetyMode.ExecutionAndPublication);
public static UserService Current => mCurrent ??= new();
public static UserService Current => mCurrent.Value;
readonly IUserManager userManager = IUserManager.Instance;
readonly IMicroServiceClient csc = IMicroServiceClient.Instance;
@@ -179,6 +179,12 @@ public sealed class UserService : ReactiveObject
public async Task RefreshUserAsync(IdentityUserInfoDTO? user, bool refreshCurrentUser = true)
{
#if DEBUG
if (user != null)
{
user.UserType = UserType.Sponsor;
}
#endif
User = user;
this.RaisePropertyChanged(nameof(IsAuthenticated));
@@ -231,7 +237,7 @@ public sealed class UserService : ReactiveObject
}
else
{
Browser2.Open(string.Format(Constants.Urls.OfficialWebsite_Fast_Login_, token.AccessToken, HttpUtility.UrlEncode(token.ExpiresIn.ToString()), HttpUtility.UrlEncode(url)));
Browser2.Open(string.Format(Constants.Urls.OfficialWebsite_Fast_Login_, token.AccessToken, HttpUtility.UrlEncode(token.ExpiresIn.ToString("R")), HttpUtility.UrlEncode(url)));
}
}
@@ -6,9 +6,9 @@ namespace BD.WTTS.Services;
public sealed class NoticeService : ReactiveObject
{
static NoticeService? mCurrent;
static readonly Lazy<NoticeService> mCurrent = new(() => new(), LazyThreadSafetyMode.ExecutionAndPublication);
public static NoticeService Current => mCurrent ??= new();
public static NoticeService Current => mCurrent.Value;
//readonly INotificationRepository notificationRepo = Ioc.Get<INotificationRepository>();
@@ -30,7 +30,7 @@ public sealed class NoticeService : ReactiveObject
//NoticeTypes = new();
}
public async void GetNewsAsync()
public async Task GetNewsAsync()
{
var client = Instance.Message;
var result = await client.GetMessage(IApplication.ClientPlatform, OfficialMessageType.News);
@@ -5,9 +5,9 @@ namespace BD.WTTS.Services;
public sealed class ResourceService : ReactiveObject
{
static ResourceService? mCurrent;
static readonly Lazy<ResourceService> mCurrent = new(() => new(), LazyThreadSafetyMode.ExecutionAndPublication);
public static ResourceService Current => mCurrent ??= new();
public static ResourceService Current => mCurrent.Value;
public static readonly IReadOnlyDictionary<string, string> Languages;
public static readonly IReadOnlyDictionary<string, string> SteamLanguages;
@@ -17,7 +17,9 @@ public sealed class SteamConnectService
public const int SteamAFKMaxCount = 32;
public static SteamConnectService Current { get; } = new();
static readonly Lazy<SteamConnectService> mCurrent = new(() => new(), LazyThreadSafetyMode.ExecutionAndPublication);
public static SteamConnectService Current => mCurrent.Value;
readonly ISteamworksLocalApiService swLocalService = ISteamworksLocalApiService.Instance;
readonly ISteamService stmService = ISteamService.Instance;
@@ -516,6 +518,7 @@ public sealed class SteamConnectService
{
return;
}
SteamUsers.Clear();
SteamUsers.AddOrUpdate(list);
#region 加载备注信息和 JumpList
@@ -4,8 +4,8 @@ namespace BD.WTTS.Services.Implementation;
internal sealed class ViewModelManager : ReactiveObject, IViewModelManager
{
bool disposedValue;
Dictionary<Type, byte[]> viewModelDataCaches = new();
Dictionary<Type, ViewModelBase> viewModelCaches = new();
ConcurrentDictionary<Type, byte[]> viewModelDataCaches = new();
ConcurrentDictionary<Type, ViewModelBase> viewModelCaches = new();
/// <summary>
/// 获取为当前主窗口提供的数据
@@ -69,7 +69,8 @@ internal sealed class ViewModelManager : ReactiveObject, IViewModelManager
var vmType = viewModel.GetType();
var value = Serializable.SMP2(vmType, viewModel);
viewModelDataCaches[vmType] = value;
viewModelCaches.Remove(vmType);
viewModelDataCaches[vmType] = value;
viewModelCaches.TryRemove(vmType, out var _);
}
void Dispose(bool disposing)
@@ -7,9 +7,9 @@ namespace BD.WTTS.Services;
[Obsolete("不再使用常驻通知")]
public sealed class ToastService : ReactiveObject
{
static ToastService? mCurrent;
static readonly Lazy<ToastService> mCurrent = new(() => new(), LazyThreadSafetyMode.ExecutionAndPublication);
public static ToastService Current => mCurrent ??= new();
public static ToastService Current => mCurrent.Value;
static readonly Lazy<bool> mIsSupported = new(() => Ioc.Get<IToast>() is ToastImpl);
@@ -118,6 +118,11 @@ public partial interface IUISettings
/// </summary>
HashSet<string>? SortMenuTabs { get; set; }
/// <summary>
/// 程序启动时默认页
/// </summary>
string? StartDefaultPageName { get; set; }
/// <summary>
/// 主题的默认值
/// </summary>
@@ -208,4 +213,9 @@ public partial interface IUISettings
/// </summary>
const HashSet<string>? DefaultSortMenuTabs = null;
/// <summary>
/// 程序启动时默认页的默认值
/// </summary>
const string? DefaultStartDefaultPageName = null;
}
+12
View File
@@ -159,6 +159,12 @@ public sealed partial class UISettings_ : IUISettings, ISettings, ISettings<UISe
[MPKey(17), MP2Key(17), JsonPropertyOrder(17)]
public HashSet<string>? SortMenuTabs { get; set; } = IUISettings.DefaultSortMenuTabs;
/// <summary>
/// 程序启动时默认页
/// </summary>
[MPKey(18), MP2Key(18), JsonPropertyOrder(18)]
public string? StartDefaultPageName { get; set; } = IUISettings.DefaultStartDefaultPageName;
}
public static partial class UISettings
@@ -279,4 +285,10 @@ public static partial class UISettings
public static SettingsProperty<string, HashSet<string>, UISettings_> SortMenuTabs { get; }
= new(DefaultSortMenuTabs);
/// <summary>
/// 程序启动时默认页
/// </summary>
public static SettingsProperty<string, UISettings_> StartDefaultPageName { get; }
= new(DefaultStartDefaultPageName);
}
@@ -6,7 +6,7 @@ public interface IWindowViewModel : IPageViewModel
/// <summary>
/// 关闭当前 ViewModel 绑定的窗口
/// </summary>
Action<bool> Close { get; set; }
Action<bool>? Close { get; set; }
/// <summary>
/// 显示当前 ViewModel 绑定的窗口
@@ -41,13 +41,13 @@ public sealed class HomePageViewModel : TabItemViewModel
//GetServerContent();
}
public override void Activation()
{
base.Activation();
GetServerContent();
}
//public override void Activation()
//{
// base.Activation();
// GetServerContent();
//}
public async void GetServerContent()
public async Task GetServerContent()
{
try
{
@@ -33,6 +33,31 @@ public sealed partial class SettingsPageViewModel : TabItemViewModel
set => this.RaiseAndSetIfChanged(ref _SelectFont, value);
}
MenuTabItemViewModel _SelectDefaultPage =
IViewModelManager.Instance.MainWindow2.TabItems.OfType<MenuTabItemViewModel>().FirstOrDefault(
s =>
{
if (string.IsNullOrEmpty(UISettings.StartDefaultPageName.Value))
{
return s.Id == "Welcome";
}
return s.Id == UISettings.StartDefaultPageName.Value;
});
[IgnoreDataMember, MPIgnore, MP2Ignore, N_JsonIgnore, S_JsonIgnore]
public MenuTabItemViewModel SelectDefaultPage
{
get => _SelectDefaultPage;
set
{
this.RaiseAndSetIfChanged(ref _SelectDefaultPage, value);
if (!string.IsNullOrEmpty(value?.Id))
{
UISettings.StartDefaultPageName.Value = value.Id;
}
}
}
[IgnoreDataMember, MPIgnore, MP2Ignore, N_JsonIgnore, S_JsonIgnore]
public ICommand? SelectImage_Click { get; }
+7
View File
@@ -15,6 +15,13 @@ static partial class VisualStudioAppCenterSDK
{
internal static void Init()
{
if (DateTime.UtcNow >= new DateTime(2025, 3, 31, default, default, default, DateTimeKind.Utc))
{
// Visual Studio App Center is scheduled for retirement on March 31, 2025.
// https://learn.microsoft.com/en-us/appcenter/retirement
return;
}
var appSecret = AppSecret;
if (string.IsNullOrWhiteSpace(appSecret))
return;