Archived
1
0

Done frame controls

Related Work Items: #314, #315
This commit is contained in:
Michael Gordeev
2019-08-11 10:51:22 +03:00
parent d6d37151b8
commit 6d093c90f5
53 changed files with 10100 additions and 464 deletions
+49 -26
View File
@@ -1,7 +1,10 @@
using FoxTube.Pages; using FoxTube.Classes;
using System.Diagnostics; using FoxTube.Controls;
using FoxTube.Pages;
using Microsoft.AppCenter.Analytics;
using System.Collections.Generic;
using Windows.ApplicationModel.Activation; using Windows.ApplicationModel.Activation;
using Windows.UI.Notifications; using Windows.Globalization;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
@@ -9,47 +12,67 @@ namespace FoxTube
{ {
sealed partial class App : Application sealed partial class App : Application
{ {
public App() public App() =>
{
InitializeComponent(); InitializeComponent();
}
/*protected override void OnLaunched(LaunchActivatedEventArgs e) /*void Initialize()
{ {
if (!(Window.Current.Content is Frame)) Settings.LoadData();
Window.Current.Content = new Frame();
if (e.PrelaunchActivated == false) switch (Settings.Theme)
{ {
if ((Window.Current.Content as Frame).Content == null) case 0:
(Window.Current.Content as Frame).Navigate(typeof(MainPage), e.Arguments); RequestedTheme = ApplicationTheme.Light;
break;
Window.Current.Activate(); case 1:
RequestedTheme = ApplicationTheme.Dark;
break;
} }
ApplicationLanguages.PrimaryLanguageOverride = Settings.Language;
Processes.InitializeApp();
Suspending += (s, e) => Processes.SuspendApp();
UnhandledException += (s, e) => Analytics.TrackEvent("The app crashed", new Dictionary<string, string>()
{
{ "Exception", e.Exception.GetType().ToString() },
{ "Details", e.Message },
{ "StackTrace", e.Exception.StackTrace }
});
}*/ }*/
protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args) protected override void OnLaunched(LaunchActivatedEventArgs e)
{ {
if (!e.PrelaunchActivated && Window.Current.Content == null)
Window.Current.Content = new MainPage(e.SplashScreen, e.Arguments);
Window.Current.Activate();
}
protected override async void OnBackgroundActivated(BackgroundActivatedEventArgs args)
{
var deferral = args.TaskInstance.GetDeferral();
base.OnBackgroundActivated(args); base.OnBackgroundActivated(args);
await Methods.ProcessBackgroundToast((args.TaskInstance.TriggerDetails as ToastNotificationActivatedEventArgs).Argument);
deferral.Complete();
} }
protected override void OnActivated(IActivatedEventArgs e) protected override void OnActivated(IActivatedEventArgs e)
{ {
base.OnActivated(e); base.OnActivated(e);
//TODO: Check this shit if (Window.Current.Content == null)
if (!(Window.Current.Content is Frame rootFrame)) Window.Current.Content = new MainPage(e.SplashScreen, e);
{
rootFrame = new Frame();
Window.Current.Content = rootFrame;
}
if (rootFrame.Content == null)
rootFrame.Navigate(typeof(MainPage));
Window.Current.Activate(); Window.Current.Activate();
Debug.WriteLine("Hello, World"); /*if(e.Kind == ActivationKind.ToastNotification)
{
if (UserManagement.IsAuthorized)
Methods.ProcessToast((e as ToastNotificationActivatedEventArgs).Argument);
else
UserManagement.AuthorizationStateChanged += (arg) => Methods.ProcessToast((e as ToastNotificationActivatedEventArgs).Argument);
}*/
} }
} }
} }
+16
View File
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FoxTube.Classes
{
public static class Downloads
{
public static void Cancel(string id)
{
}
}
}
+36
View File
@@ -0,0 +1,36 @@
using Google.Apis.YouTube.v3.Data;
using System;
using System.Collections;
using System.Linq;
namespace FoxTube
{
public delegate void AuthorizationChangedEventHandler(bool isAuthorized);
public delegate void SubscriptionChangedEventHandler(string action, Subscription subscription);
public delegate void ProVersionStateChangedEventHandler(bool purchased, string price);
public delegate void NavigatingOccured(Type sourcePageType, object parameter);
public delegate void NavigationOccured(IList pivotCollection, int? selectedPivot);
public delegate void SimpleEventHandler();
public enum LoadingScreenState
{
Loading = 0,
Loaded = 1,
Error = 2,
Blocked = 3
}
public interface INavigationPage
{
object Parameter { get; set; }
}
public static class Extensions
{
public static Uri ToUri(this string str) =>
string.IsNullOrWhiteSpace(str) ? null : new Uri(str);
public static bool Belongs<T>(this T obj, params T[] values) =>
values.Contains(obj);
}
}
+85
View File
@@ -0,0 +1,85 @@
using FoxTube.Classes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
namespace FoxTube
{
public static class Methods
{
public static void ProcessToast(string argument)
{
string[] args = argument.Split('|');
switch (args[0])
{
case "changelog":
case "inbox":
Navigation.GoToDeveloper(args[1]);
break;
case "video":
Navigation.GoToVideo(args[1]);
break;
case "channel":
Navigation.GoToChannel(args[1]);
break;
case "download":
Navigation.GoToDownloads();
break;
case "clipboard":
switch (args[1])
{
case "video":
Navigation.GoToVideo(args[2]);
break;
case "channel":
Navigation.GoToChannel(args[2]);
break;
case "playlist":
Navigation.GoToPlaylist(args[2]);
break;
}
break;
}
}
public static async Task ProcessBackgroundToast(string argument)
{
try
{
string[] args = argument.Split('|');
switch (args[0])
{
case "dcancel":
Downloads.Cancel(args[1]);
break;
case "later":
await UserManagement.AddItemToWL(args[1]);
break;
}
}
catch { }
}
public static void SetTitleBar()
{
var titleBar = ApplicationView.GetForCurrentView().TitleBar;
titleBar.ButtonBackgroundColor = Colors.Transparent;
titleBar.ButtonHoverBackgroundColor = Colors.IndianRed;
titleBar.ButtonPressedBackgroundColor = Colors.DarkRed;
titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
titleBar.ButtonInactiveForegroundColor = Colors.Gray;
titleBar.ButtonForegroundColor = Colors.White;
}
}
}
+56
View File
@@ -0,0 +1,56 @@
using FoxTube.Classes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FoxTube
{
public static class Navigation
{
public static void GoToSubscriptions()
{
}
public static void GoToSearch(SearchParameters args)
{
}
public static void GoToChannel(string id)
{
}
public static void GoToHome()
{
}
public static void GoToVideo(string id, string playlistId = null, bool incognito = false)
{
}
public static void GoToDeveloper(string id)
{
}
public static void GoToPlaylist(string id)
{
}
public static void GoToHistory()
{
}
public static void GoToDownloads()
{
}
}
}
+248
View File
@@ -0,0 +1,248 @@
using Microsoft.AppCenter;
using Microsoft.AppCenter.Analytics;
using Microsoft.Services.Store.Engagement;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Windows.ApplicationModel.Background;
using Windows.ApplicationModel.DataTransfer;
using Windows.ApplicationModel.Resources;
using Windows.Data.Xml.Dom;
using Windows.System;
using Windows.System.Power;
using Windows.UI.Core;
using Windows.UI.Notifications;
using Windows.UI.Popups;
using Windows.UI.Xaml;
namespace FoxTube.Classes
{
public static class Processes
{
static CoreWindowActivationState windowState = CoreWindowActivationState.CodeActivated;
static ResourceLoader resources = ResourceLoader.GetForViewIndependentUse("Inbox");
static Stopwatch sw = new Stopwatch();
public static async void InitializeApp() => await CoreWindow.GetForCurrentThread().Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
CheckVersion();
RegisterToastTask();
if (Settings.DevNotifications || Settings.VideoNotifications)
RegisterTask();
else
UnregisterTask();
AppCenter.Start("45774462-9ea7-438a-96fc-03982666f39e", typeof(Analytics));
AppCenter.SetCountryCode(Settings.Region);
sw.Start();
Window.Current.Activated += (s, e) => windowState = e.WindowActivationState;
// TODO: Initialize other stuff
if (Settings.ProcessClipboard)
{
Clipboard.ContentChanged += ParseClipboard;
ParseClipboard();
}
PromptFeedback();
});
public static void SuspendApp()
{
sw.Stop();
Settings.Uptime += sw.Elapsed;
// TODO: Save other stuff
Analytics.TrackEvent("Session terminated", new Dictionary<string, string>
{
{ "Uptime", sw.Elapsed.ToString() },
{ "Total time", Settings.Uptime.ToString() }
});
}
static async void ParseClipboard(object sender = null, object e = null)
{
if (windowState != CoreWindowActivationState.Deactivated || !Settings.ProcessClipboard)
return;
try
{
string link = await Clipboard.GetContent().GetTextAsync();
if (!link.Contains("youtube") && !link.Contains("youtu.be"))
return;
string type = string.Empty;
string name = string.Empty;
if (YoutubeExplode.YoutubeClient.TryParseChannelId(link, out string id))
{
type = "channel";
name = (await new YoutubeExplode.YoutubeClient().GetChannelAsync(id)).Title;
}
else if (YoutubeExplode.YoutubeClient.TryParsePlaylistId(link, out id))
{
type = "playlist";
name = (await new YoutubeExplode.YoutubeClient().GetPlaylistAsync(id)).Title;
}
else if (YoutubeExplode.YoutubeClient.TryParseUsername(link, out id))
{
id = await new YoutubeExplode.YoutubeClient().GetChannelIdAsync(id);
type = "channel";
name = (await new YoutubeExplode.YoutubeClient().GetChannelAsync(id)).Title;
}
else if (YoutubeExplode.YoutubeClient.TryParseVideoId(link, out id))
{
type = "video";
name = (await new YoutubeExplode.YoutubeClient().GetVideoAsync(id)).Title;
}
if (string.IsNullOrWhiteSpace(id))
return;
XmlDocument toastXml = new XmlDocument();
toastXml.LoadXml($@"<toast launch='clipboard|{type}|{id}'>
<visual>
<binding template='ToastGeneric'>
<text>{resources.GetString("/Toasts/clipboardHead")}</text>
<text>{name}</text>
<text>{resources.GetString($"/Generic/{type}")}</text>
</binding>
</visual>
<actions>
<action content='{resources.GetString("/Toasts/clipboardOpen")}' arguments='clipboard|{type}|{id}'/>
</actions>
</toast>");
ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toastXml));
}
catch { }
}
static void CheckVersion()
{
if (Settings.GetCurrentVersion() == Settings.Version)
return;
try
{
XmlDocument toast = new XmlDocument();
toast.LoadXml($@"<toast activationType='foreground' launch='changelog|{Settings.GetCurrentVersion()}'>
<visual>
<binding template='ToastGeneric'>
<image placement='hero' src='ms-appx:///Assets/WhatsNewThumb.png'/>
<image placement='appLogoOverride' hint-crop='circle' src='ms-appx:///Assets/NewsAvatar.png'/>
<text>{resources.GetString("/Inbox/changelog")}</text>
<text>{resources.GetString("/Inbox/whatsNew")} {Settings.GetCurrentVersion()}</text>
</binding>
</visual>
</toast>");
ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toast));
Settings.Version = Settings.GetCurrentVersion();
}
catch { }
}
static async void RegisterToastTask()
{
if (BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals("foxtubeToast")))
return;
var backgroundRequest = await BackgroundExecutionManager.RequestAccessAsync();
if (backgroundRequest == BackgroundAccessStatus.DeniedBySystemPolicy ||
backgroundRequest == BackgroundAccessStatus.DeniedByUser ||
backgroundRequest == BackgroundAccessStatus.Unspecified)
return;
BackgroundTaskBuilder builder = new BackgroundTaskBuilder() { Name = "foxtubeToast" };
builder.SetTrigger(new ToastNotificationActionTrigger());
BackgroundTaskRegistration registration = builder.Register();
}
static async void RegisterTask()
{
if (BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals("foxtubeBackground")))
return;
var backgroundRequest = await BackgroundExecutionManager.RequestAccessAsync();
if (backgroundRequest == BackgroundAccessStatus.DeniedBySystemPolicy ||
backgroundRequest == BackgroundAccessStatus.DeniedByUser ||
backgroundRequest == BackgroundAccessStatus.Unspecified ||
PowerManager.EnergySaverStatus == EnergySaverStatus.On)
return;
BackgroundTaskBuilder builder = new BackgroundTaskBuilder()
{
Name = "foxtubeBackgound",
IsNetworkRequested = true,
TaskEntryPoint = "FoxTube.Background.BackgroundProcessor"
};
builder.SetTrigger(new TimeTrigger(15, false));
BackgroundTaskRegistration registration = builder.Register();
}
static void UnregisterTask()
{
if (!(BackgroundTaskRegistration.AllTasks.Values.ToList().Find(i => i.Name == "foxtubeBackground") is IBackgroundTaskRegistration task))
return;
task.Unregister(true);
}
static async void PromptFeedback()
{
if (Settings.Uptime.TotalHours >= 12 && Settings.PromptFeedback)
{
Analytics.TrackEvent("Prompting feedback", new Dictionary<string, string>
{
{ "Total uptime", Settings.Uptime.ToString() }
});
MessageDialog dialog = new MessageDialog(resources.GetString("/Dialogs/feedbackMessage"));
dialog.Commands.Add(new UICommand(resources.GetString("/Dialogs/dontAsk"), (command) => Settings.PromptFeedback = false));
dialog.Commands.Add(new UICommand(resources.GetString("/Dialogs/promptLater")));
dialog.Commands.Add(new UICommand(resources.GetString("/Dialogs/sure"), async (command) =>
{
Settings.PromptFeedback = false;
if (StoreServicesFeedbackLauncher.IsSupported())
await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync();
else
await Launcher.LaunchUriAsync("mailto:feedback@xfox111.net".ToUri());
}));
dialog.DefaultCommandIndex = 2;
dialog.CancelCommandIndex = 1;
await dialog.ShowAsync();
}
if (Settings.Uptime.TotalHours >= 24 && Settings.PromptReview)
{
Analytics.TrackEvent("Prompting review", new Dictionary<string, string>
{
{ "Total uptime", Settings.Uptime.ToString() }
});
MessageDialog dialog = new MessageDialog(resources.GetString("/Dialogs/rate"));
dialog.Commands.Add(new UICommand(resources.GetString("/Dialogs/dontAsk"), (command) => Settings.PromptReview = false));
dialog.Commands.Add(new UICommand(resources.GetString("/Dialogs/promptLater")));
dialog.Commands.Add(new UICommand(resources.GetString("/Dialogs/sure"), async (command) =>
{
Settings.PromptReview = false;
await Launcher.LaunchUriAsync("ms-windows-store://review/?ProductId=9NCQQXJTDLFH".ToUri());
}));
dialog.DefaultCommandIndex = 2;
dialog.CancelCommandIndex = 1;
await dialog.ShowAsync();
}
}
}
}
+16
View File
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FoxTube.Classes
{
public class SearchParameters
{
public SearchParameters(string query)
{
}
}
}
+49
View File
@@ -0,0 +1,49 @@
using System;
using System.Net;
using Windows.Services.Store;
namespace FoxTube
{
public static class Service
{
public static bool AdsCheckPerformed { get; private set; } = false;
public static bool AdsDisabled { get; private set; } = true;
public static event ProVersionStateChangedEventHandler Purchased;
public static NetworkCredential EmailCredential => new NetworkCredential("foxtube.bot@xfox111.net", "JkY39w$.7?bT57O,8k3a");
private static bool TestAds => false; //TODO: Change this bool
public static string AppId => TestAds ? "d25517cb-12d4-4699-8bdc-52040c712cab" : "9ncqqxjtdlfh";
public static string AdUnitId => TestAds ? "test" : "1100037769";
public static async void CheckAddons()
{
try
{
StoreProductQueryResult requset = await StoreContext.GetDefault().GetAssociatedStoreProductsAsync(new[] { "Durable" });
if (requset.Products["9NP1QK556625"].IsInUserCollection)
return;
AdsDisabled = false;
Purchased?.Invoke(false, requset.Products["9NP1QK556625"].Price.FormattedPrice);
}
catch { }
}
public static async void GetPro()
{
try
{
StorePurchaseResult request = await StoreContext.GetDefault().RequestPurchaseAsync("9NP1QK556625");
if (!request.Status.Belongs(StorePurchaseStatus.AlreadyPurchased, StorePurchaseStatus.Succeeded))
return;
Purchased?.Invoke(true, "");
AdsDisabled = true;
}
catch { }
}
}
}
+266
View File
@@ -0,0 +1,266 @@
using Newtonsoft.Json;
using System;
using System.Globalization;
using System.Linq;
using Windows.ApplicationModel;
using Windows.Storage;
using Windows.UI.Xaml.Controls;
namespace FoxTube.Classes
{
public static class Settings
{
public class SettingsContainer
{
public string videoQuality = "remember";
public string rememberedQuality = "1080p";
public bool videoNotifications = true;
public bool devNotifications = true;
public bool checkConnection = true;
public bool autoplay = true;
public double volume = 100;
public string language = GetLanguage();
public string relevanceLanguage = CultureInfo.InstalledUICulture.TwoLetterISOLanguageName;
public string region = CultureInfo.InstalledUICulture.Name.Split('-')[1];
public int safeSearch = 0;
public bool[] hasAccount = new bool[5] { false, false, false, false, false };
public int theme = 2;
public string version = GetCurrentVersion();
public bool mature = false;
public TimeSpan uptime = TimeSpan.FromSeconds(0);
public bool promptReview = true;
public bool promptFeedback = true;
public bool processClipboard = true;
public bool minimizeCommandbar = false;
static string GetLanguage()
{
if ((new[] { "ua", "ru", "by", "kz", "kg", "md", "lv", "ee" }).Contains(CultureInfo.InstalledUICulture.TwoLetterISOLanguageName))
return "ru-RU";
else
return "en-US";
}
}
public static string GetCurrentVersion()
{
PackageVersion ver = Package.Current.Id.Version;
return $"{ver.Major}.{ver.Minor}";
}
public static string VideoQuality
{
get => Container.videoQuality;
set
{
Container.videoQuality = value;
SaveData();
}
}
public static string RememberedQuality
{
get => Container.rememberedQuality;
set
{
Container.rememberedQuality = value;
SaveData();
}
}
public static bool VideoNotifications
{
get => Container.videoNotifications;
set
{
Container.videoNotifications = value;
SaveData();
}
}
public static bool DevNotifications
{
get => Container.devNotifications;
set
{
Container.devNotifications = value;
SaveData();
}
}
public static bool CheckConnection
{
get => Container.checkConnection;
set
{
Container.checkConnection = value;
SaveData();
}
}
public static bool Autoplay
{
get => Container.autoplay;
set
{
Container.autoplay = value;
SaveData();
}
}
public static double Volume
{
get => Container.volume;
set
{
Container.volume = value;
SaveData();
}
}
public static string Language
{
get => Container.language;
set
{
Container.language = value;
SaveData();
}
}
public static string RelevanceLanguage
{
get => Container.relevanceLanguage;
set
{
Container.relevanceLanguage = value;
SaveData();
}
}
public static string Region
{
get => Container.region;
set
{
Container.region = value;
SaveData();
}
}
public static int SafeSearch
{
get => Container.safeSearch;
set
{
Container.safeSearch = value;
SaveData();
}
}
public static bool[] HasAccount => Container.hasAccount;
public static void SetAccount(int index, bool isAuthorized)
{
HasAccount[index] = isAuthorized;
SaveData();
}
public static int Theme
{
get => Container.theme;
set
{
Container.theme = value;
SaveData();
}
}
public static string Version
{
get => Container.version;
set
{
Container.version = value;
SaveData();
}
}
public static bool Mature
{
get => Container.mature;
set
{
Container.mature = value;
SaveData();
}
}
public static TimeSpan Uptime
{
get => Container.uptime;
set
{
Container.uptime = value;
SaveData();
}
}
public static bool PromptReview
{
get => Container.promptReview;
set
{
Container.promptReview = value;
SaveData();
}
}
public static bool PromptFeedback
{
get => Container.promptFeedback;
set
{
Container.promptFeedback = value;
SaveData();
}
}
public static bool ProcessClipboard
{
get => Container.processClipboard;
set
{
Container.processClipboard = value;
SaveData();
}
}
public static AppBarClosedDisplayMode AppBarClosedMode
{
get => Container.minimizeCommandbar ? AppBarClosedDisplayMode.Minimal : AppBarClosedDisplayMode.Compact;
set
{
Container.minimizeCommandbar = value == AppBarClosedDisplayMode.Minimal;
SaveData();
}
}
//Settings storage
static readonly ApplicationDataContainer storage = ApplicationData.Current.RoamingSettings;
static SettingsContainer Container = new SettingsContainer();
/// <summary>
/// Loads saved settings from storage
/// </summary>
public static void LoadData()
{
try
{
Container = JsonConvert.DeserializeObject<SettingsContainer>(storage.Values["settings"] as string);
}
catch
{
Container = new SettingsContainer();
SaveData();
}
}
static void SaveData() =>
storage.Values["settings"] = JsonConvert.SerializeObject(Container);
}
}
+99
View File
@@ -0,0 +1,99 @@
using Google.Apis.YouTube.v3.Data;
using YoutubeExplode.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Google.Apis.Oauth2.v2.Data;
using Google.Apis.Auth.OAuth2;
using Google.Apis.YouTube.v3;
using Google.Apis.Services;
using System.Threading;
using YoutubeExplode;
using System.Net.Http;
namespace FoxTube.Classes
{
public class User
{
public List<Subscription> Subscriptions { get; }
public List<YoutubeExplode.Models.Video> WatchLater { get; }
public List<YoutubeExplode.Models.Video> WebHistory { get; }
public List<object> AppHistory { get; }
public Userinfoplus UserInfo { get; }
public UserCredential Credential { get; }
public YouTubeService Service { get; }
public YoutubeClient YoutubeClient { get; }
public string AccountId { get; }
public Google.Apis.YouTube.v3.Data.Channel Channel { get; }
public async void Deauthenticate()
{
await Credential.RevokeTokenAsync(CancellationToken.None);
}
public User(UserCredential credential)
{
Credential = credential;
Service = new YouTubeService(new BaseClientService.Initializer
{
HttpClientInitializer = Credential,
ApplicationName = "FoxTube"
});
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", credential.Token.AccessToken);
YoutubeClient = new YoutubeClient(httpClient);
}
public async void RefreshToken() =>
await Credential.RefreshTokenAsync(CancellationToken.None);
/// <summary>
/// Subscribes or unsibscribes authorized user from the channel
/// </summary>
/// <param name="id">The ID of channel which has to be added/removed</param>
/// <returns>Returns 'true' if channel is in subscriptions now; 'false' if it's not</returns>
public async Task<bool> ChangeSubscriptionState(string id)
{
if (Subscriptions.Find(i => i.Snippet.ResourceId.ChannelId == id) is Subscription subscription)
{
try { await Service.Subscriptions.Delete(subscription.Id).ExecuteAsync(); }
catch { return true; }
//SubscriptionsChanged?.Invoke("remove", subscription);
Subscriptions.Remove(subscription);
return false;
}
else
{
var request = Service.Subscriptions.Insert(new Subscription
{
Snippet = new SubscriptionSnippet
{
ResourceId = new ResourceId
{
ChannelId = id,
Kind = "youtube#channel"
}
}
}, "snippet");
if (!(await request.ExecuteAsync() is Subscription sub))
return false;
Subscriptions.Add(sub);
//SubscriptionsChanged?.Invoke("add", sub);
return true;
}
}
}
}
+87
View File
@@ -0,0 +1,87 @@
using FoxTube.Classes;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Oauth2.v2.Data;
using Google.Apis.Services;
using Google.Apis.YouTube.v3;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YoutubeExplode;
namespace FoxTube
{
public static class UserManagement
{
public static event AuthorizationChangedEventHandler AuthorizationStateChanged;
public static event SubscriptionChangedEventHandler SubscriptionChanged;
static ClientSecrets[] Secrets => new ClientSecrets[5]
{
new ClientSecrets
{
ClientId = "349735264870-2ekqlm0a4mkg3mmrfcv90s3qp3o15dq0.apps.googleusercontent.com",
ClientSecret = "BkVZOAaCU2Zclf0Zlicg6y2_"
},
new ClientSecrets
{
ClientId = "1096685398208-ha281qmg52ga8g8nn4n6943oct1tq6nj.apps.googleusercontent.com",
ClientSecret = "Cew9WbJcsoisYnR-Wuyhukov"
},
new ClientSecrets
{
ClientId = "1096685398208-59ktbv2jsvalrmli1pevur9aujnjhjeb.apps.googleusercontent.com",
ClientSecret = "eHvXHlIJ9r64yReOmMnuben7"
},
new ClientSecrets
{
ClientId = "1096685398208-ebckt9ncp69qutsmdbbufov60nq4rvpa.apps.googleusercontent.com",
ClientSecret = "XCaBFmjoT2ZAKsGf-y80lBvc"
},
new ClientSecrets
{
ClientId = "1096685398208-ligbo8gqkp4qar1uo0e5o5di434qklma.apps.googleusercontent.com",
ClientSecret = "oXauUkFY9I7Q1CmNkKGmszRQ"
},
};
static YouTubeService NoAuthService { get; } = new YouTubeService(new BaseClientService.Initializer
{
ApiKey = "AIzaSyBgHrCnrlzlVmk0cJKL8RqP9Y8x6XSuk_0",
ApplicationName = "FoxTube"
});
static YoutubeClient NoAuthClient { get; } = new YoutubeClient();
public static YouTubeService Service => Current == null ? NoAuthService : Current.Service;
public static YoutubeClient YoutubeClient => Current == null ? NoAuthClient : Current.YoutubeClient;
public static bool IsAuthorized => Current != null;
public static User Current { get; set; }
static (Userinfoplus info, UserCredential credential)[] UserInfos = new (Userinfoplus, UserCredential)[5];
public static bool CheckPerformed { get; }
public static void Initialize()
{
}
public static async Task AddItemToWL(string id)
{
}
public static async void CreateNew()
{
}
public static async void Logout()
{
Current?.Deauthenticate();
}
}
}
+19
View File
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls;
namespace FoxTube
{
public class ViewModel : Page
{
public List<Microsoft.UI.Xaml.Controls.NavigationViewItem> PivotItems { get; } = new List<Microsoft.UI.Xaml.Controls.NavigationViewItem>();
public object Parameter { get; private set; }
public void PivotChanged(int index) { }
}
}
+76
View File
@@ -0,0 +1,76 @@
<StackPanel
x:Class="FoxTube.Controls.AccountManager"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:ui="using:Microsoft.UI.Xaml.Controls"
Orientation="Horizontal">
<ui:NavigationViewItem Width="42" Visibility="Visible" Name="manager" Padding="0" Tapped="Manager_Tapped">
<ui:NavigationViewItem.ContextFlyout>
<Flyout>
<Grid Width="300" Margin="-10">
<Grid.RowDefinitions>
<RowDefinition Height="60"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Image Stretch="UniformToFill" MaxHeight="60" Source="https://yt3.ggpht.com/TckH7MhPH7UcxPnHeEj6R78xe1uOmrlHdUD1Usy7sr36xLDzl86NxAyfmDOIUrxl6kpiBgtKgA=w2276-fcrop64=1,00005a57ffffa5a8-nd-c0xffffffff-rj-k-no"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.Background>
<SolidColorBrush Color="Black" Opacity=".5"/>
</Grid.Background>
<PersonPicture HorizontalAlignment="Left" Margin="5" Height="50"/>
<StackPanel VerticalAlignment="Center" Grid.Column="1">
<TextBlock Text="Michael Gordeev" FontWeight="Bold"/>
<TextBlock Text="michael.xfox@outlook.com" Style="{StaticResource CaptionTextBlockStyle}"/>
</StackPanel>
<ui:NavigationViewItem Grid.Column="2" Width="42" Icon="BlockContact" Foreground="Red"/>
</Grid>
<ui:NavigationViewList Grid.Row="1">
<ui:NavigationViewItem Content="My channel" Icon="Contact"/>
<ui:NavigationViewItem Content="Upload a video" Icon="Upload" Tapped="NavigationViewItem_Tapped"/>
<ui:NavigationViewItemSeparator/>
<ui:NavigationViewItem>
<StackPanel Orientation="Horizontal" Padding="5" Margin="-5,0,0,0">
<PersonPicture Height="20" Margin="-5,0,15,0">
<PersonPicture.ProfilePicture>
<BitmapImage UriSource="https://yt3.ggpht.com/a/AGF-l7_hHK5yjEPhlM72Tmk26zoOVkNx88pWoZnFVQ=s240-mo-c-c0xffffffff-rj-k-no" DecodePixelHeight="20" DecodePixelWidth="20"/>
</PersonPicture.ProfilePicture>
</PersonPicture>
<StackPanel>
<TextBlock FontSize="14" Text="BadComedian"/>
<TextBlock Text="eugene.comedian@fondkino-sdoh.net" FontSize="11" Margin="0,-4,0,0"/>
</StackPanel>
</StackPanel>
</ui:NavigationViewItem>
<ui:NavigationViewItem Content="Add account" Icon="Add"/>
<ui:NavigationViewItemSeparator/>
<ui:NavigationViewItem Name="removeAds" Content="Remove ads (5$)" Icon="Shop" Tapped="RemoveAds_Tapped"/>
<ui:NavigationViewItem Content="Settings" Icon="Setting" Tapped="Settings_Click"/>
</ui:NavigationViewList>
</Grid>
</Flyout>
</ui:NavigationViewItem.ContextFlyout>
<PersonPicture Height="25" Margin="-8,0,0,0"/>
</ui:NavigationViewItem>
<ui:NavigationViewItem Width="42" Name="account" Tapped="Manager_Tapped" Icon="AddFriend">
<ui:NavigationViewItem.ContextFlyout>
<ui:MenuBarItemFlyout>
<MenuFlyoutItem Icon="Add" Text="Add new account" Click="SignIn_Click"/>
<MenuFlyoutItem Icon="Setting" Text="Settings" Click="Settings_Click"/>
</ui:MenuBarItemFlyout>
</ui:NavigationViewItem.ContextFlyout>
</ui:NavigationViewItem>
</StackPanel>
+92
View File
@@ -0,0 +1,92 @@
using Microsoft.AppCenter.Analytics;
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 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.Media.Imaging;
using Windows.UI.Xaml.Navigation;
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
namespace FoxTube.Controls
{
public sealed partial class AccountManager : StackPanel
{
public AccountManager()
{
InitializeComponent();
Service.Purchased += (purchased, price) =>
{
/*removeAds.Visibility = purchased ? Visibility.Collapsed : Visibility.Visible;
removeAds.Content = $"{resources.GetString("/Main/adsFree/Content")} ({price})";*/
};
if (Service.AdsCheckPerformed)
Service.CheckAddons();
}
void SignIn_Click(object sender, RoutedEventArgs e)
{
UserManagement.CreateNew();
Analytics.TrackEvent("Initialized authorization sequence");
}
void Logout_Click(object sender, RoutedEventArgs e)
{
manager.ContextFlyout.Hide();
UserManagement.Logout();
}
public async void Logged() //=> await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
account.Visibility = Visibility.Collapsed;
/*ToolTipService.SetToolTip(avatar, $"{UserManagement.Current.UserInfo.Name} ({UserManagement.Current.UserInfo.Email})");
name.Text = UserManagement.Current.UserInfo.Name;
email.Text = UserManagement.Current.UserInfo.Email;
avatar.ProfilePicture = new BitmapImage(UserManagement.Current.UserInfo.Picture.ToUri()) { DecodePixelHeight = 65, DecodePixelWidth = 65 };
(icon.Fill as ImageBrush).ImageSource = new BitmapImage(UserManagement.Current.UserInfo.Picture.ToUri()) { DecodePixelHeight = 25, DecodePixelWidth = 25 };*/
manager.Visibility = Visibility.Visible;
}//);
public void Quit()
{
manager.Visibility = Visibility.Collapsed;
account.Visibility = Visibility.Visible;
}
private void NavigationViewItem_Tapped(object sender, TappedRoutedEventArgs e)
{
}
void Manager_Tapped(object sender, TappedRoutedEventArgs e) =>
(sender as Microsoft.UI.Xaml.Controls.NavigationViewItem).ContextFlyout.ShowAt(sender as FrameworkElement);
private void Settings_Click(object sender, RoutedEventArgs e)
{
}
private void Settings_Click(object sender, TappedRoutedEventArgs e)
{
}
private void RemoveAds_Tapped(object sender, TappedRoutedEventArgs e)
{
}
}
}
+220
View File
@@ -0,0 +1,220 @@
<UserControl
x:Class="FoxTube.Controls.ContentFrame"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="blocked">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="block" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="block" Storyboard.TargetProperty="Opacity"
From="0" To="1" Duration="0:0:1"/>
<DoubleAnimation Storyboard.TargetName="ring" Storyboard.TargetProperty="Width"
EnableDependentAnimation="True"
From="100" To="200" Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetName="ring" Storyboard.TargetProperty="Height"
EnableDependentAnimation="True"
From="100" To="200" Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetName="ring" Storyboard.TargetProperty="Opacity"
From="1" To="0" Duration="0:0:0.5">
<DoubleAnimation.EasingFunction>
<ExponentialEase EasingMode="EaseIn"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ring" Storyboard.TargetProperty="IsActive">
<DiscreteObjectKeyFrame KeyTime="0:0:0.5" Value="False"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="content" Storyboard.TargetProperty="Opacity"
Duration="0" To="0"/>
<DoubleAnimation Storyboard.TargetName="loadingScreen" Storyboard.TargetProperty="Opacity"
To="0" Duration="0:0:0.1"/>
</Storyboard>
</VisualState>
<VisualState x:Name="wifiError">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="content" Storyboard.TargetProperty="Opacity"
Duration="0:0:0.1" To="0"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="block" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="generalBtns" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="networkBtns" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="icon" Storyboard.TargetProperty="Glyph">
<DiscreteObjectKeyFrame KeyTime="0" Value="&#xEB63;"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="ring" Storyboard.TargetProperty="Width"
EnableDependentAnimation="True"
From="100" To="200" Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetName="ring" Storyboard.TargetProperty="Height"
EnableDependentAnimation="True"
From="100" To="200" Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetName="ring" Storyboard.TargetProperty="Opacity"
From="1" To="0" Duration="0:0:0.5">
<DoubleAnimation.EasingFunction>
<ExponentialEase EasingMode="EaseIn"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ring" Storyboard.TargetProperty="IsActive">
<DiscreteObjectKeyFrame KeyTime="0:0:0.5" Value="False"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="loadingScreen" Storyboard.TargetProperty="Opacity"
From="0" To="1" Duration="0:0:0.5" BeginTime="0:0:0.5">
<DoubleAnimation.EasingFunction>
<ExponentialEase EasingMode="EaseIn"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</VisualState>
<VisualState x:Name="commonError">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="content" Storyboard.TargetProperty="Opacity"
Duration="0:0:0.1" To="0"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="block" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="generalBtns" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="networkBtns" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="icon" Storyboard.TargetProperty="Glyph">
<DiscreteObjectKeyFrame KeyTime="0" Value="&#xF618;"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="ring" Storyboard.TargetProperty="Width"
EnableDependentAnimation="True"
From="100" To="200" Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetName="ring" Storyboard.TargetProperty="Height"
EnableDependentAnimation="True"
From="100" To="200" Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetName="ring" Storyboard.TargetProperty="Opacity"
From="1" To="0" Duration="0:0:0.5">
<DoubleAnimation.EasingFunction>
<ExponentialEase EasingMode="EaseIn"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ring" Storyboard.TargetProperty="IsActive">
<DiscreteObjectKeyFrame KeyTime="0:0:0.5" Value="False"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="loadingScreen" Storyboard.TargetProperty="Opacity"
From="0" To="1" Duration="0:0:0.5" BeginTime="0:0:0.5">
<DoubleAnimation.EasingFunction>
<ExponentialEase EasingMode="EaseIn"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</VisualState>
<VisualState x:Name="loading">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="block" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="loadingScreen" Storyboard.TargetProperty="Opacity"
To="0" Duration="0:0:0.1"/>
<DoubleAnimation Storyboard.TargetName="content" Storyboard.TargetProperty="Opacity"
Duration="0:0:0.2" To="0"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ring" Storyboard.TargetProperty="IsActive">
<DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="ring" Storyboard.TargetProperty="Width"
EnableDependentAnimation="True"
From="0" To="100" Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetName="ring" Storyboard.TargetProperty="Height"
EnableDependentAnimation="True"
From="0" To="100" Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetName="ring" Storyboard.TargetProperty="Opacity"
From="0" To="1" Duration="0:0:0.5">
<DoubleAnimation.EasingFunction>
<ExponentialEase EasingMode="EaseIn"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</VisualState>
<VisualState x:Name="loaded">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="block" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="ring" Storyboard.TargetProperty="Width"
EnableDependentAnimation="True"
From="100" To="200" Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetName="ring" Storyboard.TargetProperty="Height"
EnableDependentAnimation="True"
From="100" To="200" Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetName="ring" Storyboard.TargetProperty="Opacity"
From="1" To="0" Duration="0:0:0.5">
<DoubleAnimation.EasingFunction>
<ExponentialEase EasingMode="EaseIn"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ring" Storyboard.TargetProperty="IsActive">
<DiscreteObjectKeyFrame KeyTime="0:0:0.5" Value="False"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="content" Storyboard.TargetProperty="Opacity"
From="0" To="1" Duration="0:0:0.2" BeginTime="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetName="loadingScreen" Storyboard.TargetProperty="Opacity"
To="0" Duration="0:0:0.1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Frame Name="content" Navigating="Content_Navigating" Opacity="0"/>
<ProgressRing Width="100" Height="100" Name="ring" IsActive="True"/>
<FontIcon Glyph="&#xF619;" FontSize="100" Name="block" Visibility="Collapsed"/>
<StackPanel VerticalAlignment="Center" Name="loadingScreen" Opacity="0">
<FontIcon Name="icon" Glyph="&#xF618;" FontSize="100" HorizontalAlignment="Center"/>
<TextBlock Name="title" Text="We are unable to display the page" FontSize="48" HorizontalAlignment="Center" TextWrapping="WrapWholeWords"/>
<TextBlock Name="description" Text="It could be caused by YouTube internal server error or by application's bug. Please, try again later" HorizontalAlignment="Center" TextWrapping="WrapWholeWords"/>
<StackPanel Name="networkBtns">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Content="Open network settings" Margin="0,0,5,0" Click="Network_Click"/>
<Button Content="Open troubleshooter" Background="Red" Foreground="White" Click="Troubleshooter_Click"/>
</StackPanel>
<TextBlock Text="OR" FontSize="20" HorizontalAlignment="Center" Margin="5"/>
<Button Content="Refresh page" HorizontalAlignment="Center" Background="Red" Foreground="White" Click="Refresh_Click"/>
</StackPanel>
<StackPanel Name="generalBtns" Orientation="Horizontal" HorizontalAlignment="Center">
<Button Content="Refresh page" Margin="0,0,5,0" Click="Refresh_Click"/>
<Button Name="feedback" Content="Leave feedback" Background="Red" Foreground="White" Click="Feedback_Click"/>
</StackPanel>
<TextBlock Name="error" Foreground="Gray" Text="Exception:" HorizontalAlignment="Center" IsTextSelectionEnabled="True"/>
<TextBlock Name="message" Foreground="Gray" Text="Message:" HorizontalAlignment="Center" IsTextSelectionEnabled="True" TextWrapping="WrapWholeWords"/>
<TextBlock Name="stackTrace" Foreground="Gray" Text="Stack trace:" HorizontalAlignment="Center" IsTextSelectionEnabled="True" TextWrapping="WrapWholeWords"/>
</StackPanel>
</Grid>
</UserControl>
+130
View File
@@ -0,0 +1,130 @@
using Microsoft.Services.Store.Engagement;
using System;
using System.Collections;
using Windows.ApplicationModel.Resources;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace FoxTube.Controls
{
public sealed partial class ContentFrame : UserControl
{
readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("Loading");
public LoadingScreenState State { get; private set; } = LoadingScreenState.Loaded;
public Type SourcePageType => content.CurrentSourcePageType;
public bool CanGoBack => content.CanGoBack;
public object FrameContent => content.Content;
public event NavigationOccured Navigated;
public ContentFrame()
{
InitializeComponent();
if (!StoreServicesFeedbackLauncher.IsSupported())
feedback.Visibility = Visibility.Collapsed;
}
public void Navigate(Type pageType, object parameter) =>
content.Navigate(pageType, parameter);
/// <summary>
/// Navigates on the same page and deletes last back stack entry
/// </summary>
public void Refresh()
{
content.Navigate(content.CurrentSourcePageType, (content.Content as ViewModel).Parameter);
content.BackStack.RemoveAt(content.BackStack.Count - 1);
}
public void GoBack() =>
content.GoBack();
/// <summary>
/// Clears frame back stack
/// </summary>
public void ClearHistory() =>
content.BackStack.Clear();
/// <summary>
/// Hides loading indicator and show blocked icon
/// </summary>
public void Block()
{
blocked.Storyboard.Begin();
State = LoadingScreenState.Blocked;
}
/// <summary>
/// Shows error screen with common error message
/// </summary>
/// <param name="error">Error name (Exception name also will do)</param>
/// <param name="message">Error description or exception message</param>
/// <param name="stackTrace">Exception's stack trace</param>
public void ShowError(string error = "Unknown", string message = "N/A", string stackTrace = null)
{
title.Text = resources.GetString("/Loading/generalTitle");
description.Text = resources.GetString("/Loading/general");
this.error.Text = $"{resources.GetString("/Loading/error")}: {error}";
this.message.Text = $"{resources.GetString("/Loading/message")}: {message}";
this.stackTrace.Text = $"{resources.GetString("/Loading/stackTrace")}: {stackTrace}";
this.stackTrace.Visibility = string.IsNullOrWhiteSpace(stackTrace) ? Visibility.Collapsed : Visibility.Visible;
commonError.Storyboard.Begin();
State = LoadingScreenState.Error;
}
/// <summary>
/// Shows error screen with network error message
/// </summary>
/// <param name="error">Error name (Exception name also will do)</param>
/// <param name="message">Error description or exception message</param>
/// <param name="stackTrace">Exception's stack trace</param>
public void ShowConnectionError(string error = "Unknown", string message = "N/A", string stackTrace = null)
{
title.Text = resources.GetString("/Loading/generalTitle");
description.Text = resources.GetString("/Loading/general");
this.error.Text = $"{resources.GetString("/Loading/error")}: {error}";
this.message.Text = $"{resources.GetString("/Loading/message")}: {message}";
this.stackTrace.Text = $"{resources.GetString("/Loading/stackTrace")}: {stackTrace}";
this.stackTrace.Visibility = string.IsNullOrWhiteSpace(stackTrace) ? Visibility.Collapsed : Visibility.Visible;
wifiError.Storyboard.Begin();
State = LoadingScreenState.Error;
}
/// <summary>
/// Hides loading UI
/// </summary>
public void Complete(IEnumerable pivotCollection = null, int? selectedPivot = null)
{
Navigated?.Invoke(pivotCollection, selectedPivot);
loaded.Storyboard.Begin();
State = LoadingScreenState.Loaded;
}
void Content_Navigating(object sender, NavigatingCancelEventArgs e)
{
loading.Storyboard.Begin();
State = LoadingScreenState.Loading;
}
async void Troubleshooter_Click(object sender, RoutedEventArgs e) =>
await Launcher.LaunchUriAsync("ms-settings:troubleshoot".ToUri());
async void Network_Click(object sender, RoutedEventArgs e) =>
await Launcher.LaunchUriAsync("ms-settings:network".ToUri());
async void Feedback_Click(object sender, RoutedEventArgs e) =>
await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync();
void Refresh_Click(object sender, RoutedEventArgs e) =>
Refresh();
}
}
+14
View File
@@ -0,0 +1,14 @@
<UserControl
x:Class="FoxTube.Controls.MainFrame"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:FoxTube.Controls"
mc:Ignorable="d">
<Grid>
<controls:ContentFrame x:Name="content" Navigated="Content_Navigated"/>
<controls:ContentFrame x:Name="video" Visibility="Collapsed"/>
</Grid>
</UserControl>
+157
View File
@@ -0,0 +1,157 @@
using System;
using Windows.UI.Xaml.Controls;
namespace FoxTube.Controls
{
public sealed partial class MainFrame : UserControl
{
public static MainFrame Current { get; private set; }
public event NavigatingOccured Navigating;
public event NavigationOccured Navigated;
public bool CanGoBack => !VideoMinimized || content.CanGoBack;
public bool IsVideoOpened { get; } = false;
public bool VideoMinimized => video.Width != double.NaN;
public Type SourcePageType => content.SourcePageType;
public MainFrame()
{
InitializeComponent();
Current = this;
}
public void NavigateTo(Type pageType, object parameter = null)
{
if (content.SourcePageType == pageType && parameter == (content.FrameContent as INavigationPage).Parameter)
return;
content.Navigate(pageType, parameter);
if (video.FrameContent != null && !VideoMinimized)
MinimizeVideo();
Navigating?.Invoke(pageType, parameter);
}
public void Refresh()
{
content.Refresh();
Navigating?.Invoke(SourcePageType, (content.Content as ViewModel).Parameter);
}
#region Video control methods
public void OpenVideo(string id, string playlistId = null, bool incognito = false)
{
/*if (VideoMinimized)
MaximizeVideo();
video.Navigate(typeof(VideoPage), (id, playlistId, incognito));
MainPage.Current.UpdateView();*/
}
public void MinimizeVideo(bool player = false)
{
/*if (video.FrameContent == null)
return;
video.Margin = new Thickness(0, 0, 25, 50);
video.HorizontalAlignment = HorizontalAlignment.Right;
video.VerticalAlignment = VerticalAlignment.Bottom;
video.Width = 432;
if (!player)
(video.FrameContent as VideoPage).Minimize();
MainPage.Current.UpdateView();*/
}
public void MaximizeVideo(bool player = false)
{
/*if (video.FrameContent == null)
return;
video.Margin = new Thickness(0);
video.VerticalAlignment = VerticalAlignment.Stretch;
video.HorizontalAlignment = HorizontalAlignment.Stretch;
video.Width = double.NaN;
if (!player)
(video.FrameContent as VideoPage).Maximize();
MainPage.Current.UpdateView();*/
}
public void CloseVideo()
{
/*if (ApplicationView.GetForCurrentView().IsFullScreenMode)
ApplicationView.GetForCurrentView().ExitFullScreenMode();
video.FrameContent = null;
video.ClearHistory();
if (VideoMinimized)
MaximizeVideo();
GC.Collect();
MainPage.Current.UpdateView();*/
}
public void RefreshVideo()
{
/*if (video.FrameContent == null)
return;
if (VideoMinimized)
MaximizeVideo();
video.Refresh();
Methods.MainPage.UpdateView();*/
}
#endregion
public void AuthorizationChanged(bool isAuthorized)
{
//TODO: Uncomment this
/*switch (isAuthorized)
{
case true:
if (content.FrameContent == null)
NavigateTo(typeof(Home));
else if (!content.SourcePageType.Name.Belongs("Downloads", "Settings"))
content.Refresh();
break;
case false:
NavigateTo(typeof(Home));
content.ClearHistory();
video.ClearHistory();
break;
}
RefreshVideo();*/
}
public void BackRequested()
{
if (video.FrameContent != null && !VideoMinimized)
{
if (video.CanGoBack)
video.GoBack();
else if (video.State != LoadingScreenState.Loaded)
CloseVideo();
else
MinimizeVideo();
}
else if (content.CanGoBack)
content.GoBack();
}
void Content_Navigated(System.Collections.IEnumerable pivotCollection, int? selectedPivot) =>
Navigated?.Invoke(pivotCollection, selectedPivot);
}
}
+62 -25
View File
@@ -105,6 +105,29 @@
<Compile Include="App.xaml.cs"> <Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon> <DependentUpon>App.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Classes\Downloads.cs" />
<Compile Include="Classes\Extensions.cs" />
<Compile Include="Classes\Methods.cs" />
<Compile Include="Classes\Navigation.cs" />
<Compile Include="Classes\Processes.cs" />
<Compile Include="Classes\SearchParameters.cs" />
<Compile Include="Classes\Service.cs" />
<Compile Include="Classes\Settings.cs" />
<Compile Include="Classes\User.cs" />
<Compile Include="Classes\UserManagement.cs" />
<Compile Include="Classes\ViewModel.cs" />
<Compile Include="Controls\AccountManager.xaml.cs">
<DependentUpon>AccountManager.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ContentFrame.xaml.cs">
<DependentUpon>ContentFrame.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\MainFrame.xaml.cs">
<DependentUpon>MainFrame.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\Home.xaml.cs">
<DependentUpon>Home.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\MainPage.xaml.cs"> <Compile Include="Pages\MainPage.xaml.cs">
<DependentUpon>MainPage.xaml</DependentUpon> <DependentUpon>MainPage.xaml</DependentUpon>
</Compile> </Compile>
@@ -180,23 +203,40 @@
<Content Include="Assets\Square44x44Logo.scale-200.png" /> <Content Include="Assets\Square44x44Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" /> <Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Assets\Wide310x150Logo.scale-200.png" /> <Content Include="Assets\Wide310x150Logo.scale-200.png" />
<PRIResource Include="Strings\en-US\Loading.resw" />
<PRIResource Include="Strings\en-US\Generic.resw" />
<PRIResource Include="Strings\en-US\Toasts.resw" />
<PRIResource Include="Strings\en-US\Dialogs.resw" />
<PRIResource Include="Strings\en-US\Inbox.resw" />
<PRIResource Include="Strings\ru-RU\Report.resw" /> <PRIResource Include="Strings\ru-RU\Report.resw" />
<PRIResource Include="Strings\en-US\Report.resw" /> <PRIResource Include="Strings\en-US.old\Report.resw" />
<PRIResource Include="Strings\ru-RU\Translate.resw" /> <PRIResource Include="Strings\ru-RU\Translate.resw" />
<PRIResource Include="Strings\en-US\Translate.resw" /> <PRIResource Include="Strings\en-US.old\Translate.resw" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ApplicationDefinition Include="App.xaml"> <ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</ApplicationDefinition> </ApplicationDefinition>
<Page Include="Pages\MainPage.xaml"> <Page Include="Controls\AccountManager.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Themes\Generic.xaml"> <Page Include="Controls\ContentFrame.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\MainFrame.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\Home.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\MainPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Themes\Resources.xaml"> <Page Include="Themes\Resources.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
@@ -255,39 +295,39 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PRIResource Include="Strings\ru-RU\VideoPage.resw" /> <PRIResource Include="Strings\ru-RU\VideoPage.resw" />
<PRIResource Include="Strings\en-US\VideoPage.resw" /> <PRIResource Include="Strings\en-US.old\VideoPage.resw" />
<PRIResource Include="Strings\ru-RU\Channel.resw" /> <PRIResource Include="Strings\ru-RU\Channel.resw" />
<PRIResource Include="Strings\en-US\Channel.resw" /> <PRIResource Include="Strings\en-US.old\Channel.resw" />
<PRIResource Include="Strings\ru-RU\Chat.resw" /> <PRIResource Include="Strings\ru-RU\Chat.resw" />
<PRIResource Include="Strings\en-US\Chat.resw" /> <PRIResource Include="Strings\en-US.old\Chat.resw" />
<PRIResource Include="Strings\ru-RU\Cards.resw" /> <PRIResource Include="Strings\ru-RU\Cards.resw" />
<PRIResource Include="Strings\en-US\Cards.resw" /> <PRIResource Include="Strings\en-US.old\Cards.resw" />
<PRIResource Include="Strings\ru-RU\Methods.resw" /> <PRIResource Include="Strings\ru-RU\Methods.resw" />
<PRIResource Include="Strings\en-US\Methods.resw" /> <PRIResource Include="Strings\en-US.old\Methods.resw" />
<PRIResource Include="Strings\ru-RU\Search.resw" /> <PRIResource Include="Strings\ru-RU\Search.resw" />
<PRIResource Include="Strings\en-US\Search.resw" /> <PRIResource Include="Strings\en-US.old\Search.resw" />
<PRIResource Include="Strings\ru-RU\Playlist.resw" /> <PRIResource Include="Strings\ru-RU\Playlist.resw" />
<PRIResource Include="Strings\en-US\Playlist.resw" /> <PRIResource Include="Strings\en-US.old\Playlist.resw" />
<PRIResource Include="Strings\ru-RU\Downloads.resw" /> <PRIResource Include="Strings\ru-RU\Downloads.resw" />
<PRIResource Include="Strings\ru-RU\CommentsPage.resw" /> <PRIResource Include="Strings\ru-RU\CommentsPage.resw" />
<PRIResource Include="Strings\en-US\Downloads.resw" /> <PRIResource Include="Strings\en-US.old\Downloads.resw" />
<PRIResource Include="Strings\ru-RU\Home.resw" /> <PRIResource Include="Strings\ru-RU\Home.resw" />
<PRIResource Include="Strings\en-US\Home.resw" /> <PRIResource Include="Strings\en-US.old\Home.resw" />
<PRIResource Include="Strings\en-US\CommentsPage.resw" /> <PRIResource Include="Strings\en-US.old\CommentsPage.resw" />
<PRIResource Include="Strings\ru-RU\LoadingPage.resw" /> <PRIResource Include="Strings\ru-RU\LoadingPage.resw" />
<PRIResource Include="Strings\en-US\LoadingPage.resw" /> <PRIResource Include="Strings\en-US.old\LoadingPage.resw" />
<PRIResource Include="Strings\ru-RU\Inbox.resw" /> <PRIResource Include="Strings\ru-RU\Inbox.resw" />
<PRIResource Include="Strings\ru-RU\General.resw" /> <PRIResource Include="Strings\ru-RU\General.resw" />
<PRIResource Include="Strings\ru-RU\About.resw" /> <PRIResource Include="Strings\ru-RU\About.resw" />
<PRIResource Include="Strings\ru-RU\Settings.resw" /> <PRIResource Include="Strings\ru-RU\Settings.resw" />
<PRIResource Include="Strings\en-US\Inbox.resw" /> <PRIResource Include="Strings\en-US.old\Inbox.resw" />
<PRIResource Include="Strings\en-US\About.resw" /> <PRIResource Include="Strings\en-US.old\About.resw" />
<PRIResource Include="Strings\en-US\General.resw" /> <PRIResource Include="Strings\en-US.old\General.resw" />
<PRIResource Include="Strings\en-US\Settings.resw" /> <PRIResource Include="Strings\en-US.old\Settings.resw" />
<PRIResource Include="Strings\ru-RU\Main.resw"> <PRIResource Include="Strings\ru-RU\Main.resw">
<SubType>Designer</SubType> <SubType>Designer</SubType>
</PRIResource> </PRIResource>
<PRIResource Include="Strings\en-US\Main.resw" /> <PRIResource Include="Strings\en-US.old\Main.resw" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\FoxTube.Background\FoxTube.Background.csproj"> <ProjectReference Include="..\FoxTube.Background\FoxTube.Background.csproj">
@@ -303,10 +343,7 @@
<Name>Microsoft Engagement Framework</Name> <Name>Microsoft Engagement Framework</Name>
</SDKReference> </SDKReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup />
<Folder Include="Classes\" />
<Folder Include="Controls\" />
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' "> <PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion> <VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup> </PropertyGroup>
+21
View File
@@ -0,0 +1,21 @@
<local:ViewModel
x:Class="FoxTube.Pages.Home"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FoxTube"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:ui="using:Microsoft.UI.Xaml.Controls"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<local:ViewModel.PivotItems>
<ui:NavigationViewItem Content="Recommended"/>
<ui:NavigationViewItem Content="Trending"/>
<ui:NavigationViewItem Content="Subscriptions"/>
</local:ViewModel.PivotItems>
<Grid>
</Grid>
</local:ViewModel>
+30
View File
@@ -0,0 +1,30 @@
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 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;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
namespace FoxTube.Pages
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class Home : ViewModel
{
public Home()
{
this.InitializeComponent();
}
}
}
+263 -2
View File
@@ -2,13 +2,274 @@
x:Class="FoxTube.Pages.MainPage" x:Class="FoxTube.Pages.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FoxTube.Pages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> xmlns:ui="using:Microsoft.UI.Xaml.Controls"
xmlns:data="using:Google.Apis.YouTube.v3.Data"
xmlns:controls="using:FoxTube.Controls">
<Page.Background>
<AcrylicBrush BackgroundSource="HostBackdrop" TintColor="{StaticResource SystemColorBackgroundColor}" TintOpacity=".5"/>
</Page.Background>
<Page.Resources>
<AcrylicBrush x:Key="NavigationViewTopPaneBackground" BackgroundSource="HostBackdrop" TintColor="Red" TintOpacity=".7" FallbackColor="Red"/>
</Page.Resources>
<Grid> <Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="SplashScreenStates">
<VisualState x:Name="FadeOut">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="splashImage" Storyboard.TargetProperty="Opacity"
From="1" To="0" BeginTime="0:0:0.5" Duration="0:0:0.5">
<DoubleAnimation.EasingFunction>
<QuadraticEase EasingMode="EaseInOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="splashRing" Storyboard.TargetProperty="Opacity"
From="1" To="0" Duration="0:0:0.5">
<DoubleAnimation.EasingFunction>
<ExponentialEase EasingMode="EaseIn"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="splashRing" Storyboard.TargetProperty="Width"
From="50" To="180" Duration="0:0:0.5" EnableDependentAnimation="True">
<DoubleAnimation.EasingFunction>
<QuadraticEase EasingMode="EaseIn"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="splashRing" Storyboard.TargetProperty="Height"
From="50" To="100" Duration="0:0:0.5" EnableDependentAnimation="True">
<DoubleAnimation.EasingFunction>
<QuadraticEase EasingMode="EaseIn"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="splashScreen" Storyboard.TargetProperty="Opacity"
From="1" To="0" BeginTime="0:0:1" Duration="0:0:0.5">
<DoubleAnimation.EasingFunction>
<QuadraticEase EasingMode="EaseInOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="splashScreen" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0:0:1.5" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="split" Storyboard.TargetProperty="IsPaneOpen">
<DiscreteObjectKeyFrame KeyTime="0:0:1.5" Value="True"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="PaneStates">
<VisualState x:Name="ExpandedMode">
<VisualState.StateTriggers>
<AdaptiveTrigger x:Name="ExpandedThresholdWidth" MinWindowWidth="1008"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="split.DisplayMode" Value="CompactInline"/>
<Setter Target="split.IsPaneOpen" Value="True"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="CompactMode">
<VisualState.StateTriggers>
<AdaptiveTrigger x:Name="CompactThresholdWidth" MinWindowWidth="641"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="split.DisplayMode" Value="CompactOverlay"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="SearchStates">
<VisualState x:Name="SearchExpanded">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="1000"/>
</VisualState.StateTriggers>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="search" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="searchBtn" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation EnableDependentAnimation="True"
Storyboard.TargetName="search" Storyboard.TargetProperty="Width"
From="0" To="300" Duration="0:0:1">
<DoubleAnimation.EasingFunction>
<QuadraticEase EasingMode="EaseInOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</VisualState>
<VisualState x:Name="SearchCollapsed">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0"/>
</VisualState.StateTriggers>
<Storyboard>
<DoubleAnimation EnableDependentAnimation="True"
Storyboard.TargetName="search" Storyboard.TargetProperty="Width"
From="300" To="0" Duration="0:0:1">
<DoubleAnimation.EasingFunction>
<QuadraticEase EasingMode="EaseInOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="search" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0:0:1" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="searchBtn" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0:0:1" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ui:NavigationView PaneDisplayMode="Top" SelectionChanged="Nav_SelectionChanged" Name="nav" IsSettingsVisible="False"
BackRequested="Nav_BackRequested" IsBackEnabled="{x:Bind content.CanGoBack}">
<ui:NavigationView.AutoSuggestBox>
<AutoSuggestBox x:Name="search" LostFocus="Search_LostFocus" QueryIcon="Find" QuerySubmitted="Search_QuerySubmitted" TextChanged="Search_TextChanged" PlaceholderText="Search" Width="300"/>
</ui:NavigationView.AutoSuggestBox>
<ui:NavigationView.PaneHeader>
<ui:NavigationViewItem Icon="GlobalNavigationButton" Width="42" Name="menu" Tapped="Menu_Tapped"/>
</ui:NavigationView.PaneHeader>
<ui:NavigationView.PaneFooter>
<StackPanel Orientation="Horizontal">
<ui:NavigationViewItem Icon="Find" Width="42" Name="searchBtn" Tapped="SearchBtn_Tapped"/>
<ui:NavigationViewItem Width="42" Name="feedback" Tapped="Feedback_Click">
<ui:NavigationViewItem.Icon>
<FontIcon Glyph="&#xED15;"/>
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<controls:AccountManager x:Name="accountManager"/>
</StackPanel>
</ui:NavigationView.PaneFooter>
<SplitView OpenPaneLength="300" Name="split" DisplayMode="Overlay" CompactPaneLength="42"
PaneClosing="Split_PaneToggled" PaneOpened="Split_PaneToggled">
<SplitView.PaneBackground>
<AcrylicBrush BackgroundSource="Backdrop" TintColor="{StaticResource SystemColorBackgroundColor}" TintOpacity=".5"/>
</SplitView.PaneBackground>
<SplitView.Pane>
<ScrollViewer>
<StackPanel>
<ui:NavigationViewList>
<ui:NavigationViewList.ItemContainerTransitions>
<TransitionCollection>
<EntranceThemeTransition IsStaggeringEnabled="True"/>
</TransitionCollection>
</ui:NavigationViewList.ItemContainerTransitions>
<ui:NavigationViewItem Icon="Home" Content="Home" Tapped="Home_Tapped"/>
<ui:NavigationViewItem Icon="People" Content="Subscriptions" Name="subscriptions" Tapped="Subscriptions_Tapped" Visibility="Collapsed"/>
<ui:NavigationViewItemHeader Content="My library" Name="libraryHeader" Visibility="Collapsed"/>
<ui:NavigationViewItem Content="History" Name="history" Tapped="History_Tapped" Visibility="Collapsed">
<ui:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE81C;"/>
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItem Icon="Like" Content="Liked videos" Name="likedVideos" Tapped="LikedVideos_Tapped" Visibility="Collapsed"/>
<ui:NavigationViewItem Icon="Clock" Content="Watch later" Name="watchLater" Tapped="WatchLater_Tapped" Visibility="Collapsed"/>
<ui:NavigationViewItem Icon="Download" Content="Downloads" Name="downloads" Tapped="Downloads_Tapped"/>
</ui:NavigationViewList>
<ui:NavigationViewList Name="subscriptionsList">
<ui:NavigationViewList.ItemContainerTransitions>
<TransitionCollection>
<EntranceThemeTransition IsStaggeringEnabled="True"/>
</TransitionCollection>
</ui:NavigationViewList.ItemContainerTransitions>
<ui:NavigationViewList.ItemTemplate>
<DataTemplate x:DataType="data:Subscription">
<StackPanel Orientation="Horizontal" Padding="5" Margin="-5,0,0,0">
<PersonPicture Height="20" Margin="-5,0,15,0">
<PersonPicture.ProfilePicture>
<BitmapImage UriSource="{Binding Snippet.Thumbnails.Medium.Url}" DecodePixelHeight="20" DecodePixelWidth="20"/>
</PersonPicture.ProfilePicture>
</PersonPicture>
<TextBlock FontSize="14" Text="{Binding Snippet.Title}"/>
</StackPanel>
</DataTemplate>
</ui:NavigationViewList.ItemTemplate>
<ui:NavigationViewItemHeader Content="Subscriptions" Name="subscriptionsHeader" Visibility="Collapsed"/>
</ui:NavigationViewList>
<ui:NavigationViewList>
<ui:NavigationViewList.ItemContainerTransitions>
<TransitionCollection>
<EntranceThemeTransition IsStaggeringEnabled="True"/>
</TransitionCollection>
</ui:NavigationViewList.ItemContainerTransitions>
<ui:NavigationViewItemHeader Content="Best of YouTube" Name="collectionsHeader"/>
<ui:NavigationViewItem Content="Music" Tapped="Music_Tapped">
<ui:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE189;"/>
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItem Content="Sports" Tapped="Sports_Tapped">
<ui:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE95E;"/>
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItem Content="Movies" Tapped="Movies_Tapped">
<ui:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE8B2;"/>
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItem Content="News" Tapped="News_Tapped">
<ui:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE12A;"/>
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItem Content="Live" Tapped="Live_Tapped">
<ui:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE93E;"/>
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItem Content="Spotlight" Tapped="Spotlight_Tapped">
<ui:NavigationViewItem.Icon>
<FontIcon Glyph="&#xECAD;"/>
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItem Content="360&#xB0; video" Tapped="SphereVideo_Tapped">
<ui:NavigationViewItem.Icon>
<FontIcon Glyph="&#xF131;"/>
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
</ui:NavigationViewList>
</StackPanel>
</ScrollViewer>
</SplitView.Pane>
<SplitView.Content>
<controls:MainFrame x:Name="content" Navigated="Content_Navigated" Navigating="Content_Navigating"/>
</SplitView.Content>
</SplitView>
</ui:NavigationView>
<TextBlock x:Name="appTitle" Text="FoxTube" VerticalAlignment="Top" HorizontalAlignment="Left" Style="{StaticResource CaptionTextBlockStyle}" Margin="12,8"/>
<Grid Background="#282828" x:Name="splashScreen" Visibility="Visible">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="180"/>
</Grid.RowDefinitions>
<Canvas Grid.RowSpan="2">
<Image Source="/Assets/SplashScreen.png" Name="splashImage"/>
</Canvas>
<ProgressRing x:Name="splashRing" HorizontalAlignment="Center" Width="50" Height="50" IsActive="True" Grid.Row="1"/>
</Grid>
</Grid> </Grid>
</Page> </Page>
+255 -16
View File
@@ -1,30 +1,269 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.Threading.Tasks;
using System.Linq; using System.Xml;
using System.Runtime.InteropServices.WindowsRuntime; using FoxTube.Classes;
using Windows.Foundation; using Google.Apis.YouTube.v3.Data;
using Windows.Foundation.Collections; using Microsoft.Services.Store.Engagement;
using Windows.ApplicationModel.Activation;
using Windows.Graphics.Display;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; 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.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
namespace FoxTube.Pages namespace FoxTube.Pages
{ {
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page public sealed partial class MainPage : Page
{ {
public MainPage() public MainPage(SplashScreen splash, object args)
{ {
this.InitializeComponent(); InitializeComponent();
SetSplashScreen(splash);
Methods.SetTitleBar();
UserManagement.AuthorizationStateChanged += AuthorizationStateChanged;
UserManagement.SubscriptionChanged += SubscriptionChanged;
if (StoreServicesFeedbackLauncher.IsSupported())
feedback.Visibility = Visibility.Visible;
//TODO: Initialize stuff
//TODO: Remove this method
Begin();
}
async void Begin()
{
await Task.Delay(5000);
FadeOut.Storyboard.Begin();
}
void SetSplashScreen(SplashScreen splash)
{
double ScaleFactor = DisplayInformation.GetForCurrentView().RawPixelsPerViewPixel;
splashImage.SetValue(Canvas.LeftProperty, splash.ImageLocation.Left);
splashImage.SetValue(Canvas.TopProperty, splash.ImageLocation.Top);
if (Windows.Foundation.Metadata.ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))
{
splashImage.Height = splash.ImageLocation.Height / ScaleFactor;
splashImage.Width = splash.ImageLocation.Width / ScaleFactor;
}
else
{
splashImage.Height = splash.ImageLocation.Height;
splashImage.Width = splash.ImageLocation.Width;
}
}
void SubscriptionChanged(string action, Subscription subscription)
{
switch (action)
{
case "add":
subscriptionsHeader.Visibility = Visibility.Visible;
if (subscriptionsList.Items.Count < 10)
subscriptionsList.Items.Add(subscription);
break;
case "remove":
if (subscriptionsList.Items.Contains(subscription))
{
subscriptionsList.Items.Remove(subscription);
if (UserManagement.Current.Subscriptions.Count >= 10)
subscriptionsList.Items.Add(UserManagement.Current.Subscriptions[9]);
else if (UserManagement.Current.Subscriptions.Count == 0)
subscriptionsHeader.Visibility = Visibility.Collapsed;
}
break;
}
}
void AuthorizationStateChanged(bool isAuthorized)
{
if (isAuthorized)
{
accountManager.Logged();
subscriptions.Visibility =
libraryHeader.Visibility =
history.Visibility =
likedVideos.Visibility =
watchLater.Visibility = Visibility.Visible;
if (UserManagement.Current.Subscriptions.Count > 0)
{
subscriptionsHeader.Visibility = Visibility.Visible;
for (int k = 0; k < UserManagement.Current.Subscriptions.Count && k < 10; k++)
subscriptionsList.Items.Add(UserManagement.Current.Subscriptions[k]);
}
}
else
{
accountManager.Quit();
subscriptions.Visibility =
libraryHeader.Visibility =
history.Visibility =
likedVideos.Visibility =
watchLater.Visibility =
subscriptionsHeader.Visibility = Visibility.Collapsed;
subscriptionsList.Items.Clear();
}
content.AuthorizationChanged(isAuthorized);
}
public void UpdateView()
{
nav.IsBackEnabled = content.CanGoBack;
if(content.SourcePageType.Name.Belongs("Subscriptions", "Home", "Settings"))
{
ExpandedThresholdWidth.MinWindowWidth = 1008;
split.IsPaneOpen = split.DisplayMode == SplitViewDisplayMode.CompactInline ? true : false;
}
else
{
ExpandedThresholdWidth.MinWindowWidth = short.MaxValue;
split.IsPaneOpen = false;
}
}
async void Search_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args) => await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
//TODO: Finish search bar
if (search.Text.Length < 3 || args.Reason != AutoSuggestionBoxTextChangeReason.UserInput)
return;
try
{
XmlDocument doc = new XmlDocument();
doc.Load($"http://suggestqueries.google.com/complete/search?ds=yt&client=toolbar&q={search.Text}&hl={Settings.RelevanceLanguage}");
List<string> items = new List<string>();
for (int i = 0; i < doc["toplevel"].ChildNodes.Count && i < 5; i++)
items.Add(doc["toplevel"].ChildNodes[i]["suggestion"].GetAttribute("data"));
search.ItemsSource = items;
}
catch
{
search.ItemsSource = null;
}
});
private void Split_PaneToggled(SplitView sender, object args)
{
subscriptionsHeader.Visibility = split.IsPaneOpen && subscriptionsList.Items.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
libraryHeader.Visibility =
collectionsHeader.Visibility =
libraryHeader.Visibility == Visibility.Collapsed ? Visibility.Visible : Visibility.Collapsed;
}
void Nav_SelectionChanged(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs args)
{
}
#region Simple UI interaction events
void Nav_BackRequested(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewBackRequestedEventArgs args) =>
content.BackRequested();
void Menu_Tapped(object sender, TappedRoutedEventArgs e) =>
split.IsPaneOpen = !split.IsPaneOpen;
async void Feedback_Click(object sender, RoutedEventArgs e) =>
await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync();
void Home_Tapped(object sender, TappedRoutedEventArgs e) =>
Navigation.GoToHome();
void Subscriptions_Tapped(object sender, TappedRoutedEventArgs e) =>
Navigation.GoToSubscriptions();
void History_Tapped(object sender, TappedRoutedEventArgs e) =>
Navigation.GoToHistory();
void LikedVideos_Tapped(object sender, TappedRoutedEventArgs e) =>
Navigation.GoToPlaylist(UserManagement.Current.Channel.ContentDetails.RelatedPlaylists.Likes);
void WatchLater_Tapped(object sender, TappedRoutedEventArgs e) =>
Navigation.GoToPlaylist("WL");
void Downloads_Tapped(object sender, TappedRoutedEventArgs e) =>
Navigation.GoToDownloads();
void Music_Tapped(object sender, TappedRoutedEventArgs e) =>
Navigation.GoToChannel("UC-9-kyTW8ZkZNDHQJ6FgpwQ");
void Sports_Tapped(object sender, TappedRoutedEventArgs e) =>
Navigation.GoToChannel("UCEgdi0XIXXZ-qJOFPf4JSKw");
void Movies_Tapped(object sender, TappedRoutedEventArgs e) =>
Navigation.GoToChannel("UClgRkhTL3_hImCAmdLfDE4g");
void News_Tapped(object sender, TappedRoutedEventArgs e) =>
Navigation.GoToChannel("UCYfdidRxbB8Qhf0Nx7ioOYw");
void Live_Tapped(object sender, TappedRoutedEventArgs e) =>
Navigation.GoToChannel("UC4R8DWoMoI7CAwX8_LjQHig");
void Spotlight_Tapped(object sender, TappedRoutedEventArgs e) =>
Navigation.GoToChannel("UC8iNz9uwDGfomRnnKKbOhOQ");
private void SphereVideo_Tapped(object sender, TappedRoutedEventArgs e) =>
Navigation.GoToChannel("UCzuqhhs6NWbgTzMuM09WKDQ");
#endregion
#region Search bar methods
async void SearchBtn_Tapped(object sender, TappedRoutedEventArgs e)
{
menu.Visibility =
accountManager.Visibility =
feedback.Visibility = Visibility.Collapsed;
nav.IsBackButtonVisible = Microsoft.UI.Xaml.Controls.NavigationViewBackButtonVisible.Collapsed;
SearchExpanded.Storyboard.Begin();
await Task.Delay(1);
search.Focus(FocusState.Pointer);
}
async void Search_LostFocus(object sender, RoutedEventArgs e)
{
if (ActualWidth >= 1000)
return;
SearchCollapsed.Storyboard.Begin();
await Task.Delay(1000);
menu.Visibility =
accountManager.Visibility =
feedback.Visibility = Visibility.Visible;
nav.IsBackButtonVisible = Microsoft.UI.Xaml.Controls.NavigationViewBackButtonVisible.Visible;
}
void Search_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
if (!string.IsNullOrWhiteSpace(search.Text))
Navigation.GoToSearch(new SearchParameters(search.Text));
}
#endregion
private void Content_Navigated(System.Collections.IList pivotCollection, int? selectedPivot)
{
nav.MenuItemsSource = pivotCollection;
if(selectedPivot.HasValue)
nav.SelectedItem = pivotCollection[selectedPivot.Value];
}
private void Content_Navigating(Type sourcePageType, object parameter)
{
nav.MenuItems.Clear();
UpdateView();
} }
} }
} }
+144
View File
@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="all.Content" xml:space="preserve">
<value>All</value>
</data>
<data name="changelog" xml:space="preserve">
<value>Changelog</value>
</data>
<data name="changelogs.Content" xml:space="preserve">
<value>Changelogs</value>
</data>
<data name="dev" xml:space="preserve">
<value>Developer's message</value>
</data>
<data name="filter.Header" xml:space="preserve">
<value>Filter</value>
</data>
<data name="messages.Content" xml:space="preserve">
<value>Messages</value>
</data>
<data name="select.Text" xml:space="preserve">
<value>Select item from the list</value>
</data>
<data name="whatsnew" xml:space="preserve">
<value>What's new in v</value>
</data>
</root>
+135
View File
@@ -0,0 +1,135 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="dontAsk" xml:space="preserve">
<value>Don't ask me anymore</value>
</data>
<data name="feedbackMessage" xml:space="preserve">
<value>Have some thoughts to share about the app or any suggestions? Leave feedback!</value>
</data>
<data name="promptLater" xml:space="preserve">
<value>Maybe later</value>
</data>
<data name="rate" xml:space="preserve">
<value>Like our app? Review it on Microsoft Store!</value>
</data>
<data name="sure" xml:space="preserve">
<value>Sure!</value>
</data>
</root>
+129
View File
@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="channel" xml:space="preserve">
<value>Channel</value>
</data>
<data name="playlist" xml:space="preserve">
<value>Playlist</value>
</data>
<data name="video" xml:space="preserve">
<value>Video</value>
</data>
</root>
+2 -20
View File
@@ -117,28 +117,10 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="all.Content" xml:space="preserve">
<value>All</value>
</data>
<data name="changelog" xml:space="preserve"> <data name="changelog" xml:space="preserve">
<value>Changelog</value> <value>Changelog</value>
</data> </data>
<data name="changelogs.Content" xml:space="preserve"> <data name="whatsNew" xml:space="preserve">
<value>Changelogs</value> <value>What's new in version</value>
</data>
<data name="dev" xml:space="preserve">
<value>Developer's message</value>
</data>
<data name="filter.Header" xml:space="preserve">
<value>Filter</value>
</data>
<data name="messages.Content" xml:space="preserve">
<value>Messages</value>
</data>
<data name="select.Text" xml:space="preserve">
<value>Select item from the list</value>
</data>
<data name="whatsnew" xml:space="preserve">
<value>What's new in v</value>
</data> </data>
</root> </root>
+150
View File
@@ -0,0 +1,150 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="error" xml:space="preserve">
<value>Error</value>
</data>
<data name="feedback.Content" xml:space="preserve">
<value>Leave feedback</value>
</data>
<data name="general" xml:space="preserve">
<value>It could be caused by YouTube internal server error or by application's bug. Please, try again later</value>
</data>
<data name="generalTitle" xml:space="preserve">
<value>We are unable to display the page</value>
</data>
<data name="message" xml:space="preserve">
<value>Message</value>
</data>
<data name="network.Content" xml:space="preserve">
<value>Open network settings</value>
</data>
<data name="or.Text" xml:space="preserve">
<value>OR</value>
</data>
<data name="refresh.Content" xml:space="preserve">
<value>Refresh page</value>
</data>
<data name="stackTrace" xml:space="preserve">
<value>Stack trace</value>
</data>
<data name="troubleshooter.Content" xml:space="preserve">
<value>Open troubleshooter</value>
</data>
</root>
+126
View File
@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="clipboardHead" xml:space="preserve">
<value>We've found this on your clipboard</value>
</data>
<data name="clipboardOpen" xml:space="preserve">
<value>Open in FoxTube</value>
</data>
</root>
-375
View File
@@ -1,375 +0,0 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FoxTube.Controls.Player"
xmlns:controls="using:FoxTube.Controls"
xmlns:adverts="using:FoxTube.Controls.Adverts">
<Style TargetType="local:PlayerControls">
<Setter Property="Background" Value="Transparent" />
<Setter Property="UseSystemFocusVisuals" Value="True" />
<Setter Property="IsTextScaleFactorEnabled" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:PlayerControls">
<Grid Background="Transparent" Name="root">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.Resources>
<Style x:Key="PlayerSeek" TargetType="Slider">
<Setter Property="Margin" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Slider">
<Grid>
<Grid.Resources>
<Style TargetType="Thumb">
<Setter Property="Background" Value="Red" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Thumb">
<Ellipse Fill="{TemplateBinding Background}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalTrackRect" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackFillPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalThumb" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderThumbBackgroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SliderContainer" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderContainerBackgroundPressed}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalDecreaseRect" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackValueFillDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalTrackRect" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackFillDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalThumb" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderThumbBackgroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SliderContainer" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderContainerBackgroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalTrackRect" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderTrackFillPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalThumb" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderThumbBackgroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SliderContainer" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SliderContainerBackgroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusEngagementStates">
<VisualState x:Name="FocusDisengaged"/>
<VisualState x:Name="FocusEngagedHorizontal">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SliderContainer" Storyboard.TargetProperty="(Control.IsTemplateFocusTarget)">
<DiscreteObjectKeyFrame KeyTime="0" Value="False" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalThumb" Storyboard.TargetProperty="(Control.IsTemplateFocusTarget)">
<DiscreteObjectKeyFrame KeyTime="0" Value="True" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid x:Name="SliderContainer" Control.IsTemplateFocusTarget="True" Background="{ThemeResource SliderContainerBackground}">
<Grid x:Name="HorizontalTemplate">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle x:Name="HorizontalTrackRect" Height="{StaticResource SliderTrackThemeHeight}" Fill="{TemplateBinding Background}" Grid.ColumnSpan="3"/>
<ProgressBar x:Name="DownloadProgressIndicator" Grid.ColumnSpan="3"
Height="{ThemeResource SliderTrackThemeHeight}"
Foreground="{ThemeResource SystemControlHighlightChromeAltLowBrush}"
Background="Transparent"
BorderBrush="Transparent"
BorderThickness="1"/>
<Rectangle Height="4" x:Name="HorizontalDecreaseRect" Fill="{StaticResource SystemAccentColor}"/>
<Thumb x:Name="HorizontalThumb"
Height="15" Width="15" Grid.Column="1"
AutomationProperties.AccessibilityView="Raw">
<Thumb.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="112"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Image x:Name="ThumbnailImage" Width="192"/>
<TextBlock Grid.Row="1" x:Name="TimeElapsedPreview"/>
</Grid>
</Thumb.DataContext>
</Thumb>
</Grid>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ControlPanelVisibilityStates">
<VisualState x:Name="ControlPanelFadeIn">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Border">
<EasingDoubleKeyFrame KeyTime="0" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="ControlPanelFadeOut">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Border">
<EasingDoubleKeyFrame KeyTime="0" Value="1" />
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="0" />
</DoubleAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsHitTestVisible" Storyboard.TargetName="Border">
<DiscreteObjectKeyFrame KeyTime="0" Value="False" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="MediaStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Buffering">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="BufferingProgressBar">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Loading">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="BufferingProgressBar">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="ProgressSlider"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
</Storyboard>
</VisualState>
<!--<VisualState x:Name="Error">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="ErrorBorder">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>-->
<VisualState x:Name="Disabled">
<Storyboard/>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="PlayPauseStates">
<VisualState x:Name="PlayState" />
<VisualState x:Name="PauseState">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlayPauseSymbol" Storyboard.TargetProperty="Symbol">
<DiscreteObjectKeyFrame KeyTime="0" Value="Pause" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FullWindowStates">
<VisualState x:Name="NonFullWindowState" />
<VisualState x:Name="FullWindowState">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="FullWindowSymbol" Storyboard.TargetProperty="Symbol">
<DiscreteObjectKeyFrame KeyTime="0" Value="BackToWindow" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="Border">
<Border.Resources>
<Style TargetType="Button" BasedOn="{StaticResource ButtonRevealStyle}">
<Setter Property="Width" Value="50"/>
<Setter Property="Height" Value="50"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="FontFamily" Value="Segoe UI, Segoe MDL2 Assets"/>
</Style>
</Border.Resources>
<Grid x:Name="ControlPanelGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid x:Name="header">
<Grid.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black"/>
<GradientStop Color="#00000000" Offset="1"/>
</LinearGradientBrush>
</Grid.Background>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button x:Name="MinimizeButton" HorizontalAlignment="Left" VerticalAlignment="Top" Content="&#xE011;"/>
<StackPanel VerticalAlignment="Top" Grid.Column="1" Margin="10,0">
<TextBlock x:Name="title" Text="Name" TextTrimming="CharacterEllipsis" MaxLines="1" Style="{StaticResource TitleTextBlockStyle}"/>
<TextBlock x:Name="channel" Text="Channel" TextTrimming="CharacterEllipsis" FontStyle="Italic"/>
</StackPanel>
<StackPanel x:Name="RightHeaderControls" Orientation="Horizontal" VerticalAlignment="Top" Grid.Column="2">
<Button x:Name="CloseButton" HorizontalAlignment="Right" VerticalAlignment="Top" Content="&#xE106;"/>
<Button x:Name="CastButton" Content="&#xEC15;"/>
<Button x:Name="CompactOverlayButton" HorizontalAlignment="Right" VerticalAlignment="Top" Content="&#xE2B3;"/>
</StackPanel>
</Grid>
<Grid x:Name="centerTrigger" Grid.Row="1"/>
<Grid x:Name="center" Grid.Row="1" Visibility="Collapsed" Background="#7F000000">
<Button x:Name="drag" IsHitTestVisible="False" Height="32" Width="47" Margin="0,0,47,0" Content="&#xE700;" Visibility="Collapsed" Padding="0" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" VerticalAlignment="Top" HorizontalAlignment="Right"/>
<StackPanel x:Name="centerControls" Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<ProgressBar x:Name="SeekIndicator" Background="Transparent" VerticalAlignment="Bottom"/>
</Grid>
<Grid x:Name="footer" Grid.Row="2">
<Grid.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="1"/>
<GradientStop Color="#00000000" Offset="0"/>
</LinearGradientBrush>
</Grid.Background>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ProgressBar x:Name="BufferingProgressBar" VerticalAlignment="Bottom" Grid.ColumnSpan="3" Background="Transparent" IsIndeterminate="True" Visibility="Collapsed"/>
<StackPanel x:Name="LeftFooterControls" Orientation="Horizontal" VerticalAlignment="Bottom">
<Button x:Name="PlayPauseButton">
<SymbolIcon x:Name="PlayPauseSymbol" Symbol="Play"/>
<Button.KeyboardAccelerators>
<KeyboardAccelerator Key="Space"/>
</Button.KeyboardAccelerators>
</Button>
<Button x:Name="PreviousButton" Content="&#xE100;" Visibility="Collapsed"/>
<Button x:Name="NextButton" Content="&#xE101;"/>
<Button x:Name="VolumeMenuButton" Content="&#xE15D;">
<Button.Flyout>
<Flyout>
<StackPanel Orientation="Horizontal" Margin="-10">
<Button x:Name="AudioMuteButton" Content="&#xE15D;" FontFamily="Segoe MDL2 Assets" Height="50" Width="50" Background="Transparent" FontSize="20"/>
<Slider x:Name="VolumeSlider" Width="150" Margin="10,5,10,0" VerticalAlignment="Center" TickPlacement="Outside" TickFrequency="10"/>
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
<Button x:Name="PlayLiveButton" Width="NaN" Visibility="Collapsed">
<TextBlock x:Uid="/VideoPage/live" Text="🔴 LIVE"/>
</Button>
</StackPanel>
<Grid x:Name="CenterFooterControls" Height="50" Grid.Column="1" VerticalAlignment="Bottom">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="1" Margin="5,0" VerticalAlignment="Center" HorizontalAlignment="Left" x:Name="TimeElapsedElement" Text="00:00"/>
<TextBlock Grid.Row="1" Margin="5,0" VerticalAlignment="Center" HorizontalAlignment="Right" x:Name="TimeRemainingElement" Text="00:00"/>
<Slider x:Name="ProgressSlider" Style="{StaticResource PlayerSeek}" IsThumbToolTipEnabled="False" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="5,0"/>
</Grid>
<StackPanel x:Name="RightFooterControls" Grid.Column="2" VerticalAlignment="Bottom" Orientation="Horizontal">
<Button x:Name="SkipBackwardButton" Content="&#xED3C;">
<Button.KeyboardAccelerators>
<KeyboardAccelerator Key="Left"/>
</Button.KeyboardAccelerators>
</Button>
<Button x:Name="SkipForwardButton" Content="&#xED3D;">
<Button.KeyboardAccelerators>
<KeyboardAccelerator Key="Right"/>
</Button.KeyboardAccelerators>
</Button>
<Line Stroke="White" StrokeThickness="2" Y1="5" Y2="45"/>
<Button x:Name="CaptionsMenuButton" Content="&#xE7F0;">
<Button.Flyout>
<Flyout>
<StackPanel Width="225">
<ToggleSwitch x:Name="CaptionsToggleSwitch" OnContent="Subtitles" OffContent="Subtitles" x:Uid="/VideoPage/subsSwitch"/>
<ComboBox x:Name="CaptionsSelector" Header="Language" x:Uid="/VideoPage/subsSelector" PlaceholderText="No captions are available" HorizontalAlignment="Stretch"/>
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
<Button x:Name="SettingsMenuButton" Content="&#xE115;">
<Button.Flyout>
<Flyout>
<StackPanel>
<Slider x:Name="PlaybackSpeedSlider" x:Uid="/VideoPage/playbackSpeed" Orientation="Horizontal" TickPlacement="Outside" StepFrequency=".1" Value="1" Maximum="3" Header="Playback speed"/>
<ComboBox Width="225" x:Name="QualitySelector" Header="Language" x:Uid="/VideoPage/qualitySelector"/>
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
<Button x:Name="FullWindowButton">
<SymbolIcon x:Name="FullWindowSymbol" Symbol="FullScreen"/>
<Button.KeyboardAccelerators>
<KeyboardAccelerator Key="F11"/>
</Button.KeyboardAccelerators>
</Button>
</StackPanel>
</Grid>
</Grid>
</Border>
<controls:LiveCaptions Visibility="Collapsed" x:Name="CaptionControl"/>
<adverts:PlayerAdvert Grid.Row="1" x:Name="AdvertControl" VerticalAlignment="Bottom"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
+9
View File
@@ -7,6 +7,15 @@
<Style TargetType="Button" BasedOn="{StaticResource ButtonRevealStyle}"> <Style TargetType="Button" BasedOn="{StaticResource ButtonRevealStyle}">
<Setter Property="FontSize" Value="14"/> <Setter Property="FontSize" Value="14"/>
</Style> </Style>
<Style x:Key="NavigationViewHeaderButton" TargetType="Button" BasedOn="{StaticResource ButtonRevealStyle}">
<Setter Property="FontFamily" Value="Segoe MDL2 Assets"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Height" Value="41"/>
<Setter Property="Width" Value="60"/>
<Setter Property="FontSize" Value="15"/>
</Style>
<Style TargetType="ListViewItem" BasedOn="{StaticResource ListViewItemRevealStyle}"/> <Style TargetType="ListViewItem" BasedOn="{StaticResource ListViewItemRevealStyle}"/>
<Style TargetType="TextBlock"> <Style TargetType="TextBlock">
<Setter Property="SelectionHighlightColor" Value="Red"/> <Setter Property="SelectionHighlightColor" Value="Red"/>
Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

+3792
View File
File diff suppressed because one or more lines are too long
+3267
View File
File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 241 KiB

BIN
View File
Binary file not shown.