From a79213242819cb70725cc1b36a231ccc0f1770ae Mon Sep 17 00:00:00 2001 From: Michael Gordeev Date: Tue, 12 May 2020 01:32:41 +0300 Subject: [PATCH] App UI development Updated and enchanced MainPage Updated Core --- FoxTube.Core/Extensions.cs | 16 + FoxTube.Core/FoxTube.Core.csproj | 11 +- FoxTube.Core/Settings.cs | 5 +- FoxTube.Core/UserManagement.cs | 55 ++- FoxTube.Core/Utils/StoreInterop.cs | 9 +- FoxTube/App.xaml | 90 ++-- FoxTube/App.xaml.cs | 154 ++++--- FoxTube/Classes/MenuItemsList.cs | 84 ++++ FoxTube/Classes/Navigation.cs | 35 ++ FoxTube/Classes/ViewItemsCollection.cs | 55 +++ FoxTube/Controls/AccountManager.xaml | 136 +++--- FoxTube/Controls/AccountManager.xaml.cs | 113 +++-- FoxTube/Controls/Cards/AdvertCard.xaml | 72 ++-- FoxTube/Controls/Cards/AdvertCard.xaml.cs | 81 ++-- FoxTube/Controls/Cards/VideoCard.xaml | 140 ++++--- FoxTube/Controls/Cards/VideoCard.xaml.cs | 31 +- .../Controls/Dialogs/DownloadVideoDialog.xaml | 40 ++ .../Dialogs/DownloadVideoDialog.xaml.cs | 72 ++++ FoxTube/Controls/Dialogs/ProOfferDialog.xaml | 22 + .../Controls/Dialogs/ProOfferDialog.xaml.cs | 31 ++ FoxTube/Controls/DownloadListItem.xaml | 76 ++++ FoxTube/Controls/DownloadListItem.xaml.cs | 14 + FoxTube/Controls/ItemsGrid.xaml | 99 ++++- FoxTube/Controls/ItemsGrid.xaml.cs | 49 ++- FoxTube/Controls/LoadingScreen.xaml | 50 +++ FoxTube/Controls/LoadingScreen.xaml.cs | 116 +++++ FoxTube/FoxTube.csproj | 82 +++- FoxTube/MainPage.xaml | 207 +++------ FoxTube/MainPage.xaml.cs | 395 ++++++++---------- FoxTube/Package.appxmanifest | 14 +- FoxTube/Views/Downloads.xaml | 365 ++++++++++++++++ FoxTube/Views/Downloads.xaml.cs | 114 +++++ FoxTube/Views/Home.xaml | 69 +-- FoxTube/Views/Home.xaml.cs | 43 +- FoxTube/Views/HomeSections/Recommended.xaml | 17 + .../Views/HomeSections/Recommended.xaml.cs | 37 ++ FoxTube/Views/HomeSections/Subscriptions.xaml | 20 + .../Views/HomeSections/Subscriptions.xaml.cs | 38 ++ FoxTube/Views/HomeSections/Trending.xaml | 17 + FoxTube/Views/HomeSections/Trending.xaml.cs | 61 +++ FoxTube/Views/Search.xaml | 7 +- FoxTube/Views/Search.xaml.cs | 83 ++-- FoxTube/Views/Settings.xaml | 53 +-- FoxTube/Views/Settings.xaml.cs | 71 ++-- FoxTube/Views/SettingsSections/About.xaml | 80 ++-- FoxTube/Views/SettingsSections/About.xaml.cs | 34 +- FoxTube/Views/SettingsSections/General.xaml | 118 +++--- .../Views/SettingsSections/General.xaml.cs | 207 +++++---- FoxTube/Views/SettingsSections/Inbox.xaml | 161 +++---- FoxTube/Views/SettingsSections/Inbox.xaml.cs | 77 ++-- FoxTube/Views/SettingsSections/Translate.xaml | 68 --- .../Views/SettingsSections/Translate.xaml.cs | 30 -- FoxTube/Views/Subscriptions.xaml | 7 +- FoxTube/Views/Subscriptions.xaml.cs | 32 +- 54 files changed, 2684 insertions(+), 1479 deletions(-) create mode 100644 FoxTube/Classes/MenuItemsList.cs create mode 100644 FoxTube/Classes/Navigation.cs create mode 100644 FoxTube/Classes/ViewItemsCollection.cs create mode 100644 FoxTube/Controls/Dialogs/DownloadVideoDialog.xaml create mode 100644 FoxTube/Controls/Dialogs/DownloadVideoDialog.xaml.cs create mode 100644 FoxTube/Controls/Dialogs/ProOfferDialog.xaml create mode 100644 FoxTube/Controls/Dialogs/ProOfferDialog.xaml.cs create mode 100644 FoxTube/Controls/DownloadListItem.xaml create mode 100644 FoxTube/Controls/DownloadListItem.xaml.cs create mode 100644 FoxTube/Controls/LoadingScreen.xaml create mode 100644 FoxTube/Controls/LoadingScreen.xaml.cs create mode 100644 FoxTube/Views/Downloads.xaml create mode 100644 FoxTube/Views/Downloads.xaml.cs create mode 100644 FoxTube/Views/HomeSections/Recommended.xaml create mode 100644 FoxTube/Views/HomeSections/Recommended.xaml.cs create mode 100644 FoxTube/Views/HomeSections/Subscriptions.xaml create mode 100644 FoxTube/Views/HomeSections/Subscriptions.xaml.cs create mode 100644 FoxTube/Views/HomeSections/Trending.xaml create mode 100644 FoxTube/Views/HomeSections/Trending.xaml.cs delete mode 100644 FoxTube/Views/SettingsSections/Translate.xaml delete mode 100644 FoxTube/Views/SettingsSections/Translate.xaml.cs diff --git a/FoxTube.Core/Extensions.cs b/FoxTube.Core/Extensions.cs index bf43401..d923bc8 100644 --- a/FoxTube.Core/Extensions.cs +++ b/FoxTube.Core/Extensions.cs @@ -6,7 +6,9 @@ using System.IO; using System.Linq; using System.Threading; using System.Xml; +using Windows.Devices.PointOfService; using Windows.UI; +using Windows.UI.Xaml.Media.Imaging; namespace FoxTube { @@ -39,6 +41,9 @@ namespace FoxTube public static bool Belongs(this T obj, params T[] args) => args.Contains(obj); + public static bool Belongs(this int number, int lowerLimit, int upperLimit) => + number >= lowerLimit && number <= upperLimit; + public static string ToHex(this Color color) => $"#{color.R:X}{color.G:X}{color.B:X}"; @@ -57,6 +62,17 @@ namespace FoxTube }; } + public static BitmapImage LoadImage (this BitmapImage image, string source, int? height = null, int? width = null) + { + image.UriSource = source.ToUri(); + if (height.HasValue) + image.DecodePixelHeight = height.Value; + if (width.HasValue) + image.DecodePixelWidth = width.Value; + + return image; + } + public static TimeSpan GetDuration(this string rawDuration) { try diff --git a/FoxTube.Core/FoxTube.Core.csproj b/FoxTube.Core/FoxTube.Core.csproj index 8b40f20..c2b857b 100644 --- a/FoxTube.Core/FoxTube.Core.csproj +++ b/FoxTube.Core/FoxTube.Core.csproj @@ -167,9 +167,6 @@ 1.45.0.1929 - - 10.1811.22001 - 3.2.1 @@ -186,22 +183,16 @@ 2.4.0 - 5.0.3 + 5.0.4 - - Microsoft Advertising SDK for XAML - Microsoft Engagement Framework - - Visual C++ 2015 Runtime for Universal Windows Platform Apps - diff --git a/FoxTube.Core/Settings.cs b/FoxTube.Core/Settings.cs index a2c2501..e0c14b0 100644 --- a/FoxTube.Core/Settings.cs +++ b/FoxTube.Core/Settings.cs @@ -133,7 +133,10 @@ namespace FoxTube return "en-US"; } - public static void ResetSettings() => + public static void ResetSettings() + { settings.Values.Clear(); + ApplicationData.Current.LocalSettings.Values.Clear(); + } } } diff --git a/FoxTube.Core/UserManagement.cs b/FoxTube.Core/UserManagement.cs index 8b2acf3..551f6f5 100644 --- a/FoxTube.Core/UserManagement.cs +++ b/FoxTube.Core/UserManagement.cs @@ -22,7 +22,7 @@ namespace FoxTube { public static class UserManagement { - public const int MaxUsersCount = 2; + public const int MaxUsersCount = 1; #region Private members private static readonly ApplicationDataContainer storage = ApplicationData.Current.LocalSettings; @@ -48,16 +48,13 @@ namespace FoxTube { ClientId = "349735264870-2ekqlm0a4mkg3mmrfcv90s3qp3o15dq0.apps.googleusercontent.com", ClientSecret = "BkVZOAaCU2Zclf0Zlicg6y2_" - }, - new ClientSecrets // DISABLED - { - ClientId = "1096685398208-u95rcpkqb4e1ijfmb8jdq3jsg37l8igv.apps.googleusercontent.com", - ClientSecret = "IU5bbdjwvmx8ttJoXQ7e6JWd" } }; #endregion public static Userinfoplus[] Users { get; private set; } = new Userinfoplus[MaxUsersCount]; + + public static bool CanAddAccounts => Users.Any(i => i == null); public static User CurrentUser { get; set; } public static bool Authorized => CurrentUser != null; public static ExtendedYouTubeService Service => CurrentUser?.Service ?? _defaultService; @@ -108,7 +105,7 @@ namespace FoxTube public static async Task Initialize() { - Users = JsonConvert.DeserializeObject(storage.Values["UserManagement.Users"] as string); + Users = JsonConvert.DeserializeObject(storage.Values["UserManagement.Users"] as string ?? "") ?? new Userinfoplus[MaxUsersCount]; int? lastUserIndex = storage.Values["UserManagement.LastUser"] as int?; if (lastUserIndex.HasValue && Users[lastUserIndex.Value] != null || @@ -126,8 +123,12 @@ namespace FoxTube string userId = CurrentUser.UserInfo.Id; PasswordVault passwordVault = new PasswordVault(); - PasswordCredential credential = passwordVault.Retrieve("foxtube", userId); - passwordVault.Remove(credential); + try + { + PasswordCredential credential = passwordVault.Retrieve("foxtube", userId); + passwordVault.Remove(credential); + } + catch { } await CurrentUser.Credential.RevokeTokenAsync(CancellationToken.None); @@ -159,20 +160,27 @@ namespace FoxTube } } - public static async Task SwitchUser(int userIndex) + public static async Task SwitchUser(int userIndex) { Userinfoplus userInfo = Users[userIndex]; PasswordVault valut = new PasswordVault(); - PasswordCredential vaultCredential = valut.Retrieve("foxtube", userInfo.Id); - if (vaultCredential == null) - throw new NullReferenceException("No user found to switch on"); + try + { + PasswordCredential vaultCredential = valut.Retrieve("foxtube", userInfo.Id); - vaultCredential.RetrievePassword(); - string token = vaultCredential.Password; - YouTube.Authorization.UserCredential credential = await AuthorizationHelpers.RestoreUser(ClientSecrets[userIndex], token); + vaultCredential.RetrievePassword(); + string token = vaultCredential.Password; + YouTube.Authorization.UserCredential credential = await AuthorizationHelpers.RestoreUser(ClientSecrets[userIndex], token); - await LoadUser(credential, userIndex); + await LoadUser(credential, userIndex); + + return true; + } + catch + { + return false; + } } private static async Task LoadUser(YouTube.Authorization.UserCredential credential, int userIndex) @@ -192,15 +200,16 @@ namespace FoxTube private static void UpdateToken(string id, string refreshToken) { PasswordVault passwordVault = new PasswordVault(); - PasswordCredential vaultCredential = passwordVault.Retrieve("foxtube", id); - - if (vaultCredential == null) + try { - vaultCredential = new PasswordCredential("foxtube", CurrentUser.UserInfo.Id, refreshToken); + PasswordCredential vaultCredential = passwordVault.Retrieve("foxtube", id); + vaultCredential.Password = refreshToken; + } + catch + { + PasswordCredential vaultCredential = new PasswordCredential("foxtube", CurrentUser.UserInfo.Id, refreshToken); passwordVault.Add(vaultCredential); } - else - vaultCredential.Password = refreshToken; } internal static void SubscriptionsChangedInvoker(User sender, Subscription subscription) => diff --git a/FoxTube.Core/Utils/StoreInterop.cs b/FoxTube.Core/Utils/StoreInterop.cs index 4e01151..fa52ec3 100644 --- a/FoxTube.Core/Utils/StoreInterop.cs +++ b/FoxTube.Core/Utils/StoreInterop.cs @@ -1,5 +1,4 @@ -using Microsoft.Advertising.WinRT.UI; -using Microsoft.AppCenter.Crashes; +using Microsoft.AppCenter.Crashes; using System; using System.Threading.Tasks; using Windows.Services.Store; @@ -14,12 +13,10 @@ namespace FoxTube.Utils private static bool UseTestAds => true; - private static string ApplicationId => UseTestAds ? "d25517cb-12d4-4699-8bdc-52040c712cab" : "9ncqqxjtdlfh"; - private static string AdsId => UseTestAds ? "test" : "1100044398"; + public static string ApplicationId => UseTestAds ? "d25517cb-12d4-4699-8bdc-52040c712cab" : "9ncqqxjtdlfh"; + public static string AdsId => UseTestAds ? "test" : "1100044398"; private static string ProProductId => "9NP1QK556625"; - public static NativeAdsManagerV2 AdsManager => new NativeAdsManagerV2(ApplicationId, AdsId); - public static async Task UpdateStoreState() { StoreProductQueryResult requset = await StoreContext.GetDefault().GetAssociatedStoreProductsAsync(new[] { "Durable" }); diff --git a/FoxTube/App.xaml b/FoxTube/App.xaml index dd044f5..be15912 100644 --- a/FoxTube/App.xaml +++ b/FoxTube/App.xaml @@ -3,44 +3,64 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> - - - - - + + + + + - Red + Red - Gray - Gray - DarkRed - DarkRed + Gray + Gray + DarkRed + DarkRed - Red - Gray - DarkRed + Red + Gray + DarkRed - Gray + Gray - - - - - + + + + + + + + + + diff --git a/FoxTube/App.xaml.cs b/FoxTube/App.xaml.cs index a4c2dc4..9ef7847 100644 --- a/FoxTube/App.xaml.cs +++ b/FoxTube/App.xaml.cs @@ -1,74 +1,114 @@ using System; -using FoxTube.Core.Helpers; +using FoxTube.Services; +using FoxTube.Utils; using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; -using Windows.UI.Popups; +using Windows.UI; +using Windows.UI.ViewManagement; using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; namespace FoxTube { - sealed partial class App : Application - { - public App() - { - InitializeComponent(); - Suspending += OnSuspending; - UnhandledException += ErrorOccured; - Metrics.StartSession(); - } + sealed partial class App : Application + { + public App() + { + InitializeComponent(); - protected override async void OnLaunched(LaunchActivatedEventArgs e) - { - await UsersControl.Initialize(); - await StoreInterop.UpdateStoreState(); - if (Settings.LastReviewedVersion != Metrics.CurrentVersion) - Inbox.PushChangelog(); + Suspending += OnSuspending; + UnhandledException += ErrorOccured; + } - if (!e.PrelaunchActivated && Window.Current.Content == null) - Window.Current.Content = new MainPage(); - Window.Current.Activate(); - } + protected override async void OnLaunched(LaunchActivatedEventArgs e) + { + await UserManagement.Initialize(); + await StoreInterop.UpdateStoreState(); + if (Settings.LastReviewedVersion != Metrics.CurrentVersion) + Inbox.PushChangelog(); - protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args) - { - var deferral = args.TaskInstance.GetDeferral(); - base.OnBackgroundActivated(args); + if (!e.PrelaunchActivated && Window.Current.Content == null) + Window.Current.Content = new MainPage(); + Window.Current.Activate(); + } - deferral.Complete(); - } + protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args) + { + var deferral = args.TaskInstance.GetDeferral(); + base.OnBackgroundActivated(args); - protected override void OnActivated(IActivatedEventArgs e) - { - base.OnActivated(e); + deferral.Complete(); + } - if (Window.Current.Content == null) - Window.Current.Content = new MainPage(); - Window.Current.Activate(); - } + protected override void OnActivated(IActivatedEventArgs e) + { + base.OnActivated(e); - void OnSuspending(object sender, SuspendingEventArgs e) - { - Metrics.EndSession(); - } + if (Window.Current.Content == null) + Window.Current.Content = new MainPage(); + Window.Current.Activate(); + } - async void ErrorOccured(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e) - { - Metrics.AddEvent("Application chrashed", - ("Exception", e.Exception.GetType().ToString()), - ("Message", e.Message), - ("StackTrace", e.Exception.StackTrace)); - e.Handled = true; + void OnSuspending(object sender, SuspendingEventArgs e) + { + Metrics.EndSession(); + } - MessageDialog alert = new MessageDialog($"Exception: {e.Exception.GetType().ToString()}\nMessage: {e.Message}\n\nIf this happens again try to reset your app settings or report the problem", - "Unhandled error occured"); - alert.Commands.Add(new UICommand("Reset application", (command) => Utils.InitializeFailsafeProtocol())); - if(Feedback.HasFeedbackHub) - alert.Commands.Add(new UICommand("Report the problem", (command) => Feedback.OpenFeedbackHub())); - alert.Commands.Add(new UICommand("Close")); + async void ErrorOccured(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e) + { + Metrics.AddEvent("Application chrashed", + ("Exception", e.Exception.GetType().ToString()), + ("Message", e.Message), + ("StackTrace", e.Exception.StackTrace)); + e.Handled = true; - alert.DefaultCommandIndex = 0; - alert.CancelCommandIndex = 2; - await alert.ShowAsync(); - } - } -} + + ContentDialogResult result = await new ContentDialog + { + Title = "Something went wrong...", + Content = "It may be a bug or temporary server issues. Please, try again later\n\nIf this happens again try to reset your app settings or report the problem", + PrimaryButtonText = "Report the problem", + SecondaryButtonText = "Reset application", + CloseButtonText = "Close", + DefaultButton = ContentDialogButton.Primary + }.ShowAsync(); + + switch (result) + { + case ContentDialogResult.Primary: + Feedback.OpenFeedbackHub(); + break; + case ContentDialogResult.Secondary: + Utils.Utils.InitializeFailsafeProtocol(); + break; + default: + break; + } + } + + public static void UpdateTitleBar(bool isDark) + { + ApplicationViewTitleBar titleBar = ApplicationView.GetForCurrentView().TitleBar; + titleBar.ButtonBackgroundColor = Colors.Transparent; + titleBar.ButtonInactiveBackgroundColor = Colors.Transparent; + titleBar.ButtonInactiveForegroundColor = Colors.Gray; + + if (isDark) + { + titleBar.ButtonForegroundColor = + titleBar.ButtonHoverForegroundColor = + titleBar.ButtonPressedForegroundColor = Colors.White; + titleBar.ButtonHoverBackgroundColor = Color.FromArgb(50, 255, 255, 255); + titleBar.ButtonPressedBackgroundColor = Color.FromArgb(30, 255, 255, 255); + } + else + { + titleBar.ButtonForegroundColor = + titleBar.ButtonHoverForegroundColor = + titleBar.ButtonPressedForegroundColor = Colors.Black; + titleBar.ButtonHoverBackgroundColor = Color.FromArgb(50, 0, 0, 0); + titleBar.ButtonPressedBackgroundColor = Color.FromArgb(70, 0, 0, 0); + } + } + } +} \ No newline at end of file diff --git a/FoxTube/Classes/MenuItemsList.cs b/FoxTube/Classes/MenuItemsList.cs new file mode 100644 index 0000000..1470158 --- /dev/null +++ b/FoxTube/Classes/MenuItemsList.cs @@ -0,0 +1,84 @@ +using Google.Apis.YouTube.v3.Data; +using System.Collections.Generic; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Media.Imaging; + +namespace FoxTube.Utils +{ + public static class MenuItemsList + { + public static List GetMenuItems(bool authorized) + { + List list = new List + { + GetItem("Home", new SymbolIcon(Symbol.Home), "home") + }; + + if (authorized) + list.AddRange(new Microsoft.UI.Xaml.Controls.NavigationViewItemBase[] + { + GetItem("Subscriptions", new SymbolIcon(Symbol.People), "subscriptions"), + + new Microsoft.UI.Xaml.Controls.NavigationViewItemHeader { Content = "Library" }, + GetItem("History", new FontIcon { Glyph = "\xE81C" }, "history"), + GetItem("Liked videos", new SymbolIcon(Symbol.Like), "liked"), + GetItem("Watch later", new SymbolIcon(Symbol.Clock), "watchLater"), + GetItem("Downloads", new SymbolIcon(Symbol.Download), "downloads") + }); + else + list.AddRange(new Microsoft.UI.Xaml.Controls.NavigationViewItemBase[] + { + GetItem("Downloads", new SymbolIcon(Symbol.Download), "downloads"), + + new Microsoft.UI.Xaml.Controls.NavigationViewItemHeader { Content = "Best of YouTube" }, + GetItem("Music", new FontIcon { Glyph = "\xE189" }, "UC-9-kyTW8ZkZNDHQJ6FgpwQ"), + GetItem("Sports", new FontIcon { Glyph = "\xE95E" }, "UCEgdi0XIXXZ-qJOFPf4JSKw"), + GetItem("Movies", new FontIcon { Glyph = "\xE8B2" }, "UClgRkhTL3_hImCAmdLfDE4g"), + GetItem("News", new FontIcon { Glyph = "\xE12A" }, "UCYfdidRxbB8Qhf0Nx7ioOYw"), + GetItem("Live", new FontIcon { Glyph = "\xE93E" }, "UC4R8DWoMoI7CAwX8_LjQHig"), + GetItem("Spotlight", new FontIcon { Glyph = "\xECAD" }, "UC8iNz9uwDGfomRnnKKbOhOQ"), + GetItem("360° videos", new FontIcon { Glyph = "\xF131" }, "UCzuqhhs6NWbgTzMuM09WKDQ"), + }); + + return list; + } + + private static Microsoft.UI.Xaml.Controls.NavigationViewItem GetItem(string content, IconElement icon, string tag) => + new Microsoft.UI.Xaml.Controls.NavigationViewItem + { + Content = content, + Icon = icon, + Tag = tag + }; + + public static Microsoft.UI.Xaml.Controls.NavigationViewItem GenerateItemFromSubscription(Subscription subscription) + { + StackPanel stack = new StackPanel + { + Orientation = Orientation.Horizontal, + Padding = new Thickness(5), + Margin = new Thickness(-5, 0, 0, 0) + }; + + stack.Children.Add(new Microsoft.UI.Xaml.Controls.PersonPicture + { + Height = 20, + Margin = new Thickness(-5, 0, 15, 0), + ProfilePicture = new BitmapImage().LoadImage(subscription.Snippet.Thumbnails.Default__.Url, 20, 20) + }); + + stack.Children.Add(new TextBlock + { + FontSize = 14, + Text = subscription.Snippet.Title + }); + + return new Microsoft.UI.Xaml.Controls.NavigationViewItem + { + Content = stack, + Tag = subscription.Snippet.ChannelId + }; + } + } +} \ No newline at end of file diff --git a/FoxTube/Classes/Navigation.cs b/FoxTube/Classes/Navigation.cs new file mode 100644 index 0000000..d1b3318 --- /dev/null +++ b/FoxTube/Classes/Navigation.cs @@ -0,0 +1,35 @@ +namespace FoxTube +{ + public enum NavigationTarget + { + Home, + Settings, + Downloads + } + + public static class Navigation + { + public static NavigationTarget CurrentPage { get; private set; } + public static object CurrentParameter { get; private set; } + + public static void NavigateTo(NavigationTarget destination) => + NavigateTo(destination, null); + + public static void NavigateTo(NavigationTarget destination, object parameters) + { + MainPage.Current.Navigate(destination switch + { + NavigationTarget.Settings => typeof(Views.Settings), + NavigationTarget.Downloads => typeof(Views.Downloads), + _ => UserManagement.Authorized ? typeof(Views.Home) : typeof(Views.HomeSections.Trending), + }, + parameters); + + CurrentPage = destination; + CurrentParameter = parameters; + } + + public static void RefreshCurrentPage() => + MainPage.Current.Refresh(); + } +} \ No newline at end of file diff --git a/FoxTube/Classes/ViewItemsCollection.cs b/FoxTube/Classes/ViewItemsCollection.cs new file mode 100644 index 0000000..2771186 --- /dev/null +++ b/FoxTube/Classes/ViewItemsCollection.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Threading.Tasks; +using Windows.Foundation; +using Windows.UI.Core; +using Windows.UI.Xaml.Data; + +namespace FoxTube.Models +{ + public class ViewItemsCollection : ObservableCollection, ISupportIncrementalLoading + { + public event EventHandler ItemsUpdated; + + public bool HasMoreItems + { + get => _hasMoreItems; + private set + { + _hasMoreItems = value; + ItemsUpdated?.Invoke(this, null); + } + } + private bool _hasMoreItems = true; + + private IIncrementalLoadingHost Host { get; set; } + public ViewItemsCollection(IIncrementalLoadingHost host) => + Host = host; + + public IAsyncOperation LoadMoreItemsAsync(uint count) => + AsyncInfo.Run((c) => LoadItems()); + + private async Task LoadItems() + { + (List items, bool hasMore) = await Host.LoadMoreItems(); + + HasMoreItems = hasMore; + + await Host.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => + { + items.ForEach(i => + Add(i)); + }); + + return new LoadMoreItemsResult { Count = (uint)items.Count }; + } + } + + public interface IIncrementalLoadingHost + { + Task<(List, bool)> LoadMoreItems(); + CoreDispatcher Dispatcher { get; } + } +} \ No newline at end of file diff --git a/FoxTube/Controls/AccountManager.xaml b/FoxTube/Controls/AccountManager.xaml index 9d22632..155ce7f 100644 --- a/FoxTube/Controls/AccountManager.xaml +++ b/FoxTube/Controls/AccountManager.xaml @@ -1,64 +1,92 @@ - + xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls" xmlns:data="using:Google.Apis.Oauth2.v2.Data" + Loaded="AppBarButton_Loaded" + Style="{StaticResource ButtonRevealStyle}" + CornerRadius="0" + Padding="10,0" + Height="32" + Background="Transparent"> - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/FoxTube/Controls/AccountManager.xaml.cs b/FoxTube/Controls/AccountManager.xaml.cs index 2a4870f..eea474d 100644 --- a/FoxTube/Controls/AccountManager.xaml.cs +++ b/FoxTube/Controls/AccountManager.xaml.cs @@ -1,43 +1,92 @@ -using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using FoxTube.Models; +using System.Linq; +using Google.Apis.Oauth2.v2.Data; using Windows.UI.Xaml.Media.Imaging; namespace FoxTube.Controls { - public sealed partial class AccountManager : AppBarButton - { - public AccountManager() => - InitializeComponent(); + public sealed partial class AccountManager : Button + { + private User CurrentUser { get; set; } - async void Flyout_Opening(object sender, object e) - { - if (UsersControl.Authorized) - return; + public AccountManager() => + InitializeComponent(); - (sender as Flyout).Hide(); + private async void Flyout_Opening(object sender, object e) + { + if (UserManagement.Authorized) + return; - if (await UsersControl.AddUser()) - UpdateData(); - } + (sender as Flyout).Hide(); - void AppBarButton_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e) - { - if (UsersControl.Authorized) - UpdateData(); - else - MainPage.Current.Update(); - } + await UserManagement.AddUser(); + } - void UpdateData() - { - Label = UsersControl.CurrentUser.UserInfo.Name; - Icon = new SymbolIcon(Symbol.Contact); + private void AppBarButton_Loaded(object sender, RoutedEventArgs e) + { + if (UserManagement.Authorized) + UpdateData(true); - banner.Source = new BitmapImage(UsersControl.CurrentUser.Channel.BrandingSettings.Image.BannerMobileLowImageUrl.ToUri()) { DecodePixelWidth = 300, DecodePixelHeight = 60 }; - avatar.ProfilePicture = new BitmapImage(UsersControl.CurrentUser.Channel.Snippet.Thumbnails.Default__.Url.ToUri()) { DecodePixelHeight = 40, DecodePixelWidth = 40 }; - name.Text = UsersControl.CurrentUser.UserInfo.Name; - email.Text = UsersControl.CurrentUser.UserInfo.Email; + UserManagement.UserStateUpdated += (s, e) => UpdateData(e); + } - MainPage.Current.Update(); - } - } -} + private void UpdateData(bool authorized) + { + CurrentUser = UserManagement.CurrentUser; + if (authorized) + { + buttonLabel.Text = name.Text = CurrentUser.Channel.Snippet.Title; + email.Text = CurrentUser.UserInfo.Email; + + profileIcon.ProfilePicture = new BitmapImage().LoadImage(CurrentUser.Channel.Snippet.Thumbnails.Default__.Url, 20, 20); + avatar.ProfilePicture = new BitmapImage().LoadImage(CurrentUser.Channel.Snippet.Thumbnails.Default__.Url, 40, 40); + bannerImage.Source = new BitmapImage().LoadImage(CurrentUser.Channel.BrandingSettings.Image.BannerMobileLowImageUrl, 60); + + ToolTipService.SetToolTip(this, $"{CurrentUser.UserInfo.Name} ({CurrentUser.UserInfo.Email})"); + + icon.Visibility = Visibility.Collapsed; + profileIcon.Visibility = Visibility.Visible; + + var users = UserManagement.Users.Where(i => i.Id != CurrentUser.UserInfo.Id).ToList(); + usersList.ItemsSource = users; + usersList.Visibility = users.Count > 0 ? Visibility.Visible : Visibility.Visible; + + addAccountButton.Visibility = UserManagement.CanAddAccounts ? Visibility.Visible : Visibility.Collapsed; + } + else + { + buttonLabel.Text = name.Text = "Sign in"; + ToolTipService.SetToolTip(this, null); + + icon.Visibility = Visibility.Visible; + profileIcon.Visibility = Visibility.Collapsed; + } + } + + private async void SwitchUser(object sender, ItemClickEventArgs e) + { + Userinfoplus user = (Userinfoplus)e.ClickedItem; + await UserManagement.SwitchUser(UserManagement.Users.ToList().IndexOf(user)); + } + + private async void NavigationViewItem_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) + { + switch ((sender as NavigationViewItem).Tag as string) + { + case "channel": + case "upload": + case "broadcast": + case "creatorStudio": + break; + case "addUser": + await UserManagement.AddUser(); + break; + case "logout": + await UserManagement.Logout(); + break; + } + } + } +} \ No newline at end of file diff --git a/FoxTube/Controls/Cards/AdvertCard.xaml b/FoxTube/Controls/Cards/AdvertCard.xaml index 962cdbe..3acc0be 100644 --- a/FoxTube/Controls/Cards/AdvertCard.xaml +++ b/FoxTube/Controls/Cards/AdvertCard.xaml @@ -5,60 +5,48 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls" - xmlns:Windows10version1809="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 7)" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400" HorizontalAlignment="Stretch" Margin="3" SizeChanged="UserControl_SizeChanged" - Visibility="Collapsed" Opacity="0"> - - - + + + - - - + + + diff --git a/FoxTube/Controls/Cards/AdvertCard.xaml.cs b/FoxTube/Controls/Cards/AdvertCard.xaml.cs index 76ec312..06e683c 100644 --- a/FoxTube/Controls/Cards/AdvertCard.xaml.cs +++ b/FoxTube/Controls/Cards/AdvertCard.xaml.cs @@ -1,51 +1,54 @@ -using FoxTube.Core.Helpers; +using FoxTube.Utils; using Microsoft.Advertising.WinRT.UI; -using Microsoft.Toolkit.Uwp.UI.Controls; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace FoxTube.Controls.Cards { - /// - /// Advert which is looks similar to video cards - /// - public sealed partial class AdvertCard : UserControl - { - NativeAdsManagerV2 manager = new NativeAdsManagerV2(Constants.ApplicationId, Constants.AdsId); - NativeAdV2 advert; - public AdvertCard() - { - InitializeComponent(); - manager.AdReady += AdReady; - manager.ErrorOccurred += ErrorOccurred; - manager.RequestAd(); - } + /// + /// Advert which is looks similar to video cards + /// + public sealed partial class AdvertCard : UserControl + { + readonly NativeAdsManagerV2 manager = new NativeAdsManagerV2(StoreInterop.ApplicationId, StoreInterop.AdsId); + NativeAdV2 advert; - void ErrorOccurred(object sender, NativeAdErrorEventArgs e) - { - (Parent as StaggeredPanel)?.Children.Remove(this); - Metrics.AddEvent("Error has occured while loading ad", - ("Code", e.ErrorCode.ToString()), - ("Message", e.ErrorMessage), - ("Request ID", e.RequestId)); - } + public AdvertCard() + { + InitializeComponent(); + manager.AdReady += AdReady; + manager.ErrorOccurred += ErrorOccurred; + manager.RequestAd(); + } - void AdReady(object sender, NativeAdReadyEventArgs e) - { - advert = e.NativeAd; - - if (string.IsNullOrWhiteSpace(advert.CallToActionText)) - cta.Visibility = Visibility.Collapsed; + void ErrorOccurred(object sender, NativeAdErrorEventArgs e) + { + (Parent as Panel)?.Children.Remove(this); + Metrics.AddEvent("Error has occured while loading ad", + ("Code", e.ErrorCode.ToString()), + ("Message", e.ErrorMessage), + ("Request ID", e.RequestId)); + } - description.Text += $"{advert.Price} {advert.Rating}"; + void AdReady(object sender, NativeAdReadyEventArgs e) + { + advert = e.NativeAd; - e.NativeAd.RegisterAdContainer(grid); + if (string.IsNullOrWhiteSpace(advert.CallToActionText)) + cta.Visibility = Visibility.Collapsed; - Metrics.AddEvent("Advert loaded", - ("Region", Settings.Region), - ("Version", Metrics.CurrentVersion)); - } - void UserControl_SizeChanged(object sender, SizeChangedEventArgs e) => - Height = .75 * ActualWidth; - } + description.Text = $"{advert.Price} {advert.Rating}"; + + e.NativeAd.RegisterAdContainer(grid); + + Opacity = 1; + + Metrics.AddEvent("Advert loaded", + ("Region", Settings.Region), + ("Version", Metrics.CurrentVersion)); + } + + void UserControl_SizeChanged(object sender, SizeChangedEventArgs e) => + Height = .75 * ActualWidth; + } } \ No newline at end of file diff --git a/FoxTube/Controls/Cards/VideoCard.xaml b/FoxTube/Controls/Cards/VideoCard.xaml index d1e04e0..65a703f 100644 --- a/FoxTube/Controls/Cards/VideoCard.xaml +++ b/FoxTube/Controls/Cards/VideoCard.xaml @@ -5,86 +5,88 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls" - xmlns:Windows10version1809="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 7)" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400" HorizontalAlignment="Stretch" Margin="3" - SizeChanged="UserControl_SizeChanged"> + Opacity="0"> + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/FoxTube/Controls/Cards/VideoCard.xaml.cs b/FoxTube/Controls/Cards/VideoCard.xaml.cs index abc82d0..0208f6b 100644 --- a/FoxTube/Controls/Cards/VideoCard.xaml.cs +++ b/FoxTube/Controls/Cards/VideoCard.xaml.cs @@ -1,14 +1,27 @@ -using Windows.UI.Xaml; +using Google.Apis.YouTube.v3.Data; +using System.Collections.Generic; +using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace FoxTube.Controls.Cards { - public sealed partial class VideoCard : UserControl - { - public VideoCard() => - InitializeComponent(); + public sealed partial class VideoCard : UserControl + { + Video VideoData { get; set; } - void UserControl_SizeChanged(object sender, SizeChangedEventArgs e) => - Height = .75 * ActualWidth; - } -} + public VideoCard(Video meta) + { + InitializeComponent(); + + VideoData = meta; + + Opacity = 1; + } + + public VideoCard(Video meta, Playlist playlist, IEnumerable playlistItems) => + InitializeComponent(); + + //void UserControl_SizeChanged(object sender, SizeChangedEventArgs e) => + // Height = .75 * ActualWidth; + } +} \ No newline at end of file diff --git a/FoxTube/Controls/Dialogs/DownloadVideoDialog.xaml b/FoxTube/Controls/Dialogs/DownloadVideoDialog.xaml new file mode 100644 index 0000000..96543b9 --- /dev/null +++ b/FoxTube/Controls/Dialogs/DownloadVideoDialog.xaml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +