diff --git a/Directory.Packages.props b/Directory.Packages.props index f1d9b205d..6bddd34fa 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -257,6 +257,7 @@ + diff --git a/references/ArchiSteamFarm b/references/ArchiSteamFarm index 461ed4096..fc238ca10 160000 --- a/references/ArchiSteamFarm +++ b/references/ArchiSteamFarm @@ -1 +1 @@ -Subproject commit 461ed409644a7cec567fc64e2a8552cdc79945db +Subproject commit fc238ca1065aa264d92e51b9d1bf65755e0b28e5 diff --git a/src/Common.CoreLib/DateTimeFormat.cs b/src/Common.CoreLib/DateTimeFormat.cs index add69fdd0..94120b980 100644 --- a/src/Common.CoreLib/DateTimeFormat.cs +++ b/src/Common.CoreLib/DateTimeFormat.cs @@ -31,5 +31,7 @@ namespace System public const string RFC1123 = "r"; public const string File = "yyyy-MM-dd HHmmssfffffff"; + + public const string YearMonth = "yyyy/MM"; } } \ No newline at end of file diff --git a/src/Common.CoreLib/Extensions/DateTimeExtensions.cs b/src/Common.CoreLib/Extensions/DateTimeExtensions.cs index 57823015b..a405efb45 100644 --- a/src/Common.CoreLib/Extensions/DateTimeExtensions.cs +++ b/src/Common.CoreLib/Extensions/DateTimeExtensions.cs @@ -25,7 +25,7 @@ namespace System /// /// public static DateTimeOffset GetCurrentMonth(this DateTimeOffset dt) - => new(dt.Year, dt.Month, 1, 0, 0, 0, TimeSpan.Zero); + => new(dt.Year, dt.Month, 1, 0, 0, 0, dt.Offset); /// /// 根据时间日期获取当前月(最后一天) @@ -33,6 +33,6 @@ namespace System /// /// public static DateTimeOffset GetCurrentMonthLastDay(this DateTimeOffset dt) - => new DateTimeOffset(dt.Year, dt.Month, 1, 0, 0, 0, TimeSpan.Zero).AddMonths(1).AddDays(-1); + => new DateTimeOffset(dt.Year, dt.Month, 1, 0, 0, 0, dt.Offset).AddMonths(1).AddDays(-1); } } diff --git a/src/ST.Client.Android/ST.Client.Android.csproj b/src/ST.Client.Android/ST.Client.Android.csproj index 1c8b06add..640d220d5 100644 --- a/src/ST.Client.Android/ST.Client.Android.csproj +++ b/src/ST.Client.Android/ST.Client.Android.csproj @@ -119,6 +119,7 @@ + @@ -456,6 +457,9 @@ Resources\layout\activity_detail_auth.xml + + Resources\layout\activity_donate.xml + Resources\layout\activity_encryption_auth.xml @@ -582,6 +586,9 @@ Resources\layout\layout_card_steam_auth_trade_confirmation_content.xml + + Resources\layout\layout_donate_user.xml + Resources\layout\layout_file_or_dir.xml @@ -730,27 +737,4 @@ Resources\xml\local_auth_appwidget_info.xml - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/src/ST.Client.Android/UI/Activities/DonateActivity.cs b/src/ST.Client.Android/UI/Activities/DonateActivity.cs new file mode 100644 index 000000000..fec23a279 --- /dev/null +++ b/src/ST.Client.Android/UI/Activities/DonateActivity.cs @@ -0,0 +1,86 @@ +using Android.App; +using Android.Content.PM; +using Android.OS; +using Android.Runtime; +using Android.Views; +using AndroidX.RecyclerView.Widget; +using Binding; +using ReactiveUI; +using System.Application.UI.Adapters; +using System.Application.UI.ViewModels; +using System.Application.UI.Resx; +using Google.Android.Material.DatePicker; + +namespace System.Application.UI.Activities +{ + [Register(JavaPackageConstants.Activities + nameof(ExplorerActivity))] + [Activity(Theme = ManifestConstants.MainTheme2_NoActionBar, + LaunchMode = LaunchMode.SingleTask, + ConfigurationChanges = ManifestConstants.ConfigurationChanges)] + internal sealed class DonateActivity : BaseActivity, DatePickerHelper.IOnPositiveButtonClickListener + { + protected override int? LayoutResource => Resource.Layout.activity_donate; + + protected override AboutPageViewModel? OnCreateViewModel() => AboutPageViewModel.Instance; + + protected override void OnCreate2(Bundle? savedInstanceState) + { + base.OnCreate2(savedInstanceState); + + this.SetSupportActionBarWithNavigationClick(binding!.toolbar, true); + + R.Subscribe(() => + { + Title = AppResources.About_Donate; + if (binding == null) return; + binding.tvTitle.Text = AppResources.About_Donate_Title; + binding.btnUID.Text = AppResources.ClickHereCopyUIDToClipboard; + binding.tvDesc.Text = AppResources.About_Donate_Desc; + binding.tvDonateType.Text = AppResources.About_DonateType; + binding.tvDonateRecord.Text = AppResources.About_DonateRecord; + binding.tvDonateRecordPrompt.Text = AppResources.About_DonateRecord_Prompt; + }).AddTo(this); + + binding.btn_afdian.Text = "爱发电"; + binding.btn_ko_fi.Text = "Ko-fi"; + binding.btn_patreon.Text = "Patreon"; + + if (!ViewModel!.DonateList.Any_Nullable()) + { + ViewModel.LoadDonateRankListData(); + } + + binding.tbDate.Text = ViewModel!.DonateFliterDate.ToString(DateTimeFormat.YearMonth); + + var adapter = new DonateUserAdapter(ViewModel); + binding.rvDonateList.SetAdapter(adapter); + + SetOnClickListener( + binding.btnUID, + binding.layoutDate); + } + + protected override void OnClick(View view) + { + base.OnClick(view); + + if (view.Id == Resource.Id.btnUID) + { + ViewModel!.UIDCommand.Invoke(); + return; + } + else if (view.Id == Resource.Id.layoutDate) + { + this.ShowBirthDatePicker(); + return; + } + } + + void DatePickerHelper.IOnPositiveButtonClickListener.OnPositiveButtonClick(DateTimeOffset selection) + { + ViewModel!.DonateFliterDate = selection.GetCurrentMonth(); + } + + void IMaterialPickerOnPositiveButtonClickListener.OnPositiveButtonClick(Java.Lang.Object p0) => this.OnPositiveButtonClick(p0); + } +} diff --git a/src/ST.Client.Android/UI/Adapters/DonateUserAdapter.cs b/src/ST.Client.Android/UI/Adapters/DonateUserAdapter.cs new file mode 100644 index 000000000..59ab1d3b0 --- /dev/null +++ b/src/ST.Client.Android/UI/Adapters/DonateUserAdapter.cs @@ -0,0 +1,54 @@ +using Android.Views; +using Binding; +using ReactiveUI; +using System.Application.UI.Resx; +using System.Application.UI.ViewModels; +using TViewHolder = System.Application.UI.Adapters.DonateUserViewHolder; +using TViewModel = System.Application.Models.RankingResponse; +using static System.Application.UI.Resx.AppResources; +using System.Application.Services; +using System.Collections.Generic; +using DynamicData; +using System.Linq; +using System.Collections.ObjectModel; +using System.Application.Models; + +namespace System.Application.UI.Adapters +{ + sealed class DonateUserAdapter : BaseReactiveRecycleViewAdapter + { + public DonateUserAdapter(ObservableCollection collection) : base(collection) + { + + } + + public DonateUserAdapter(AboutPageViewModel viewModel) : base(viewModel.DonateList!) + { + + } + + public override int? GetLayoutResource(int viewType) + { + return Resource.Layout.layout_donate_user; + } + } + + sealed class DonateUserViewHolder : BaseReactiveViewHolder + { + readonly layout_donate_user binding; + + public DonateUserViewHolder(View itemView) : base(itemView) + { + binding = new(itemView); + } + + public override void OnBind() + { + base.OnBind(); + + binding.ivAvatar.SetImageSource(ViewModel!.Avatar); + binding.tvNickName.Text = ViewModel.Name; + binding.tvValue.Text = ViewModel.Amount.ToString("C", ViewModel.CurrencyCode.GetCultureInfo()); + } + } +} diff --git a/src/ST.Client.Android/UI/Controllers/AboutController.cs b/src/ST.Client.Android/UI/Controllers/AboutController.cs index b47ceb156..31dd5c584 100644 --- a/src/ST.Client.Android/UI/Controllers/AboutController.cs +++ b/src/ST.Client.Android/UI/Controllers/AboutController.cs @@ -66,6 +66,9 @@ namespace System.Application.UI.Controllers { switch (e.Current.Id) { + case PreferenceButton.捐助: + Activity.StartActivity(); + break; case PreferenceButton.检查更新: ViewModel!.CheckUpdateCommand.Invoke(); break; diff --git a/src/ST.Client/UI/ViewModels/Pages/About/AboutPageViewModel.Mobile.cs b/src/ST.Client/UI/ViewModels/Pages/About/AboutPageViewModel.Mobile.cs index 968e8f67e..851774f3c 100644 --- a/src/ST.Client/UI/ViewModels/Pages/About/AboutPageViewModel.Mobile.cs +++ b/src/ST.Client/UI/ViewModels/Pages/About/AboutPageViewModel.Mobile.cs @@ -23,6 +23,7 @@ namespace System.Application.UI.ViewModels public enum PreferenceButton { + 捐助, 检查更新 = 1, 更新日志, 常见问题疑难解答, @@ -57,6 +58,7 @@ namespace System.Application.UI.ViewModels { var title = id switch { + PreferenceButton.捐助 => AppResources.About_Donate, PreferenceButton.检查更新 => AppResources.CheckUpdate, PreferenceButton.更新日志 => AppResources.About_UpdateLog, PreferenceButton.常见问题疑难解答 => AppResources.About_FAQ, diff --git a/src/ST.Client/UI/ViewModels/Pages/About/AboutPageViewModel.cs b/src/ST.Client/UI/ViewModels/Pages/About/AboutPageViewModel.cs index 50538d8bc..f76252403 100644 --- a/src/ST.Client/UI/ViewModels/Pages/About/AboutPageViewModel.cs +++ b/src/ST.Client/UI/ViewModels/Pages/About/AboutPageViewModel.cs @@ -131,18 +131,16 @@ namespace System.Application.UI.ViewModels } }).AddTo(this); } - else - { - DonateFliterDate = DateTimeOffset.Now.GetCurrentMonth(); - this.WhenValueChanged(x => x.DonateFliterDate, false) - .Subscribe(x => LoadDonateRankListData(true)); - } + DonateFliterDate = DateTimeOffset.Now.GetCurrentMonth(); + + this.WhenValueChanged(x => x.DonateFliterDate, false) + .Subscribe(x => LoadDonateRankListData(true)); } public override void Activation() { - if (IApplication.IsDesktopPlatform) + if (IApplication.IsDesktopPlatform) // Android 上手动调用加载 { LoadDonateRankListData(true); } @@ -158,7 +156,17 @@ namespace System.Application.UI.ViewModels public async void LoadDonateRankListData(bool refresh = false, bool nextPage = false) { if (refresh) - DonateList = null; + { + if (IApplication.IsDesktopPlatform) + { + DonateList = null; + } + else + { + // Android 上 List 关联 Adapter 在初始化时,这里不能执行 null 赋值 + DonateList?.Clear(); + } + } DonateList ??= new(); diff --git a/src/ST.Services.CloudService.Models/Models/ExternalTransaction/RankingResponse.cs b/src/ST.Services.CloudService.Models/Models/ExternalTransaction/RankingResponse.cs index 514172cfb..daeb3a201 100644 --- a/src/ST.Services.CloudService.Models/Models/ExternalTransaction/RankingResponse.cs +++ b/src/ST.Services.CloudService.Models/Models/ExternalTransaction/RankingResponse.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Text; using MPIgnore = MessagePack.IgnoreMemberAttribute; @@ -12,7 +11,7 @@ using S_JsonProperty = System.Text.Json.Serialization.JsonPropertyNameAttribute; namespace System.Application.Models { [MPObj] - public class RankingResponse + public partial class RankingResponse { /// /// 用户昵称 @@ -62,4 +61,11 @@ namespace System.Application.Models [S_JsonProperty("5")] public CurrencyCode CurrencyCode { get; set; } } + +#if MVVM_VM + partial class RankingResponse : ReactiveUI.ReactiveObject + { + + } +#endif } diff --git a/src/ST.Tools.AndroidResourceLink/Program.cs b/src/ST.Tools.AndroidResourceLink/Program.cs index 1b9900318..c723a8625 100644 --- a/src/ST.Tools.AndroidResourceLink/Program.cs +++ b/src/ST.Tools.AndroidResourceLink/Program.cs @@ -38,7 +38,7 @@ foreach (var file in files) } sb.AppendLine(" "); Array.ForEach(bottom, x => sb.AppendLine(x)); -csprojContent = sb.ToString(); +csprojContent = sb.ToString().Trim(); File.WriteAllText(androidProjFilePath, csprojContent); Console.WriteLine("OK"); \ No newline at end of file diff --git a/src/ST/CurrencyCode.cs b/src/ST/CurrencyCode.cs index 9a5da4e12..e0d574f45 100644 --- a/src/ST/CurrencyCode.cs +++ b/src/ST/CurrencyCode.cs @@ -36,10 +36,8 @@ namespace System public static partial class CurrencyCodeEnumExtensions { public static CultureInfo? GetCultureInfo(this CurrencyCode eCurrencyCode) - { - return CultureInfo.GetCultures(CultureTypes.SpecificCultures) - .FirstOrDefault(culture => new RegionInfo(culture.LCID).ISOCurrencySymbol == eCurrencyCode.ToString()); - } + => CultureInfo.GetCultures(CultureTypes.SpecificCultures) + .FirstOrDefault(culture => new RegionInfo(culture.LCID).ISOCurrencySymbol == eCurrencyCode.ToString()); public static CurrencyCode GetCurrencyCode(this ICurrencyCodeString @interface, CurrencyCode @default = CurrencyCode.CNY) => Enum.TryParse(@interface.CurrencyCode, true, out CurrencyCode currencyCode) ? currencyCode : @default;