diff --git a/src/BiliLite.UWP/BiliLite.UWP.csproj b/src/BiliLite.UWP/BiliLite.UWP.csproj index 0e940315..f82f6883 100644 --- a/src/BiliLite.UWP/BiliLite.UWP.csproj +++ b/src/BiliLite.UWP/BiliLite.UWP.csproj @@ -143,6 +143,9 @@ DynamicItemV2Control.xaml + + PlayerToast.xaml + UserFollowingTagsFlyout.xaml @@ -151,16 +154,33 @@ + + + + + + + + + + UserDynamicPage.xaml + + + + + + + @@ -213,6 +233,7 @@ DynamicSpacePage.xaml + @@ -722,9 +743,6 @@ RecommendPage.xaml - - UserDynamicPage.xaml - LiveDetailPage.xaml @@ -958,6 +976,10 @@ MSBuild:Compile Designer + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -966,6 +988,10 @@ MSBuild:Compile Designer + + MSBuild:Compile + Designer + Designer MSBuild:Compile @@ -1118,10 +1144,6 @@ Designer MSBuild:Compile - - Designer - MSBuild:Compile - Designer MSBuild:Compile @@ -1322,9 +1344,15 @@ 107.3.0 + + 2.0.2 + 1.0.7 + + 0.17.0 + 0.16.8 diff --git a/src/BiliLite.UWP/Controls/DataTemplateSelectors/UserDynamicItemV2DataTemplateSelector.cs b/src/BiliLite.UWP/Controls/DataTemplateSelectors/UserDynamicItemV2DataTemplateSelector.cs index 54f8981a..6104b425 100644 --- a/src/BiliLite.UWP/Controls/DataTemplateSelectors/UserDynamicItemV2DataTemplateSelector.cs +++ b/src/BiliLite.UWP/Controls/DataTemplateSelectors/UserDynamicItemV2DataTemplateSelector.cs @@ -38,6 +38,11 @@ DataTemplate SelectRowTemplate(UserDynamicItemDataTemplateSelector selector, { Constants.DynamicTypes.MUSIC, (selector, model) => selector.MusicTemplate }, { Constants.DynamicTypes.COMMON_SQUARE, (selector, model) => selector.CommonSquareTemplate }, { Constants.DynamicTypes.LIVE_RCMD, (selector, model) => selector.LiveRcmdTemplate }, + { Constants.DynamicTypes.LIVE, (selector, model) => selector.LiveTemplate }, + { Constants.DynamicTypes.CUSTOM_SEASON, (selector, model) => selector.CustomSeasonTemplate }, + { Constants.DynamicTypes.CUSTOM_ARTICLE, (selector, model) => selector.CustomArticleTemplate }, + { Constants.DynamicTypes.UGC_SEASON, (selector, model) => selector.UgcSeasonTemplate }, + { Constants.DynamicTypes.FOLD, (selector, model) => selector.FoldTemplate }, }; } @@ -63,6 +68,16 @@ DataTemplate SelectRowTemplate(UserDynamicItemDataTemplateSelector selector, public DataTemplate LiveRcmdTemplate { get; set; } + public DataTemplate LiveTemplate { get; set; } + + public DataTemplate UgcSeasonTemplate { get; set; } + + public DataTemplate FoldTemplate { get; set; } + + public DataTemplate CustomSeasonTemplate { get; set; } + + public DataTemplate CustomArticleTemplate { get; set; } + public DataTemplate OtherTemplate { get; set; } protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) diff --git a/src/BiliLite.UWP/Controls/Dynamic/DynamicItemV2Control.xaml b/src/BiliLite.UWP/Controls/Dynamic/DynamicItemV2Control.xaml index b638f5ad..3820962e 100644 --- a/src/BiliLite.UWP/Controls/Dynamic/DynamicItemV2Control.xaml +++ b/src/BiliLite.UWP/Controls/Dynamic/DynamicItemV2Control.xaml @@ -56,6 +56,10 @@ + + + + @@ -108,6 +114,8 @@ - + - + - + + + + diff --git a/src/BiliLite.UWP/Controls/Dynamic/DynamicV2Template.xaml b/src/BiliLite.UWP/Controls/Dynamic/DynamicV2Template.xaml index 07f91c81..32588bf4 100644 --- a/src/BiliLite.UWP/Controls/Dynamic/DynamicV2Template.xaml +++ b/src/BiliLite.UWP/Controls/Dynamic/DynamicV2Template.xaml @@ -16,7 +16,12 @@ Draw2x2Template="{StaticResource DynamicDraw2x2}" Draw3x3Template="{StaticResource DynamicDraw3x3}" CommonSquareTemplate="{StaticResource DynamicCommonSquare}" - LiveRcmdTemplate="{StaticResource DynamicLiveRcmd}"> + LiveRcmdTemplate="{StaticResource DynamicLiveRcmd}" + LiveTemplate="{StaticResource DynamicLiveRcmd}" + UgcSeasonTemplate="{StaticResource DynamicUgcSeason}" + FoldTemplate="{StaticResource DynamicFold}" + CustomSeasonTemplate="{StaticResource DynamicCustomSeason}" + CustomArticleTemplate="{StaticResource DynamicCustomArticle}"> @@ -147,23 +152,37 @@ - - - - - - - + + + + + + + + + + + + + 资源已失效 + + + @@ -197,6 +216,35 @@ + + + + + + + + @@ -340,6 +388,77 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/BiliLite.UWP/Controls/PlayerControl.xaml b/src/BiliLite.UWP/Controls/PlayerControl.xaml index c9e56aaa..13918e7a 100644 --- a/src/BiliLite.UWP/Controls/PlayerControl.xaml +++ b/src/BiliLite.UWP/Controls/PlayerControl.xaml @@ -586,19 +586,8 @@ - - - - - - + + diff --git a/src/BiliLite.UWP/Controls/PlayerControl.xaml.cs b/src/BiliLite.UWP/Controls/PlayerControl.xaml.cs index ac4c834d..3b77c24a 100644 --- a/src/BiliLite.UWP/Controls/PlayerControl.xaml.cs +++ b/src/BiliLite.UWP/Controls/PlayerControl.xaml.cs @@ -54,6 +54,7 @@ public sealed partial class PlayerControl : UserControl, IDisposable private readonly bool m_useNsDanmaku = true; private readonly IDanmakuController m_danmakuController; private readonly PlayControlViewModel m_viewModel; + private readonly PlayerToastService m_playerToastService; public event PropertyChangedEventHandler PropertyChanged; private GestureRecognizer gestureRecognizer; private void DoPropertyChanged(string name) @@ -63,6 +64,8 @@ private void DoPropertyChanged(string name) InteractionVideoVM interactionVideoVM; + public Canvas PlayerToastContainer => ToolTipContainer; + /// /// 铺满窗口事件 /// @@ -126,6 +129,8 @@ public BiliPlayUrlQualitesInfo playUrlInfo public PlayerControl() { m_viewModel = new PlayControlViewModel(); + m_playerToastService = App.ServiceProvider.GetRequiredService(); + m_playerToastService.Init(this); this.InitializeComponent(); dispRequest = new DisplayRequest(); playerHelper = new PlayerVM(); @@ -234,25 +239,6 @@ private async void PlayerControl_Loaded(object sender, RoutedEventArgs e) danmuTimer.Start(); timer_focus.Start(); - - // 检查音量是否偏低 - if (Player.Volume > 0.95) return; - var toolTipText = ""; - if (Player.Volume == 0) - { - toolTipText = "静音"; - } - else - { - toolTipText = "音量:" + Player.Volume.ToString("P"); - } - - TxtToolTip.Text = toolTipText; - ToolTip.Background = new SolidColorBrush(Color.FromArgb(90, 240, 240, 240)); - ToolTip.Visibility = Visibility.Visible; - await Task.Delay(2000); - ToolTip.Visibility = Visibility.Collapsed; - ToolTip.Background = new SolidColorBrush(Color.FromArgb(204, 255, 255, 255)); } private async void _systemMediaTransportControls_ButtonPressed(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args) @@ -322,10 +308,9 @@ private async void PlayerControl_KeyDown(CoreWindow sender, KeyEventArgs args) _position = 0; } Player.Position = _position; - TxtToolTip.Text = "进度:" + TimeSpan.FromSeconds(Player.Position).ToString(@"hh\:mm\:ss"); - ToolTip.Visibility = Visibility.Visible; - await Task.Delay(2000); - ToolTip.Visibility = Visibility.Collapsed; + + m_playerToastService.Show( + PlayerToastService.PROGRESS_KEY, "进度:" + TimeSpan.FromSeconds(Player.Position).ToString(@"hh\:mm\:ss")); } } @@ -344,34 +329,25 @@ private async void PlayerControl_KeyDown(CoreWindow sender, KeyEventArgs args) _position = Player.Duration; } Player.Position = _position; - TxtToolTip.Text = "进度:" + TimeSpan.FromSeconds(Player.Position).ToString(@"hh\:mm\:ss"); - ToolTip.Visibility = Visibility.Visible; - await Task.Delay(2000); - ToolTip.Visibility = Visibility.Collapsed; + + m_playerToastService.Show( + PlayerToastService.PROGRESS_KEY, "进度:" + TimeSpan.FromSeconds(Player.Position).ToString(@"hh\:mm\:ss")); } } break; case Windows.System.VirtualKey.Up: Player.Volume += 0.1; - TxtToolTip.Text = "音量:" + Player.Volume.ToString("P"); - ToolTip.Visibility = Visibility.Visible; - await Task.Delay(2000); - ToolTip.Visibility = Visibility.Collapsed; + m_playerToastService.Show(PlayerToastService.VOLUME_KEY, "音量:" + Player.Volume.ToString("P")); break; case Windows.System.VirtualKey.Down: Player.Volume -= 0.1; - if (Player.Volume == 0) + var txtToolTipText = "静音"; + if (Player.Volume > 0) { - TxtToolTip.Text = "静音"; + txtToolTipText = "音量:" + Player.Volume.ToString("P"); } - else - { - TxtToolTip.Text = "音量:" + Player.Volume.ToString("P"); - } - ToolTip.Visibility = Visibility.Visible; - await Task.Delay(2000); - ToolTip.Visibility = Visibility.Collapsed; + m_playerToastService.Show(PlayerToastService.VOLUME_KEY, txtToolTipText); break; case Windows.System.VirtualKey.Escape: IsFullScreen = false; @@ -404,10 +380,8 @@ private async void PlayerControl_KeyDown(CoreWindow sender, KeyEventArgs args) _position = Player.Duration; } Player.Position = _position; - TxtToolTip.Text = "跳过OP(快进90秒)"; - ToolTip.Visibility = Visibility.Visible; - await Task.Delay(2000); - ToolTip.Visibility = Visibility.Collapsed; + + m_playerToastService.Show(PlayerToastService.MSG_KEY, "跳过OP(快进90秒)"); } } break; @@ -456,7 +430,7 @@ private async void PlayerControl_KeyDown(CoreWindow sender, KeyEventArgs args) } BottomCBSpeed.SelectedIndex += 1; - + m_playerToastService.Show(PlayerToastService.SPEED_KEY,(BottomCBSpeed.SelectedItem as ComboBoxItem).Content.ToString()); break; case Windows.System.VirtualKey.F2: case (Windows.System.VirtualKey)222: @@ -467,6 +441,7 @@ private async void PlayerControl_KeyDown(CoreWindow sender, KeyEventArgs args) return; } BottomCBSpeed.SelectedIndex -= 1; + m_playerToastService.Show(PlayerToastService.SPEED_KEY, (BottomCBSpeed.SelectedItem as ComboBoxItem).Content.ToString()); break; case Windows.System.VirtualKey.F3: case Windows.System.VirtualKey.V: @@ -485,6 +460,29 @@ private async void PlayerControl_KeyDown(CoreWindow sender, KeyEventArgs args) } } + /// + /// 检查音量和亮度是否偏低 + /// + private void CheckVolumeAndBrightnessLower() + { + // 检查音量是否偏低 + if (Player.Volume > 0.95) return; + var toolTipText = ""; + if (Player.Volume == 0) + { + toolTipText = "静音"; + } + else + { + toolTipText = "音量:" + Player.Volume.ToString("P"); + } + + m_playerToastService.Show(PlayerToastService.VOLUME_KEY, toolTipText); + + // 检查亮度是否偏低 + if (Math.Abs(Brightness - 1) > 0.8) return; + m_playerToastService.Show(PlayerToastService.BRIGHTNESS_KEY, "亮度:" + Math.Abs(Brightness - 1).ToString("P")); + } private void LoadDanmuSetting() { @@ -660,16 +658,21 @@ private void LoadDanmuSetting() } private void LoadPlayerSetting() { - //音量 - Player.Volume = SettingService.GetValue(SettingConstants.Player.PLAYER_VOLUME, 1.0); - SliderVolume.ValueChanged += new RangeBaseValueChangedEventHandler((e, args) => + Player.Volume = SettingService.GetValue(SettingConstants.Player.PLAYER_VOLUME, SettingConstants.Player.DEFAULT_PLAYER_VOLUME); + + var lockPlayerVolume = SettingService.GetValue(SettingConstants.Player.LOCK_PLAYER_VOLUME, SettingConstants.Player.DEFAULT_LOCK_PLAYER_VOLUME); + if (!lockPlayerVolume) { - SettingService.SetValue(SettingConstants.Player.PLAYER_VOLUME, SliderVolume.Value); - }); + SliderVolume.ValueChanged += new RangeBaseValueChangedEventHandler((e, args) => + { + SettingService.SetValue(SettingConstants.Player.PLAYER_VOLUME, SliderVolume.Value); + }); + } //亮度 - //_brightness = SettingService.GetValue(SettingConstants.Player.PLAYER_BRIGHTNESS, 0); - //BrightnessShield.Opacity = _brightness; + lockBrightness = SettingService.GetValue(SettingConstants.Player.LOCK_PLAYER_BRIGHTNESS, SettingConstants.Player.DEFAULT_LOCK_PLAYER_BRIGHTNESS); + _brightness = SettingService.GetValue(SettingConstants.Player.PLAYER_BRIGHTNESS, SettingConstants.Player.DEFAULT_PLAYER_BRIGHTNESS); + BrightnessShield.Opacity = _brightness; //播放模式 var selectedValue = (PlayUrlCodecMode)SettingService.GetValue(SettingConstants.Player.DEFAULT_VIDEO_TYPE, (int)DefaultVideoTypeOptions.DEFAULT_VIDEO_TYPE); @@ -1056,6 +1059,8 @@ private async Task SetPlayItem(int index) await GetPlayerInfo(); + CheckVolumeAndBrightnessLower(); + Player.ABPlay = VideoPlayHistoryHelper.FindABPlayHistory(CurrentPlayItem); if (Player.ABPlay == null) { @@ -1922,6 +1927,7 @@ private void TopBtnCloseDanmaku_Click(object sender, RoutedEventArgs e) double ssValue = 0; bool ManipulatingBrightness = false; double _brightness = 0; + private bool lockBrightness = true; PlayerHoldingAction m_playerHoldingAction; double Brightness { @@ -1930,8 +1936,8 @@ double Brightness { _brightness = value; BrightnessShield.Opacity = value; - //SettingHelper.SetValue(SettingHelper.Player.PLAYER_BRIGHTNESS, _brightness); - //} + if(!lockBrightness) + SettingService.SetValue(SettingConstants.Player.PLAYER_BRIGHTNESS, _brightness); } } private void InitializeGesture() @@ -1986,23 +1992,20 @@ private void StopHolding() private void StartHighRateSpeedPlay() { - TxtToolTip.Text = "倍速播放中"; - ToolTip.Visibility = Visibility.Visible; + m_playerToastService.KeepStart(PlayerToastService.ACCELERATING_KEY, "倍速播放中"); var highRatePlaySpeed = SettingService.GetValue(SettingConstants.Player.HIGH_RATE_PLAY_SPEED, 2.0d); Player.SetRate(highRatePlaySpeed); } private void StopHighRateSpeedPlay() { - ToolTip.Visibility = Visibility.Collapsed; + m_playerToastService.KeepClose(PlayerToastService.ACCELERATING_KEY); Player.SetRate(SettingService.GetValue(SettingConstants.Player.DEFAULT_VIDEO_SPEED, 1.0d)); } private void OnManipulationStarted(object sender, ManipulationStartedEventArgs e) { ssValue = 0; - //TxtToolTip.Text = ""; - ToolTip.Visibility = Visibility.Visible; if (e.Position.X < this.ActualWidth / 2) ManipulatingBrightness = true; @@ -2068,7 +2071,6 @@ private void OnManipulationCompleted(object sender, ManipulationCompletedEventAr { Player.Position = Player.Position + ssValue; } - ToolTip.Visibility = Visibility.Collapsed; } private void Grid_PointerPressed(object sender, PointerRoutedEventArgs e) @@ -2204,8 +2206,7 @@ private void HandleSlideProgressDelta(double delta) pos = Player.Duration; //txt_Post.Text = ts.Hours.ToString("00") + ":" + ts.Minutes.ToString("00") + ":" + ts.Seconds.ToString("00") + "/" + mediaElement.MediaPlayer.PlaybackSession.NaturalDuration.TimeSpan.Hours.ToString("00") + ":" + mediaElement.MediaPlayer.PlaybackSession.NaturalDuration.TimeSpan.Minutes.ToString("00") + ":" + mediaElement.MediaPlayer.PlaybackSession.NaturalDuration.TimeSpan.Seconds.ToString("00"); - TxtToolTip.Text = TimeSpan.FromSeconds(pos).ToString(@"hh\:mm\:ss"); - //Notify.ShowMessageToast(ts.Hours.ToString("00") + ":" + ts.Minutes.ToString("00") + ":" + ts.Seconds.ToString("00"), 3000); + m_playerToastService.Show(PlayerToastService.PROGRESS_KEY, TimeSpan.FromSeconds(pos).ToString(@"hh\:mm\:ss")); } private void HandleSlideVolumeDelta(double delta) @@ -2226,8 +2227,7 @@ private void HandleSlideVolumeDelta(double delta) Player.Volume = volume; //slider_V.Value += d; } - TxtToolTip.Text = "音量:" + Player.Volume.ToString("P"); - //Notify.ShowMessageToast("音量:" + mediaElement.MediaPlayer.Volume.ToString("P"), 3000); + m_playerToastService.Show(PlayerToastService.VOLUME_KEY, "音量:" + Player.Volume.ToString("P")); } private void HandleSlideBrightnessDelta(double delta) { @@ -2240,7 +2240,7 @@ private void HandleSlideBrightnessDelta(double delta) { Brightness = Math.Max(Brightness - dd, 0); } - TxtToolTip.Text = "亮度:" + Math.Abs(Brightness - 1).ToString("P"); + m_playerToastService.Show(PlayerToastService.BRIGHTNESS_KEY, "亮度:" + Math.Abs(Brightness - 1).ToString("P")); } #endregion private void BottomBtnList_Click(object sender, RoutedEventArgs e) @@ -2443,7 +2443,7 @@ private void Player_PlayBufferEnd(object sender, EventArgs e) m_danmakuController.Resume(); } - private void Player_PlayMediaEnded(object sender, EventArgs e) + private async void Player_PlayMediaEnded(object sender, EventArgs e) { if (CurrentPlayItem.is_interaction) { @@ -2458,7 +2458,10 @@ private void Player_PlayMediaEnded(object sender, EventArgs e) } _logger.Debug("视频结束,上报进度"); - playerHelper.ReportHistory(CurrentPlayItem, Player.Duration).RunWithoutAwait(); + await playerHelper.ReportHistory(CurrentPlayItem, Player.Duration); + + _logger.Debug("进度归0"); + await playerHelper.ReportHistory(CurrentPlayItem, 0); //列表顺序播放 if (PlayerSettingPlayMode.SelectedIndex == 0) { diff --git a/src/BiliLite.UWP/Controls/PlayerToast.xaml b/src/BiliLite.UWP/Controls/PlayerToast.xaml new file mode 100644 index 00000000..b9779b25 --- /dev/null +++ b/src/BiliLite.UWP/Controls/PlayerToast.xaml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/BiliLite.UWP/Controls/PlayerToast.xaml.cs b/src/BiliLite.UWP/Controls/PlayerToast.xaml.cs new file mode 100644 index 00000000..1b3e7dd6 --- /dev/null +++ b/src/BiliLite.UWP/Controls/PlayerToast.xaml.cs @@ -0,0 +1,45 @@ +using System.Threading.Tasks; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media.Animation; +using BiliLite.ViewModels; + +//https://go.microsoft.com/fwlink/?LinkId=234236 上介绍了“用户控件”项模板 + +namespace BiliLite.Controls +{ + public sealed partial class PlayerToast : UserControl + { + private readonly PlayerToastViewModel m_viewModel; + + public PlayerToast(PlayerToastViewModel viewModel) + { + m_viewModel = viewModel; + this.InitializeComponent(); + } + + public string Text + { + set => m_viewModel.Text = value; + } + + public void Show() + { + var storyboard = (Storyboard)this.Resources["ShowToast"]; + storyboard.Begin(); + } + + public async Task Hide() + { + var storyboard = (Storyboard)this.Resources["HideToast"]; + storyboard.Begin(); + var tcs = new TaskCompletionSource(); + + storyboard.Completed += (s, e) => + { + tcs.SetResult(true); + }; + storyboard.Begin(); + await tcs.Task; + } + } +} diff --git a/src/BiliLite.UWP/Extensions/ControlsExtensions.cs b/src/BiliLite.UWP/Extensions/ControlsExtensions.cs index 955ccf7a..c192b9d3 100644 --- a/src/BiliLite.UWP/Extensions/ControlsExtensions.cs +++ b/src/BiliLite.UWP/Extensions/ControlsExtensions.cs @@ -1,4 +1,5 @@ -using BiliLite.Dialogs; +using BiliLite.Controls; +using BiliLite.Dialogs; using Microsoft.Extensions.DependencyInjection; namespace BiliLite.Extensions @@ -9,6 +10,7 @@ public static IServiceCollection AddControls(this IServiceCollection services) { services.AddTransient(); services.AddTransient(); + services.AddTransient(); return services; } } diff --git a/src/BiliLite.UWP/Extensions/MapperExtensions.cs b/src/BiliLite.UWP/Extensions/MapperExtensions.cs index 60751366..144a79de 100644 --- a/src/BiliLite.UWP/Extensions/MapperExtensions.cs +++ b/src/BiliLite.UWP/Extensions/MapperExtensions.cs @@ -144,7 +144,16 @@ public static IServiceCollection AddMapper(this IServiceCollection services) src.Modules.FirstOrDefault(x => x.ModuleType == DynModuleType.ModuleOpusSummary).ModuleOpusSummary)) .ForMember(dest => dest.Stat, opt => opt.MapFrom(src => - src.Modules.FirstOrDefault(x => x.ModuleType == DynModuleType.ModuleStat).ModuleStat)); + src.Modules.FirstOrDefault(x => x.ModuleType == DynModuleType.ModuleStat).ModuleStat)) + .ForMember(dest => dest.Fold, + opt => opt.MapFrom(src => + src.Modules.FirstOrDefault(x => x.ModuleType == DynModuleType.ModuleFold).ModuleFold)) + .ForMember(dest => dest.SourceJson, + opt => opt.MapFrom(src => + src.ToString())); + + expression.CreateMap(); + expression.CreateMap(); })); services.AddSingleton(mapper); diff --git a/src/BiliLite.UWP/Extensions/ViewModelExtensions.cs b/src/BiliLite.UWP/Extensions/ViewModelExtensions.cs index 1b309f2d..a4e5d98a 100644 --- a/src/BiliLite.UWP/Extensions/ViewModelExtensions.cs +++ b/src/BiliLite.UWP/Extensions/ViewModelExtensions.cs @@ -34,6 +34,8 @@ public static IServiceCollection AddViewModels(this IServiceCollection services) services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); return services; } } diff --git a/src/BiliLite.UWP/MainPage.xaml.cs b/src/BiliLite.UWP/MainPage.xaml.cs index 6a47ea94..dd3df3df 100644 --- a/src/BiliLite.UWP/MainPage.xaml.cs +++ b/src/BiliLite.UWP/MainPage.xaml.cs @@ -12,6 +12,7 @@ using BiliLite.Models.Common; using BiliLite.Extensions; using BiliLite.Services; +using Microsoft.Extensions.DependencyInjection; // https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x804 上介绍了“空白页”项模板 @@ -20,12 +21,15 @@ namespace BiliLite /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class MainPage : Windows.UI.Xaml.Controls.Page + public sealed partial class MainPage : Windows.UI.Xaml.Controls.Page, IMainPage { private static readonly ILogger _logger = GlobalLogger.FromCurrentType(); + private readonly ShortcutKeyService m_shortcutKeyService; public MainPage() { + m_shortcutKeyService = App.ServiceProvider.GetRequiredService(); + m_shortcutKeyService.SetMainPage(this); this.InitializeComponent(); // 处理标题栏 var coreTitleBar = CoreApplication.GetCurrentView().TitleBar; @@ -42,6 +46,26 @@ public MainPage() App.Current.Suspending += Current_Suspending; // Window.Current.Content.PointerPressed += Content_PointerPressed; + + Window.Current.CoreWindow.Dispatcher.AcceleratorKeyActivated += Dispatcher_AcceleratorKeyActivated; + } + + public object CurrentPage + { + get + { + if (!(tabView.SelectedItem is TabViewItem tabItem)) return null; + if (!(tabItem.Content is Frame frame)) return null; + return frame.Content; + } + } + + private void Dispatcher_AcceleratorKeyActivated(Windows.UI.Core.CoreDispatcher sender, Windows.UI.Core.AcceleratorKeyEventArgs args) + { + if (args.EventType.ToString().Contains("Down")) + { + m_shortcutKeyService.HandleKeyDown(args.VirtualKey); + } } private async void Current_Suspending(object sender, Windows.ApplicationModel.SuspendingEventArgs e) @@ -259,7 +283,6 @@ private void CloseSelectedTabKeyboardAccelerator_Invoked(KeyboardAccelerator sen ClosePage((TabViewItem)tabView.SelectedItem); } args.Handled = true; - } private void tabView_TabItemsChanged(TabView sender, IVectorChangedEventArgs args) diff --git a/src/BiliLite.UWP/Models/Attributes/SettingDefaultValueAttribute.cs b/src/BiliLite.UWP/Models/Attributes/SettingDefaultValueAttribute.cs new file mode 100644 index 00000000..3e8746b4 --- /dev/null +++ b/src/BiliLite.UWP/Models/Attributes/SettingDefaultValueAttribute.cs @@ -0,0 +1,8 @@ +using System; + +namespace BiliLite.Models.Attributes +{ + public class SettingDefaultValueAttribute : Attribute + { + } +} diff --git a/src/BiliLite.UWP/Models/Attributes/SettingKeyAttribute.cs b/src/BiliLite.UWP/Models/Attributes/SettingKeyAttribute.cs new file mode 100644 index 00000000..6cdd8913 --- /dev/null +++ b/src/BiliLite.UWP/Models/Attributes/SettingKeyAttribute.cs @@ -0,0 +1,19 @@ +using System; + +namespace BiliLite.Models.Attributes +{ + public class SettingKeyAttribute:Attribute + { + public SettingKeyAttribute() + { + Type = typeof(string); + } + + public SettingKeyAttribute(Type type) + { + Type = type; + } + + public Type Type { get; set; } + } +} diff --git a/src/BiliLite.UWP/Models/Builders/DynamicItemDisplayModelBuilder.cs b/src/BiliLite.UWP/Models/Builders/DynamicItemDisplayModelBuilder.cs index 821b3282..ab32b07b 100644 --- a/src/BiliLite.UWP/Models/Builders/DynamicItemDisplayModelBuilder.cs +++ b/src/BiliLite.UWP/Models/Builders/DynamicItemDisplayModelBuilder.cs @@ -82,7 +82,7 @@ public DynamicItemDisplayModelBuilder SwitchType(IMapper mapper, JObject card, J m_displayViewModel.ImagesInfo = imgs; break; } - case UserDynamicDisplayType.Repost when card.ContainsKey("origin_user"): + case UserDynamicDisplayType.Repost when card.ContainsKey("origin_user") && card["origin_user"].ToString() != string.Empty: { var originUser = JsonConvert.DeserializeObject(card["origin_user"].ToString()); var model = new DynamicCardModel diff --git a/src/BiliLite.UWP/Models/Common/Anime/AnimeRankModel.cs b/src/BiliLite.UWP/Models/Common/Anime/AnimeRankModel.cs index 2526638d..15449ffa 100644 --- a/src/BiliLite.UWP/Models/Common/Anime/AnimeRankModel.cs +++ b/src/BiliLite.UWP/Models/Common/Anime/AnimeRankModel.cs @@ -16,11 +16,11 @@ public class AnimeRankModel : ISeasonItem [JsonProperty("index_show")] public string IndexShow { get; set; } - public int Follow { get; set; } + public long Follow { get; set; } - public int Danmaku { get; set; } + public long Danmaku { get; set; } - public int View { get; set; } + public long View { get; set; } [JsonProperty("show_badge")] public bool ShowBadge => !string.IsNullOrEmpty(Badge); diff --git a/src/BiliLite.UWP/Models/Common/Constants.cs b/src/BiliLite.UWP/Models/Common/Constants.cs index 6fce164c..d83ae20d 100644 --- a/src/BiliLite.UWP/Models/Common/Constants.cs +++ b/src/BiliLite.UWP/Models/Common/Constants.cs @@ -101,6 +101,18 @@ public static class DynamicTypes public const string COMMON_SQUARE = "CommonSquare"; public const string LIVE_RCMD = "LiveRcmd"; + + public const string LIVE = "Live"; + + public const string UGC_SEASON = "UgcSeason"; + + public const string FOLD = "Fold"; + + public const string BANNER = "Banner"; + + public const string CUSTOM_SEASON = "CustomSeason"; + + public const string CUSTOM_ARTICLE = "CustomArticle"; } } } diff --git a/src/BiliLite.UWP/Models/Common/Dynamic/DynamicDescModel.cs b/src/BiliLite.UWP/Models/Common/Dynamic/DynamicDescModel.cs index d6ad714d..3e212886 100644 --- a/src/BiliLite.UWP/Models/Common/Dynamic/DynamicDescModel.cs +++ b/src/BiliLite.UWP/Models/Common/Dynamic/DynamicDescModel.cs @@ -14,11 +14,11 @@ public class DynamicDescModel public string Rid { get; set; } - public int View { get; set; } + public long View { get; set; } - public int Like { get; set; } + public long Like { get; set; } - public int Comment { get; set; } + public long Comment { get; set; } [JsonProperty("is_liked")] public int IsLiked { get; set; } diff --git a/src/BiliLite.UWP/Models/Common/Dynamic/DynamicVideoCardStatModel.cs b/src/BiliLite.UWP/Models/Common/Dynamic/DynamicVideoCardStatModel.cs index 79d93dab..eb436cc7 100644 --- a/src/BiliLite.UWP/Models/Common/Dynamic/DynamicVideoCardStatModel.cs +++ b/src/BiliLite.UWP/Models/Common/Dynamic/DynamicVideoCardStatModel.cs @@ -2,18 +2,18 @@ { public class DynamicVideoCardStatModel { - public int Coin { get; set; } + public long Coin { get; set; } - public int Danmaku { get; set; } + public long Danmaku { get; set; } - public int Favorite { get; set; } + public long Favorite { get; set; } - public int Like { get; set; } + public long Like { get; set; } - public int Reply { get; set; } + public long Reply { get; set; } - public int Share { get; set; } + public long Share { get; set; } - public int View { get; set; } + public long View { get; set; } } } \ No newline at end of file diff --git a/src/BiliLite.UWP/Models/Common/Enumerates.cs b/src/BiliLite.UWP/Models/Common/Enumerates.cs index 5e61cd59..a8d96782 100644 --- a/src/BiliLite.UWP/Models/Common/Enumerates.cs +++ b/src/BiliLite.UWP/Models/Common/Enumerates.cs @@ -435,4 +435,12 @@ public enum SeasonIdType SeasonId, EpId, } + + public enum UserDynamicShowType + { + All = 0, + Video = 1, + Season = 2, + Article = 3 + } } \ No newline at end of file diff --git a/src/BiliLite.UWP/Models/Common/Home/DefaultHomeNavItems.cs b/src/BiliLite.UWP/Models/Common/Home/DefaultHomeNavItems.cs index 829c070c..bf47bf1f 100644 --- a/src/BiliLite.UWP/Models/Common/Home/DefaultHomeNavItems.cs +++ b/src/BiliLite.UWP/Models/Common/Home/DefaultHomeNavItems.cs @@ -1,9 +1,60 @@ -using System.Collections.Generic; +using System; +using BiliLite.Services; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; namespace BiliLite.Models.Common.Home { public static class DefaultHomeNavItems { + private static readonly ILogger _logger = GlobalLogger.FromCurrentType(); + + public static List CheckHomeNavItems(List navList) + { + var defaultItems = GetDefaultHomeNavItems(); + defaultItems.AddRange(GetDefaultHideHomeNavItems()); + var result = new List(navList); + foreach (var homeNavItem in navList.Where(homeNavItem => + defaultItems.All(x => x.Title != homeNavItem.Title || x.Page != homeNavItem.Page))) + { + result.Remove(homeNavItem); + } + SettingService.SetValue(SettingConstants.UI.HOEM_ORDER, result); + + return result; + } + + public static List GetHomeNavItems() + { + var homeNavItemList = new List(); + var tempHomeNavItemList = SettingService.GetValue>(SettingConstants.UI.HOEM_ORDER, + null); + + if (tempHomeNavItemList == null) + { + homeNavItemList = DefaultHomeNavItems.GetDefaultHomeNavItems(); + return homeNavItemList; + } + else + { + foreach (var item in tempHomeNavItemList) + { + try + { + var navItem = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(item)); + homeNavItemList.Add(navItem); + } + catch (Exception ex) + { + _logger.Warn(ex.Message, ex); + } + } + } + homeNavItemList = DefaultHomeNavItems.CheckHomeNavItems(homeNavItemList); + return homeNavItemList; + } + public static List GetDefaultHomeNavItems() { return new List() @@ -143,7 +194,7 @@ public static List GetDefaultHideHomeNavItems() Title = "我的收藏", NeedLogin = true, Show = false - } + }, }; } } diff --git a/src/BiliLite.UWP/Models/Common/Recommend/RecommendItemArgsModel.cs b/src/BiliLite.UWP/Models/Common/Recommend/RecommendItemArgsModel.cs index 734803ef..46c6929a 100644 --- a/src/BiliLite.UWP/Models/Common/Recommend/RecommendItemArgsModel.cs +++ b/src/BiliLite.UWP/Models/Common/Recommend/RecommendItemArgsModel.cs @@ -18,6 +18,6 @@ public class RecommendItemArgsModel public string Rname { get; set; } - public int Aid { get; set; } + public long Aid { get; set; } } } \ No newline at end of file diff --git a/src/BiliLite.UWP/Models/Common/Season/SeasonDetailStatModel.cs b/src/BiliLite.UWP/Models/Common/Season/SeasonDetailStatModel.cs index f40534b0..777da5f5 100644 --- a/src/BiliLite.UWP/Models/Common/Season/SeasonDetailStatModel.cs +++ b/src/BiliLite.UWP/Models/Common/Season/SeasonDetailStatModel.cs @@ -2,20 +2,20 @@ { public class SeasonDetailStatModel { - public int Coins { get; set; } + public long Coins { get; set; } - public int Danmakus { get; set; } + public long Danmakus { get; set; } - public int Favorites { get; set; } + public long Favorites { get; set; } public string Followers { get; set; } public string Play { get; set; } - public int Reply { get; set; } + public long Reply { get; set; } - public int Share { get; set; } + public long Share { get; set; } - public int Views { get; set; } + public long Views { get; set; } } } \ No newline at end of file diff --git a/src/BiliLite.UWP/Models/Common/SettingConstants.cs b/src/BiliLite.UWP/Models/Common/SettingConstants.cs index 84ac4dec..8770c526 100644 --- a/src/BiliLite.UWP/Models/Common/SettingConstants.cs +++ b/src/BiliLite.UWP/Models/Common/SettingConstants.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using BiliLite.Models.Attributes; using BiliLite.Services; namespace BiliLite.Models.Common @@ -10,75 +11,98 @@ public class UI /// /// 加载原图 /// + [SettingKey(typeof(bool))] public const string ORTGINAL_IMAGE = "originalImage"; /// /// 主题颜色 /// + [SettingKey(typeof(int))] public const string THEME_COLOR = "themeColor"; /// /// 主题,0为默认,1为浅色,2为深色 /// + [SettingKey(typeof(int))] public const string THEME = "theme"; /// /// 显示模式,0为多标签,1为单窗口,2为多窗口 /// + [SettingKey(typeof(int))] public const string DISPLAY_MODE = "displayMode"; /// /// 缓存首页 /// + [SettingKey(typeof(bool))] public const string CACHE_HOME = "cacheHome"; /// /// 首页排序 /// + [SettingKey(typeof(object))] public const string HOEM_ORDER = "homePageOrder"; /// /// 右侧详情宽度 /// + [SettingKey(typeof(double))] public const string RIGHT_DETAIL_WIDTH = "PlayerRightDetailWidth"; /// /// 右侧详情宽度可调整 /// + [SettingKey(typeof(bool))] public const string RIGHT_WIDTH_CHANGEABLE = "PlayerRightDetailWidthChangeable"; + [SettingKey(typeof(double))] + public const string DYNAMIC_COMMENT_WIDTH = "DynamicCommentWidth"; + + [SettingDefaultValue] + public const double DEFAULT_DYNAMIC_COMMENT_WIDTH = 480; + /// /// 图片圆角半径 /// + [SettingKey(typeof(double))] public const string IMAGE_CORNER_RADIUS = "ImageCornerRadius"; /// /// 视频详情显示封面 /// + [SettingKey(typeof(bool))] public const string SHOW_DETAIL_COVER = "showDetailCover"; + /// /// 新窗口打开图片预览 /// + [SettingKey(typeof(bool))] public const string NEW_WINDOW_PREVIEW_IMAGE = "newWindowPreviewImage"; /// /// 动态显示样式 /// + [SettingKey(typeof(int))] public const string DYNAMIC_DISPLAY_MODE = "dynamicDiaplayMode"; /// /// 首页推荐样式 /// + [SettingKey(typeof(int))] public const string RECMEND_DISPLAY_MODE = "recomendDiaplayMode"; /// /// 右侧选项卡 /// + [SettingKey(typeof(int))] public const string DETAIL_DISPLAY = "detailDisplay"; /// /// 动态显示样式 /// + [SettingKey] public const string BACKGROUND_IMAGE = "BackgroundImage"; /// /// 鼠标功能键行为 /// + [SettingKey(typeof(int))] public const string MOUSE_MIDDLE_ACTION = "MouseMiddleAction"; /// /// 隐藏赞助按钮 @@ -87,35 +111,42 @@ public class UI /// /// 隐藏广告按钮 /// + [SettingKey(typeof(bool))] public const string HIDE_AD = "HideAD"; /// /// 浏览器打开无法处理的链接 /// + [SettingKey(typeof(bool))] public const string OPEN_URL_BROWSER = "OpenUrlWithBrowser"; /// /// 启用长评论折叠 /// + [SettingKey(typeof(bool))] public const string ENABLE_COMMENT_SHRINK = "EnableCommentShrink"; /// /// 折叠评论长度 /// + [SettingKey(typeof(int))] public const string COMMENT_SHRINK_LENGTH = "CommentShrinkLength"; /// /// 默认折叠评论长度 /// + [SettingDefaultValue] public const int COMMENT_SHRINK_DEFAULT_LENGTH = 75; /// /// 显示评论热门回复 /// + [SettingKey(typeof(bool))] public const string SHOW_HOT_REPLIES = "ShowHotReplies"; /// /// 显示评论热门回复默认选项 /// + [SettingDefaultValue] public const bool DEFAULT_SHOW_HOT_REPLIES = true; } @@ -124,62 +155,75 @@ public class Account /// /// 登录后ACCESS_KEY /// + [SettingKey] public const string ACCESS_KEY = "accesskey"; /// /// 登录后REFRESH_KEY /// + [SettingKey] public const string REFRESH_KEY = "refreshkey"; /// /// 到期时间 /// + [SettingKey(typeof(object))] public const string ACCESS_KEY_EXPIRE_DATE = "expireDate"; /// /// 用户ID /// + [SettingKey(typeof(long))] public const string USER_ID = "uid"; /// /// 到期时间 /// + [SettingKey(typeof(object))] public const string USER_PROFILE = "userProfile"; /// /// 是否web登录 /// + [SettingKey(typeof(bool))] public const string IS_WEB_LOGIN = "isWebLogin"; /// /// Cookies /// + [SettingKey] public const string BILIBILI_COOKIES = "BiliBiliCookies"; /// /// 登录用AppKey /// + [SettingKey] public const string LOGIN_APP_KEY_SECRET = "LoginAppKeySecret"; /// /// 默认登录用AppKey /// + [SettingDefaultValue] public static ApiKeyInfo DefaultLoginAppKeySecret = ApiHelper.AndroidKey; /// /// Wbi令牌ImgKey参数 /// + [SettingKey] public const string WBI_IMG_KEY = "WbiImgKey"; /// /// Wbi令牌SubKey参数 /// + [SettingKey] public const string WBI_SUB_KEY = "WbiSubKey"; /// /// Wbi令牌参数获取时间(unix时间戳) /// + [SettingKey(typeof(long))] public const string WBI_KEY_TIME = "WbiKeyTime"; /// /// Wbi令牌参数刷新时间(单位分钟,暂定2小时) /// + [SettingDefaultValue] public const int WBI_KEY_REFRESH_TIME = 120; } @@ -188,90 +232,111 @@ public class VideoDanmaku /// /// 默认弹幕引擎 /// + [SettingDefaultValue] public const DanmakuEngineType DEFAULT_DANMAKU_ENGINE = DanmakuEngineType.NSDanmaku; /// /// 弹幕引擎 /// + [SettingKey(typeof(int))] public const string DANMAKU_ENGINE = "DanmakuEngine"; /// /// 显示弹幕 Visibility /// + [SettingKey(typeof(object))] public const string SHOW = "VideoDanmuShow"; /// /// 弹幕缩放 double /// + [SettingKey(typeof(double))] public const string FONT_ZOOM = "VideoDanmuFontZoom"; /// /// 弹幕显示区域 /// + [SettingKey(typeof(double))] public const string AREA = "VideoDanmuArea"; /// /// 弹幕速度 int /// + [SettingKey(typeof(int))] public const string SPEED = "VideoDanmuSpeed"; /// /// 弹幕加粗 bool /// + [SettingKey(typeof(bool))] public const string BOLD = "VideoDanmuBold"; /// /// 弹幕边框样式 int /// + [SettingKey(typeof(int))] public const string BORDER_STYLE = "VideoDanmuStyle"; /// /// 弹幕合并 bool /// + [SettingKey(typeof(bool))] public const string MERGE = "VideoDanmuMerge"; /// /// 弹幕半屏显示 bool /// + [SettingKey(typeof(bool))] public const string DOTNET_HIDE_SUBTITLE = "VideoDanmuDotHide"; /// /// 弹幕透明度 double,0-1 /// + [SettingKey(typeof(double))] public const string OPACITY = "VideoDanmuOpacity"; /// /// 隐藏顶部 bool /// + [SettingKey(typeof(bool))] public const string HIDE_TOP = "VideoDanmuHideTop"; /// /// 隐藏底部 bool /// + [SettingKey(typeof(bool))] public const string HIDE_BOTTOM = "VideoDanmuHideBottom"; /// /// 隐藏滚动 bool /// + [SettingKey(typeof(bool))] public const string HIDE_ROLL = "VideoDanmuHideRoll"; /// /// 隐藏高级弹幕 bool /// + [SettingKey(typeof(bool))] public const string HIDE_ADVANCED = "VideoDanmuHideAdvanced"; /// /// 关键词屏蔽 ObservableCollection /// + [SettingKey(typeof(object))] public const string SHIELD_WORD = "VideoDanmuShieldWord"; /// /// 用户屏蔽 ObservableCollection /// + [SettingKey(typeof(object))] public const string SHIELD_USER = "VideoDanmuShieldUser"; /// /// 正则屏蔽 ObservableCollection /// + [SettingKey(typeof(object))] public const string SHIELD_REGULAR = "VideoDanmuShieldRegular"; /// /// 顶部距离 /// + [SettingKey(typeof(double))] public const string TOP_MARGIN = "VideoDanmuTopMargin"; /// /// 最大数量 /// + [SettingKey(typeof(double))] public const string MAX_NUM = "VideoDanmuMaxNum"; /// /// 弹幕云屏蔽等级 /// + [SettingKey(typeof(int))] public const string SHIELD_LEVEL = "VideoDanmuShieldLevel"; } @@ -280,49 +345,62 @@ public class Live /// /// 直播默认清晰度 /// + [SettingKey(typeof(int))] public const string DEFAULT_QUALITY = "LiveDefaultQuality"; /// /// 显示弹幕 Visibility /// + [SettingKey(typeof(object))] public const string SHOW = "LiveDanmuShow"; + + [SettingKey(typeof(double))] public const string AREA = "LiveDanmuArea"; /// /// 弹幕缩放 double /// + [SettingKey(typeof(double))] public const string FONT_ZOOM = "LiveDanmuFontZoom"; /// /// 弹幕速度 int /// + [SettingKey(typeof(int))] public const string SPEED = "LiveDanmuSpeed"; /// /// 弹幕加粗 bool /// + [SettingKey(typeof(bool))] public const string BOLD = "LiveDanmuBold"; /// /// 弹幕边框样式 int /// + [SettingKey(typeof(int))] public const string BORDER_STYLE = "LiveDanmuStyle"; /// /// 弹幕半屏显示 bool /// + [SettingKey(typeof(bool))] public const string DOTNET_HIDE_SUBTITLE = "LiveDanmuDotHide"; /// /// 弹幕透明度 double,0-1 /// + [SettingKey(typeof(double))] public const string OPACITY = "LiveDanmuOpacity"; /// /// 关键词屏蔽 ObservableCollection /// + [SettingKey(typeof(object))] public const string SHIELD_WORD = "LiveDanmuShieldWord"; /// /// 硬解 bool /// + [SettingKey(typeof(bool))] public const string HARDWARE_DECODING = "LiveHardwareDecoding"; /// /// 自动开启宝箱 bool /// + [SettingKey(typeof(bool))] public const string AUTO_OPEN_BOX = "LiveAutoOpenBox"; /// @@ -333,16 +411,19 @@ public class Live /// /// 直播弹幕清理 /// + [SettingKey(typeof(int))] public const string DANMU_CLEAN_COUNT = "LiveCleanCount"; /// /// 隐藏进场 /// + [SettingKey(typeof(bool))] public const string HIDE_WELCOME = "LiveHideWelcome"; /// /// 隐藏礼物 /// + [SettingKey(typeof(bool))] public const string HIDE_GIFT = "LiveHideGift"; /// @@ -352,12 +433,26 @@ public class Live /// /// 隐藏抽奖 /// + [SettingKey(typeof(bool))] public const string HIDE_LOTTERY = "LiveHideLottery"; /// /// 直播流默认源 /// + [SettingKey(typeof(string))] public const string DEFAULT_LIVE_PLAY_URL_SOURCE = "DefaultLivePlayUrlSource"; + + /// + /// 显示底部礼物栏 + /// + [SettingKey(typeof(bool))] + public const string SHOW_BOTTOM_GIFT_BAR = "ShowBottomGiftBar"; + + /// + /// 默认显示底部礼物栏 + /// + [SettingDefaultValue] + public const bool DEFAULT_SHOW_BOTTOM_GIFT_BAR = true; } public class Player @@ -365,75 +460,131 @@ public class Player /// /// 使用外站视频替换无法播放的视频 bool /// + [SettingKey(typeof(bool))] public const string USE_OTHER_SITEVIDEO = "PlayerUseOther"; /// /// 硬解 bool /// + [SettingKey(typeof(bool))] public const string HARDWARE_DECODING = "PlayerHardwareDecoding"; /// /// 自动播放 bool /// + [SettingKey(typeof(bool))] public const string AUTO_PLAY = "PlayerAutoPlay"; /// /// 自动切换下一个视频 /// + [SettingKey(typeof(bool))] public const string AUTO_NEXT = "PlayerAutoNext"; /// /// 默认清晰度 int /// + [SettingKey(typeof(int))] public const string DEFAULT_QUALITY = "PlayerDefaultQuality"; /// /// 默认音质 int /// + [SettingKey(typeof(int))] public const string DEFAULT_SOUND_QUALITY = "PlayerDefaultSoundQuality"; /// /// 比例 int /// + [SettingKey(typeof(int))] public const string RATIO = "PlayerDefaultRatio"; /// /// 默认视频类型 int flv=0, dash=1,dash_hevc=2 /// + [SettingKey(typeof(int))] public const string DEFAULT_VIDEO_TYPE = "PlayerDefaultVideoType"; + + [SettingDefaultValue] public static List VideoSpeed = new List() { 2.0d, 1.5d, 1.25d, 1.0d, 0.75d, 0.5d }; /// /// 默认视频类型 int 1.0 /// + [SettingKey(typeof(int))] public const string DEFAULT_VIDEO_SPEED = "PlayerDefaultSpeed"; /// /// 播放模式 int 0=顺序播放,1=单集循环,2=列表循环 /// + [SettingKey(typeof(int))] public const string DEFAULT_PLAY_MODE = "PlayerDefaultPlayMode"; /// /// 音量 /// + [SettingKey(typeof(double))] public const string PLAYER_VOLUME = "PlayerVolume"; + + /// + /// 音量默认值 + /// + [SettingDefaultValue] + public const double DEFAULT_PLAYER_VOLUME = 1.0; + /// /// 亮度 /// + [SettingKey(typeof(double))] public const string PLAYER_BRIGHTNESS = "PlayeBrightness"; + + /// + /// 亮度默认值 + /// + [SettingDefaultValue] + public const double DEFAULT_PLAYER_BRIGHTNESS = 0; + + /// + /// 锁定播放器音量设置(播放器内修改音量时不写设置) + /// + [SettingKey(typeof(bool))] + public const string LOCK_PLAYER_VOLUME = "LockPlayerVolume"; + + /// + /// 锁定播放器音量设置默认值 + /// + [SettingDefaultValue] + public const bool DEFAULT_LOCK_PLAYER_VOLUME = false; + + /// + /// 锁定播放器亮度设置(播放器内修改亮度时不写设置) + /// + [SettingKey(typeof(bool))] + public const string LOCK_PLAYER_BRIGHTNESS = "LockPlayerBrightness"; + + /// + /// 锁定播放器亮度设置默认值 + /// + [SettingDefaultValue] + public const bool DEFAULT_LOCK_PLAYER_BRIGHTNESS = true; + /// /// A-B 循环播放模式的播放记录 /// + [SettingKey(typeof(object))] public const string PLAYER_ABPLAY_HISTORIES = "PlayerABPlayHistories"; /// /// 字幕颜色 /// + [SettingKey(typeof(int))] public const string SUBTITLE_COLOR = "subtitleColor"; /// /// 字幕背景颜色 /// + [SettingKey(typeof(int))] public const string SUBTITLE_BORDER_COLOR = "subtitleBorderColor"; /// /// 字幕大小 /// + [SettingKey(typeof(double))] public const string SUBTITLE_SIZE = "subtitleSize"; /// /// 字幕显示 @@ -442,77 +593,95 @@ public class Player /// /// 字幕透明度 /// + [SettingKey(typeof(double))] public const string SUBTITLE_OPACITY = "subtitleOpacity"; /// /// 字幕底部距离 /// + [SettingKey(typeof(double))] public const string SUBTITLE_BOTTOM = "subtitleBottom"; /// /// 字幕加粗 /// + [SettingKey(typeof(bool))] public const string SUBTITLE_BOLD = "subtitleBold"; /// /// 字幕对齐 /// 0=居中对齐,1=左对齐,2=右对齐 /// + [SettingKey(typeof(int))] public const string SUBTITLE_ALIGN = "subtitleAlign"; /// /// 自动跳转进度 /// + [SettingKey(typeof(bool))] public const string AUTO_TO_POSITION = "PlayerAutoToPosition"; /// /// 自动铺满窗口 /// + [SettingKey(typeof(bool))] public const string AUTO_FULL_WINDOW = "PlayerAutoToFullWindow"; /// /// 自动铺满全屏 /// + [SettingKey(typeof(bool))] public const string AUTO_FULL_SCREEN = "PlayerAutoToFullScreen"; /// /// 双击全屏 /// + [SettingKey(typeof(bool))] public const string DOUBLE_CLICK_FULL_SCREEN = "PlayerDoubleClickFullScreen"; /// /// 方向键右键行为 /// + [SettingKey(typeof(int))] public const string PLAYER_KEY_RIGHT_ACTION = "PlayerKeyRightAction"; /// /// 按住手势行为 /// + [SettingKey(typeof(int))] public const string HOLDING_GESTURE_ACTION = "HoldingGestureAction"; /// /// 按住手势可被其他手势取消 /// + [SettingKey(typeof(bool))] public const string HOLDING_GESTURE_CAN_CANCEL = "HoldingGestureCanCancel"; /// /// 倍速播放速度 /// + [SettingKey(typeof(double))] public const string HIGH_RATE_PLAY_SPEED = "HighRatePlaySpeed"; + + [SettingDefaultValue] public static List HIGH_RATE_PLAY_SPEED_LIST = new List() { 3.0d, 2.0d }; /// /// 自动打开AI字幕 /// + [SettingKey(typeof(bool))] public const string AUTO_OPEN_AI_SUBTITLE = "PlayerAutoOpenAISubtitle"; /// /// 替换CDN /// + [SettingKey(typeof(int))] public const string REPLACE_CDN = "PlayerReplaceCDN"; /// /// CDN服务器 /// + [SettingKey] public const string CDN_SERVER = "PlayerCDNServer"; /// /// 直播播放器默认模式 /// + [SettingKey(typeof(int))] public const string DEFAULT_LIVE_PLAYER_MODE = "DefaultLivePlayerMode"; } @@ -525,30 +694,35 @@ public class Roaming /// /// 自定义服务器链接 /// + [SettingKey(typeof(string))] public const string CUSTOM_SERVER_URL = "RoamingCustomServerUrl"; /// /// 自定义香港服务器链接 /// + [SettingKey(typeof(string))] public const string CUSTOM_SERVER_URL_HK = "RoamingCustomServerUrlHK"; /// /// 自定义台湾服务器链接 /// + [SettingKey(typeof(string))] public const string CUSTOM_SERVER_URL_TW = "RoamingCustomServerUrlTW"; /// /// 自定义大陆服务器链接 /// + [SettingKey(typeof(string))] public const string CUSTOM_SERVER_URL_CN = "RoamingCustomServerUrlCN"; /// /// 简体中文 /// + [SettingKey(typeof(bool))] public const string TO_SIMPLIFIED = "RoamingSubtitleToSimplified"; - /// - /// 只使用AkamaiCDN链接 - /// + ///// + ///// 只使用AkamaiCDN链接 + ///// //public const string AKAMAI_CDN = "RoamingAkamaiCDN"; } @@ -557,36 +731,45 @@ public class Download /// /// 下载目录 /// + [SettingKey(typeof(string))] public const string DOWNLOAD_PATH = "downloadPath"; + [SettingDefaultValue] public const string DEFAULT_PATH = "视频库/哔哩哔哩下载"; /// /// 旧版下载目录 /// + [SettingKey(typeof(string))] public const string OLD_DOWNLOAD_PATH = "downloadOldPath"; + [SettingDefaultValue] public const string DEFAULT_OLD_PATH = "视频库/BiliBiliDownload"; /// /// 允许付费网络下载 /// + [SettingKey(typeof(bool))] public const string ALLOW_COST_NETWORK = "allowCostNetwork"; /// /// 并行下载 /// + [SettingKey(typeof(bool))] public const string PARALLEL_DOWNLOAD = "parallelDownload"; /// /// 并行下载 /// + [SettingKey(typeof(bool))] public const string SEND_TOAST = "sendToast"; /// /// 加载旧版下载视频 /// + [SettingKey(typeof(bool))] public const string LOAD_OLD_DOWNLOAD = "loadOldDownload"; /// /// 下载视频类型 /// + [SettingKey(typeof(int))] public const string DEFAULT_VIDEO_TYPE = "DownloadDefaultVideoType"; } @@ -644,6 +827,7 @@ public class Other /// /// 默认发起请求时使用的build值 /// + [SettingDefaultValue] public const string DEFAULT_REQUEST_BUILD = "75900200"; } } diff --git a/src/BiliLite.UWP/Models/Common/UserDynamic/IUserDynamicCommands.cs b/src/BiliLite.UWP/Models/Common/UserDynamic/IUserDynamicCommands.cs new file mode 100644 index 00000000..61dce016 --- /dev/null +++ b/src/BiliLite.UWP/Models/Common/UserDynamic/IUserDynamicCommands.cs @@ -0,0 +1,31 @@ +using System.Windows.Input; + +namespace BiliLite.Models.Common.UserDynamic +{ + public interface IUserDynamicCommands + { + public ICommand LaunchUrlCommand { get; set; } + + public ICommand RepostCommand { get; set; } + + public ICommand LikeCommand { get; set; } + + public ICommand CommentCommand { get; set; } + + public ICommand UserCommand { get; set; } + + public ICommand LoadMoreCommand { get; } + + public ICommand WebDetailCommand { get; set; } + + public ICommand DetailCommand { get; set; } + + public ICommand ImageCommand { get; set; } + + public ICommand WatchLaterCommand { get; set; } + + public ICommand CopyDynCommand { get; set; } + + public ICommand TagCommand { get; set; } + } +} diff --git a/src/BiliLite.UWP/Models/Common/UserDynamic/NavDynArticle.cs b/src/BiliLite.UWP/Models/Common/UserDynamic/NavDynArticle.cs new file mode 100644 index 00000000..d0ec07a8 --- /dev/null +++ b/src/BiliLite.UWP/Models/Common/UserDynamic/NavDynArticle.cs @@ -0,0 +1,87 @@ +using Bilibili.App.Dynamic.V2; +using BiliLite.ViewModels.UserDynamic; +using Newtonsoft.Json; + +namespace BiliLite.Models.Common.UserDynamic +{ + public class NavDynArticle + { + public string Cover { get; set; } + + [JsonProperty("id_str")] + public string Id { get; set; } + + [JsonProperty("jump_url")] + public string JumpUrl { get; set; } + + [JsonProperty("pub_time")] + public string PubTime { get; set; } + + public long Rid { get; set; } + + public string Title { get; set; } + + public bool Visible { get; set; } + + public NavDynArticleAuthor Author { get; set; } + + public DynamicV2ItemViewModel ToDynamicItem() + { + var item = new DynamicV2ItemViewModel() + { + CardType = Constants.DynamicTypes.CUSTOM_ARTICLE, + CustomArticle = this, + Author = new ModuleAuthor() + { + Mid = Author.Mid, + Author = new UserInfo() + { + Face = Author.Face, + Official = new OfficialVerify() + { + Type = Author.Official.Type + }, + Name = Author.Name, + Vip = new VipInfo() + { + Status = Author.Vip.Status + }, + }, + PtimeLabelText = PubTime, + }, + Extend = new Extend() + { + DynIdStr = Id + } + }; + return item; + } + } + + public class NavDynArticleAuthor + { + public string Face { get; set; } + + public long Mid { get; set; } + + public string Name { get; set; } + + public NavDynArticleAuthorOfficial Official { get; set; } + + public NavDynArticleAuthorVip Vip { get; set; } + } + + public class NavDynArticleAuthorOfficial + { + public int Role { get; set; } + + public int Type { get; set; } + + public string Title { get; set; } + } + + public class NavDynArticleAuthorVip + { + public int Status { get; set; } + } +} diff --git a/src/BiliLite.UWP/Models/Common/UserDynamic/NavDynArticles.cs b/src/BiliLite.UWP/Models/Common/UserDynamic/NavDynArticles.cs new file mode 100644 index 00000000..8c337148 --- /dev/null +++ b/src/BiliLite.UWP/Models/Common/UserDynamic/NavDynArticles.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace BiliLite.Models.Common.UserDynamic +{ + public class NavDynArticles + { + [JsonProperty("has_more")] + public bool HasMore { get; set; } + + public List Items { get; set; } + + public string Offset { get; set; } + + [JsonProperty("update_baseline")] + public string UpdateBaseline { get; set; } + } +} diff --git a/src/BiliLite.UWP/Models/Common/UserDynamic/UserDynamicSeasonInfo.cs b/src/BiliLite.UWP/Models/Common/UserDynamic/UserDynamicSeasonInfo.cs new file mode 100644 index 00000000..7e5a0edc --- /dev/null +++ b/src/BiliLite.UWP/Models/Common/UserDynamic/UserDynamicSeasonInfo.cs @@ -0,0 +1,36 @@ +using BiliLite.ViewModels.UserDynamic; + +namespace BiliLite.Models.Common.UserDynamic +{ + public class UserDynamicSeasonInfo + { + public string SeasonId { get; set; } + + public string Url { get; set; } + + public string Cover { get; set; } + + public string Title { get; set; } + + public string SubTitle { get; set; } + + public UserDynamicSeasonNewEpInfo NewEp { get; set; } + + public DynamicV2ItemViewModel ToDynamicItem() + { + var item = new DynamicV2ItemViewModel() + { + CardType = Constants.DynamicTypes.CUSTOM_SEASON, + Season = this + }; + return item; + } + } + + public class UserDynamicSeasonNewEpInfo + { + public string IndexShow { get; set; } + + public string Cover { get; set; } + } +} diff --git a/src/BiliLite.UWP/Models/Common/Video/Detail/VideoDetailStatModel.cs b/src/BiliLite.UWP/Models/Common/Video/Detail/VideoDetailStatModel.cs index 2d7736be..ed0e2e14 100644 --- a/src/BiliLite.UWP/Models/Common/Video/Detail/VideoDetailStatModel.cs +++ b/src/BiliLite.UWP/Models/Common/Video/Detail/VideoDetailStatModel.cs @@ -9,42 +9,42 @@ public class VideoDetailStatModel /// /// 播放 /// - public int View { get; set; } + public long View { get; set; } /// /// 弹幕 /// - public int Danmaku { get; set; } + public long Danmaku { get; set; } /// /// 评论 /// - public int Reply { get; set; } + public long Reply { get; set; } /// /// 收藏 /// - public int Favorite { get; set; } + public long Favorite { get; set; } /// /// 投币 /// - public int Coin { get; set; } + public long Coin { get; set; } /// /// 分享 /// - public int Share { get; set; } + public long Share { get; set; } /// /// 点赞 /// - public int Like { get; set; } + public long Like { get; set; } /// /// 不喜欢,固定0 /// [JsonProperty("dislike")] - public int DisLike { get; set; } + public long DisLike { get; set; } } } \ No newline at end of file diff --git a/src/BiliLite.UWP/Models/Dynamic/DynamicModel.cs b/src/BiliLite.UWP/Models/Dynamic/DynamicModel.cs index 90d1111a..7586bf48 100644 --- a/src/BiliLite.UWP/Models/Dynamic/DynamicModel.cs +++ b/src/BiliLite.UWP/Models/Dynamic/DynamicModel.cs @@ -32,10 +32,10 @@ public class DynamicCardDescModel public string rid_str { get; set; } public int r_type { get; set; } public string bvid { get; set; } - public int view { get; set; } - public int repost { get; set; } - public int comment { get; set; } - public int like { get; set; } + public long view { get; set; } + public long repost { get; set; } + public long comment { get; set; } + public long like { get; set; } public int is_liked { get; set; } public long timestamp { get; set; } public string pre_dy_id { get; set; } diff --git a/src/BiliLite.UWP/Models/Functions/IShortcutFunction.cs b/src/BiliLite.UWP/Models/Functions/IShortcutFunction.cs new file mode 100644 index 00000000..bcba2d7a --- /dev/null +++ b/src/BiliLite.UWP/Models/Functions/IShortcutFunction.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; + +namespace BiliLite.Models.Functions +{ + public interface IShortcutFunction + { + public string Name { get; } + + public Task Action(object param); + } +} diff --git a/src/BiliLite.UWP/Models/Functions/RefreshShortcutFunction.cs b/src/BiliLite.UWP/Models/Functions/RefreshShortcutFunction.cs new file mode 100644 index 00000000..d4b14234 --- /dev/null +++ b/src/BiliLite.UWP/Models/Functions/RefreshShortcutFunction.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using BiliLite.Pages; + +namespace BiliLite.Models.Functions +{ + public class RefreshShortcutFunction : IShortcutFunction + { + public string Name { get; } = "刷新"; + + public async Task Action(object param) + { + if (!(param is IRefreshablePage page)) return; + await page.Refresh(); + } + } +} diff --git a/src/BiliLite.UWP/Models/Requests/Api/User/DynamicAPI.cs b/src/BiliLite.UWP/Models/Requests/Api/User/DynamicAPI.cs index f5da85fa..d8e03522 100644 --- a/src/BiliLite.UWP/Models/Requests/Api/User/DynamicAPI.cs +++ b/src/BiliLite.UWP/Models/Requests/Api/User/DynamicAPI.cs @@ -46,7 +46,8 @@ public ApiModel DyanmicNew(UserDynamicType type) //使用Web的API if (SettingService.Account.Logined) { - api.parameter += $"&access_key={SettingService.Account.AccessKey}"; + api.parameter += "&"; + api.parameter += ApiHelper.MustParameter(AppKey, true); } api.parameter += ApiHelper.GetSign(api.parameter, AppKey); return api; @@ -115,7 +116,8 @@ public ApiModel HistoryDynamic(string dynamic_id, UserDynamicType type) };//使用Web的API if (SettingService.Account.Logined) { - api.parameter += $"&access_key={SettingService.Account.AccessKey}"; + api.parameter += "&"; + api.parameter += ApiHelper.MustParameter(AppKey, true); } api.parameter += ApiHelper.GetSign(api.parameter, AppKey); return api; @@ -155,6 +157,20 @@ public ApiModel SpaceHistoryV2(string mid, string offset = "") return api; } + + public ApiModel Article(string updateBaseline,string type="article") + { + var api = new ApiModel() + { + method = RestSharp.Method.Get, + baseUrl = $"https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/nav", + parameter = $"type={type}&update_baseline={updateBaseline}", + need_cookie = true, + }; + + return api; + } + /// /// 推荐话题 /// diff --git a/src/BiliLite.UWP/Modules/Home/CinemaVM.cs b/src/BiliLite.UWP/Modules/Home/CinemaVM.cs index eefeb05e..87c80217 100644 --- a/src/BiliLite.UWP/Modules/Home/CinemaVM.cs +++ b/src/BiliLite.UWP/Modules/Home/CinemaVM.cs @@ -372,9 +372,9 @@ public bool ShowBadge } public class CinemaHomeStatModel { - public int view { get; set; } + public long view { get; set; } public string follow_view { get; set; } - public int follow { get; set; } - public int danmaku { get; set; } + public long follow { get; set; } + public long danmaku { get; set; } } } diff --git a/src/BiliLite.UWP/Modules/RankVM.cs b/src/BiliLite.UWP/Modules/RankVM.cs index 5188c83f..1b39cc5f 100644 --- a/src/BiliLite.UWP/Modules/RankVM.cs +++ b/src/BiliLite.UWP/Modules/RankVM.cs @@ -194,20 +194,20 @@ public class RankItemModel public RankItemOwnerModel owner { get; set; } public RankItemStatModel stat { get; set; } public string dynamic { get; set; } - public int cid { get; set; } + public long cid { get; set; } public string bvid { get; set; } public int score { get; set; } } public class RankItemStatModel { - public int aid { get; set; } - public int view { get; set; } - public int danmaku { get; set; } - public int reply { get; set; } - public int favorite { get; set; } - public int coin { get; set; } - public int share { get; set; } + public long aid { get; set; } + public long view { get; set; } + public long danmaku { get; set; } + public long reply { get; set; } + public long favorite { get; set; } + public long coin { get; set; } + public long share { get; set; } } public class RankItemOwnerModel { diff --git a/src/BiliLite.UWP/Modules/SearchVM.cs b/src/BiliLite.UWP/Modules/SearchVM.cs index 66c957b2..e9dd9309 100644 --- a/src/BiliLite.UWP/Modules/SearchVM.cs +++ b/src/BiliLite.UWP/Modules/SearchVM.cs @@ -1007,9 +1007,9 @@ public string title public string category_name { get; set; } public string type { get; set; } public string desc { get; set; } - public int like { get; set; } - public int view { get; set; } - public int reply { get; set; } + public long like { get; set; } + public long view { get; set; } + public long reply { get; set; } public string id { get; set; } public List image_urls { get; set; } public string cover diff --git a/src/BiliLite.UWP/Modules/Season/SeasonRankVM.cs b/src/BiliLite.UWP/Modules/Season/SeasonRankVM.cs index 8187f593..50ef7e7f 100644 --- a/src/BiliLite.UWP/Modules/Season/SeasonRankVM.cs +++ b/src/BiliLite.UWP/Modules/Season/SeasonRankVM.cs @@ -141,9 +141,9 @@ public class SeasonRankItemModel } public class SeasonRankItemStatModel { - public int danmaku { get; set; } - public int follow { get; set; } - public int view { get; set; } + public long danmaku { get; set; } + public long follow { get; set; } + public long view { get; set; } } public class SeasonRankItemNewEPModel { diff --git a/src/BiliLite.UWP/Modules/User/UserDetail/UserSubmitArticleVM.cs b/src/BiliLite.UWP/Modules/User/UserDetail/UserSubmitArticleVM.cs index 97300dad..3ca6b0d8 100644 --- a/src/BiliLite.UWP/Modules/User/UserDetail/UserSubmitArticleVM.cs +++ b/src/BiliLite.UWP/Modules/User/UserDetail/UserSubmitArticleVM.cs @@ -180,13 +180,13 @@ public string cover } public class SubmitArticleStatsModel { - public int view { get; set; } - public int favorite { get; set; } - public int like { get; set; } - public int reply { get; set; } - public int share { get; set; } - public int coin { get; set; } - public int dynamic { get; set; } + public long view { get; set; } + public long favorite { get; set; } + public long like { get; set; } + public long reply { get; set; } + public long share { get; set; } + public long coin { get; set; } + public long dynamic { get; set; } } public class SubmitArticleCategoryModel { diff --git a/src/BiliLite.UWP/NoTabMainPage.xaml.cs b/src/BiliLite.UWP/NoTabMainPage.xaml.cs index 397aa470..8db9dc75 100644 --- a/src/BiliLite.UWP/NoTabMainPage.xaml.cs +++ b/src/BiliLite.UWP/NoTabMainPage.xaml.cs @@ -3,6 +3,7 @@ using BiliLite.Models.Common; using BiliLite.Pages; using BiliLite.Services; +using Microsoft.Extensions.DependencyInjection; using System; using System.Linq; using Windows.ApplicationModel.Core; @@ -20,10 +21,14 @@ namespace BiliLite /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class NoTabMainPage : Page + public sealed partial class NoTabMainPage : Page, IMainPage { + private readonly ShortcutKeyService m_shortcutKeyService; + public NoTabMainPage() { + m_shortcutKeyService = App.ServiceProvider.GetRequiredService(); + m_shortcutKeyService.SetMainPage(this); this.InitializeComponent(); var coreTitleBar = CoreApplication.GetCurrentView().TitleBar; mode = SettingService.GetValue(SettingConstants.UI.DISPLAY_MODE, 0); @@ -34,7 +39,20 @@ public NoTabMainPage() MessageCenter.ViewImageEvent += MessageCenter_ViewImageEvent; MessageCenter.MiniWindowEvent += MessageCenter_MiniWindowEvent; Window.Current.Content.PointerPressed += Content_PointerPressed; + + Window.Current.CoreWindow.Dispatcher.AcceleratorKeyActivated += Dispatcher_AcceleratorKeyActivated; } + + public object CurrentPage => frame.Content; + + private void Dispatcher_AcceleratorKeyActivated(Windows.UI.Core.CoreDispatcher sender, Windows.UI.Core.AcceleratorKeyEventArgs args) + { + if (args.EventType.ToString().Contains("Down")) + { + m_shortcutKeyService.HandleKeyDown(args.VirtualKey); + } + } + private void MessageCenter_MiniWindowEvent(object sender, bool e) { if (e) diff --git a/src/BiliLite.UWP/Pages/DownloadPage.xaml.cs b/src/BiliLite.UWP/Pages/DownloadPage.xaml.cs index 14d91801..cb03cb9c 100644 --- a/src/BiliLite.UWP/Pages/DownloadPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/DownloadPage.xaml.cs @@ -1,19 +1,12 @@ -using BiliLite.Modules; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Foundation; -using Windows.Foundation.Collections; +using System.Threading.Tasks; using Windows.Storage; using Windows.System; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; using BiliLite.Extensions; using BiliLite.Services; @@ -23,6 +16,7 @@ using BiliLite.Models.Common.Video.PlayUrlInfos; using BiliLite.ViewModels.Download; using Microsoft.Extensions.DependencyInjection; +using System.Text.RegularExpressions; // https://go.microsoft.com/fwlink/?LinkId=234238 上介绍了“空白页”项模板 @@ -31,7 +25,7 @@ namespace BiliLite.Pages /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class DownloadPage : BasePage + public sealed partial class DownloadPage : BasePage, IRefreshablePage { private static readonly ILogger logger = GlobalLogger.FromCurrentType(); private readonly DownloadPageViewModel m_viewModel; @@ -50,6 +44,12 @@ protected override void OnNavigatedTo(NavigationEventArgs e) m_viewModel.RefreshDownloaded(); } } + + public async Task Refresh() + { + m_viewModel.RefreshDownloaded(); + } + private void listDowned_ItemClick(object sender, ItemClickEventArgs e) { var data = e.ClickedItem as DownloadedItem; @@ -307,7 +307,8 @@ private async void OutputFile(DownloadedItem data, DownloadedSubItem item) savePicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.DocumentsLibrary; savePicker.FileTypeChoices.Add("MP4", new List() { ".mp4" }); - savePicker.SuggestedFileName = "导出的视频"; + var fileName = Regex.Replace(data.Title + "-" + item.Title, "[<>/\\\\|:\":?*]", ""); + savePicker.SuggestedFileName = fileName; var file = await savePicker.PickSaveFileAsync(); if (file == null) return; diff --git a/src/BiliLite.UWP/Pages/Home/AnimePage.xaml.cs b/src/BiliLite.UWP/Pages/Home/AnimePage.xaml.cs index c8c33fc8..2b4c6cbb 100644 --- a/src/BiliLite.UWP/Pages/Home/AnimePage.xaml.cs +++ b/src/BiliLite.UWP/Pages/Home/AnimePage.xaml.cs @@ -19,7 +19,7 @@ namespace BiliLite.Pages.Home /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class AnimePage : Page + public sealed partial class AnimePage : Page, IRefreshablePage { private AnimeType animeType; public AnimePageViewModel m_viewModel { get; set; } @@ -100,11 +100,16 @@ private void btnTimeline_Click(object sender, RoutedEventArgs e) }); } - private async void RefreshContainer_RefreshRequested(Microsoft.UI.Xaml.Controls.RefreshContainer sender, Microsoft.UI.Xaml.Controls.RefreshRequestedEventArgs args) + public async Task Refresh() { await LoadData(); } + private async void RefreshContainer_RefreshRequested(Microsoft.UI.Xaml.Controls.RefreshContainer sender, Microsoft.UI.Xaml.Controls.RefreshRequestedEventArgs args) + { + await Refresh(); + } + private async void BannerItem_Click(object sender, RoutedEventArgs e) { var result = await MessageCenter.HandelUrl(((sender as HyperlinkButton).DataContext as AnimeBannerModel).Url); diff --git a/src/BiliLite.UWP/Pages/Home/DynamicPage.xaml.cs b/src/BiliLite.UWP/Pages/Home/DynamicPage.xaml.cs index a6addb75..15cc4a90 100644 --- a/src/BiliLite.UWP/Pages/Home/DynamicPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/Home/DynamicPage.xaml.cs @@ -1,4 +1,5 @@ -using BiliLite.Extensions; +using System.Threading.Tasks; +using BiliLite.Extensions; using BiliLite.Models.Common; using BiliLite.Services; using Windows.UI.Xaml; @@ -16,7 +17,7 @@ namespace BiliLite.Pages.Home /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class DynamicPage : Page + public sealed partial class DynamicPage : Page, IRefreshablePage { private readonly DynamicPageViewModel m_viewModel; @@ -38,11 +39,16 @@ protected override async void OnNavigatedTo(NavigationEventArgs e) } } - private void RefreshContainer_RefreshRequested(Microsoft.UI.Xaml.Controls.RefreshContainer sender, Microsoft.UI.Xaml.Controls.RefreshRequestedEventArgs args) + public async Task Refresh() { m_viewModel.Refresh(); } + private async void RefreshContainer_RefreshRequested(Microsoft.UI.Xaml.Controls.RefreshContainer sender, Microsoft.UI.Xaml.Controls.RefreshRequestedEventArgs args) + { + await Refresh(); + } + private void AdaptiveGridView_ItemClick(object sender, ItemClickEventArgs e) { var item = e.ClickedItem as DynamicItemModel; diff --git a/src/BiliLite.UWP/Pages/Home/HotPage.xaml.cs b/src/BiliLite.UWP/Pages/Home/HotPage.xaml.cs index d8c16b57..42cb53c7 100644 --- a/src/BiliLite.UWP/Pages/Home/HotPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/Home/HotPage.xaml.cs @@ -16,7 +16,7 @@ namespace BiliLite.Pages.Home /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class HotPage : Page + public sealed partial class HotPage : Page, IRefreshablePage { HotVM hotVM; public HotPage() @@ -100,5 +100,10 @@ private void AddToWatchLater_Click(object sender, RoutedEventArgs e) var data = (sender as MenuFlyoutItem).DataContext as HotDataItemModel; WatchLaterVM.Instance.AddToWatchlater(data.Param); } + + public async Task Refresh() + { + hotVM.Refresh(); + } } } diff --git a/src/BiliLite.UWP/Pages/Home/LivePage.xaml.cs b/src/BiliLite.UWP/Pages/Home/LivePage.xaml.cs index f8cb21cb..1cbc0d20 100644 --- a/src/BiliLite.UWP/Pages/Home/LivePage.xaml.cs +++ b/src/BiliLite.UWP/Pages/Home/LivePage.xaml.cs @@ -18,7 +18,7 @@ namespace BiliLite.Pages.Home /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class LivePage : Page + public sealed partial class LivePage : Page,IRefreshablePage { private Modules.LiveVM liveVM; public LivePage() @@ -53,11 +53,16 @@ private async Task LoadData() } } - private async void btnRefresh_Click(object sender, RoutedEventArgs e) + public async Task Refresh() { await LoadData(); } + private async void btnRefresh_Click(object sender, RoutedEventArgs e) + { + await Refresh(); + } + private async void BannerItem_Click(object sender, RoutedEventArgs e) { var result = await MessageCenter.HandelUrl(((sender as HyperlinkButton).DataContext as LiveHomeBannerModel).link); diff --git a/src/BiliLite.UWP/Pages/Home/MoviePage.xaml.cs b/src/BiliLite.UWP/Pages/Home/MoviePage.xaml.cs index 048d8fe7..3802c375 100644 --- a/src/BiliLite.UWP/Pages/Home/MoviePage.xaml.cs +++ b/src/BiliLite.UWP/Pages/Home/MoviePage.xaml.cs @@ -4,19 +4,9 @@ using BiliLite.Modules; using BiliLite.Services; using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; using System.Threading.Tasks; -using Windows.Foundation; -using Windows.Foundation.Collections; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; // https://go.microsoft.com/fwlink/?LinkId=234238 上介绍了“空白页”项模板 @@ -26,7 +16,7 @@ namespace BiliLite.Pages.Home /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class MoviePage : Page + public sealed partial class MoviePage : Page, IRefreshablePage { readonly Modules.CinemaVM cinemaVM; public MoviePage() @@ -95,7 +85,7 @@ private async void gvFall_ItemClick(object sender, ItemClickEventArgs e) } private async void RefreshContainer_RefreshRequested(Microsoft.UI.Xaml.Controls.RefreshContainer sender, Microsoft.UI.Xaml.Controls.RefreshRequestedEventArgs args) { - await LoadData(); + await Refresh(); } private async void BannerItem_Click(object sender, RoutedEventArgs e) @@ -184,5 +174,10 @@ private void GridView_ItemClick(object sender, ItemClickEventArgs e) var item = e.ClickedItem as PageEntranceModel; MessageCenter.NavigateToPage(this, item.NavigationInfo); } + + public async Task Refresh() + { + await LoadData(); + } } } diff --git a/src/BiliLite.UWP/Pages/Home/RecommendPage.xaml.cs b/src/BiliLite.UWP/Pages/Home/RecommendPage.xaml.cs index ea74521e..2981b658 100644 --- a/src/BiliLite.UWP/Pages/Home/RecommendPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/Home/RecommendPage.xaml.cs @@ -21,7 +21,7 @@ namespace BiliLite.Pages.Home /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class RecommendPage : Page + public sealed partial class RecommendPage : Page, IRefreshablePage { #region Fields @@ -42,6 +42,15 @@ public RecommendPage() #endregion + #region Public Methods + + public async Task Refresh() + { + m_viewModel.Refresh(); + } + + #endregion + #region Protected Methods protected override async void OnNavigatedTo(NavigationEventArgs e) { @@ -87,7 +96,7 @@ private async void RecommendGridView_ItemClick(object sender, ItemClickEventArgs private void RefreshContainer_RefreshRequested(Microsoft.UI.Xaml.Controls.RefreshContainer sender, Microsoft.UI.Xaml.Controls.RefreshRequestedEventArgs args) { - m_viewModel.Refresh(); + Refresh(); } private async void BannerItem_Click(object sender, RoutedEventArgs e) diff --git a/src/BiliLite.UWP/Pages/Home/UserDynamicPage.xaml b/src/BiliLite.UWP/Pages/Home/UserDynamicPage.xaml index cc8f093d..ec9ec508 100644 --- a/src/BiliLite.UWP/Pages/Home/UserDynamicPage.xaml +++ b/src/BiliLite.UWP/Pages/Home/UserDynamicPage.xaml @@ -15,7 +15,7 @@ - + - + + + - - - - - - - - - - - - - - - - + - + + + + + + + + + + + + + + + + + + + + + + + - - - + + diff --git a/src/BiliLite.UWP/Pages/Home/UserDynamicPage.xaml.cs b/src/BiliLite.UWP/Pages/Home/UserDynamicPage.xaml.cs index d7bffab0..5e307ec1 100644 --- a/src/BiliLite.UWP/Pages/Home/UserDynamicPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/Home/UserDynamicPage.xaml.cs @@ -1,13 +1,17 @@ -using BiliLite.Services; +using System.Linq; +using System.Threading.Tasks; +using BiliLite.Services; using BiliLite.Models.Common; using BiliLite.Models.Requests.Api; -using BiliLite.Models.Requests.Api.User; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; using BiliLite.Extensions; -using BiliLite.Models.Common.UserDynamic; using BiliLite.ViewModels.UserDynamic; +using Microsoft.Extensions.DependencyInjection; +using BiliLite.Models.Common.Comment; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media.Animation; // https://go.microsoft.com/fwlink/?LinkId=234238 上介绍了“空白页”项模板 @@ -16,91 +20,28 @@ namespace BiliLite.Pages.Home /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class UserDynamicPage : Page + public sealed partial class UserDynamicPage : Page,IRefreshablePage { - readonly UserDynamicViewModel m_userDynamicViewModel; - private bool IsStaggered { get; set; } = false; + readonly UserDynamicAllViewModel m_viewModel; + private bool m_isStaggered = false; + private UserDynamicShowType m_currentShowType; + public UserDynamicPage() { + m_viewModel = App.ServiceProvider.GetRequiredService(); + m_viewModel.OpenCommentEvent += UserDynamicViewModelOpenCommentEvent; this.InitializeComponent(); - m_userDynamicViewModel = new UserDynamicViewModel(); - m_userDynamicViewModel.OpenCommentEvent += UserDynamicViewModelOpenCommentEvent; - splitView.PaneClosed += SplitView_PaneClosed; - this.DataContext = m_userDynamicViewModel; - if (SettingService.GetValue(SettingConstants.UI.CACHE_HOME, true)) - { - this.NavigationCacheMode = NavigationCacheMode.Enabled; - } - else - { - this.NavigationCacheMode = NavigationCacheMode.Disabled; - } + m_currentShowType = (UserDynamicShowType)DynPivot.SelectedIndex; } - private void SplitView_PaneClosed(SplitView sender, object args) - { - comment.ClearComment(); - repost.UserDynamicRepostViewModel.Clear(); - } - string dynamic_id; - private void UserDynamicViewModelOpenCommentEvent(object sender, UserDynamicItemDisplayViewModel e) - { - // splitView.IsPaneOpen = true; - dynamic_id = e.DynamicID; - pivotRight.SelectedIndex = 1; - repostCount.Text = e.ShareCount.ToString(); - commentCount.Text = e.CommentCount.ToString(); - CommentApi.CommentType commentType = CommentApi.CommentType.Dynamic; - var id = e.ReplyID; - switch (e.Type) - { - - case UserDynamicDisplayType.Photo: - commentType = CommentApi.CommentType.Photo; - break; - case UserDynamicDisplayType.Video: - - commentType = CommentApi.CommentType.Video; - break; - case UserDynamicDisplayType.Season: - id = e.OneRowInfo.AID; - commentType = CommentApi.CommentType.Video; - break; - case UserDynamicDisplayType.ShortVideo: - commentType = CommentApi.CommentType.MiniVideo; - break; - case UserDynamicDisplayType.Music: - commentType = CommentApi.CommentType.Song; - break; - case UserDynamicDisplayType.Article: - commentType = CommentApi.CommentType.Article; - break; - case UserDynamicDisplayType.MediaList: - if (e.OneRowInfo.Tag != "收藏夹") - commentType = CommentApi.CommentType.Video; - break; - default: - id = e.DynamicID; - break; - } - - //comment.LoadComment(new Controls.LoadCommentInfo() - //{ - // commentMode = (int)commentType, - // commentSort = Api.CommentApi.commentSort.Hot, - // oid = id - //}); - Notify.ShowComment(id, (int)commentType, CommentApi.CommentSort.Hot); - } - - protected async override void OnNavigatedTo(NavigationEventArgs e) + protected override async void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); SetStaggered(); - if (e.NavigationMode == NavigationMode.New && m_userDynamicViewModel.Items == null) + if (e.NavigationMode == NavigationMode.New && m_viewModel.DynamicItems == null) { - await m_userDynamicViewModel.GetDynamicItems(); + await m_viewModel.GetDynamicItems(); if (SettingService.GetValue("动态切换提示", true) && SettingService.GetValue(SettingConstants.UI.DYNAMIC_DISPLAY_MODE, 0) != 1) { SettingService.SetValue("动态切换提示", false); @@ -109,89 +50,148 @@ protected async override void OnNavigatedTo(NavigationEventArgs e) } } - void SetStaggered() + private void SetStaggered() { var staggered = SettingService.GetValue(SettingConstants.UI.DYNAMIC_DISPLAY_MODE, 0) == 1; - if (staggered != IsStaggered) + if (staggered != m_isStaggered) { - IsStaggered = staggered; + m_isStaggered = staggered; if (staggered) { - btnGrid_Click(this, null); + SetGridCore(); } else { - btnList_Click(this, null); + SetListCore(); } } } - private void pivot_SelectionChanged(object sender, SelectionChangedEventArgs e) + private void SetGridCore() { - if ((int)m_userDynamicViewModel.UserDynamicType == pivot.SelectedIndex) return; - m_userDynamicViewModel.UserDynamicType = (UserDynamicType)pivot.SelectedIndex; - m_userDynamicViewModel.Refresh(); + m_isStaggered = true; + BtnGrid.Visibility = Visibility.Collapsed; + BtnList.Visibility = Visibility.Visible; + //XAML + ListDyn.ItemsPanel = (ItemsPanelTemplate)this.Resources["GridPanel"]; + + //顶部 + GridTopBar.MaxWidth = double.MaxValue; + GridTopBar.Margin = new Thickness(0, 0, 0, 4); + BorderTopBar.CornerRadius = new CornerRadius(0); + BorderTopBar.Margin = new Thickness(0); } - private void btnGrid_Click(object sender, RoutedEventArgs e) + private void SetListCore() { - SettingService.SetValue(SettingConstants.UI.DYNAMIC_DISPLAY_MODE, 1); - IsStaggered = true; - btnGrid.Visibility = Visibility.Collapsed; - btnList.Visibility = Visibility.Visible; + m_isStaggered = false; + //右下角按钮 + BtnGrid.Visibility = Visibility.Visible; + BtnList.Visibility = Visibility.Collapsed; + //XAML + ListDyn.ItemsPanel = (ItemsPanelTemplate)this.Resources["ListPanel"]; + //顶部 - gridTopBar.MaxWidth = double.MaxValue; - gridTopBar.Margin = new Thickness(0, 0, 0, 4); - borderTopBar.CornerRadius = new CornerRadius(0); - borderTopBar.Margin = new Thickness(0); + GridTopBar.MaxWidth = 800; + GridTopBar.Margin = new Thickness(8, 0, 8, 0); + BorderTopBar.CornerRadius = new CornerRadius(4); + BorderTopBar.Margin = new Thickness(12, 4, 12, 4); + } - //XAML - // var tmp = @" "; - // var xaml = $@" - // {tmp} - // "; - //list.ItemsPanel = (ItemsPanelTemplate)XamlReader.Load(xaml); - list.ItemsPanel = (ItemsPanelTemplate)this.Resources["GridPanel"]; + private async void BtnRefreshDynamic_OnClick(object sender, RoutedEventArgs e) + { + await Refresh(); } - private void btnList_Click(object sender, RoutedEventArgs e) + private void BtnTop_OnClick(object sender, RoutedEventArgs e) + { + ListDyn.ScrollIntoView(ListDyn.Items.FirstOrDefault()); + } + + private void BtnList_OnClick(object sender, RoutedEventArgs e) { - IsStaggered = false; - //右下角按钮 - btnGrid.Visibility = Visibility.Visible; - btnList.Visibility = Visibility.Collapsed; - //设置 SettingService.SetValue(SettingConstants.UI.DYNAMIC_DISPLAY_MODE, 0); - //顶部 - gridTopBar.MaxWidth = 800; - gridTopBar.Margin = new Thickness(8, 0, 8, 0); - borderTopBar.CornerRadius = new CornerRadius(4); - borderTopBar.Margin = new Thickness(12, 4, 12, 4); - //XAML - // var tmp = @" "; - // var xaml = $@" - // {tmp} - // "; - //list.ItemsPanel = (ItemsPanelTemplate)XamlReader.Load(xaml); - list.ItemsPanel = (ItemsPanelTemplate)this.Resources["ListPanel"]; + SetListCore(); } - private void btnTop_Click(object sender, RoutedEventArgs e) + private void BtnGrid_OnClick(object sender, RoutedEventArgs e) { - list.ScrollIntoView(list.Items[0]); + SettingService.SetValue(SettingConstants.UI.DYNAMIC_DISPLAY_MODE, 1); + SetGridCore(); } - private void pivotRight_SelectionChanged(object sender, SelectionChangedEventArgs e) + private async void Pivot_OnSelectionChanged(object sender, SelectionChangedEventArgs e) { - if (pivotRight.SelectedIndex == 0 && splitView.IsPaneOpen && (repost.UserDynamicRepostViewModel.Items == null || repost.UserDynamicRepostViewModel.Items.Count == 0)) + var showType = (UserDynamicShowType)DynPivot.SelectedIndex; + if (showType == m_currentShowType) return; + m_currentShowType = showType; + await m_viewModel.GetDynamicItems(showType: showType); + } + + private void CloseCommentCore() + { + var storyboard = (Storyboard)this.Resources["HideComment"]; + storyboard.Begin(); + } + + private void UserDynamicViewModelOpenCommentEvent(object sender, DynamicV2ItemViewModel e) + { + CommentApi.CommentType commentType = CommentApi.CommentType.Dynamic; + var id = e.Extend.BusinessId; + switch (e.CardType) { - repost.LoadData(dynamic_id); + case Constants.DynamicTypes.DRAW: + commentType = CommentApi.CommentType.Photo; + break; + case Constants.DynamicTypes.AV: + commentType = CommentApi.CommentType.Video; + break; + case Constants.DynamicTypes.PGC: + id = e.Dynamic.DynPgc.Aid.ToString(); + commentType = CommentApi.CommentType.Video; + break; + //case UserDynamicDisplayType.ShortVideo: + // commentType = CommentApi.CommentType.MiniVideo; + // break; + case Constants.DynamicTypes.MUSIC: + commentType = CommentApi.CommentType.Song; + break; + case Constants.DynamicTypes.ARTICLE: + commentType = CommentApi.CommentType.Article; + break; + //case UserDynamicDisplayType.MediaList: + // if (e.OneRowInfo.Tag != "收藏夹") + // commentType = CommentApi.CommentType.Video; + // break; + default: + id = e.Extend.DynIdStr; + break; } + + OpenCommentCore(id, (int)commentType, CommentApi.CommentSort.Hot); + } + + private void OpenCommentCore(string oid, int commentMode, CommentApi.CommentSort commentSort) + { + Comment.LoadComment(new LoadCommentInfo() + { + CommentMode = commentMode, + CommentSort = commentSort, + Oid = oid, + IsDialog = true + }); + var storyboard = (Storyboard)this.Resources["ShowComment"]; + storyboard.Begin(); + } + + private void CommentPanel_OnTapped(object sender, TappedRoutedEventArgs e) + { + CloseCommentCore(); + } + + public async Task Refresh() + { + await m_viewModel.GetDynamicItems(showType: m_currentShowType); } } } diff --git a/src/BiliLite.UWP/Pages/HomePage.xaml.cs b/src/BiliLite.UWP/Pages/HomePage.xaml.cs index ee619c6f..ac99a0e8 100644 --- a/src/BiliLite.UWP/Pages/HomePage.xaml.cs +++ b/src/BiliLite.UWP/Pages/HomePage.xaml.cs @@ -4,6 +4,7 @@ using BiliLite.Services; using Microsoft.Toolkit.Uwp.Connectivity; using System; +using System.Threading.Tasks; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; @@ -18,7 +19,7 @@ namespace BiliLite.Pages /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class HomePage : Page + public sealed partial class HomePage : Page, IRefreshablePage { private static readonly ILogger logger = GlobalLogger.FromCurrentType(); @@ -36,6 +37,15 @@ public HomePage() m_downloadPageViewModel = App.ServiceProvider.GetRequiredService(); this.DataContext = m_viewModel; } + + public async Task Refresh() + { + if (frame.Content is IRefreshablePage page) + { + await page.Refresh(); + } + } + private void MessageCenter_LogoutedEvent(object sender, EventArgs e) { LaodUserStatus(); diff --git a/src/BiliLite.UWP/Pages/IMainPage.cs b/src/BiliLite.UWP/Pages/IMainPage.cs new file mode 100644 index 00000000..869a58c8 --- /dev/null +++ b/src/BiliLite.UWP/Pages/IMainPage.cs @@ -0,0 +1,7 @@ +namespace BiliLite.Pages +{ + public interface IMainPage + { + public object CurrentPage { get; } + } +} diff --git a/src/BiliLite.UWP/Pages/IRefreshablePage.cs b/src/BiliLite.UWP/Pages/IRefreshablePage.cs new file mode 100644 index 00000000..b33ec6c6 --- /dev/null +++ b/src/BiliLite.UWP/Pages/IRefreshablePage.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace BiliLite.Pages +{ + public interface IRefreshablePage + { + public Task Refresh(); + } +} diff --git a/src/BiliLite.UWP/Pages/Live/LiveAreaDetailPage.xaml.cs b/src/BiliLite.UWP/Pages/Live/LiveAreaDetailPage.xaml.cs index 22537848..800cf433 100644 --- a/src/BiliLite.UWP/Pages/Live/LiveAreaDetailPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/Live/LiveAreaDetailPage.xaml.cs @@ -1,19 +1,11 @@ using BiliLite.Models.Common; using BiliLite.Modules.Live; using BiliLite.Services; -using System; -using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Foundation; -using Windows.Foundation.Collections; +using System.Threading.Tasks; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; // https://go.microsoft.com/fwlink/?LinkId=234238 上介绍了“空白页”项模板 @@ -28,7 +20,7 @@ public class LiveAreaPar /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class LiveAreaDetailPage : BasePage + public sealed partial class LiveAreaDetailPage : BasePage, IRefreshablePage { LiveAreaDetailVM liveAreaDetailVM; public LiveAreaDetailPage() @@ -48,9 +40,9 @@ protected async override void OnNavigatedTo(NavigationEventArgs e) } } - private void RefreshContainer_RefreshRequested(Microsoft.UI.Xaml.Controls.RefreshContainer sender, Microsoft.UI.Xaml.Controls.RefreshRequestedEventArgs args) + private async void RefreshContainer_RefreshRequested(Microsoft.UI.Xaml.Controls.RefreshContainer sender, Microsoft.UI.Xaml.Controls.RefreshRequestedEventArgs args) { - liveAreaDetailVM.Refresh(); + await Refresh(); } private void AdaptiveGridView_ItemClick(object sender, ItemClickEventArgs e) @@ -75,5 +67,10 @@ private void ToggleButton_Click(object sender, RoutedEventArgs e) liveAreaDetailVM.SelectTag = data; liveAreaDetailVM.Refresh(); } + + public async Task Refresh() + { + liveAreaDetailVM.Refresh(); + } } } diff --git a/src/BiliLite.UWP/Pages/Live/LiveCenterPage.xaml.cs b/src/BiliLite.UWP/Pages/Live/LiveCenterPage.xaml.cs index 7c4658a6..48c53aa4 100644 --- a/src/BiliLite.UWP/Pages/Live/LiveCenterPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/Live/LiveCenterPage.xaml.cs @@ -2,18 +2,8 @@ using BiliLite.Modules.Live.LiveCenter; using BiliLite.Services; using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Windows.UI.Xaml; +using System.Threading.Tasks; using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; // https://go.microsoft.com/fwlink/?LinkId=234238 上介绍了“空白页”项模板 @@ -23,7 +13,7 @@ namespace BiliLite.Pages.Live /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class LiveCenterPage : BasePage + public sealed partial class LiveCenterPage : BasePage, IRefreshablePage { readonly LiveAttentionVM liveAttentionVM; readonly LiveAttentionUnLiveVM liveAttentionUnLiveVM; @@ -97,5 +87,10 @@ private async void pivot_SelectionChanged(object sender, SelectionChangedEventAr await liveCenterHistoryVM.Get(); } } + + public async Task Refresh() + { + throw new NotImplementedException(); + } } } diff --git a/src/BiliLite.UWP/Pages/Live/LiveRecommendPage.xaml.cs b/src/BiliLite.UWP/Pages/Live/LiveRecommendPage.xaml.cs index 768a520f..b7608309 100644 --- a/src/BiliLite.UWP/Pages/Live/LiveRecommendPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/Live/LiveRecommendPage.xaml.cs @@ -1,19 +1,10 @@ using BiliLite.Models.Common; using BiliLite.Modules.Live; using BiliLite.Services; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Windows.UI.Xaml; +using System.Threading.Tasks; + using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; + using Windows.UI.Xaml.Navigation; // https://go.microsoft.com/fwlink/?LinkId=234238 上介绍了“空白页”项模板 @@ -23,7 +14,7 @@ namespace BiliLite.Pages.Live /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class LiveRecommendPage : BasePage + public sealed partial class LiveRecommendPage : BasePage, IRefreshablePage { readonly LiveRecommendVM liveRecommendVM; public LiveRecommendPage() @@ -72,10 +63,17 @@ private void AdaptiveGridView_ItemClick(object sender, ItemClickEventArgs e) }); } - private void RefreshContainer_RefreshRequested(Microsoft.UI.Xaml.Controls.RefreshContainer sender, Microsoft.UI.Xaml.Controls.RefreshRequestedEventArgs args) + private async void RefreshContainer_RefreshRequested(Microsoft.UI.Xaml.Controls.RefreshContainer sender, Microsoft.UI.Xaml.Controls.RefreshRequestedEventArgs args) { - var data = sender.DataContext as LiveRecommendItem; - data.Refresh(); + await Refresh(); + } + + public async Task Refresh() + { + if(pivot.SelectedItem is LiveRecommendItem item) + { + item.Refresh(); + } } } } diff --git a/src/BiliLite.UWP/Pages/LiveDetailPage.xaml.cs b/src/BiliLite.UWP/Pages/LiveDetailPage.xaml.cs index 91ea9b3e..76d8294f 100644 --- a/src/BiliLite.UWP/Pages/LiveDetailPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/LiveDetailPage.xaml.cs @@ -534,15 +534,18 @@ private void PreLoadSetting() private void LoadSetting() { //音量 - m_player.Volume = SettingService.GetValue(SettingConstants.Player.PLAYER_VOLUME, 1.0); + m_player.Volume = SettingService.GetValue(SettingConstants.Player.PLAYER_VOLUME, SettingConstants.Player.DEFAULT_PLAYER_VOLUME); SliderVolume.Value = m_player.Volume; + var lockPlayerVolume = SettingService.GetValue(SettingConstants.Player.LOCK_PLAYER_VOLUME, SettingConstants.Player.DEFAULT_LOCK_PLAYER_VOLUME); SliderVolume.ValueChanged += (e, args) => { m_player.Volume = SliderVolume.Value; - SettingService.SetValue(SettingConstants.Player.PLAYER_VOLUME, SliderVolume.Value); + if(!lockPlayerVolume) + SettingService.SetValue(SettingConstants.Player.PLAYER_VOLUME, SliderVolume.Value); }; //亮度 - _brightness = SettingService.GetValue(SettingConstants.Player.PLAYER_BRIGHTNESS, 0); + lockBrightness = SettingService.GetValue(SettingConstants.Player.LOCK_PLAYER_BRIGHTNESS, SettingConstants.Player.DEFAULT_LOCK_PLAYER_BRIGHTNESS); + _brightness = SettingService.GetValue(SettingConstants.Player.PLAYER_BRIGHTNESS, SettingConstants.Player.DEFAULT_PLAYER_BRIGHTNESS); BrightnessShield.Opacity = _brightness; //弹幕顶部距离 @@ -656,6 +659,10 @@ private void LoadSetting() m_liveRoomViewModel.ReceiveLotteryMsg = !LiveSettingDotReceiveLotteryMsg.IsOn; SettingService.SetValue(SettingConstants.Live.HIDE_LOTTERY, LiveSettingDotReceiveLotteryMsg.IsOn); }); + + // 显示底部礼物栏 + m_viewModel.ShowBottomGiftBar = SettingService.GetValue(SettingConstants.Live.SHOW_BOTTOM_GIFT_BAR, + SettingConstants.Live.DEFAULT_SHOW_BOTTOM_GIFT_BAR); } public void ChangeTitle(string title) @@ -1185,6 +1192,7 @@ private void Grid_ManipulationStarted(object sender, ManipulationStartedRoutedEv } + private bool lockBrightness = true; double _brightness; double Brightness { @@ -1193,7 +1201,8 @@ double Brightness { _brightness = value; BrightnessShield.Opacity = value; - SettingService.SetValue(SettingConstants.Player.PLAYER_BRIGHTNESS, _brightness); + if (!lockBrightness) + SettingService.SetValue(SettingConstants.Player.PLAYER_BRIGHTNESS, _brightness); } } @@ -1252,6 +1261,7 @@ private async void PlayUrlSourceComboBox_OnSelectionChanged(object sender, Selec private void BottomBtnSwitchGiftBar_Click(object sender, RoutedEventArgs e) { m_viewModel.ShowBottomGiftBar = !m_viewModel.ShowBottomGiftBar; + SettingService.SetValue(SettingConstants.Live.SHOW_BOTTOM_GIFT_BAR, m_viewModel.ShowBottomGiftBar); } } } diff --git a/src/BiliLite.UWP/Pages/RegionDetailPage.xaml.cs b/src/BiliLite.UWP/Pages/RegionDetailPage.xaml.cs index c2cd55a7..a503de10 100644 --- a/src/BiliLite.UWP/Pages/RegionDetailPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/RegionDetailPage.xaml.cs @@ -2,19 +2,9 @@ using BiliLite.Modules; using BiliLite.Pages.Bangumi; using BiliLite.Services; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Foundation; -using Windows.Foundation.Collections; +using System.Threading.Tasks; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; // https://go.microsoft.com/fwlink/?LinkId=234238 上介绍了“空白页”项模板 @@ -24,7 +14,7 @@ namespace BiliLite.Pages /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class RegionDetailPage : BasePage + public sealed partial class RegionDetailPage : BasePage, IRefreshablePage { RegionDetailVM regionDetailVM; OpenRegionInfo regionInfo; @@ -155,6 +145,15 @@ private void AddToWatchLater_Click(object sender, RoutedEventArgs e) Modules.User.WatchLaterVM.Instance.AddToWatchlater(data.param); } + + public async Task Refresh() + { + if (cbTags.SelectedItem == null) + { + return; + } + (pivot.SelectedItem as RegionDetailChildVM).Refresh(); + } } public class RegionDataTemplateSelector : DataTemplateSelector { diff --git a/src/BiliLite.UWP/Pages/SearchPage.xaml.cs b/src/BiliLite.UWP/Pages/SearchPage.xaml.cs index 3bc486c9..74412c49 100644 --- a/src/BiliLite.UWP/Pages/SearchPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/SearchPage.xaml.cs @@ -1,4 +1,5 @@ -using BiliLite.Extensions; +using System.Threading.Tasks; +using BiliLite.Extensions; using BiliLite.Models.Common; using BiliLite.Modules; using BiliLite.Services; @@ -55,7 +56,7 @@ public class SearchParameter /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class SearchPage : BasePage + public sealed partial class SearchPage : BasePage, IRefreshablePage { SearchVM searchVM; public SearchPage() @@ -271,6 +272,12 @@ private async void txtKeyword_TextChanged(AutoSuggestBox sender, AutoSuggestBoxT searchVM.SuggestSearchContents.ReplaceRange(suggestSearchContents); } } + + public async Task Refresh() + { + if (!(pivot.SelectedItem is ISearchVM searchVm)) return; + searchVm.Refresh(); + } } public class SearchDataTemplateSelector : DataTemplateSelector { diff --git a/src/BiliLite.UWP/Pages/SettingPage.xaml b/src/BiliLite.UWP/Pages/SettingPage.xaml index 505e8a8e..b4513de7 100644 --- a/src/BiliLite.UWP/Pages/SettingPage.xaml +++ b/src/BiliLite.UWP/Pages/SettingPage.xaml @@ -153,6 +153,12 @@ + + 动态 + 动态评论宽度(下次打开应用生效) + + + 图片圆角半径(重启生效) @@ -313,6 +319,18 @@ + 音量 + + + 亮度 + + + 锁定播放器音量设置(播放器内修改音量时不写设置) + + + 锁定播放器亮度设置(播放器内修改亮度时不写设置) + + 自动打开AI字幕 部分自动生成的AI字幕会与视频自带字幕冲突 @@ -486,7 +504,7 @@ - 直播弹幕 + 直播 @@ -600,6 +618,11 @@ + + + + + 更新json请求地址(用于解决github在国内访问不佳的问题) (SettingConstants.UI.DYNAMIC_COMMENT_WIDTH, SettingConstants.UI.DEFAULT_DYNAMIC_COMMENT_WIDTH); + NumBoxDynamicCommentWidth.Loaded += (sender, e) => + { + NumBoxDynamicCommentWidth.ValueChanged += (obj, args) => + { + SettingService.SetValue(SettingConstants.UI.DYNAMIC_COMMENT_WIDTH, args.NewValue); + }; + }; + //图片圆角半径 numImageCornerRadius.Value = SettingService.GetValue(SettingConstants.UI.IMAGE_CORNER_RADIUS, 0); ImageCornerRadiusExample.CornerRadius = new CornerRadius(numImageCornerRadius.Value); @@ -421,6 +433,70 @@ private void LoadPlayer() }; }; + // 音量 + NumBoxVolume.Value = Math.Round((SettingService.GetValue(SettingConstants.Player.PLAYER_VOLUME, + SettingConstants.Player.DEFAULT_PLAYER_VOLUME)) * 100, 2); + NumBoxVolume.Loaded += (sender, e) => + { + NumBoxVolume.ValueChanged += (obj, args) => + { + if (NumBoxVolume.Value > 100) + { + NumBoxVolume.Value = 100; + } + + if (NumBoxVolume.Value < 0) + { + NumBoxVolume.Value = 0; + } + SettingService.SetValue(SettingConstants.Player.PLAYER_VOLUME, NumBoxVolume.Value/100); + }; + }; + + // 锁定播放器音量设置 + SwLockPlayerVolume.IsOn = SettingService.GetValue(SettingConstants.Player.LOCK_PLAYER_VOLUME, SettingConstants.Player.DEFAULT_LOCK_PLAYER_VOLUME); + SwLockPlayerVolume.Loaded += (sender, e) => + { + SwLockPlayerVolume.Toggled += (obj, args) => + { + SettingService.SetValue(SettingConstants.Player.LOCK_PLAYER_VOLUME, SwLockPlayerVolume.IsOn); + }; + }; + + // 亮度 + NumBoxBrightness.Value = Math.Round( + (Math.Abs(SettingService.GetValue( + SettingConstants.Player.PLAYER_BRIGHTNESS, + SettingConstants.Player.DEFAULT_PLAYER_BRIGHTNESS) - 1)) * 100, 2); + NumBoxBrightness.Loaded += (sender, e) => + { + NumBoxBrightness.ValueChanged += (obj, args) => + { + if (NumBoxBrightness.Value > 100) + { + NumBoxBrightness.Value = 100; + } + + if (NumBoxBrightness.Value < 0) + { + NumBoxBrightness.Value = 0; + } + + var brightness = Math.Abs((NumBoxBrightness.Value / 100) - 1); + SettingService.SetValue(SettingConstants.Player.PLAYER_BRIGHTNESS, brightness); + }; + }; + + // 锁定播放器亮度设置 + SwLockPlayerBrightness.IsOn = SettingService.GetValue(SettingConstants.Player.LOCK_PLAYER_BRIGHTNESS, SettingConstants.Player.DEFAULT_LOCK_PLAYER_BRIGHTNESS); + SwLockPlayerBrightness.Loaded += (sender, e) => + { + SwLockPlayerBrightness.Toggled += (obj, args) => + { + SettingService.SetValue(SettingConstants.Player.LOCK_PLAYER_BRIGHTNESS, SwLockPlayerBrightness.IsOn); + }; + }; + //自动打开AI字幕 swPlayerSettingAutoOpenAISubtitle.IsOn = SettingService.GetValue(SettingConstants.Player.AUTO_OPEN_AI_SUBTITLE, false); swPlayerSettingAutoOpenAISubtitle.Loaded += new RoutedEventHandler((sender, e) => @@ -1071,5 +1147,35 @@ private void RequestBuildDefaultBtn_OnClick(object sender, RoutedEventArgs e) RequestBuildTextBox.Text = build; Notify.ShowMessageToast("已恢复默认"); } + + private async void BtnExportSettings_OnClick(object sender, RoutedEventArgs e) + { + var exportService = App.ServiceProvider.GetRequiredService(); + await exportService.ExportSettings(); + } + + private async void BtnImportSettings_OnClick(object sender, RoutedEventArgs e) + { + var importService = App.ServiceProvider.GetRequiredService(); + if (!await importService.ImportSettings()) + { + return; + } + Notify.ShowMessageToast("导入成功,正在重启应用"); + // 等用户看提示 + await Task.Delay(3000); + var result = await CoreApplication.RequestRestartAsync(""); + + if (result == AppRestartFailureReason.NotInForeground || result == AppRestartFailureReason.Other) + { + Notify.ShowMessageToast("重启失败,请手动重启应用"); + } + } + + private async void BtnExportSettingsWithAccount_OnClick(object sender, RoutedEventArgs e) + { + var exportService = App.ServiceProvider.GetRequiredService(); + await exportService.ExportSettingsWithAccount(); + } } } diff --git a/src/BiliLite.UWP/Pages/User/DynamicSpacePage.xaml b/src/BiliLite.UWP/Pages/User/DynamicSpacePage.xaml index 8ba235e1..691291fc 100644 --- a/src/BiliLite.UWP/Pages/User/DynamicSpacePage.xaml +++ b/src/BiliLite.UWP/Pages/User/DynamicSpacePage.xaml @@ -26,44 +26,74 @@ - - - - - - + - + + + Collapsed + + + Visible - + + + Collapsed + + + Visible + + - - - - + - - + + + + + + + + - + + + Visible + + + Collapsed - + + + Visible + + + Collapsed + + + + + + + + + + @@ -106,16 +136,21 @@ Visibility="Collapsed" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" - Background="{ThemeResource HalfTransparentBackground}"> - + - + + VerticalAlignment="Stretch" RenderTransformOrigin="0.5,0.5"> + + + diff --git a/src/BiliLite.UWP/Pages/User/DynamicSpacePage.xaml.cs b/src/BiliLite.UWP/Pages/User/DynamicSpacePage.xaml.cs index 4a21e0bb..2b231a09 100644 --- a/src/BiliLite.UWP/Pages/User/DynamicSpacePage.xaml.cs +++ b/src/BiliLite.UWP/Pages/User/DynamicSpacePage.xaml.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Threading.Tasks; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Input; @@ -18,7 +19,7 @@ namespace BiliLite.Pages.User /// /// An empty page that can be used on its own or navigated to within a Frame. /// - public sealed partial class DynamicSpacePage : Page + public sealed partial class DynamicSpacePage : Page, IRefreshablePage { private readonly UserDynamicSpaceViewModel m_viewModel; private bool m_isStaggered = false; @@ -75,7 +76,7 @@ protected override async void OnNavigatedTo(NavigationEventArgs e) private async void BtnRefreshDynamic_OnClick(object sender, RoutedEventArgs e) { - await m_viewModel.GetDynamicItems(); + await Refresh(); } private void BtnTop_OnClick(object sender, RoutedEventArgs e) @@ -156,5 +157,10 @@ private void CommentPanel_OnTapped(object sender, TappedRoutedEventArgs e) { CloseCommentCore(); } + + public async Task Refresh() + { + await m_viewModel.GetDynamicItems(); + } } } diff --git a/src/BiliLite.UWP/Pages/User/FavoriteDetailPage.xaml.cs b/src/BiliLite.UWP/Pages/User/FavoriteDetailPage.xaml.cs index 01f9b6a3..23f29a7c 100644 --- a/src/BiliLite.UWP/Pages/User/FavoriteDetailPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/User/FavoriteDetailPage.xaml.cs @@ -5,6 +5,7 @@ using BiliLite.Services; using System; using System.Collections.Generic; +using System.Threading.Tasks; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Input; @@ -22,7 +23,7 @@ public class FavoriteDetailArgs /// /// 收藏夹详情、播放列表详情 /// - public sealed partial class FavoriteDetailPage : BasePage + public sealed partial class FavoriteDetailPage : BasePage, IRefreshablePage { FavoriteDetailVM favoriteDetailVM; public FavoriteDetailPage() @@ -192,5 +193,10 @@ private async void PlayAll_Click(object sender, RoutedEventArgs e) } }); } + + public async Task Refresh() + { + favoriteDetailVM.Refresh(); + } } } diff --git a/src/BiliLite.UWP/Pages/User/FavoritePage.xaml.cs b/src/BiliLite.UWP/Pages/User/FavoritePage.xaml.cs index 6349f5e0..45fb1924 100644 --- a/src/BiliLite.UWP/Pages/User/FavoritePage.xaml.cs +++ b/src/BiliLite.UWP/Pages/User/FavoritePage.xaml.cs @@ -4,18 +4,11 @@ using BiliLite.Modules; using BiliLite.Services; using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Foundation; -using Windows.Foundation.Collections; +using System.Threading.Tasks; + using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; // https://go.microsoft.com/fwlink/?LinkId=234238 上介绍了“空白页”项模板 @@ -32,7 +25,7 @@ public enum OpenFavoriteType /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class FavoritePage : BasePage + public sealed partial class FavoritePage : BasePage, IRefreshablePage { MyFollowSeasonVM animeVM; MyFollowSeasonVM cinemaVM; @@ -164,5 +157,10 @@ private async void btnFavBoxDel_Click(object sender, RoutedEventArgs e) await videoVM.DelFavorite(data.id); videoVM.Refresh(); } + + public async Task Refresh() + { + videoVM.Refresh(); + } } } diff --git a/src/BiliLite.UWP/Pages/User/HistoryPage.xaml.cs b/src/BiliLite.UWP/Pages/User/HistoryPage.xaml.cs index 34d07b71..8cbacf75 100644 --- a/src/BiliLite.UWP/Pages/User/HistoryPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/User/HistoryPage.xaml.cs @@ -1,4 +1,5 @@ -using BiliLite.Models.Common; +using System.Threading.Tasks; +using BiliLite.Models.Common; using BiliLite.Services; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -14,7 +15,7 @@ namespace BiliLite.Pages.User /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class HistoryPage : BasePage + public sealed partial class HistoryPage : BasePage, IRefreshablePage { private readonly HistoryViewModel m_viewModel; public HistoryPage() @@ -109,5 +110,10 @@ private async void SearchBox_OnQuerySubmitted(AutoSuggestBox sender, AutoSuggest var keyword = sender.Text; await m_viewModel.SearchHistory(keyword); } + + public async Task Refresh() + { + m_viewModel.Refresh(); + } } } diff --git a/src/BiliLite.UWP/Pages/User/WatchlaterPage.xaml.cs b/src/BiliLite.UWP/Pages/User/WatchlaterPage.xaml.cs index cae560ae..f8c1f446 100644 --- a/src/BiliLite.UWP/Pages/User/WatchlaterPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/User/WatchlaterPage.xaml.cs @@ -1,19 +1,9 @@ using BiliLite.Models.Common; using BiliLite.Modules.User; using BiliLite.Services; -using System; using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Windows.UI.Xaml; +using System.Threading.Tasks; using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; // https://go.microsoft.com/fwlink/?LinkId=234238 上介绍了“空白页”项模板 @@ -23,7 +13,7 @@ namespace BiliLite.Pages.User /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class WatchlaterPage : BasePage + public sealed partial class WatchlaterPage : BasePage, IRefreshablePage { WatchLaterVM watchLaterVM; public WatchlaterPage() @@ -72,5 +62,10 @@ private void Video_ItemClick(object sender, ItemClickEventArgs e) } }); } + + public async Task Refresh() + { + watchLaterVM.Refresh(); + } } } diff --git a/src/BiliLite.UWP/Pages/UserInfoPage.xaml.cs b/src/BiliLite.UWP/Pages/UserInfoPage.xaml.cs index b0df033f..7746477f 100644 --- a/src/BiliLite.UWP/Pages/UserInfoPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/UserInfoPage.xaml.cs @@ -1,4 +1,5 @@ -using BiliLite.Extensions; +using System.Threading.Tasks; +using BiliLite.Extensions; using BiliLite.Models.Common; using BiliLite.Models.Requests.Api; using BiliLite.Modules.User.UserDetail; @@ -34,7 +35,7 @@ public class UserInfoParameter /// /// 可用于自身或导航至 Frame 内部的空白页。 /// - public sealed partial class UserInfoPage : BasePage + public sealed partial class UserInfoPage : BasePage, IRefreshablePage { readonly UserDynamicViewModel m_userDynamicViewModel; UserDetailViewModel m_viewModel; @@ -405,5 +406,10 @@ private void BtnFollowingTag_OnClick(object sender, RoutedEventArgs e) { UserFollowingTagsFlyout.ShowAt(sender as DependencyObject); } + + public async Task Refresh() + { + throw new System.NotImplementedException(); + } } } diff --git a/src/BiliLite.UWP/Pages/VideoDetailPage.xaml.cs b/src/BiliLite.UWP/Pages/VideoDetailPage.xaml.cs index 9a282891..455f87bf 100644 --- a/src/BiliLite.UWP/Pages/VideoDetailPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/VideoDetailPage.xaml.cs @@ -41,7 +41,7 @@ public class VideoPlaylistItem public string Cover { get; set; } public string Title { get; set; } } - public sealed partial class VideoDetailPage : PlayPage + public sealed partial class VideoDetailPage : PlayPage, IRefreshablePage { private static readonly ILogger logger = GlobalLogger.FromCurrentType(); @@ -610,12 +610,17 @@ private async void btnDownload_Click(object sender, RoutedEventArgs e) await downloadDialog.ShowAsync(); } - private async void btnRefresh_Click(object sender, RoutedEventArgs e) + public async Task Refresh() { if (m_viewModel.Loading) return; await InitializeVideo(_id); } + private async void btnRefresh_Click(object sender, RoutedEventArgs e) + { + await Refresh(); + } + private void btnOpenQR_Click(object sender, RoutedEventArgs e) { qrFlyout.ShowAt(btnMore); diff --git a/src/BiliLite.UWP/Services/GrpcService.cs b/src/BiliLite.UWP/Services/GrpcService.cs index 5dcd9ccb..86227020 100644 --- a/src/BiliLite.UWP/Services/GrpcService.cs +++ b/src/BiliLite.UWP/Services/GrpcService.cs @@ -40,12 +40,17 @@ public async Task SearchSpaceArchive(string mid, int page = } } - public async Task GetDynAll(int page = 1) + public async Task GetDynAll(int page = 1, string offset = null) { var message = new DynAllReq() { - Page = page + Page = page, }; + if (offset != null) + { + message.Offset = offset; + message.RefreshType = Refresh.History; + } var requestUserInfo = new GrpcBiliUserInfo( SettingService.Account.AccessKey, SettingService.Account.UserID, diff --git a/src/BiliLite.UWP/Services/MessageCenter.cs b/src/BiliLite.UWP/Services/MessageCenter.cs index 0d916fd1..38198326 100644 --- a/src/BiliLite.UWP/Services/MessageCenter.cs +++ b/src/BiliLite.UWP/Services/MessageCenter.cs @@ -94,6 +94,13 @@ private static void ClearCookie() /// public static async Task HandelUrl(string url, bool dontGoTo = false) { + var uriHref = ""; + if (url.IsUrl()) + { + var uri = new Uri(url); + uriHref = uri.GetLeftPart(UriPartial.Path); + } + _logger.Debug($"处理链接:{url}"); if (url.First() == '@') { @@ -115,7 +122,7 @@ public static async Task HandelUrl(string url, bool dontGoTo = false) * bilibili://live/5619438 */ - var live = StringExtensions.RegexMatch(url.Replace("h5", "live").Replace("live.bilibili.com", "live").Replace("/", ""), @"live(\d+)"); + var live = StringExtensions.RegexMatch(uriHref.Replace("h5", "live").Replace("live.bilibili.com", "live").Replace("/", ""), @"live(\d+)"); if (live != "") { NavigateToPage(null, new NavigationInfo() diff --git a/src/BiliLite.UWP/Services/PlayerToastService.cs b/src/BiliLite.UWP/Services/PlayerToastService.cs new file mode 100644 index 00000000..6952292f --- /dev/null +++ b/src/BiliLite.UWP/Services/PlayerToastService.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Timers; +using System.Threading.Tasks; +using Windows.UI.Xaml.Controls; +using BiliLite.Controls; +using Windows.UI.Core; +using Microsoft.Extensions.DependencyInjection; + +namespace BiliLite.Services +{ + public class PlayerToastService + { + private PlayerControl m_playerControl; + private int[] m_bottomList = new[] { 170, 240, 310, 380, 450, 520 }; + private readonly Dictionary m_showPlayerToasts = new Dictionary(); + private readonly Dictionary m_showPlayerToastTimers = new Dictionary(); + public const string VOLUME_KEY = "Volume"; + public const string BRIGHTNESS_KEY = "Brightness"; + public const string PROGRESS_KEY = "Progress"; + public const string MSG_KEY = "Msg"; + public const string ACCELERATING_KEY = "Accelerating"; + public const string SPEED_KEY = "Speed"; + private readonly IServiceProvider m_serviceProvider; + + public PlayerToastService(IServiceProvider serviceProvider) + { + m_serviceProvider = serviceProvider; + } + + public void Init(PlayerControl control) + { + m_playerControl = control; + } + + public async void KeepStart(string key, string msg) + { + var newToast = m_serviceProvider.GetRequiredService(); + newToast.Height = 80; + newToast.Width = 200; + newToast.Text = msg; + Canvas.SetLeft(newToast, 0); + double distanceFromBottom = m_bottomList[m_showPlayerToasts.Count]; + while (m_playerControl.ActualHeight == 0) + { + await Task.Delay(500); + } + Canvas.SetTop(newToast, m_playerControl.ActualHeight - distanceFromBottom); + + m_showPlayerToasts.Add(key, newToast); + m_playerControl.PlayerToastContainer.Children.Add(newToast); + newToast.Show(); + } + + public async void KeepClose(string key) + { + if (!m_showPlayerToasts.TryGetValue(key, out var toast)) return; + m_playerControl.PlayerToastContainer.Children.Remove(toast); + m_showPlayerToasts.Remove(key); + } + + public async void Show(string key, string msg) + { + if (m_showPlayerToasts.TryGetValue(key, out var toast)) + { + toast.Text = msg; + var timer = m_showPlayerToastTimers[key]; + timer.Stop(); + timer.Start(); + return; + } + + var newToast = m_serviceProvider.GetRequiredService(); + newToast.Height = 80; + newToast.Width = 200; + newToast.Text = msg; + Canvas.SetLeft(newToast, 0); + double distanceFromBottom = m_bottomList[m_showPlayerToasts.Count]; + while (m_playerControl.ActualHeight == 0) + { + await Task.Delay(500); + } + Canvas.SetTop(newToast, m_playerControl.ActualHeight - distanceFromBottom); + + m_showPlayerToasts.Add(key, newToast); + m_playerControl.PlayerToastContainer.Children.Add(newToast); + newToast.Show(); + + var newTimer = new Timer(); + newTimer.Interval = 2000; + newTimer.Elapsed += async (o, e) => + { + await m_playerControl.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => + { + newTimer.Stop(); + m_playerControl.PlayerToastContainer.Children.Remove(newToast); + m_showPlayerToasts.Remove(key); + m_showPlayerToastTimers.Remove(key); + }); + }; + m_showPlayerToastTimers.Add(key, newTimer); + newTimer.Start(); + } + } +} diff --git a/src/BiliLite.UWP/Services/SettingService.cs b/src/BiliLite.UWP/Services/SettingService.cs index 17c599d7..37356911 100644 --- a/src/BiliLite.UWP/Services/SettingService.cs +++ b/src/BiliLite.UWP/Services/SettingService.cs @@ -25,6 +25,11 @@ public static void SetValue(string key, T value) storageHelper.Save(key, value); } + public static bool HasValue(string key) + { + return storageHelper.KeyExists(key); + } + public class UI { private static bool? _loadOriginalImage = null; diff --git a/src/BiliLite.UWP/Services/SettingsImportExportService.cs b/src/BiliLite.UWP/Services/SettingsImportExportService.cs new file mode 100644 index 00000000..963fb4ed --- /dev/null +++ b/src/BiliLite.UWP/Services/SettingsImportExportService.cs @@ -0,0 +1,257 @@ +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Windows.Storage.Pickers; +using BiliLite.Models.Attributes; +using BiliLite.Models.Common; +using Newtonsoft.Json; +using Tomlyn; +using Tomlyn.Model; +using BiliLite.Extensions; +using System.Security.Cryptography; +using Windows.Storage; +using ArtisanCode.SimpleAesEncryption; + +namespace BiliLite.Services +{ + public class SettingsImportExportService + { + private static readonly ILogger _logger = GlobalLogger.FromCurrentType(); + private const string SETTINGS_EXPORT_KEY = "4bmsD9chgoP1jdohv2+OV9Pv2403r4IfJU18ixpFWQA="; + private const string SETTINGS_EXPORT_IV = "ABxrLAWa7MrKg6w1xxtZmw=="; + private readonly RijndaelMessageEncryptor m_encryptor; + private readonly RijndaelMessageDecryptor m_decryptor; + + public SettingsImportExportService() + { + var config = new SimpleAesEncryptionConfiguration() + { + CipherMode = CipherMode.CBC, + Padding = PaddingMode.PKCS7, + EncryptionKey = new EncryptionKeyConfigurationElement(256, SETTINGS_EXPORT_KEY), + }; + m_encryptor = new RijndaelMessageEncryptor(config); + m_decryptor = new RijndaelMessageDecryptor(config); + } + + private void ExportSettingsCore(TomlTable model, Type settingsType) + { + var fields = settingsType.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); + foreach (var field in fields) + { + var attributes = field.GetCustomAttributes(false); + var keyAttribute = attributes.FirstOrDefault(x => x.GetType() == typeof(SettingKeyAttribute)); + if (!(keyAttribute is SettingKeyAttribute settingKeyAttribute)) continue; + var key = field.GetRawConstantValue().ToString(); + + if (!SettingService.HasValue(key)) continue; + + object value = null; + if (settingKeyAttribute.Type == typeof(string)) + { + value = SettingService.GetValue(key, ""); + } + else if (settingKeyAttribute.Type == typeof(int)) + { + value = SettingService.GetValue(key, 0); + } + else if (settingKeyAttribute.Type == typeof(long)) + { + value = SettingService.GetValue(key, 0); + } + else if (settingKeyAttribute.Type == typeof(double)) + { + value = SettingService.GetValue(key, 0); + } + else if (settingKeyAttribute.Type == typeof(object)) + { + value = SettingService.GetValue(key, null); + value = JsonConvert.SerializeObject(value); + } + else if (settingKeyAttribute.Type == typeof(bool)) + { + value = SettingService.GetValue(key, false); + } + + model[key] = value; + } + } + + private void ImportSettingsCore(TomlTable model, Type settingsType) + { + var fields = settingsType.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); + foreach (var field in fields) + { + var attributes = field.GetCustomAttributes(false); + var keyAttribute = attributes.FirstOrDefault(x => x.GetType() == typeof(SettingKeyAttribute)); + if (!(keyAttribute is SettingKeyAttribute settingKeyAttribute)) continue; + var key = field.GetRawConstantValue().ToString(); + + if (!model.ContainsKey(key)) continue; + + if (settingKeyAttribute.Type == typeof(object)) + { + var value = JsonConvert.DeserializeObject(model[key].ToString()); + SettingService.SetValue(key, value); + } + else if (settingKeyAttribute.Type == typeof(string)) + { + var value = model[key].ToString(); + SettingService.SetValue(key, value); + } + else if (settingKeyAttribute.Type == typeof(int)) + { + var value = model[key].ToInt32(); + SettingService.SetValue(key, value); + } + else if (settingKeyAttribute.Type == typeof(long)) + { + var value = model[key].ToInt64(); + SettingService.SetValue(key, value); + } + else if (settingKeyAttribute.Type == typeof(double)) + { + var value = (double)model[key]; + SettingService.SetValue(key, value); + } + else + { + SettingService.SetValue(key, model[key]); + } + } + } + + public byte[] EncryptToBinary(string plainText) + { + var cyphertext = m_encryptor.Encrypt(plainText); + return System.Text.Encoding.UTF8.GetBytes(cyphertext); + } + + public string DecryptFromBinary(byte[] cipherTextBinary) + { + var binText = System.Text.Encoding.UTF8.GetString(cipherTextBinary); + var plaintext = m_decryptor.Decrypt(binText); + return plaintext; + } + + private string Decode(byte[] bin) + { + var text = DecryptFromBinary(bin); + return text; + } + + private byte[] Encode(string text) + { + var bin = EncryptToBinary(text); + return bin; + } + + public async Task ExportSettings() + { + var folder = await new FolderPicker().PickSingleFolderAsync(); + if (folder == null) return false; + var file = await folder.CreateFileAsync($"{DateTime.Now.ToString("yyyy-M-d-HH_mm_ss")}.bililiteSettings"); + + try + { + var model = Toml.ToModel(""); + + ExportSettingsCore(model, typeof(SettingConstants.UI)); + ExportSettingsCore(model, typeof(SettingConstants.VideoDanmaku)); + ExportSettingsCore(model, typeof(SettingConstants.Live)); + ExportSettingsCore(model, typeof(SettingConstants.Player)); + ExportSettingsCore(model, typeof(SettingConstants.Roaming)); + ExportSettingsCore(model, typeof(SettingConstants.Download)); + ExportSettingsCore(model, typeof(SettingConstants.Other)); + + var modelString = Toml.FromModel(model); + + var bin = Encode(modelString); + + using var stream = await file.OpenStreamForWriteAsync(); + await stream.WriteAsync(bin, 0, bin.Length); + await stream.FlushAsync(); + return true; + } + catch (Exception ex) + { + _logger.Error("导出失败", ex); + Notify.ShowMessageToast("导出失败,已记录错误"); + return false; + } + } + + public async Task ExportSettingsWithAccount() + { + var folder = await new FolderPicker().PickSingleFolderAsync(); + if (folder == null) return false; + var file = await folder.CreateFileAsync($"{DateTime.Now.ToString("yyyy-M-d-HH_mm_ss")}.bililiteSettings"); + + try + { + var model = Toml.ToModel(""); + + ExportSettingsCore(model, typeof(SettingConstants.UI)); + ExportSettingsCore(model, typeof(SettingConstants.Account)); + ExportSettingsCore(model, typeof(SettingConstants.VideoDanmaku)); + ExportSettingsCore(model, typeof(SettingConstants.Live)); + ExportSettingsCore(model, typeof(SettingConstants.Player)); + ExportSettingsCore(model, typeof(SettingConstants.Roaming)); + ExportSettingsCore(model, typeof(SettingConstants.Download)); + ExportSettingsCore(model, typeof(SettingConstants.Other)); + + var modelString = Toml.FromModel(model); + + var bin = Encode(modelString); + + using var stream = await file.OpenStreamForWriteAsync(); + await stream.WriteAsync(bin, 0, bin.Length); + await stream.FlushAsync(); + return true; + } + catch (Exception ex) + { + _logger.Error("导出失败", ex); + Notify.ShowMessageToast("导出失败,已记录错误"); + return false; + } + } + + public async Task ImportSettings() + { + var filePicker = new FileOpenPicker(); + filePicker.FileTypeFilter.Add(".bililiteSettings"); + var file = await filePicker.PickSingleFileAsync(); + if (file == null) return false; + using var openFile = await file.OpenAsync(FileAccessMode.Read); + using var stream = openFile.AsStreamForRead(); + var bin = new byte[stream.Length]; + + await stream.ReadAsync(bin, 0, bin.Length); + + try + { + var text = Decode(bin); + var model = Toml.ToModel(text); + + ImportSettingsCore(model, typeof(SettingConstants.UI)); + ImportSettingsCore(model, typeof(SettingConstants.Account)); + ImportSettingsCore(model, typeof(SettingConstants.VideoDanmaku)); + ImportSettingsCore(model, typeof(SettingConstants.Live)); + ImportSettingsCore(model, typeof(SettingConstants.Player)); + ImportSettingsCore(model, typeof(SettingConstants.Roaming)); + ImportSettingsCore(model, typeof(SettingConstants.Download)); + ImportSettingsCore(model, typeof(SettingConstants.Other)); + } + catch (Exception ex) + { + _logger.Error("导入失败", ex); + Notify.ShowMessageToast("导入失败,已记录错误"); + return false; + } + return true; + } + } +} diff --git a/src/BiliLite.UWP/Services/ShortcutKeyService.cs b/src/BiliLite.UWP/Services/ShortcutKeyService.cs new file mode 100644 index 00000000..04124b3e --- /dev/null +++ b/src/BiliLite.UWP/Services/ShortcutKeyService.cs @@ -0,0 +1,46 @@ +using BiliLite.Models.Functions; +using System.Collections.Generic; +using System.Linq; +using Windows.System; +using Windows.UI.Core; +using Windows.UI.Xaml; +using BiliLite.Pages; + +namespace BiliLite.Services +{ + public class ShortcutKeyService + { + private Dictionary> m_shortcutKeyMaps; + private IMainPage m_mainPage; + + public ShortcutKeyService() + { + m_shortcutKeyMaps = new Dictionary>(); + // TODO: 读取设置 + m_shortcutKeyMaps.Add(new RefreshShortcutFunction(), new List() { VirtualKey.Control, VirtualKey.R }); + m_shortcutKeyMaps.Add(new RefreshShortcutFunction(), new List() { VirtualKey.F5 }); + } + + public void SetMainPage(IMainPage mainPage) + { + m_mainPage = mainPage; + } + + public void HandleKeyDown(VirtualKey key) + { + foreach (var (shortcutKeyFunction, shortcutKeyCodes) in m_shortcutKeyMaps) + { + if (shortcutKeyCodes.LastOrDefault() != key) continue; + if (shortcutKeyCodes + .Select(keyCode => Window.Current.CoreWindow.GetKeyState(keyCode)) + .Any(keyState => !keyState.HasFlag(CoreVirtualKeyStates.Down))) + { + continue; + } + + var page = m_mainPage.CurrentPage; + shortcutKeyFunction.Action(page); + } + } + } +} diff --git a/src/BiliLite.UWP/Services/WbiKeyService.cs b/src/BiliLite.UWP/Services/WbiKeyService.cs index 3e645ee9..06dd62a4 100644 --- a/src/BiliLite.UWP/Services/WbiKeyService.cs +++ b/src/BiliLite.UWP/Services/WbiKeyService.cs @@ -36,7 +36,7 @@ private WbiKey GetCurrentWbiKeys() public async Task GetWbiKeys() { - var lastKeySaveTimestamp = SettingService.GetValue(SettingConstants.Account.WBI_KEY_TIME, 0); + var lastKeySaveTimestamp = SettingService.GetValue(SettingConstants.Account.WBI_KEY_TIME, 0); if (lastKeySaveTimestamp <= 0) return await GetNewWbiKeys(); var lastKeySaveTime = DateTimeOffset.FromUnixTimeSeconds(lastKeySaveTimestamp); if ((DateTimeOffset.Now - lastKeySaveTime) >= diff --git a/src/BiliLite.UWP/Startup.cs b/src/BiliLite.UWP/Startup.cs index 811ce56d..d6eb4f88 100644 --- a/src/BiliLite.UWP/Startup.cs +++ b/src/BiliLite.UWP/Startup.cs @@ -15,6 +15,9 @@ public void ConfigureServices(HostBuilderContext context, IServiceCollection ser services.AddDanmakuController(); services.AddSingleton(); + services.AddSingleton(); + services.AddTransient(); + services.AddTransient(); services.AddQrCodeService(); services.AddSingleton(); diff --git a/src/BiliLite.UWP/ViewModels/Home/HomeViewModel.cs b/src/BiliLite.UWP/ViewModels/Home/HomeViewModel.cs index 026eef5f..9ab3e3f2 100644 --- a/src/BiliLite.UWP/ViewModels/Home/HomeViewModel.cs +++ b/src/BiliLite.UWP/ViewModels/Home/HomeViewModel.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Threading.Tasks; using AutoMapper; -using BiliLite.Models.Common; using BiliLite.Models.Common.Home; using BiliLite.Modules; using BiliLite.Services; @@ -15,6 +14,7 @@ public class HomeViewModel : BaseViewModel { #region Fields + private static readonly ILogger _logger = GlobalLogger.FromCurrentType(); private readonly IMapper m_mapper; private readonly Account m_account; @@ -26,7 +26,7 @@ public HomeViewModel() { m_account = new Account(); m_mapper = App.ServiceProvider.GetRequiredService(); - var homeNavItemList = SettingService.GetValue(SettingConstants.UI.HOEM_ORDER, DefaultHomeNavItems.GetDefaultHomeNavItems()); + var homeNavItemList = DefaultHomeNavItems.GetHomeNavItems(); HomeNavItems = m_mapper.Map>(homeNavItemList); SelectItem = HomeNavItems.FirstOrDefault(); if (!SettingService.Account.Logined) return; diff --git a/src/BiliLite.UWP/ViewModels/PlayerToastViewModel.cs b/src/BiliLite.UWP/ViewModels/PlayerToastViewModel.cs new file mode 100644 index 00000000..75276632 --- /dev/null +++ b/src/BiliLite.UWP/ViewModels/PlayerToastViewModel.cs @@ -0,0 +1,9 @@ +using BiliLite.ViewModels.Common; + +namespace BiliLite.ViewModels +{ + public class PlayerToastViewModel : BaseViewModel + { + public string Text { get; set; } + } +} diff --git a/src/BiliLite.UWP/ViewModels/UserDynamic/DynamicV2ItemViewModel.cs b/src/BiliLite.UWP/ViewModels/UserDynamic/DynamicV2ItemViewModel.cs index 28cc8a8f..9bcd3cb0 100644 --- a/src/BiliLite.UWP/ViewModels/UserDynamic/DynamicV2ItemViewModel.cs +++ b/src/BiliLite.UWP/ViewModels/UserDynamic/DynamicV2ItemViewModel.cs @@ -25,7 +25,7 @@ public DynamicV2ItemViewModel() m_mapper = App.ServiceProvider.GetRequiredService(); } - public UserDynamicSpaceViewModel Parent { get; set; } + public IUserDynamicCommands Parent { get; set; } public string CardType { get; set; } @@ -35,6 +35,12 @@ public DynamicV2ItemViewModel() public ModuleAuthorForward AuthorForward { get; set; } + public UserDynamicSeasonInfo Season { get; set; } + + public NavDynArticle CustomArticle { get; set; } + + public string SourceJson { get; set; } + public ModuleDynamic Dynamic { get @@ -53,6 +59,23 @@ public ModuleDynamic Dynamic { LiveInfo = JsonConvert.DeserializeObject(value.DynLiveRcmd.Content); } + + if (value.DynCommonLive != null) + { + LiveInfo = new DynLiveInfo() + { + PlayInfo = new DynLivePlayInfo() + { + Cover = value.DynCommonLive.Cover, + Title = value.DynCommonLive.Title, + AreaName = value.DynCommonLive.CoverLabel, + WatchedShow = new DynLiveWatchedShow() + { + TextLarge = value.DynCommonLive.CoverLabel2, + } + } + }; + } } } @@ -60,14 +83,20 @@ public ModuleDynamic Dynamic public ModuleDesc Desc { get; set; } + public ModuleFold Fold { get; set; } + public ModuleOpusSummary OpusSummary { get; set; } + [DependsOn(nameof(Item))] + public bool ForwardMissed => Item == null; + public DynamicV2ItemViewModel Item { get; set; } public List Items { get { + if (Item == null) return null; Item.Parent = Parent; return new List() { @@ -164,9 +193,35 @@ public List ImageInfos } [DoNotNotify] - public string Verify { get; set; } = Constants.App.TRANSPARENT_IMAGE; + public string Verify + { + get + { + if (Author?.Author?.Official == null) + return Constants.App.TRANSPARENT_IMAGE; + return Author.Author.Official.Type switch + { + 1 => Constants.App.VERIFY_OGANIZATION_IMAGE, + 0 => Constants.App.VERIFY_PERSONAL_IMAGE, + -1 => Constants.App.TRANSPARENT_IMAGE, + _ => Constants.App.TRANSPARENT_IMAGE + }; + } + } [DoNotNotify] - public string Pendant { get; set; } = Constants.App.TRANSPARENT_IMAGE; + public string Pendant + { + get + { + if (Author == null) return Constants.App.TRANSPARENT_IMAGE; + if (Author.Author.Pendant != null && !string.IsNullOrEmpty(Author.Author.Pendant.Image)) + { + return Author.Author.Pendant.Image; + } + + return Constants.App.TRANSPARENT_IMAGE; + } + } } } \ No newline at end of file diff --git a/src/BiliLite.UWP/ViewModels/UserDynamic/UserDynamicAllViewModel.cs b/src/BiliLite.UWP/ViewModels/UserDynamic/UserDynamicAllViewModel.cs new file mode 100644 index 00000000..45188291 --- /dev/null +++ b/src/BiliLite.UWP/ViewModels/UserDynamic/UserDynamicAllViewModel.cs @@ -0,0 +1,400 @@ +using AutoMapper; +using BiliLite.Models.Exceptions; +using BiliLite.Models.Requests.Api.User; +using BiliLite.Modules.User; +using BiliLite.Services; +using BiliLite.ViewModels.Common; +using System; +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using System.Windows.Input; +using BiliLite.Extensions; +using Bilibili.App.Dynamic.V2; +using System.Collections.Generic; +using System.Linq; +using Windows.UI.Xaml.Controls; +using BiliLite.Dialogs; +using BiliLite.Models; +using BiliLite.Models.Common; +using BiliLite.Models.Common.UserDynamic; +using BiliLite.Modules; +using BiliLite.Pages; +using BiliLite.Pages.User; +using Microsoft.Extensions.DependencyInjection; +using DynamicType = Bilibili.App.Dynamic.V2.DynamicType; + +namespace BiliLite.ViewModels.UserDynamic +{ + public class UserDynamicAllViewModel : BaseViewModel, IUserDynamicCommands + { + #region Fields + + private static readonly ILogger _logger = GlobalLogger.FromCurrentType(); + private readonly GrpcService m_grpcService; + private readonly IMapper m_mapper; + private readonly DynamicAPI m_dynamicApi; + private readonly WatchLaterVM m_watchLaterVm; + private int m_page = 1; + private string m_offset = null; + private string m_baseline = null; + private UserDynamicShowType m_showType; + + #endregion + + #region Constructors + + public UserDynamicAllViewModel(GrpcService grpcService, IMapper mapper) + { + m_grpcService = grpcService; + m_mapper = mapper; + m_dynamicApi = new DynamicAPI(); + m_watchLaterVm = new WatchLaterVM(); + LoadMoreCommand = new RelayCommand(LoadMore); + UserCommand = new RelayCommand(OpenUser); + DetailCommand = new RelayCommand(OpenDetail); + ImageCommand = new RelayCommand(OpenImage); + WebDetailCommand = new RelayCommand(OpenWebDetail); + CommentCommand = new RelayCommand(OpenComment); + LikeCommand = new RelayCommand(DoLike); + RepostCommand = new RelayCommand(OpenSendDynamicDialog); + LaunchUrlCommand = new RelayCommand(LaunchUrl); + CopyDynCommand = new RelayCommand(CopyDyn); + TagCommand = new RelayCommand(OpenTag); + WatchLaterCommand = m_watchLaterVm.AddCommandWithAvId; + } + + #endregion + + #region Properties + + public ICommand LaunchUrlCommand { get; set; } + + public ICommand RepostCommand { get; set; } + + public ICommand LikeCommand { get; set; } + + public ICommand CommentCommand { get; set; } + + public ICommand UserCommand { get; set; } + + public ICommand LoadMoreCommand { get; private set; } + + public ICommand WebDetailCommand { get; set; } + + public ICommand DetailCommand { get; set; } + + public ICommand ImageCommand { get; set; } + + public ICommand WatchLaterCommand { get; set; } + + public ICommand CopyDynCommand { get; set; } + + public ICommand TagCommand { get; set; } + + public bool Loading { get; set; } + + public bool CanLoadMore { get; set; } + + public ObservableCollection DynamicItems { get; set; } + + public double CommentControlWidth => SettingService.GetValue(SettingConstants.UI.DYNAMIC_COMMENT_WIDTH, SettingConstants.UI.DEFAULT_DYNAMIC_COMMENT_WIDTH); + + #endregion + + #region Events + + public event EventHandler OpenCommentEvent; + + #endregion + + #region Private Methods + + private void OpenUser(object userId) + { + MessageCenter.NavigateToPage(this, new NavigationInfo() + { + icon = Symbol.Contact, + page = typeof(UserInfoPage), + title = "用户中心", + parameters = userId + }); + } + + private void OpenComment(DynamicV2ItemViewModel data) + { + OpenCommentEvent?.Invoke(this, data); + } + + private void OpenWebDetail(string dynId) + { + var url = $"https://www.bilibili.com/opus/{dynId}"; + MessageCenter.NavigateToPage(null, new NavigationInfo() + { + icon = Symbol.World, + page = typeof(WebPage), + title = "加载中...", + parameters = url + }); + } + + private void OpenTag(object name) + { + //TODO 打开话题 + MessageCenter.NavigateToPage(this, new NavigationInfo() + { + icon = Symbol.World, + page = typeof(WebPage), + title = name.ToString(), + parameters = "https://t.bilibili.com/topic/name/" + Uri.EscapeDataString(name.ToString()) + }); + } + + private void OpenImage(object data) + { + if (!(data is UserDynamicItemDisplayImageInfo imageInfo)) + { + return; + } + MessageCenter.OpenImageViewer(imageInfo.AllImages, imageInfo.Index); + } + + private void OpenDetail(string dynId) + { + MessageCenter.NavigateToPage(this, new NavigationInfo() + { + icon = Symbol.Document, + page = typeof(DynamicDetailPage), + title = "动态详情", + parameters = dynId + }); + } + + private async void LaunchUrl(string url) + { + var result = await MessageCenter.HandelUrl(url); + if (!result) + { + Notify.ShowMessageToast("无法打开Url"); + } + } + + private async Task DoLikeCore(DynamicV2ItemViewModel item) + { + var results = await m_dynamicApi.Like(item.Extend.DynIdStr, item.Liked ? 2 : 1).Request(); + if (!results.status) + { + throw new CustomizedErrorException(results.message); + } + + var data = await results.GetJson>(); + if (!data.success) + { + throw new CustomizedErrorException(data.message); + } + + if (item.Liked) + { + item.Liked = false; + item.LikeCount -= 1; + } + else + { + item.Liked = true; + item.LikeCount += 1; + } + } + + private async void DoLike(DynamicV2ItemViewModel item) + { + if (!await BiliExtensions.ActionCheckLogin()) return; + + try + { + await DoLikeCore(item); + } + catch (CustomizedErrorException ex) + { + Notify.ShowMessageToast(ex.Message); + _logger.Error(ex.Message); + } + catch (Exception ex) + { + var handel = HandelError(ex); + Notify.ShowMessageToast(handel.message); + } + } + + private async void OpenSendDynamicDialog(DynamicV2ItemViewModel data) + { + if (!await BiliExtensions.ActionCheckLogin()) return; + + var sendDynamicDialog = App.ServiceProvider.GetRequiredService(); + if (data != null) + { + sendDynamicDialog.SetRepost(data); + } + await sendDynamicDialog.ShowAsync(); + } + + private void CopyDyn(DynamicV2ItemViewModel data) + { + var dataStr = data.SourceJson; + Notify.ShowMessageToast(dataStr.SetClipboard() ? "已复制" : "复制失败"); + } + + private void HandleDynamicResults(DynAllReply results) + { + CanLoadMore = results.DynamicList.HasMore; + m_offset = results.DynamicList.HistoryOffset; + var items = m_mapper.Map>(results.DynamicList.List + .Where(x => x.CardType != DynamicType.Banner).ToList()); + foreach (var item in items) + { + item.Parent = this; + } + if (m_page == 1) + DynamicItems = new ObservableCollection(items); + else + { + DynamicItems.AddRange(items); + } + } + + private void HandleDynamicSeasonResults(DynVideoReply results) + { + CanLoadMore = false; + var seasonItems = m_mapper.Map>(results.VideoFollowList.List.ToList()); + var dynamicItems = seasonItems.Select(x => x.ToDynamicItem()).ToList(); + DynamicItems = new ObservableCollection(dynamicItems); + } + + private void HandleDynamicVideoResults(DynVideoReply results) + { + CanLoadMore = results.DynamicList.HasMore; + m_offset = results.DynamicList.HistoryOffset; + m_baseline = results.DynamicList.UpdateBaseline; + var items = m_mapper.Map>(results.DynamicList.List.ToList()); + + foreach (var item in items) + { + item.Parent = this; + } + if (m_page == 1) + DynamicItems = new ObservableCollection(items); + else + { + DynamicItems.AddRange(items); + } + } + + private async Task GetDynArticle() + { + if (m_offset == null) + { + m_baseline = "0"; + } + var api = m_dynamicApi.Article(m_baseline); + var results = await api.Request(); + if (!results.status) + { + throw new CustomizedErrorException(results.message); + } + + var data = await results.GetData(); + + if (!data.success) + { + throw new CustomizedErrorException(data.message); + } + + return data.data; + } + + private void HandleDynamicArticleResults(NavDynArticles results) + { + CanLoadMore = results.HasMore; + m_offset = results.Offset; + m_baseline = results.UpdateBaseline; + var dynamicItems = results.Items.Select(x => x.ToDynamicItem()).ToList(); + DynamicItems = new ObservableCollection(dynamicItems); + } + + #endregion + + #region Public Methods + + public async void LoadMore() + { + if (Loading) + { + return; + } + if (DynamicItems == null || DynamicItems.Count == 0) + { + return; + } + + await GetDynamicItems(m_page + 1, m_showType); + } + + public async Task GetDynamicItems(int page = 1, UserDynamicShowType showType = UserDynamicShowType.All) + { + try + { + CanLoadMore = false; + Loading = true; + m_page = page; + m_showType = showType; + if (page == 1) + { + m_offset = null; + DynamicItems?.Clear(); + } + + switch (showType) + { + case UserDynamicShowType.All: + { + var results = await m_grpcService.GetDynAll(page: page, offset: m_offset); + HandleDynamicResults(results); + break; + } + case UserDynamicShowType.Video: + { + var results = await m_grpcService.GetDynVideo(page, m_offset, m_baseline); + HandleDynamicVideoResults(results); + break; + } + case UserDynamicShowType.Season: + { + var results = await m_grpcService.GetDynVideo(page, m_offset, m_baseline); + HandleDynamicSeasonResults(results); + break; + } + case UserDynamicShowType.Article: + { + var results = await GetDynArticle(); + HandleDynamicArticleResults(results); + break; + } + } + } + catch (CustomizedErrorException ex) + { + Notify.ShowMessageToast(ex.Message); + _logger.Error(ex.Message); + } + catch (Exception ex) + { + var handel = HandelError(ex); + Notify.ShowMessageToast(handel.message); + } + finally + { + Loading = false; + } + } + + #endregion + } +} diff --git a/src/BiliLite.UWP/ViewModels/UserDynamic/UserDynamicSpaceViewModel.cs b/src/BiliLite.UWP/ViewModels/UserDynamic/UserDynamicSpaceViewModel.cs index 73396fd4..19a2f2f3 100644 --- a/src/BiliLite.UWP/ViewModels/UserDynamic/UserDynamicSpaceViewModel.cs +++ b/src/BiliLite.UWP/ViewModels/UserDynamic/UserDynamicSpaceViewModel.cs @@ -25,7 +25,7 @@ namespace BiliLite.ViewModels.UserDynamic { - public class UserDynamicSpaceViewModel : BaseViewModel + public class UserDynamicSpaceViewModel : BaseViewModel, IUserDynamicCommands { #region Fields @@ -97,13 +97,7 @@ public UserDynamicSpaceViewModel(GrpcService grpcService, IMapper mapper) public ObservableCollection DynamicItems { get; set; } - public int CommentControlWidth - { - get - { - return SettingService.GetValue(SettingConstants.UI.RIGHT_DETAIL_WIDTH, 320); - } - } + public double CommentControlWidth => SettingService.GetValue(SettingConstants.UI.DYNAMIC_COMMENT_WIDTH, SettingConstants.UI.DEFAULT_DYNAMIC_COMMENT_WIDTH); #endregion @@ -261,7 +255,7 @@ private async void OpenSendDynamicDialog(DynamicV2ItemViewModel data) private void CopyDyn(DynamicV2ItemViewModel data) { - var dataStr = JsonConvert.SerializeObject(data); + var dataStr = data.SourceJson; Notify.ShowMessageToast(dataStr.SetClipboard() ? "已复制" : "复制失败"); } diff --git a/src/BiliLite.UWP/ViewModels/Video/VideoDetailStatViewModel.cs b/src/BiliLite.UWP/ViewModels/Video/VideoDetailStatViewModel.cs index 72317112..da82b54b 100644 --- a/src/BiliLite.UWP/ViewModels/Video/VideoDetailStatViewModel.cs +++ b/src/BiliLite.UWP/ViewModels/Video/VideoDetailStatViewModel.cs @@ -10,42 +10,42 @@ public class VideoDetailStatViewModel : BaseViewModel /// /// 播放 /// - public int View { get; set; } + public long View { get; set; } /// /// 弹幕 /// - public int Danmaku { get; set; } + public long Danmaku { get; set; } /// /// 评论 /// - public int Reply { get; set; } + public long Reply { get; set; } /// /// 收藏 /// - public int Favorite { get; set; } + public long Favorite { get; set; } /// /// 投币 /// - public int Coin { get; set; } + public long Coin { get; set; } /// /// 分享 /// - public int Share { get; set; } + public long Share { get; set; } /// /// 点赞 /// - public int Like { get; set; } + public long Like { get; set; } /// /// 不喜欢,固定0 /// [JsonProperty("dislike")] - public int DisLike { get; set; } + public long DisLike { get; set; } } } \ No newline at end of file