Archived
1
0

Merged PR 26: Submission #6

##Potential 1.0 release candidate. If there is no critical issues will be found this submission will be pushed to public
- Updated package version
- In-video advert now doesn't appear on minimized playback
- Fixed small header appearing on channel page
- Fixed home page loading after in long active sessions
- Optimization, bugfixes and refactoring
- Fixed crash when local watch history reaches 87 entries (now its capacity is 200)
- Added backward navigation to video page
- Improved authentication process
- Removed outdated logo from 'About' page and updated Twitter link
- Updated package version
This commit is contained in:
Michael Gordeev
2019-05-27 18:23:39 +00:00
63 changed files with 1194 additions and 1263 deletions
+2 -1
View File
@@ -258,4 +258,5 @@ paket-files/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
*.pyc
/Src/Trailer
+61 -61
View File
@@ -10,7 +10,6 @@ using System.Xml;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.ApplicationModel.Background;
using Windows.ApplicationModel.Core;
using Windows.Globalization;
using Windows.Storage;
using Windows.System.Power;
@@ -57,7 +56,7 @@ namespace FoxTube
public async void CheckVersion()
{
PackageVersion ver = Package.Current.Id.Version;
if (SettingsStorage.Version != $"{ver.Major}.{ver.Minor}")
if (SettingsStorage.Version != $"{ver.Major}.{ver.Minor}.{ver.Build}")
{
try
{
@@ -68,40 +67,34 @@ namespace FoxTube
ToastNotificationManager.CreateToastNotifier().Show(Background.Notification.GetChangelogToast(e.GetAttribute("version")));
SettingsStorage.Version = $"{ver.Major}.{ver.Minor}";
SettingsStorage.Version = $"{ver.Major}.{ver.Minor}.{ver.Build}";
}
catch
catch (Exception e)
{
Debug.WriteLine("Unable to retrieve changelog");
Analytics.TrackEvent("Unable to retrieve changelog", new Dictionary<string, string>
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message },
{ "App version", $"{ver.Major}.{ver.Minor}.{ver.Revision}.{ver.Build}" }
});
}
}
}
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (!(Window.Current.Content is Frame rootFrame))
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
if (e.PrelaunchActivated == false)
{
if (rootFrame.Content == null)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
// Ensure the current window is active
Window.Current.Activate();
}
@@ -153,7 +146,7 @@ namespace FoxTube
BackgroundTaskRegistration registration = builder.Register();
}
protected async override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
{
var deferral = args.TaskInstance.GetDeferral();
base.OnBackgroundActivated(args);
@@ -167,29 +160,36 @@ namespace FoxTube
case "later":
try
{
if (!SecretsVault.IsAuthorized)
SecretsVault.CheckAuthorization(false);
if (!SecretsVault.IsAuthorized)
throw new Exception("Not authenticated");
PlaylistItem item = new PlaylistItem()
SecretsVault.AuthorizationStateChanged += async (s, e) =>
{
Snippet = new PlaylistItemSnippet()
if ((bool)e[0])
{
ResourceId = new ResourceId()
PlaylistItem item = new PlaylistItem()
{
Kind = "youtube#video",
VideoId = arguments[1]
},
PlaylistId = "WL"
Snippet = new PlaylistItemSnippet()
{
ResourceId = new ResourceId()
{
Kind = "youtube#video",
VideoId = arguments[1]
},
PlaylistId = "WL"
}
};
await SecretsVault.Service.PlaylistItems.Insert(item, "snippet").ExecuteAsync();
}
};
await SecretsVault.Service.PlaylistItems.Insert(item, "snippet").ExecuteAsync();
SecretsVault.CheckAuthorization(false);
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
Analytics.TrackEvent("Failed to add video to WL from toast", new Dictionary<string, string>
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message },
{ "Video ID", arguments[1] }
});
}
break;
}
@@ -211,46 +211,46 @@ namespace FoxTube
}
if (rootFrame.Content == null)
{
rootFrame.Navigate(typeof(MainPage));
}
Window.Current.Activate();
switch (e.Kind)
{
case ActivationKind.Protocol:
break;
case ActivationKind.ToastNotification:
string[] args = (e as ToastNotificationActivatedEventArgs).Argument.Split('|');
switch (args[0])
{
case "changelog":
case "inbox":
Methods.MainPage.GoToDeveloper(args[1]);
break;
case "video":
Methods.MainPage.GoToVideo(args[1]);
break;
case "channel":
Methods.MainPage.GoToChannel(args[1]);
break;
case "download":
Methods.MainPage.GoToDownloads();
break;
case "dcancel":
DownloadAgent.Cancel(args[1]);
break;
}
if (SecretsVault.IsAuthorized)
ProcessToast((e as ToastNotificationActivatedEventArgs).Argument);
else
SecretsVault.AuthorizationStateChanged += (s, arg) => ProcessToast((e as ToastNotificationActivatedEventArgs).Argument);
break;
}
}
void Launch(string e = null)
private void ProcessToast(string arg)
{
string[] args = arg.Split('|');
switch (args[0])
{
case "changelog":
case "inbox":
Methods.MainPage.GoToDeveloper(args[1]);
break;
case "video":
Methods.MainPage.GoToVideo(args[1]);
break;
case "channel":
Methods.MainPage.GoToChannel(args[1]);
break;
case "download":
Methods.MainPage.GoToDownloads();
break;
case "dcancel":
DownloadAgent.Cancel(args[1]);
break;
}
}
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
@@ -265,6 +265,7 @@ namespace FoxTube
sw.Stop();
SettingsStorage.Uptime += sw.Elapsed;
HistorySet.Save();
SettingsStorage.SaveData();
DownloadAgent.QuitPrompt();
Controls.Player.ManifestGenerator.ClearRoaming();
@@ -277,7 +278,6 @@ namespace FoxTube
Analytics.TrackEvent("The app crashed", new Dictionary<string, string>()
{
{ "Exception", e.Exception.GetType().ToString() },
{ "Class", e.ToString() },
{ "Details", e.Message }
});
}
+28 -2
View File
@@ -1,6 +1,32 @@
<?xml version="1.0" encoding="utf-8" ?>
<items>
<item time="2019-04-28" version="0.6">
<item time="2019-05-21" version="0.6.2">
<content>
<en-US>##[Patch #1]
### What's new:
- In-video advert now doesn't appear on minimized playback
- Fixed small header appearing on channel page
- Fixed home page loading in long active sessions
- Optimization and bugfixes
- Fixed crash when local watch history reaches 87 entries (now its capacity is 200)
- Added backward navigation to video page
- Improved authentication process
- Removed outdated logo from 'About' page and updated Twitter link
</en-US>
<ru-RU>##[Патч #1]
### Что нового:
- Теперь реклама в видео не появляется в компактном режиме
- Исправлено появление меленького заголовка на странице канала
- Исправлено отображение видео на домашней странице при долгой активной сессии
- Оптимизация и исправление ошибок
- Исправлен сбой приложения при достижении локальной историей 87 записей (текущая емкость журнала - 200 записей)
- Добавлена обратная навигация для страниц просмотра
- Улучшен процесс аутентификации
- Удален старый логотип с страницы 'О приложении' и обновлена ссылка на Твиттер
</ru-RU>
</content>
</item>
<item time="2019-05-20" version="0.6">
<content>
<en-US>##[Final pre-release version]
### What's new:
@@ -70,7 +96,7 @@ This is the final pre-release minor version. That means that until 1.0 release t
- Fixed header titles
- Some items were moved from menu to header
- Added "Share" button to video cards
- Added "Delete video from playlist" button to video cards on playlist page\
- Added "Delete video from playlist" button to video cards on playlist page
- Improved channel cover quality
- If available, shows localized titles and descriptions (based on "Search relevance language" parameter set in settings)
- Updated russian localization
+22 -14
View File
@@ -6,13 +6,14 @@ using YoutubeExplode.Models.MediaStreams;
using Google.Apis.YouTube.v3.Data;
using FoxTube.Controls;
using FoxTube.Pages;
using Microsoft.AppCenter.Analytics;
namespace FoxTube
{
public static class DownloadAgent
{
public static List<DownloadItem> items = new List<DownloadItem>();
private static ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
public static List<DownloadItem> Items { get; set; } = new List<DownloadItem>();
private static readonly ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
public static Downloads Page { get; set; }
public static StorageFolder Downloads { get; set; }
@@ -21,45 +22,52 @@ namespace FoxTube
Downloads = await KnownFolders.VideosLibrary.CreateFolderAsync("FoxTube", CreationCollisionOption.OpenIfExists);
try
{
List<DownloadItemContainer> containers = JsonConvert.DeserializeObject<List<DownloadItemContainer>>((string)settings.Values["downloads"]);
containers.ForEach(i => items.Add(new DownloadItem(i)));
List<DownloadItemContainer> containers = JsonConvert.DeserializeObject<List<DownloadItemContainer>>((string)settings.Values[$"downloads"]);
containers.ForEach(i => Items.Add(new DownloadItem(i)));
}
catch (Exception e)
{
Analytics.TrackEvent("Failed to load downloads history", new Dictionary<string, string>
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message }
});
}
catch { }
}
public static void Add(MediaStreamInfo info, Video meta, string qualty)
{
items.Insert(0, new DownloadItem(info, meta, qualty));
Items.Insert(0, new DownloadItem(info, meta, qualty));
}
public static void Remove(DownloadItem item)
{
try { Page.Remove(item); }
catch { }
items.Remove(item);
Page?.Remove(item);
Items.Remove(item);
}
public static void Cancel(string id)
{
DownloadItem item = items.Find(i => i.Container.Id == id);
DownloadItem item = Items.Find(i => i.Container.Id == id);
if (item != null)
item.Cancel();
}
public static void QuitPrompt()
{
foreach (DownloadItem i in items.FindAll(i => !i.Container.IsDownloaded))
foreach (DownloadItem i in Items.FindAll(i => !i.Container.IsDownloaded))
{
i.Cancel();
items.Remove(i);
Items.Remove(i);
}
List<DownloadItemContainer> containers = new List<DownloadItemContainer>();
items.ForEach(i => containers.Add(i.Container));
Items.ForEach(i => containers.Add(i.Container));
string data = JsonConvert.SerializeObject(containers);
settings.Values["downloads"] = data;
settings.Values[$"downloads"] = data;
}
}
}
+87
View File
@@ -0,0 +1,87 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using Windows.Storage;
namespace FoxTube
{
public class HistoryItem
{
public string Id { get; set; }
public TimeSpan LeftOn { get; set; } = TimeSpan.FromSeconds(0);
}
public static class HistorySet
{
public static List<HistoryItem> Items { get; set; } = new List<HistoryItem>();
public static void Update(HistoryItem item)
{
if (!SecretsVault.IsAuthorized)
return;
Items.RemoveAll(i => i.Id == item.Id);
Items.Insert(0, item);
if (Items.Count > 200)
Items.RemoveRange(200, Items.Count - 200);
Save();
}
public static void Delete(HistoryItem item)
{
if (!SecretsVault.IsAuthorized)
return;
Items.Remove(item);
Save();
}
public static void Clear()
{
if (!SecretsVault.IsAuthorized)
return;
Items.Clear();
Save();
}
public static void Save()
{
List<HistoryItem>[] parts = new List<HistoryItem>[4]
{
new List<HistoryItem>(), new List<HistoryItem>(),
new List<HistoryItem>(), new List<HistoryItem>()
};
foreach(HistoryItem i in Items)
if (parts[0].Count < 50)
parts[0].Add(i);
else
{
if (parts[1].Count < 50)
parts[1].Add(i);
else
{
if (parts[2].Count < 50)
parts[2].Add(i);
else
parts[3].Add(i);
}
}
for(int k = 0; k < parts.Length; k++)
ApplicationData.Current.RoamingSettings.Values[$"history-{SecretsVault.AccountId}-level{k}"] = JsonConvert.SerializeObject(parts[k]);
}
public static void Load()
{
try
{
for (int k = 0; k < 4; k++)
Items.AddRange(JsonConvert.DeserializeObject<List<HistoryItem>>(ApplicationData.Current.RoamingSettings.Values[$"history-{SecretsVault.AccountId}-level{k}"] as string));
}
catch { }
}
}
}
+3 -3
View File
@@ -3,7 +3,7 @@ using Windows.ApplicationModel.Resources;
namespace FoxTube.Classes
{
public enum InboxItemType { Default, PatchNote}
public enum InboxItemType { Default, PatchNote }
public class InboxItem
{
@@ -59,11 +59,11 @@ namespace FoxTube.Classes
}
public InboxItem(string version, string content, string timeStamp)
public InboxItem(string version, string content, DateTime timeStamp)
{
Type = InboxItemType.PatchNote;
Content = content;
TimeStamp = DateTime.Parse(timeStamp);
TimeStamp = timeStamp;
Id = version;
}
+12 -10
View File
@@ -1,6 +1,7 @@
using AngleSharp.Dom.Html;
using AngleSharp.Parser.Html;
using Google.Apis.YouTube.v3.Data;
using Microsoft.AppCenter.Analytics;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
@@ -23,17 +24,11 @@ namespace FoxTube.Controls.Player
public static async Task<Uri> GetManifest(Video meta, VideoStreamInfo requestedQuality, MediaStreamInfoSet list)
{
StorageFile manifest;
try
{
manifest = await roaming.CreateFileAsync("manifest.mpd", CreationCollisionOption.ReplaceExisting);
}
catch
{
manifest = await roaming.CreateFileAsync("manifest.mpd", CreationCollisionOption.GenerateUniqueName);
}
try
{
try { manifest = await roaming.CreateFileAsync("manifest.mpd", CreationCollisionOption.ReplaceExisting); }
catch { manifest = await roaming.CreateFileAsync("manifest.mpd", CreationCollisionOption.GenerateUniqueName); }
try
{
XmlDocument doc = new XmlDocument();
XmlElement mpd = doc.CreateElement("MPD");
@@ -75,6 +70,13 @@ namespace FoxTube.Controls.Player
}
catch (Exception e)
{
Analytics.TrackEvent("Failed to generate manifest", new Dictionary<string, string>
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message },
{ "Video ID", meta.Id },
{ "Requested quality", requestedQuality.VideoQualityLabel }
});
return null;
}
}
+30 -80
View File
@@ -1,4 +1,5 @@
using FoxTube.Pages;
using Microsoft.AppCenter.Analytics;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
@@ -10,7 +11,6 @@ using System.Xml;
using Windows.ApplicationModel.Core;
using Windows.ApplicationModel.DataTransfer;
using Windows.ApplicationModel.Resources;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.System;
using Windows.UI.Xaml;
@@ -21,64 +21,21 @@ using YoutubeExplode.Models.MediaStreams;
namespace FoxTube
{
public interface NavigationPage
public delegate void Event();
public delegate void ObjectEventHandler(object sender = null, params object[] args);
public interface INavigationPage
{
object Parameter { get; set; }
}
public class HistoryItem
{
public string Id { get; set; }
public TimeSpan LeftOn { get; set; } = TimeSpan.FromSeconds(0);
}
public static class HistorySet
{
public static List<HistoryItem> Items { get; set; } = new List<HistoryItem>();
public static void Update(HistoryItem item)
{
if(Items.Exists(i => i.Id == item.Id))
Items.RemoveAll(i => i.Id == item.Id);
Items.Insert(0, item);
Save();
}
public static void Delete(HistoryItem item)
{
Items.Remove(item);
Save();
}
public static void Clear()
{
Items.Clear();
Save();
}
private static void Save()
{
try { ApplicationData.Current.RoamingSettings.Values[$"history-{SecretsVault.AccountId}"] = JsonConvert.SerializeObject(Items); }
catch { }
}
public static void Load()
{
if (ApplicationData.Current.RoamingSettings.Values[$"history-{SecretsVault.AccountId}"] != null)
Items = JsonConvert.DeserializeObject<List<HistoryItem>>(ApplicationData.Current.RoamingSettings.Values[$"history-{SecretsVault.AccountId}"] as string);
}
}
public static class Methods
{
private static ResourceLoader resources = ResourceLoader.GetForCurrentView("Methods");
public static CommentsPage CommentsPage { get; set; }
private static readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("Methods");
public static MainPage MainPage
{
get { return (Window.Current.Content as Frame).Content as MainPage; }
}
public static CommentsPage CommentsPage { get; set; }
public static MainPage MainPage => (Window.Current.Content as Frame).Content as MainPage;
public static void CloseApp()
{
@@ -87,20 +44,15 @@ namespace FoxTube
public static Uri ToUri(this string url)
{
if (string.IsNullOrWhiteSpace(url))
return null;
else
return new Uri(url);
return string.IsNullOrWhiteSpace(url) ? null : new Uri(url);
}
public static string GuardFromNull(string str)
{
if (string.IsNullOrWhiteSpace(str))
return string.Empty;
else
return str;
return str ?? string.Empty;
}
[Obsolete]
public static string GetChars(this string str, int count)
{
try
@@ -140,6 +92,7 @@ namespace FoxTube
return array.ToList().FindAll(match);
}
[Obsolete]
public static string ReplaceInvalidChars(this string str, char newValue)
{
foreach (char i in Path.GetInvalidFileNameChars())
@@ -147,6 +100,7 @@ namespace FoxTube
return str;
}
[Obsolete]
public static string Last(this string[] arr)
{
return arr[arr.Length - 1];
@@ -161,13 +115,18 @@ namespace FoxTube
catch (FormatException)
{
TimeSpan time = XmlConvert.ToTimeSpan("PT" + str.Split('T')[1]);
TimeSpan date = TimeSpan.FromDays(int.Parse(str.Split('W')[0].Replace("P", "")) * 7);
TimeSpan date = TimeSpan.FromDays(int.Parse(str.Split('W')[0].Remove('P')) * 7);
date.Add(time);
return date;
}
catch
catch (Exception e)
{
Analytics.TrackEvent("Failed to parse duration", new Dictionary<string, string>
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message }
});
return TimeSpan.FromMilliseconds(0);
}
}
@@ -204,7 +163,7 @@ namespace FoxTube
return Math.Round(span.TotalDays / 365) + " " + resources.GetString("/Methods/years");
}
public static void FormatText(ref TextBlock block, string text)
public static void FormatText(this TextBlock block, string text)
{
block.Inlines.Clear();
Regex filter = new Regex(@"\b((?:https?://|www\.)\S+)|(\S+@\S+)\b", RegexOptions.IgnoreCase);
@@ -268,10 +227,9 @@ namespace FoxTube
public async static void ProcessLink(string url)
{
string output;
string type;
if (YoutubeClient.TryParseChannelId(url, out output))
if (YoutubeClient.TryParseChannelId(url, out string output))
{
type = "channel";
goto LinkFound;
@@ -295,7 +253,7 @@ namespace FoxTube
await Launcher.LaunchUriAsync(new Uri(url));
return;
LinkFound:
LinkFound:
switch (type)
{
case "channel":
@@ -313,32 +271,23 @@ namespace FoxTube
}
}
public static async void Share(DataRequestedEventArgs args, string thumbnail, string title, string url, string type)
public static void Share(DataRequestedEventArgs args, string thumbnail, string title, string url, string type)
{
DataRequest request = args.Request;
request.Data.Properties.Title = title;
request.Data.Properties.Description = $"{resources.GetString("/Methods/sharing")} {type}";
request.Data.SetText(title + "\n" + "#YouTube #FoxTube #SharedWithFoxTube");
request.Data.SetWebLink(url.ToUri());
DataRequestDeferral deferral = request.GetDeferral();
try
{
StorageFile thumbnailFile = await StorageFile.CreateStreamedFileFromUriAsync("tempThumb.jpg", thumbnail.ToUri(), null);
request.Data.Properties.Thumbnail = RandomAccessStreamReference.CreateFromFile(thumbnailFile);
StorageFile imageFile = thumbnailFile;
request.Data.SetBitmap(RandomAccessStreamReference.CreateFromFile(imageFile));
}
finally
{
deferral.Complete();
}
request.Data.Properties.Thumbnail = RandomAccessStreamReference.CreateFromUri(thumbnail.ToUri());
request.Data.SetBitmap(RandomAccessStreamReference.CreateFromUri(thumbnail.ToUri()));
}
public static async Task<List<string>> GetHistory()
{
SecretsVault.RefreshToken();
List<string> list = new List<string>();
string output = await SecretsVault.HttpClient.GetStringAsync($"https://www.youtube.com/list_ajax?style=json&action_get_list=1&list=HL&hl={SettingsStorage.RelevanceLanguage}");
@@ -352,6 +301,7 @@ namespace FoxTube
public static async Task<List<string>> GetLater()
{
SecretsVault.RefreshToken();
List<string> list = new List<string>();
string output = await SecretsVault.HttpClient.GetStringAsync($"https://www.youtube.com/list_ajax?style=json&action_get_list=1&list=WL&hl={SettingsStorage.RelevanceLanguage}");
-4
View File
@@ -4,10 +4,6 @@ using static Google.Apis.YouTube.v3.SearchResource.ListRequest;
namespace FoxTube
{
public delegate void Event();
public delegate void ObjectEventHandler(object sender = null, params object[] args);
public class SearchParameters
{
public class Filters
+58 -52
View File
@@ -13,6 +13,7 @@ using System.Net.Http;
using Google.Apis.Oauth2.v2.Data;
using Google.Apis.Oauth2.v2;
using static Google.Apis.Auth.OAuth2.UwpCodeReceiver;
using Microsoft.AppCenter.Analytics;
namespace FoxTube
{
@@ -24,7 +25,7 @@ namespace FoxTube
public static event ObjectEventHandler SubscriptionsChanged;
public static event ObjectEventHandler Purchased; //Rising when app finds out that it's not a PRO version
//Private properties
//Properties
private static ClientSecrets Secrets => new ClientSecrets()
{
ClientId = "349735264870-2ekqlm0a4mkg3mmrfcv90s3qp3o15dq0.apps.googleusercontent.com",
@@ -41,15 +42,16 @@ namespace FoxTube
ApplicationName = "FoxTube"
};
public static YouTubeService Service => IsAuthorized ? new YouTubeService(Initializer) : NoAuthService;
public static HttpClient HttpClient { get; } = new HttpClient();
private static bool TestAds => true; //Change this bool
private static bool TestAds => true; //TODO: Change this bool
public static string AppId => TestAds ? "d25517cb-12d4-4699-8bdc-52040c712cab" : "9ncqqxjtdlfh";
public static string AdUnitId => TestAds ? "test" : "1100044398";
public static bool AdsDisabled { get; private set; } = true;
//User info
public static bool IsAuthorized => Credential != null;
private static UserCredential Credential { get; set; }
private static UserCredential Credential { get; set; } = null;
public static string AccountId => UserChannel?.Id;
public static Channel UserChannel { get; private set; }
@@ -61,6 +63,11 @@ namespace FoxTube
#endregion
#region Methods
public static void RefreshToken()
{
Credential?.RefreshTokenAsync(CancellationToken.None);
}
/// <summary>
/// Subscribes or unsibscribes authorized user from the channel
/// </summary>
@@ -68,9 +75,6 @@ namespace FoxTube
/// <returns>Returns 'true' if channel is in subscriptions now; 'false' if it's not</returns>
public static async Task<bool> ChangeSubscriptionState(string id)
{
if (!IsAuthorized)
return false;
if(Subscriptions.Exists(x => x.Snippet.ResourceId.ChannelId == id))
{
Subscription s = Subscriptions.Find(x => x.Snippet.ResourceId.ChannelId == id);
@@ -78,7 +82,7 @@ namespace FoxTube
try { await Service.Subscriptions.Delete(s.Id).ExecuteAsync(); }
catch { return true; }
SubscriptionsChanged?.Invoke(null, "remove", s);
SubscriptionsChanged?.Invoke(null, "remove", s.Snippet.ResourceId.ChannelId);
Subscriptions.Remove(s);
return false;
}
@@ -124,19 +128,19 @@ namespace FoxTube
try
{
Credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
Secrets,
new[]
{
Oauth2Service.Scope.UserinfoProfile,
Oauth2Service.Scope.UserinfoEmail,
YouTubeService.Scope.YoutubeForceSsl,
YouTubeService.Scope.Youtube,
YouTubeService.Scope.YoutubeUpload,
YouTubeService.Scope.YoutubeReadonly,
YouTubeService.Scope.Youtubepartner
},
"user",
CancellationToken.None);
Secrets,
new[]
{
Oauth2Service.Scope.UserinfoProfile,
Oauth2Service.Scope.UserinfoEmail,
YouTubeService.Scope.YoutubeForceSsl,
YouTubeService.Scope.Youtube,
YouTubeService.Scope.YoutubeUpload,
YouTubeService.Scope.YoutubeReadonly,
YouTubeService.Scope.Youtubepartner
},
"user",
CancellationToken.None);
await Credential.RefreshTokenAsync(CancellationToken.None);
}
@@ -145,15 +149,19 @@ namespace FoxTube
if (e.Message.Contains("UserCancel"))
return;
else
throw e;
Analytics.TrackEvent("Failed to authorize", new Dictionary<string, string>
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message }
});
}
if (Credential == null || !retrieveSubs)
return;
SettingsStorage.HasAccount = true;
HttpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Credential.Token.AccessToken);
SettingsStorage.HasAccount = true;
#endregion
try
@@ -161,12 +169,8 @@ namespace FoxTube
#region Retrieving user's data
UserInfo = await new Oauth2Service(Initializer).Userinfo.Get().ExecuteAsync();
try
{
WatchLater = await Methods.GetLater();
History = await Methods.GetHistory();
}
catch { }
WatchLater = await Methods.GetLater();
History = await Methods.GetHistory();
SubscriptionsResource.ListRequest subRequest = Service.Subscriptions.List("snippet");
subRequest.Mine = true;
@@ -196,9 +200,14 @@ namespace FoxTube
AuthorizationStateChanged?.Invoke(args: true);
}
catch
catch (Exception e)
{
AuthorizationStateChanged?.Invoke(args: new bool?[] { null });
Analytics.TrackEvent("Failed to retrieve user's info", new Dictionary<string, string>
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message }
});
}
}
@@ -217,14 +226,19 @@ namespace FoxTube
/// </summary>
public static async void Deauthenticate()
{
if(await Credential.RevokeTokenAsync(CancellationToken.None))
{
Credential = null;
AuthorizationStateChanged?.Invoke(args: false);
SettingsStorage.HasAccount = false;
if (!await Credential.RevokeTokenAsync(CancellationToken.None))
return;
Credential = null;
UserChannel = null;
UserInfo = null;
History.Clear();
WatchLater.Clear();
Subscriptions.Clear();
ApplicationData.Current.RoamingSettings.Values["subscriptions"] = "";
ApplicationData.Current.RoamingSettings.Values["subscriptions"] = "";
}
AuthorizationStateChanged?.Invoke(args: false);
SettingsStorage.HasAccount = false;
}
/// <summary>
@@ -249,32 +263,24 @@ namespace FoxTube
{
try
{
StoreContext store = StoreContext.GetDefault();
StoreProductQueryResult requset = await store.GetAssociatedStoreProductsAsync(new[] { "Durable" });
Dictionary<string, StoreProduct> l = new Dictionary<string, StoreProduct>();
requset.Products.ForEach(i => l.Add(i.Key, i.Value));
StoreProductQueryResult requset = await StoreContext.GetDefault().GetAssociatedStoreProductsAsync(new[] { "Durable" });
if (!requset.Products["9NP1QK556625"].IsInUserCollection)
{
AdsDisabled = false;
Purchased?.Invoke(null, false, requset.Products["9NP1QK556625"].Price.FormattedPrice);
}
if (requset.Products["9NP1QK556625"].IsInUserCollection)
return;
AdsDisabled = false;
Purchased?.Invoke(null, false, requset.Products["9NP1QK556625"].Price.FormattedPrice);
}
catch { }
}
public static async void GetAdblock()
{
StoreContext store = StoreContext.GetDefault();
StorePurchaseResult request = await store.RequestPurchaseAsync("9NP1QK556625");
StorePurchaseResult request = await StoreContext.GetDefault().RequestPurchaseAsync("9NP1QK556625");
switch (request.Status)
{
case StorePurchaseStatus.AlreadyPurchased:
Purchased?.Invoke(args: true);
AdsDisabled = true;
break;
case StorePurchaseStatus.Succeeded:
Purchased?.Invoke(args: true);
AdsDisabled = true;
+11 -4
View File
@@ -1,5 +1,7 @@
using Newtonsoft.Json;
using Microsoft.AppCenter.Analytics;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Windows.ApplicationModel;
@@ -221,7 +223,7 @@ namespace FoxTube
//Settings storage
private static readonly ApplicationDataContainer storage = ApplicationData.Current.RoamingSettings;
private static SettingsContainer Container;
private static SettingsContainer Container = new SettingsContainer();
public static void LoadData()
{
@@ -229,10 +231,15 @@ namespace FoxTube
{
Container = JsonConvert.DeserializeObject<SettingsContainer>(storage.Values["settings"] as string);
}
catch
catch (Exception e)
{
Container = new SettingsContainer();
SaveData();
if (storage.Values["settings"] != null)
Analytics.TrackEvent("Failed to retrieve settings", new Dictionary<string, string>
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message }
});
}
}
+1 -8
View File
@@ -13,21 +13,14 @@ namespace FoxTube.Controls.Adverts
{
NativeAdsManagerV2 manager = new NativeAdsManagerV2(SecretsVault.AppId, SecretsVault.AdUnitId);
public NativeAdV2 advert;
public CardAdvert(bool isOnVideoPage = false)
public CardAdvert()
{
InitializeComponent();
if(!isOnVideoPage)
MainPage.VideoPageSizeChanged += Methods_VideoPageSizeChanged;
manager.AdReady += AdReady;
manager.ErrorOccurred += ErrorOccurred;
manager.RequestAd();
}
private void Methods_VideoPageSizeChanged(object sender = null, params object[] args)
{
Visibility = !(bool)args[0] && advert != null ? Visibility.Visible : Visibility.Collapsed;
}
private void ErrorOccurred(object sender, NativeAdErrorEventArgs e)
{
System.Diagnostics.Debug.WriteLine("Error has occured while loading ad");
+6 -10
View File
@@ -9,22 +9,18 @@
VerticalAlignment="Top"
d:DesignHeight="290"
d:DesignWidth="384"
MaxWidth="700"
Opacity="0"
Name="card">
Name="card"
SizeChanged="Card_SizeChanged">
<UserControl.Resources>
<Storyboard x:Name="show">
<DoubleAnimation Storyboard.TargetName="card" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
<Storyboard x:Name="hide">
<DoubleAnimation Storyboard.TargetName="card" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
<Storyboard x:Name="showThumb">
<DoubleAnimation Storyboard.TargetName="cover" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
<Storyboard x:Name="hideThumb">
<DoubleAnimation Storyboard.TargetName="cover" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
</UserControl.Resources>
<Button Padding="0" Margin="1" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" Click="Button_Click">
@@ -61,13 +57,13 @@
</StackPanel>
</Grid>
<TextBlock Height="75" Name="description" Grid.Row="2" Margin="10" Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin molestie vulputate leo, sed faucibus ex rutrum nec. Donec quis diam nisi. Suspendisse sollicitudin sapien quis eros vulputate, sed scelerisque enim ullamcorper. Donec vulputate commodo mi, vel vestibulum quam posuere ac. Curabitur ac nunc augue. Phasellus aliquam neque ac condimentum bibendum." TextWrapping="WrapWholeWords" TextTrimming="CharacterEllipsis"/>
<TextBlock Height="75" Name="description" Grid.Row="2" MaxLines="3" Margin="10,0" Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin molestie vulputate leo, sed faucibus ex rutrum nec. Donec quis diam nisi. Suspendisse sollicitudin sapien quis eros vulputate, sed scelerisque enim ullamcorper. Donec vulputate commodo mi, vel vestibulum quam posuere ac. Curabitur ac nunc augue. Phasellus aliquam neque ac condimentum bibendum." TextWrapping="WrapWholeWords" TextTrimming="CharacterEllipsis"/>
<TextBlock Grid.Row="3" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Height="50" Margin="10" TextAlignment="Center" Padding="0,16,0,0" Foreground="Gray">
<TextBlock Grid.Row="3" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Height="50" Margin="5" TextAlignment="Center" Padding="0,16,0,0" Foreground="Gray">
<Hyperlink Click="Hyperlink_Click"><Run x:Uid="/Cards/login">Log in</Run></Hyperlink> <Run x:Uid="/Cards/tomanage">to manage your subscriptions</Run>
</TextBlock>
<Grid Visibility="Collapsed" Grid.Row="3" VerticalAlignment="Stretch" Margin="10" Name="subscriptionPane" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
<Grid Visibility="Collapsed" Grid.Row="3" VerticalAlignment="Stretch" Margin="5" Name="subscriptionPane" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
<Button x:Uid="/Cards/subscribe" VerticalAlignment="Stretch" Click="subscribe_Click" Name="subscribe" HorizontalAlignment="Stretch" Height="50" Background="Red" Foreground="White" FontSize="18" FontWeight="SemiBold" Content="Subscirbe" Margin="0,0,0,0"/>
<ToggleButton Name="notify" Height="50" Width="50" Visibility="Collapsed" FontFamily="Segoe MDL2 Assets" FontSize="18" FontWeight="SemiBold" Content="&#xE7ED;" Foreground="White" Background="Red" HorizontalAlignment="Right"/>
</Grid>
+5
View File
@@ -127,5 +127,10 @@ namespace FoxTube.Controls
{
showThumb.Begin();
}
private void Card_SizeChanged(object sender, SizeChangedEventArgs e)
{
Height = e.NewSize.Width * 0.75;
}
}
}
+1 -1
View File
@@ -28,7 +28,7 @@ namespace FoxTube.Controls
VerticalAlignment = VerticalAlignment.Top,
TextWrapping = TextWrapping.WrapWholeWords
};
Methods.FormatText(ref block, message.Snippet.DisplayMessage);
block.FormatText(message.Snippet.DisplayMessage);
return block;
}
}
+2 -2
View File
@@ -71,7 +71,7 @@ namespace FoxTube.Controls
author.Text = comment.Snippet.TopLevelComment.Snippet.AuthorDisplayName;
meta.Text = string.Format("{0} {1}", Methods.GetAgo(comment.Snippet.TopLevelComment.Snippet.PublishedAt.Value), comment.Snippet.TopLevelComment.Snippet.UpdatedAt != comment.Snippet.TopLevelComment.Snippet.PublishedAt ? resources.GetString("/CommentsPage/edited") : "");
Methods.FormatText(ref text, comment.Snippet.TopLevelComment.Snippet.TextDisplay);
text.FormatText(comment.Snippet.TopLevelComment.Snippet.TextDisplay);
try { avatar.ProfilePicture = new BitmapImage(new Uri(comment.Snippet.TopLevelComment.Snippet.AuthorProfileImageUrl)) { DecodePixelWidth = 50, DecodePixelHeight = 50 }; }
catch { }
@@ -127,7 +127,7 @@ namespace FoxTube.Controls
author.Text = comment.Snippet.AuthorDisplayName;
meta.Text = string.Format("{0} {1}", Methods.GetAgo(comment.Snippet.PublishedAt.Value), comment.Snippet.UpdatedAt != comment.Snippet.PublishedAt ? resources.GetString("/CommentsPage/edited") : "");
Methods.FormatText(ref text, comment.Snippet.TextDisplay);
text.FormatText(comment.Snippet.TextDisplay);
try { avatar.ProfilePicture = new BitmapImage(new Uri(comment.Snippet.AuthorProfileImageUrl)) { DecodePixelWidth = 50, DecodePixelHeight = 50 }; }
catch { }
+14
View File
@@ -0,0 +1,14 @@
<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"
xmlns:foxtube="using:FoxTube"
mc:Ignorable="d">
<Grid>
<Frame x:Name="content" Navigating="Content_Navigating"/>
<foxtube:LoadingPage x:Name="loading" RefreshPage="Loading_RefreshPage" Visibility="Collapsed"/>
</Grid>
</UserControl>
+35
View File
@@ -0,0 +1,35 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace FoxTube.Controls
{
public sealed partial class ContentFrame : UserControl
{
public Frame Frame => content;
public LoadingPage LoadingPage => loading;
public event NavigatedEventHandler Navigated;
public ContentFrame()
{
InitializeComponent();
content.Navigated += (s, e) => Navigated?.Invoke(s, e);
}
private void Content_Navigating(object sender, NavigatingCancelEventArgs e)
{
loading.Refresh();
}
private void Loading_RefreshPage(object sender, RoutedEventArgs e)
{
content.Navigate(content.CurrentSourcePageType, (content.Content as INavigationPage).Parameter);
content.BackStack.RemoveAt(content.BackStack.Count - 1);
}
public void Refresh()
{
Loading_RefreshPage(this, null);
}
}
}
-1
View File
@@ -9,7 +9,6 @@ using YoutubeExplode;
using Windows.Storage;
using Google.Apis.YouTube.v3.Data;
using System.Threading;
using System.Xml;
using Windows.UI.Popups;
using Windows.UI.Notifications;
using Microsoft.Toolkit.Uwp.Notifications;
+8 -21
View File
@@ -412,25 +412,6 @@ namespace FoxTube
{
ClosedCaptions = await new YoutubeClient().GetVideoClosedCaptionTrackInfosAsync(meta.Id);
/*foreach (MuxedStreamInfo i in MediaStreams.Muxed)
quality.Items.Add(new ComboBoxItem
{
Content = $"{i.VideoQualityLabel} (muxed)",
Tag = i
});
foreach (VideoStreamInfo i in MediaStreams.Video)
quality.Items.Add(new ComboBoxItem
{
Content = $"{i.VideoQualityLabel} (video-only)",
Tag = i
});
foreach (AudioStreamInfo i in MediaStreams.Audio)
quality.Items.Add(new ComboBoxItem
{
Content = $"{i.Bitrate} (audio-only)",
Tag = i
});*/
uint screenHeight = DisplayInformation.GetForCurrentView().ScreenHeightInRawPixels;
List<string> qualityList = MediaStreams.GetAllVideoQualityLabels().ToList();
@@ -461,8 +442,8 @@ namespace FoxTube
string s = SettingsStorage.VideoQuality == "remember" ? SettingsStorage.RememberedQuality : SettingsStorage.VideoQuality;
if (quality.Items.Any(i => (i as ComboBoxItem).Content as string == s))
quality.SelectedItem = quality.Items.Find(i => (i as ComboBoxItem).Content as string == s);
if (quality.Items.Any(i => ((i as ComboBoxItem).Content as string).Contains(s)))
quality.SelectedItem = quality.Items.Find(i => ((i as ComboBoxItem).Content as string).Contains(s));
else
quality.SelectedIndex = 0;
@@ -518,5 +499,11 @@ namespace FoxTube
quality.SelectedIndex = 0;
}
}
public void PushAdvert()
{
if(State == PlayerDisplayState.Normal)
Advert.PushAdvert();
}
}
}
+1 -15
View File
@@ -6,20 +6,6 @@ using Google.Apis.YouTube.v3.Data;
using Windows.UI.Xaml.Media.Imaging;
using Windows.Media;
using Windows.Storage.Streams;
using YoutubeExplode.Models.MediaStreams;
using YoutubeExplode;
using System.IO;
using FoxTube.Classes;
using Windows.Media.Core;
using System.Linq;
using Windows.Media.Playback;
using System.Threading.Tasks;
using Windows.Media.Editing;
using Windows.Storage.Pickers;
using Windows.Storage;
using Windows.Media.MediaProperties;
using FoxTube.Controls.Player;
using System.Diagnostics;
using Windows.UI.Xaml.Media;
namespace FoxTube
@@ -180,7 +166,7 @@ namespace FoxTube
private void VideoSource_MarkerReached(object sender, TimelineMarkerRoutedEventArgs e)
{
Controls.Advert.PushAdvert();
Controls.PushAdvert();
}
}
}
+3 -8
View File
@@ -4,28 +4,23 @@
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:Windows10version1809="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 7)"
mc:Ignorable="d"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
d:DesignHeight="290"
d:DesignWidth="384"
MaxWidth="700"
Opacity="0"
Name="card">
Name="card"
SizeChanged="Card_SizeChanged">
<UserControl.Resources>
<Storyboard x:Name="show">
<DoubleAnimation Storyboard.TargetName="card" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
<Storyboard x:Name="hide">
<DoubleAnimation Storyboard.TargetName="card" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
<Storyboard x:Name="showThumb">
<DoubleAnimation Storyboard.TargetName="thumbnail" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
<Storyboard x:Name="hideThumb">
<DoubleAnimation Storyboard.TargetName="thumbnail" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
</UserControl.Resources>
<Button Padding="0" Margin="1" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" Click="Button_Click">
+7 -4
View File
@@ -45,10 +45,8 @@ namespace FoxTube.Controls
ChannelsResource.ListRequest r = SecretsVault.Service.Channels.List("snippet");
r.Id = item.Snippet.ChannelId;
try { thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); }
catch { }
try { avatar.ProfilePicture = new BitmapImage(new Uri((await r.ExecuteAsync()).Items[0].Snippet.Thumbnails.Medium.Url)) { DecodePixelWidth = 46, DecodePixelHeight = 46 }; }
catch { }
thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri());
avatar.ProfilePicture = new BitmapImage(new Uri((await r.ExecuteAsync()).Items[0].Snippet.Thumbnails.Medium.Url)) { DecodePixelWidth = 46, DecodePixelHeight = 46 };
show.Begin();
}
@@ -90,5 +88,10 @@ namespace FoxTube.Controls
{
showThumb.Begin();
}
private void Card_SizeChanged(object sender, SizeChangedEventArgs e)
{
Height = e.NewSize.Width * 0.75;
}
}
}
+3 -7
View File
@@ -9,22 +9,18 @@
VerticalAlignment="Top"
d:DesignHeight="290"
d:DesignWidth="384"
MaxWidth="700"
Opacity="0"
Name="card">
Name="card"
SizeChanged="Card_SizeChanged">
<UserControl.Resources>
<Storyboard x:Name="show">
<DoubleAnimation Storyboard.TargetName="card" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
<Storyboard x:Name="hide">
<DoubleAnimation Storyboard.TargetName="card" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
<Storyboard x:Name="showThumb">
<DoubleAnimation Storyboard.TargetName="thumbnail" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
<Storyboard x:Name="hideThumb">
<DoubleAnimation Storyboard.TargetName="thumbnail" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
</UserControl.Resources>
<Button Padding="0" Margin="1" Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}" Click="Button_Click">
+102 -53
View File
@@ -14,6 +14,7 @@ using Windows.UI.Popups;
using YoutubeExplode.Models.MediaStreams;
using Windows.Foundation;
using FoxTube.Pages;
using Windows.Networking.Connectivity;
namespace FoxTube.Controls
{
@@ -25,7 +26,6 @@ namespace FoxTube.Controls
ResourceLoader resources = ResourceLoader.GetForCurrentView("Cards");
public string playlistId;
public string videoId;
Video item;
HistoryItem history;
@@ -48,9 +48,7 @@ namespace FoxTube.Controls
{
try
{
videoId = id;
playlistId = playlist;
delete.Visibility = string.IsNullOrWhiteSpace(playlistId) ? Visibility.Collapsed : Visibility.Visible;
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("snippet,contentDetails,statistics,liveStreamingDetails");
@@ -100,12 +98,10 @@ namespace FoxTube.Controls
}
LoadAddTo();
try { thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); }
catch { }
try { avatar.ProfilePicture = new BitmapImage((await new YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelWidth = 46, DecodePixelHeight = 46 }; }
catch { }
thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri());
avatar.ProfilePicture = new BitmapImage((await new YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelWidth = 46, DecodePixelHeight = 46 };
if (SecretsVault.History.Contains(videoId))
if (SecretsVault.History.Contains(item.Id))
watched.Visibility = Visibility.Visible;
if (HistorySet.Items.Exists(i => i.Id == item.Id))
{
@@ -123,7 +119,7 @@ namespace FoxTube.Controls
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message },
{ "Video ID", videoId }
{ "Video ID", item.Id }
});
}
}
@@ -132,7 +128,7 @@ namespace FoxTube.Controls
{
try
{
MediaStreamInfoSet infoSet = await new YoutubeClient().GetVideoMediaStreamInfosAsync(videoId);
MediaStreamInfoSet infoSet = await new YoutubeClient().GetVideoMediaStreamInfosAsync(item.Id);
foreach (MuxedStreamInfo i in infoSet.Muxed)
{
MenuFlyoutItem menuItem = new MenuFlyoutItem()
@@ -165,10 +161,16 @@ namespace FoxTube.Controls
public async void LoadMeta()
{
videoId = item.Id;
title.Text = item.Snippet.Title;
channelName.Text = item.Snippet.ChannelTitle;
if (item.Snippet.Title == "Deleted video")
{
ContextFlyout = null;
show.Begin();
return;
}
if (item.Snippet.LiveBroadcastContent == "live")
{
views.Text = $"{item.LiveStreamingDetails.ConcurrentViewers:0,0} {resources.GetString("/Cards/viewers")}";
@@ -197,12 +199,10 @@ namespace FoxTube.Controls
info.Text = $"{item.ContentDetails.Duration.GetDuration()} | {Methods.GetAgo(item.Snippet.PublishedAt.Value)}";
}
try { thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); }
catch { }
try { avatar.ProfilePicture = new BitmapImage((await new YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelHeight = 50, DecodePixelWidth = 50 }; }
catch { }
thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri());
avatar.ProfilePicture = new BitmapImage((await new YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelHeight = 50, DecodePixelWidth = 50 };
if (SecretsVault.History.Contains(videoId))
if (SecretsVault.History.Contains(item.Id))
watched.Visibility = Visibility.Visible;
if (HistorySet.Items.Exists(i => i.Id == item.Id))
leftOn.Value = 100 * HistorySet.Items.Find(i => i.Id == item.Id).LeftOn.TotalSeconds / Methods.GetDuration(item.ContentDetails.Duration).TotalSeconds;
@@ -244,7 +244,44 @@ namespace FoxTube.Controls
}
}
Methods.MainPage.GoToVideo(videoId, playlistId == "HL" ? null : playlistId, ((FrameworkElement)sender).Name == "incognito" ? true : false);
ConnectionCost connection = NetworkInformation.GetInternetConnectionProfile().GetConnectionCost();
if (SettingsStorage.CheckConnection && (connection.NetworkCostType == NetworkCostType.Fixed || connection.NetworkCostType == NetworkCostType.Variable))
{
MessageDialog dialog = new MessageDialog(resources.GetString("/Main/metered"))
{
DefaultCommandIndex = 2,
CancelCommandIndex = 1
};
dialog.Commands.Add(new UICommand(resources.GetString("/Main/yes"), null, false));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/no"), null, true));
if(SecretsVault.IsAuthorized)
dialog.Commands.Add(new UICommand(resources.GetString("/Main/metered"), async (command) =>
{
try
{
PlaylistItem playlistItem = new PlaylistItem()
{
Snippet = new PlaylistItemSnippet()
{
ResourceId = new ResourceId()
{
Kind = "youtube#video",
VideoId = item.Id
},
PlaylistId = "WL"
}
};
await SecretsVault.Service.PlaylistItems.Insert(playlistItem, "snippet").ExecuteAsync();
}
catch { }
}, true));
if ((bool)(await dialog.ShowAsync()).Id)
return;
}
Methods.MainPage.GoToVideo(item.Id, playlistId == "HL" ? null : playlistId, ((FrameworkElement)sender).Name == "incognito" ? true : false);
}
private void Share(DataTransferManager sender, DataRequestedEventArgs args)
@@ -252,7 +289,7 @@ namespace FoxTube.Controls
Methods.Share(args,
item.Snippet.Thumbnails.Medium.Url,
item.Snippet.Title,
$"https://www.youtube.com/watch?v={videoId}",
$"https://www.youtube.com/watch?v={item.Id}",
resources.GetString("/Cards/videoShare"));
}
@@ -270,13 +307,13 @@ namespace FoxTube.Controls
private void GetLink_Click(object sender, RoutedEventArgs e)
{
DataPackage data = new DataPackage();
data.SetText($"https://www.youtube.com/watch?v={videoId}");
data.SetText($"https://www.youtube.com/watch?v={item.Id}");
Clipboard.SetContent(data);
}
private async void InBrowser_Click(object sender, RoutedEventArgs e)
{
await Launcher.LaunchUriAsync($"https://www.youtube.com/watch?v={videoId}".ToUri());
await Launcher.LaunchUriAsync($"https://www.youtube.com/watch?v={item.Id}".ToUri());
}
private void Thumbnail_ImageOpened(object sender, RoutedEventArgs e)
@@ -401,39 +438,46 @@ namespace FoxTube.Controls
async void LoadAddTo()
{
if (SecretsVault.UserChannel == null)
try
{
if (SecretsVault.UserChannel == null)
{
addTo.Visibility = Visibility.Collapsed;
return;
}
if (SecretsVault.WatchLater.Contains(item.Id))
(addTo.Items[1] as ToggleMenuFlyoutItem).IsChecked = true;
PlaylistsResource.ListRequest request = SecretsVault.Service.Playlists.List("snippet");
request.Mine = true;
request.MaxResults = 50;
PlaylistListResponse response = await request.ExecuteAsync();
PlaylistItemsResource.ListRequest itemRequest = SecretsVault.Service.PlaylistItems.List("snippet");
itemRequest.VideoId = item.Id;
foreach (Playlist i in response.Items)
{
itemRequest.PlaylistId = i.Id;
ToggleMenuFlyoutItem menuItem = new ToggleMenuFlyoutItem
{
Text = i.Snippet.Title,
IsChecked = (await itemRequest.ExecuteAsync()).Items.Count > 0,
Tag = i,
Icon = new FontIcon
{
Glyph = "\xE728"
}
};
menuItem.Click += Item_Click;
addTo.Items.Add(menuItem);
}
}
catch
{
addTo.Visibility = Visibility.Collapsed;
return;
}
if (SecretsVault.WatchLater.Contains(item.Id))
(addTo.Items[1] as ToggleMenuFlyoutItem).IsChecked = true;
PlaylistsResource.ListRequest request = SecretsVault.Service.Playlists.List("snippet");
request.Mine = true;
request.MaxResults = 50;
PlaylistListResponse response = await request.ExecuteAsync();
PlaylistItemsResource.ListRequest itemRequest = SecretsVault.Service.PlaylistItems.List("snippet");
itemRequest.VideoId = item.Id;
foreach (Playlist i in response.Items)
{
itemRequest.PlaylistId = i.Id;
ToggleMenuFlyoutItem menuItem = new ToggleMenuFlyoutItem
{
Text = i.Snippet.Title,
IsChecked = (await itemRequest.ExecuteAsync()).Items.Count > 0,
Tag = i,
Icon = new FontIcon
{
Glyph = "\xE728"
}
};
menuItem.Click += Item_Click;
addTo.Items.Add(menuItem);
}
}
@@ -497,7 +541,7 @@ namespace FoxTube.Controls
else
{
HistorySet.Delete(history);
(Methods.MainPage.PageContent as History).Delete(this);
(Methods.MainPage.PageContent.Frame.Content as History).Delete(this);
}
return;
}
@@ -512,12 +556,17 @@ namespace FoxTube.Controls
await SecretsVault.Service.PlaylistItems.Delete(playlistItem.Id).ExecuteAsync();
(Methods.MainPage.PageContent as PlaylistPage).DeleteItem(this);
(Methods.MainPage.PageContent.Frame.Content as PlaylistPage).DeleteItem(this);
}
catch
{
}
}
private void Card_SizeChanged(object sender, SizeChangedEventArgs e)
{
Height = e.NewSize.Width * 0.75;
}
}
}
+9 -1
View File
@@ -21,7 +21,7 @@
<PackageCertificateThumbprint>50B93E6A246058D555BA65CD203D7A02064A7409</PackageCertificateThumbprint>
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
<AppxAutoIncrementPackageRevision>True</AppxAutoIncrementPackageRevision>
<AppxPackageDir>E:\XFox\Documents\FoxTube builds\0.5\</AppxPackageDir>
<AppxPackageDir>E:\XFox\Documents\FoxTube builds\0.6\</AppxPackageDir>
<AppxBundle>Always</AppxBundle>
<AppxBundlePlatforms>x86|x64|arm</AppxBundlePlatforms>
<AppInstallerUpdateFrequency>1</AppInstallerUpdateFrequency>
@@ -103,6 +103,7 @@
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="Classes\HistorySet.cs" />
<Compile Include="Classes\InboxItem.cs" />
<Compile Include="Classes\Methods.cs" />
<Compile Include="Classes\SearchPaameters.cs" />
@@ -130,6 +131,9 @@
<DependentUpon>CommentCard.xaml</DependentUpon>
</Compile>
<Compile Include="Classes\DownloadAgent.cs" />
<Compile Include="Controls\ContentFrame.xaml.cs">
<DependentUpon>ContentFrame.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\DownloadItem.xaml.cs">
<DependentUpon>DownloadItem.xaml</DependentUpon>
</Compile>
@@ -309,6 +313,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\ContentFrame.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\DownloadItem.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3" IgnorableNamespaces="uap mp uap3">
<Identity Name="53949MichaelXFoxGordeev.FoxTube" Publisher="CN=FD7A34DD-FE4D-4D7D-9D33-2DA9EBBE7725" Version="0.6.0.0" />
<Identity Name="53949MichaelXFoxGordeev.FoxTube" Publisher="CN=FD7A34DD-FE4D-4D7D-9D33-2DA9EBBE7725" Version="0.6.2.0" />
<mp:PhoneIdentity PhoneProductId="04fd81c1-6473-4174-afd7-4ac71dd85721" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
<Properties>
<DisplayName>FoxTube</DisplayName>
+1
View File
@@ -11,6 +11,7 @@ namespace FoxTube.Pages
public Home1()
{
InitializeComponent();
Methods.MainPage.PageContent.LoadingPage.Close();
}
public async void Initialize()
+28 -32
View File
@@ -8,30 +8,28 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pages="using:FoxTube.Pages"
xmlns:controls="using:FoxTube.Controls"
mc:Ignorable="d">
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Resources>
<Storyboard x:Name="showHeader">
<DoubleAnimation Storyboard.TargetName="ColapsedHeader" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
<DoubleAnimation Storyboard.TargetName="ColapsedHeader" Storyboard.TargetProperty="Opacity" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
<Storyboard x:Name="hideHeader">
<DoubleAnimation Storyboard.TargetName="ColapsedHeader" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="{StaticResource CardOpacityDuration}"/>
<DoubleAnimation Storyboard.TargetName="ColapsedHeader" Storyboard.TargetProperty="Opacity" To="0" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
<Storyboard x:Name="showThumb">
<DoubleAnimation Storyboard.TargetName="channelCover" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
<Storyboard x:Name="hideThumb">
<DoubleAnimation Storyboard.TargetName="channelCover" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="{StaticResource CardOpacityDuration}"/>
</Storyboard>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" SizeChanged="Grid_SizeChanged">
<Grid SizeChanged="Grid_SizeChanged">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Pivot SelectedIndex="0" Name="content" IsHeaderItemsCarouselEnabled="False" SelectionChanged="Content_SelectionChanged">
<Pivot Name="content" IsHeaderItemsCarouselEnabled="False" SelectionChanged="Content_SelectionChanged">
<PivotItem x:Uid="/Channel/videos" Header="Videos">
<Grid>
<ParallaxView Source="{x:Bind videoScroll}" VerticalShift="100">
@@ -40,27 +38,27 @@
</Grid>
</ParallaxView>
<ScrollViewer ViewChanged="ScrollViewer_ViewChanged" Name="videoScroll">
<StackPanel Background="{ThemeResource AppBarBackgroundThemeBrush}" Margin="0,300,0,0" Name="infoStack" Visibility="Visible">
<Grid Name="infoPanel">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Name="infoStack">
<Grid Name="infoPanel" Height="80">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Ellipse HorizontalAlignment="Left" Margin="10,-40,0,0" Fill="Black" Width="100" Height="1"/>
<PersonPicture Name="avatar" HorizontalAlignment="Left" Margin="10,-40,0,0"/>
<StackPanel Grid.Column="1" Orientation="Vertical" Margin="10,0,0,5">
<Ellipse Fill="{ThemeResource ApplicationPageBackgroundThemeBrush}" Margin="5,-30,5,0"/>
<PersonPicture Name="avatar" Margin="10,-30,10,0"/>
<StackPanel Grid.Column="1">
<TextBlock Name="title" FontWeight="SemiBold" FontSize="22" Text="Channel name"/>
<TextBlock Name="subscribers" Foreground="Gray" Text="1,000,000 subscribers"/>
<TextBlock Name="videosCount" Foreground="Gray" Text="563,000 videos"/>
</StackPanel>
<TextBlock Grid.Column="2" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Height="50" Margin="10" TextAlignment="Center" Padding="0,16,0,0" Foreground="Gray">
<Hyperlink Click="Hyperlink_Click"><Run x:Uid="/Cards/login">Log in</Run></Hyperlink> <Run x:Uid="/Cards/tomanage">to manage your subscriptions</Run>
<TextBlock x:Name="logIn" Grid.Column="2" VerticalAlignment="Center" Margin="10" Width="250" HorizontalTextAlignment="Center" Foreground="Gray">
<Hyperlink Click="Hyperlink_Click"><Run x:Uid="/Cards/login">Log in</Run></Hyperlink> <Run x:Uid="/Cards/tomanage">to manage your subscriptions</Run>
</TextBlock>
<Grid Visibility="Collapsed" Grid.Column="2" VerticalAlignment="Bottom" Margin="10" Name="subscriptionPane" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button x:Uid="/Cards/subscribe" Click="Subscribe_Click" Name="subscribe" Width="250" Height="50" Background="Red" Foreground="White" FontSize="18" FontWeight="SemiBold" Content="Subscirbe"/>
</Grid>
<Button Visibility="Collapsed" x:Name="subscribe" x:Uid="/Cards/subscribe" Grid.Column="2" Content="Subscribe" Height="50" Width="250" Margin="10" VerticalAlignment="Center" FontSize="18" FontWeight="SemiBold" Foreground="White" Background="Red"/>
</Grid>
<pages:VideoGrid x:Name="videoList"/>
<controls:ShowMore Clicked="VideoMore_Clicked" x:Name="videoMore"/>
@@ -69,40 +67,38 @@
</Grid>
</PivotItem>
<PivotItem x:Uid="/Channel/playlists" Header="Playlists">
<ScrollViewer>
<Grid>
<StackPanel Margin="10" Visibility="Visible">
<Grid>
<ScrollViewer>
<StackPanel>
<pages:VideoGrid x:Name="playlistList"/>
<controls:ShowMore Clicked="ShowMorePlaylists_Click" x:Name="playlistMore"/>
</StackPanel>
<local:LoadingPage Visibility="Collapsed" x:Name="playlistLoading"/>
</Grid>
</ScrollViewer>
</ScrollViewer>
<local:LoadingPage Visibility="Collapsed" x:Name="playlistLoading" RefreshPage="Refresh_Click"/>
</Grid>
</PivotItem>
<PivotItem x:Uid="/Channel/about" Header="About channel">
<ScrollViewer>
<TextBlock Name="description" TextWrapping="WrapWholeWords" Margin="10" IsTextSelectionEnabled="True" Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum justo erat, dapibus sit amet maximus eget, volutpat non turpis. Suspendisse. "/>
<TextBlock Name="description" TextWrapping="WrapWholeWords" IsTextSelectionEnabled="True" Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum justo erat, dapibus sit amet maximus eget, volutpat non turpis. Suspendisse."/>
</ScrollViewer>
</PivotItem>
<Pivot.RightHeader>
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Horizontal" Name="ColapsedHeader" Opacity="0">
<StackPanel Orientation="Horizontal" Margin="10,0">
<StackPanel Orientation="Horizontal" Name="ColapsedHeader" Opacity="0" Margin="10,0">
<PersonPicture Height="32" Name="collapsedAvatar"/>
<TextBlock Text="Channel name" VerticalAlignment="Center" Margin="10,0" Name="collapsedTitle"/>
<Button x:Uid="/Cards/subscribe" Background="Red" Foreground="White" FontWeight="SemiBold" Content="Subscribe" Width="150" Name="collapsedBtn" Click="Subscribe_Click" Padding="2"/>
<Button x:Uid="/Cards/subscribe" Background="Red" Foreground="White" FontWeight="SemiBold" Content="Subscribe" Width="150" Name="collapsedBtn" Click="Subscribe_Click" Padding="2" Visibility="Collapsed"/>
</StackPanel>
<AutoSuggestBox FontSize="14" x:Uid="/Channel/search" VerticalAlignment="Center" Width="250" Margin="8" PlaceholderText="Search on channel" QueryIcon="Find" Name="search" QuerySubmitted="AutoSuggestBox_QuerySubmitted"/>
<AutoSuggestBox x:Uid="/Channel/search" VerticalAlignment="Center" Width="250" PlaceholderText="Search on channel" QueryIcon="Find" Name="search" QuerySubmitted="AutoSuggestBox_QuerySubmitted"/>
</StackPanel>
</Pivot.RightHeader>
</Pivot>
<CommandBar Grid.Row="1" VerticalAlignment="Bottom" Name="commandBar">
<AppBarButton x:Uid="/Channel/openWeb" Icon="Globe" Label="Open in browser" Name="inBrowser" Click="InBrowser_Click"/>
<AppBarButton x:Uid="/Channel/refresh" Icon="Refresh" Label="Refresh" Name="refresh" Click="Refresh_Click"/>
<AppBarButton x:Uid="/Channel/share" Icon="Share" Label="Share" Name="share" Click="Share_Click"/>
</CommandBar>
<local:LoadingPage Grid.RowSpan="2" Visibility="Collapsed" x:Name="loading" RefreshPage="Refresh_Click"/>
</Grid>
</Page>
+45 -57
View File
@@ -21,12 +21,11 @@ namespace FoxTube.Pages
/// <summary>
/// Channel page
/// </summary>
public sealed partial class ChannelPage : Page, NavigationPage
public sealed partial class ChannelPage : Page, INavigationPage
{
public object Parameter { get; set; } = null;
readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("Cards");
public string channelId;
public Channel item;
SearchResource.ListRequest request;
@@ -37,35 +36,22 @@ namespace FoxTube.Pages
public ChannelPage()
{
InitializeComponent();
DataTransferManager.GetForCurrentView().DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(Share);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
Parameter = e.Parameter;
if ((string)e.Parameter == null)
loading.Error("NullReferenceException", "Unable to initialize search. Search term is not stated.");
else
Initialize(e.Parameter as string);
Initialize(e.Parameter as string);
}
public async void Initialize(string id)
{
loading.Refresh();
playlistLoading.Refresh();
try
{
if (id == SecretsVault.AccountId)
{
infoPanel.ColumnDefinitions[2].Width = new GridLength(0);
collapsedBtn.Visibility = Visibility.Collapsed;
}
ChannelsResource.ListRequest infoRequest = SecretsVault.Service.Channels.List("snippet,statistics,brandingSettings");
infoRequest.Id = channelId = id;
infoRequest.Id = id;
item = (await infoRequest.ExecuteAsync()).Items[0];
@@ -73,14 +59,13 @@ namespace FoxTube.Pages
subscribers.Text = $"{item.Statistics.SubscriberCount:0,0} {resources.GetString("/Cards/subscribers")}";
videosCount.Text = $"{item.Statistics.VideoCount:0,0} {resources.GetString("/Cards/videos")}";
if (!item.BrandingSettings.Image.BannerImageUrl.Contains("default"))
try { channelCover.Source = new BitmapImage(item.BrandingSettings.Image.BannerTabletExtraHdImageUrl == null ? item.BrandingSettings.Image.BannerImageUrl.ToUri() : item.BrandingSettings.Image.BannerTabletHdImageUrl.ToUri()); }
catch { }
if (item.BrandingSettings != null && !item.BrandingSettings.Image.BannerImageUrl.Contains("default"))
channelCover.Source = new BitmapImage(item.BrandingSettings.Image.BannerTabletExtraHdImageUrl == null ?
item.BrandingSettings.Image.BannerImageUrl.ToUri() : item.BrandingSettings.Image.BannerTabletHdImageUrl.ToUri());
try { avatar.ProfilePicture = collapsedAvatar.ProfilePicture = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()) { DecodePixelHeight = 100, DecodePixelWidth = 100 }; }
catch { }
avatar.ProfilePicture = collapsedAvatar.ProfilePicture = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()) { DecodePixelHeight = 100, DecodePixelWidth = 100 };
Methods.FormatText(ref description, item.Snippet.Description);
description.FormatText(item.Snippet.Description);
request = SecretsVault.Service.Search.List("id");
request.ChannelId = id;
@@ -100,34 +85,42 @@ namespace FoxTube.Pages
if (SecretsVault.IsAuthorized)
{
if (SecretsVault.Subscriptions.Any(i => i.Snippet.ResourceId.ChannelId == channelId))
logIn.Visibility = Visibility.Collapsed;
if (SecretsVault.Subscriptions.Any(i => i.Snippet.ResourceId.ChannelId == item.Id))
{
subscribe.Background = new SolidColorBrush(Colors.Transparent);
subscribe.Background = Background;
subscribe.Foreground = new SolidColorBrush(Colors.Gray);
subscribe.Content = resources.GetString("/Cards/unsubscribe");
collapsedBtn.Visibility = Visibility.Collapsed;
}
subscriptionPane.Visibility = Visibility.Visible;
else
collapsedBtn.Visibility = Visibility.Visible;
subscribe.Visibility = Visibility.Visible;
}
loading.Close();
if (id == SecretsVault.AccountId)
{
infoPanel.ColumnDefinitions[2].Width = new GridLength(0);
collapsedBtn.Visibility = Visibility.Collapsed;
}
ScrollViewer_ViewChanged(this, null);
Methods.MainPage.PageContent.LoadingPage.Close();
}
catch (System.Net.Http.HttpRequestException)
{
loading.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
Methods.MainPage.PageContent.LoadingPage.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
}
catch (Exception e)
{
loading.Error(e.GetType().ToString(), e.Message);
Methods.MainPage.PageContent.LoadingPage.Error(e.GetType().ToString(), e.Message);
Analytics.TrackEvent("Channel loading error", new Dictionary<string, string>()
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message },
{ "Channel ID", channelId }
{ "Channel ID", item.Id }
});
}
ScrollViewer_ViewChanged(this, null);
}
async void LoadPlaylist()
@@ -162,7 +155,7 @@ namespace FoxTube.Pages
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message },
{ "Channel ID", channelId }
{ "Channel ID", item.Id }
});
}
}
@@ -216,9 +209,9 @@ namespace FoxTube.Pages
private async void Subscribe_Click(object sender, RoutedEventArgs e)
{
if(await SecretsVault.ChangeSubscriptionState(channelId))
if(await SecretsVault.ChangeSubscriptionState(item.Id))
{
subscribe.Background = new SolidColorBrush(Colors.Transparent);
subscribe.Background = Background;
subscribe.Foreground = new SolidColorBrush(Colors.Gray);
subscribe.Content = resources.GetString("/Cards/unsubscribe");
collapsedBtn.Visibility = Visibility.Collapsed;
@@ -239,18 +232,18 @@ namespace FoxTube.Pages
private void AutoSuggestBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
if(search.Text.Length > 2)
{
if(content.Items.Count < 4)
content.Items.Add(new PivotItem()
{
Header = resources.GetString("/Channel/searchHeader"),
Content = new Search()
});
if (search.Text.Length < 3)
return;
((content.Items[3] as PivotItem).Content as Search).Initialize(new SearchParameters(search.Text, item.Id));
content.SelectedIndex = 3;
}
if (content.Items.Count < 4)
content.Items.Add(new PivotItem()
{
Header = resources.GetString("/Channel/searchHeader"),
Content = new ContentFrame()
});
((content.Items[3] as PivotItem).Content as ContentFrame).Frame.Navigate(typeof(Search), new object[] { new SearchParameters(search.Text, item.Id), (content.Items[3] as PivotItem).Content as ContentFrame });
content.SelectedIndex = 3;
}
private void ScrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
@@ -259,20 +252,14 @@ namespace FoxTube.Pages
Rect view = new Rect(0.0, 0.0, videoScroll.ActualWidth, videoScroll.ActualHeight);
if (view.Contains(new Point(panel.Left, panel.Bottom)))
{
if (ColapsedHeader.Opacity == 1)
hideHeader.Begin();
}
hideHeader.Begin();
else
{
if (ColapsedHeader.Opacity == 0)
showHeader.Begin();
}
showHeader.Begin();
}
private void Refresh_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.GoToChannel(channelId);
Methods.MainPage.PageContent.Refresh();
}
private async void InBrowser_Click(object sender, RoutedEventArgs e)
@@ -282,6 +269,7 @@ namespace FoxTube.Pages
private void Share_Click(object sender, RoutedEventArgs e)
{
DataTransferManager.GetForCurrentView().DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(Share);
DataTransferManager.ShowShareUI();
}
+22 -27
View File
@@ -5,52 +5,47 @@
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"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
mc:Ignorable="d">
<Grid>
<Grid x:Name="grid">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="30"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Name="grid">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<TextBox x:Uid="/CommentsPage/textbox" Margin="5,5,42,5" PlaceholderText="Add a public comment" Name="newComment" VerticalAlignment="Center" MinHeight="32" TextWrapping="Wrap" AcceptsReturn="True"/>
<Button HorizontalAlignment="Right" Name="send" Click="send_Click" VerticalAlignment="Top"
<TextBox x:Uid="/CommentsPage/textbox" PlaceholderText="Add a public comment" Name="newComment" MinHeight="32" TextWrapping="Wrap" AcceptsReturn="True" Margin="0,0,40,0"/>
<Button HorizontalAlignment="Right" Name="send" Click="send_Click" VerticalAlignment="Top"
Height="32" Width="32"
Margin="0,5,5,0" Padding="0"
Margin="5,0" Padding="0"
Background="Transparent"
FontFamily="Segoe MDL2 Assets"
Content="&#xE122;" FontSize="30"/>
<ProgressBar Name="sending" IsIndeterminate="True" Foreground="Red" Visibility="Collapsed" VerticalAlignment="Bottom" HorizontalAlignment="Stretch"/>
<ProgressBar Name="sending" IsIndeterminate="True" Visibility="Collapsed" VerticalAlignment="Bottom" HorizontalAlignment="Stretch"/>
<TextBlock Name="counter" Grid.Row="1" Text="[Comments count] Comments" Margin="5,0,0,0" VerticalAlignment="Center" FontWeight="SemiBold"/>
<StackPanel Padding="0" Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,10,0">
<TextBlock x:Uid="/CommentsPage/sortBy" Text="Sort by: " VerticalAlignment="Center" Margin="0,0,5,0"/>
<Button Name="orderBtn" Background="Transparent" Content="Relevance" Foreground="Red" Padding="0" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="0,0,0,3">
<TextBlock Name="counter" Grid.Row="1" Text="[Comments count] Comments" VerticalAlignment="Center" FontWeight="SemiBold"/>
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">
<TextBlock x:Uid="/CommentsPage/sortBy" Text="Sort by: " VerticalAlignment="Center"/>
<Button Name="orderBtn" Background="Transparent" Content="Relevance" Foreground="Red" Padding="0" VerticalAlignment="Bottom" Margin="5,3">
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem x:Uid="/CommentsPage/relevance" Click="toRelevance_Click" Name="toRelevance" Text="Relevance"/>
<MenuFlyoutItem x:Uid="/CommentsPage/date" Click="toDate_Click" Name="toDate" Text="Date"/>
</MenuFlyout>
</Button.Flyout>
</Button>
</StackPanel>
</Grid>
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem x:Uid="/CommentsPage/relevance" Click="toRelevance_Click" Name="toRelevance" Text="Relevance"/>
<MenuFlyoutItem x:Uid="/CommentsPage/date" Click="toDate_Click" Name="toDate" Text="Date"/>
</MenuFlyout>
</Button.Flyout>
</Button>
</StackPanel>
<ScrollViewer Grid.Row="1" Name="scroll">
<ScrollViewer Grid.Row="2" Name="scroll">
<StackPanel>
<StackPanel Name="placeholder">
<StackPanel x:Name="list">
<StackPanel.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition IsStaggeringEnabled="True"/>
</TransitionCollection>
</StackPanel.ChildrenTransitions>
</StackPanel>
<controls:ShowMore x:Name="more" Clicked="ShowMore_Clicked"/>
</StackPanel>
</ScrollViewer>
+45 -25
View File
@@ -6,6 +6,8 @@ using Google.Apis.YouTube.v3.Data;
using FoxTube.Controls;
using Windows.UI.Popups;
using Windows.ApplicationModel.Resources;
using Microsoft.AppCenter.Analytics;
using System.Collections.Generic;
namespace FoxTube.Pages
{
@@ -52,18 +54,18 @@ namespace FoxTube.Pages
foreach (CommentThread comment in response.Items)
{
if ((placeholder.Children.Count - 5) % 20 == 0 && !SecretsVault.AdsDisabled)
placeholder.Children.Add(new Controls.Adverts.CommentAdvert());
placeholder.Children.Add(new CommentCard(comment));
if ((list.Children.Count - 5) % 20 == 0 && !SecretsVault.AdsDisabled)
list.Children.Add(new Controls.Adverts.CommentAdvert());
list.Children.Add(new CommentCard(comment));
}
}
public void RemoveComment(CommentCard commentCard, string topCommentId = null)
{
if (string.IsNullOrWhiteSpace(topCommentId))
placeholder.Children.Remove(commentCard);
list.Children.Remove(commentCard);
else
(placeholder.Children.Find(i => (i as CommentCard).thread.Id == topCommentId) as CommentCard).DeleteComment(commentCard);
(list.Children.Find(i => (i as CommentCard).thread.Id == topCommentId) as CommentCard)?.DeleteComment(commentCard);
}
private async void toRelevance_Click(object sender, RoutedEventArgs e)
@@ -71,12 +73,12 @@ namespace FoxTube.Pages
if (order == CommentThreadsResource.ListRequest.OrderEnum.Relevance)
return;
more.Show();
more.Visibility = Visibility.Visible;
order = CommentThreadsResource.ListRequest.OrderEnum.Relevance;
orderBtn.Content = resources.GetString("/CommentsPage/relevance/Text");
placeholder.Children.Clear();
list.Children.Clear();
request.Order = order;
var response = await request.ExecuteAsync();
@@ -86,7 +88,11 @@ namespace FoxTube.Pages
more.Visibility = Visibility.Collapsed;
foreach (CommentThread comment in response.Items)
placeholder.Children.Add(new CommentCard(comment));
{
if ((list.Children.Count - 5) % 20 == 0 && !SecretsVault.AdsDisabled)
list.Children.Add(new Controls.Adverts.CommentAdvert());
list.Children.Add(new CommentCard(comment));
}
more.Complete();
}
@@ -96,12 +102,12 @@ namespace FoxTube.Pages
if (order == CommentThreadsResource.ListRequest.OrderEnum.Time)
return;
more.Show();
more.Visibility = Visibility.Visible;
order = CommentThreadsResource.ListRequest.OrderEnum.Time;
orderBtn.Content = resources.GetString("/CommentsPage/publish");
placeholder.Children.Clear();
list.Children.Clear();
request.Order = order;
var response = await request.ExecuteAsync();
@@ -111,12 +117,16 @@ namespace FoxTube.Pages
more.Visibility = Visibility.Collapsed;
foreach (CommentThread comment in response.Items)
placeholder.Children.Add(new CommentCard(comment));
{
if ((list.Children.Count - 5) % 20 == 0 && !SecretsVault.AdsDisabled)
list.Children.Add(new Controls.Adverts.CommentAdvert());
list.Children.Add(new CommentCard(comment));
}
more.Complete();
}
private async void send_Click(object sender, RoutedEventArgs e)
private async void send_Click(object sender, RoutedEventArgs args)
{
if (string.IsNullOrWhiteSpace(newComment.Text))
return;
@@ -125,13 +135,13 @@ namespace FoxTube.Pages
send.IsEnabled = false;
sending.Visibility = Visibility.Visible;
CommentThread thread = new CommentThread()
CommentThread thread = new CommentThread
{
Snippet = new CommentThreadSnippet()
Snippet = new CommentThreadSnippet
{
TopLevelComment = new Comment()
TopLevelComment = new Comment
{
Snippet = new CommentSnippet()
Snippet = new CommentSnippet
{
TextOriginal = newComment.Text
}
@@ -143,11 +153,20 @@ namespace FoxTube.Pages
try
{
CommentThread response = await SecretsVault.Service.CommentThreads.Insert(thread, "snippet").ExecuteAsync();
placeholder.Children.Insert(0, new CommentCard(response));
list.Children.Insert(0, new CommentCard(response));
newComment.Text = "";
scroll.ChangeView(null, 0, null);
}
catch { await new MessageDialog("Failed to publish your comment. Please, try again later.").ShowAsync(); }
catch (Exception e)
{
await new MessageDialog("Failed to publish your comment. Please, try again later.").ShowAsync();
Analytics.TrackEvent("Failed to post comment", new Dictionary<string, string>
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message },
{ "Thread ID", threadId }
});
}
newComment.IsEnabled = true;
send.IsEnabled = true;
@@ -158,18 +177,19 @@ namespace FoxTube.Pages
{
request.PageToken = token;
var response = await request.ExecuteAsync();
token = response.NextPageToken;
if (string.IsNullOrWhiteSpace(token))
more.Visibility = Visibility.Collapsed;
foreach (CommentThread comment in response.Items)
{
if ((placeholder.Children.Count - 5) % 20 == 0 && !SecretsVault.AdsDisabled)
placeholder.Children.Add(new Controls.Adverts.CommentAdvert());
placeholder.Children.Add(new CommentCard(comment));
if ((list.Children.Count - 5) % 20 == 0 && !SecretsVault.AdsDisabled)
list.Children.Add(new Controls.Adverts.CommentAdvert());
list.Children.Add(new CommentCard(comment));
}
token = response.NextPageToken;
more.Complete();
if (string.IsNullOrWhiteSpace(token))
more.Visibility = Visibility.Collapsed;
}
}
}
-6
View File
@@ -11,7 +11,6 @@
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid Margin="10">
<Grid.ColumnDefinitions>
@@ -22,7 +21,6 @@
<Button Grid.Column="1" x:Uid="/Downloads/openFolder" Content="Open folder" Name="open" Click="Open_Click" VerticalAlignment="Center"/>
</Grid>
<TextBlock x:Uid="/Downloads/noItems" Grid.Row="1" Name="empty" HorizontalAlignment="Center" VerticalAlignment="Top" FontSize="28" Text="You haven't downloaded anything yet" Margin="10" TextWrapping="WrapWholeWords" Foreground="Gray" FontWeight="SemiBold"/>
<ScrollViewer Grid.Row="1">
@@ -34,9 +32,5 @@
</StackPanel.ChildrenTransitions>
</StackPanel>
</ScrollViewer>
<CommandBar Grid.Row="2">
<AppBarButton x:Uid="/Downloads/refresh" Label="Refresh" Icon="Refresh" Click="Refresh"/>
</CommandBar>
</Grid>
</Page>
+15 -10
View File
@@ -3,13 +3,14 @@ using System;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace FoxTube.Pages
{
/// <summary>
/// Downloads page
/// </summary>
public sealed partial class Downloads : Page, NavigationPage
public sealed partial class Downloads : Page, INavigationPage
{
public object Parameter { get; set; } = null;
public Downloads()
@@ -17,7 +18,19 @@ namespace FoxTube.Pages
InitializeComponent();
DownloadAgent.Page = this;
path.Text = DownloadAgent.Downloads.Path;
Refresh(this, null);
list.Children.Clear();
DownloadAgent.Items.ForEach(i => list.Children.Add(i));
empty.Visibility = list.Children.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
Methods.MainPage.PageContent.LoadingPage.Close();
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
DownloadAgent.Page = null;
}
private async void Open_Click(object sender, RoutedEventArgs e)
@@ -25,14 +38,6 @@ namespace FoxTube.Pages
await Launcher.LaunchFolderAsync(DownloadAgent.Downloads);
}
private void Refresh(object sender, RoutedEventArgs e)
{
list.Children.Clear();
DownloadAgent.items.ForEach(i => list.Children.Add(i));
empty.Visibility = list.Children.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
}
public void Remove(DownloadItem item)
{
list.Children.Remove(item);
-7
View File
@@ -55,13 +55,6 @@
</Grid>
</PivotItem>
</Pivot>
<!--<ScrollViewer>
<StackPanel>
<local:VideoGrid x:Name="list"/>
<controls:ShowMore Clicked="ShowMore_Clicked" x:Name="more"/>
</StackPanel>
</ScrollViewer>-->
<CommandBar Grid.Row="1" DefaultLabelPosition="Right">
<AppBarButton x:Uid="/Playlist/refresh" Icon="Refresh" Label="Refresh" Name="refresh" Click="Refresh_Click"/>
+9 -7
View File
@@ -14,7 +14,7 @@ namespace FoxTube.Pages
/// <summary>
/// YouTube history page
/// </summary>
public sealed partial class History : Page, NavigationPage
public sealed partial class History : Page, INavigationPage
{
public object Parameter { get; set; } = null;
int page = 1;
@@ -31,10 +31,12 @@ namespace FoxTube.Pages
Parameter = e.Parameter;
Initialize();
Methods.MainPage.PageContent.LoadingPage.Close();
}
public async void Initialize()
{
Methods.MainPage.VideoContent.LoadingPage.Close();
try
{
loading.Refresh();
@@ -71,7 +73,7 @@ namespace FoxTube.Pages
private void Refresh_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.GoToHistory();
Methods.MainPage.VideoContent.Refresh();
}
private async void ShowMore_Clicked()
@@ -135,10 +137,10 @@ namespace FoxTube.Pages
{
websiteLoading.Error(e.GetType().ToString(), e.Message);
Analytics.TrackEvent("History loading error", new Dictionary<string, string>()
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message }
});
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message }
});
}
});
}
@@ -148,7 +150,7 @@ namespace FoxTube.Pages
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
for (int k = 25 * webPage++; k < 25 * webPage && k < SecretsVault.History.Count; k++)
websiteList.Add(new VideoCard(HistorySet.Items[k].Id, "HL"));
websiteList.Add(new VideoCard(SecretsVault.History[k], "HL"));
if (websiteList.Count >= SecretsVault.History.Count)
websiteMore.Visibility = Visibility.Collapsed;
+46 -62
View File
@@ -7,71 +7,55 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pages="using:FoxTube.Pages"
xmlns:controls="using:FoxTube.Controls"
mc:Ignorable="d">
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Pivot SelectionChanged="pivot_SelectionChanged" Name="pivot">
<PivotItem Name="recommended" Header="Recommended" x:Uid="/Home/recommended">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Pivot SelectionChanged="pivot_SelectionChanged" Name="pivot">
<PivotItem Name="recommended" Header="Recommended" x:Uid="/Home/recommended">
<Grid>
<ScrollViewer>
<StackPanel>
<pages:VideoGrid x:Name="recGrid"/>
<controls:ShowMore Clicked="Recommended_More" x:Name="recommendedMore"/>
</StackPanel>
</ScrollViewer>
<ScrollViewer>
<StackPanel>
<pages:VideoGrid x:Name="recGrid"/>
<controls:ShowMore Clicked="Recommended_More" x:Name="recommendedMore"/>
</StackPanel>
</ScrollViewer>
<local:LoadingPage x:Name="recsLoading" Grid.RowSpan="2" RefreshPage="Refresh_Click"/>
</Grid>
</PivotItem>
<PivotItem Name="trending" Header="Trending" x:Uid="/Home/trending">
<Grid>
<ScrollViewer>
<StackPanel>
<pages:VideoGrid x:Name="trendGrid"/>
<controls:ShowMore Clicked="Trending_More" x:Name="trendingMore"/>
</StackPanel>
</ScrollViewer>
<CommandBar Grid.Row="1">
<AppBarButton Icon="Refresh" Label="Refresh" x:Uid="/Home/refresh" Click="Recommended_Refresh"/>
</CommandBar>
<local:LoadingPage x:Name="trendsLoading" Grid.RowSpan="2" RefreshPage="Refresh_Click"/>
</Grid>
</PivotItem>
<PivotItem Name="subscriptions" Header="Subscriptions" x:Uid="/Home/subs">
<Grid>
<ScrollViewer>
<StackPanel>
<pages:VideoGrid x:Name="subsGrid"/>
<controls:ShowMore Clicked="Subscriptions_More" x:Name="subscriptionsMore"/>
</StackPanel>
</ScrollViewer>
<local:LoadingPage x:Name="recsLoading" Grid.RowSpan="2" RefreshPage="Recommended_Refresh"/>
</Grid>
</PivotItem>
<PivotItem Name="trending" Header="Trending" x:Uid="/Home/trending">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<local:LoadingPage x:Name="subsLoading" Grid.RowSpan="2" RefreshPage="Refresh_Click"/>
</Grid>
</PivotItem>
</Pivot>
<ScrollViewer>
<StackPanel>
<pages:VideoGrid x:Name="trendGrid"/>
<controls:ShowMore Clicked="Trending_More" x:Name="trendingMore"/>
</StackPanel>
</ScrollViewer>
<CommandBar Grid.Row="1">
<AppBarButton Icon="Refresh" Label="Refresh" x:Uid="/Home/refresh" Click="Trends_Refresh"/>
</CommandBar>
<local:LoadingPage x:Name="trendsLoading" Grid.RowSpan="2" RefreshPage="Trends_Refresh"/>
</Grid>
</PivotItem>
<PivotItem Name="subscriptions" Header="Subscriptions" x:Uid="/Home/subs">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<ScrollViewer>
<StackPanel>
<pages:VideoGrid x:Name="subsGrid"/>
<controls:ShowMore Clicked="Subscriptions_More" x:Name="subscriptionsMore"/>
</StackPanel>
</ScrollViewer>
<CommandBar Grid.Row="1">
<AppBarButton Icon="Refresh" Label="Refresh" x:Uid="/Home/refresh" Click="Subscriptions_Refresh"/>
</CommandBar>
<local:LoadingPage x:Name="subsLoading" Grid.RowSpan="2" RefreshPage="Subscriptions_Refresh"/>
</Grid>
</PivotItem>
</Pivot>
<CommandBar Grid.Row="1" Name="commandbar">
<AppBarButton Icon="Refresh" Label="Refresh" x:Uid="/Home/refresh" Click="Refresh_Click"/>
</CommandBar>
</Grid>
</Page>
+24 -27
View File
@@ -14,7 +14,7 @@ namespace FoxTube
/// <summary>
/// Home page
/// </summary>
public sealed partial class Home : Page, NavigationPage
public sealed partial class Home : Page, INavigationPage
{
public object Parameter { get; set; } = null;
private bool trendLoaded = false, recLoaded = false, subsLoaded = false;
@@ -27,6 +27,7 @@ namespace FoxTube
public Home()
{
InitializeComponent();
SecretsVault.RefreshToken();
Initialize();
}
@@ -41,6 +42,8 @@ namespace FoxTube
private void pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Methods.MainPage.PageContent.LoadingPage.Close();
if (pivot.SelectedItem == recommended && !recLoaded)
LoadRecommendations();
else if (pivot.SelectedItem == trending && !trendLoaded)
@@ -52,6 +55,7 @@ namespace FoxTube
#region Initializing tabs
async void LoadRecommendations()
{
recLoaded = false;
try
{
recsLoading.Refresh();
@@ -71,12 +75,10 @@ namespace FoxTube
}
catch (HttpRequestException)
{
recLoaded = false;
recsLoading.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
}
catch (Exception e)
{
recLoaded = false;
recsLoading.Error(e.GetType().ToString(), e.Message);
Analytics.TrackEvent("Failed to load recommendations", new Dictionary<string, string>()
@@ -88,6 +90,7 @@ namespace FoxTube
}
async void LoadTrending()
{
trendLoaded = false;
try
{
trendsLoading.Refresh();
@@ -111,12 +114,10 @@ namespace FoxTube
}
catch (HttpRequestException)
{
trendLoaded = false;
trendsLoading.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
}
catch(Exception e)
{
trendLoaded = false;
trendsLoading.Error(e.GetType().ToString(), e.Message);
Analytics.TrackEvent("Failed to load trendings", new Dictionary<string, string>()
@@ -128,11 +129,11 @@ namespace FoxTube
}
async void LoadSubscriptions()
{
subsLoaded = false;
try
{
subsLoading.Refresh();
await SecretsVault.HttpClient.GetStringAsync("https://www.youtube.com/list_ajax?style=json&action_get_list=1&list=WL");
string response = await SecretsVault.HttpClient.GetStringAsync("https://www.youtube.com/feed/subscriptions");
foreach (Match match in Regex.Matches(response, @"\bdata-context-item-id=(\S*)\b", RegexOptions.IgnoreCase))
@@ -148,12 +149,10 @@ namespace FoxTube
}
catch (HttpRequestException)
{
subsLoaded = false;
subsLoading.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
}
catch (Exception e)
{
subsLoaded = false;
subsLoading.Error(e.GetType().ToString(), e.Message);
Analytics.TrackEvent("Failed to load subscriptions", new Dictionary<string, string>()
@@ -198,26 +197,24 @@ namespace FoxTube
}
#endregion
#region Refreshing tabs
private void Recommended_Refresh(object sender, RoutedEventArgs e)
private void Refresh_Click(object sender, RoutedEventArgs e)
{
recGrid.Clear();
LoadRecommendations();
switch(pivot.SelectedIndex)
{
case 0:
recGrid.Clear();
LoadRecommendations();
break;
case 1:
trendsRequest = null;
trendGrid.Clear();
LoadTrending();
break;
case 2:
subsGrid.Clear();
LoadSubscriptions();
break;
}
}
private void Trends_Refresh(object sender, RoutedEventArgs e)
{
trendsRequest = null;
trendGrid.Clear();
LoadTrending();
}
private void Subscriptions_Refresh(object sender, RoutedEventArgs e)
{
subsGrid.Clear();
LoadSubscriptions();
}
#endregion
}
}
+8 -8
View File
@@ -4,16 +4,16 @@
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:Windows10version1809="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 7)"
mc:Ignorable="d">
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<ProgressRing Name="ring" IsActive="True" Foreground="Red" Width="100" Height="100"/>
<StackPanel Name="wifiTrouble" Visibility="Collapsed" VerticalAlignment="Center">
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="&#xEB5E;" FontSize="100" HorizontalAlignment="Center"/>
<TextBlock x:Uid="/LoadingPage/checkConnection" Text="Check your internet connection" TextWrapping="WrapWholeWords" FontSize="48" HorizontalAlignment="Center" HorizontalTextAlignment="Center"/>
<TextBlock x:Uid="/LoadingPage/wifiDesc" Text="Please, make sure you are connected to the internet and try again." HorizontalAlignment="Center" TextWrapping="WrapWholeWords" HorizontalTextAlignment="Center"/>
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="&#xEB5E;" FontSize="100"/>
<TextBlock x:Uid="/LoadingPage/checkConnection" Text="Check your internet connection" TextWrapping="WrapWholeWords" FontSize="48" HorizontalAlignment="Center"/>
<TextBlock x:Uid="/LoadingPage/wifiDesc" Text="Please, make sure you are connected to the internet and try again." TextWrapping="WrapWholeWords" HorizontalAlignment="Center"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button x:Uid="/LoadingPage/openWifi" Content="Open network settings" Margin="5" Name="openWifi" Click="openWifi_Click"/>
<Button x:Uid="/LoadingPage/openTroubleshooter" Content="Open troubleshooter" Background="Red" Foreground="White" Margin="5" Name="openTroubleshoot" Click="openTroubleshoot_Click"/>
@@ -26,8 +26,8 @@
<StackPanel Name="trouble" Visibility="Collapsed" VerticalAlignment="Center">
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="&#xE7BA;" FontSize="100" HorizontalAlignment="Center"/>
<TextBlock x:Uid="/LoadingPage/err" Text="We are unable to display the page" FontSize="48" HorizontalAlignment="Center" TextWrapping="WrapWholeWords" HorizontalTextAlignment="Center"/>
<TextBlock x:Uid="/LoadingPage/errDescription" Text="It could be caused by YouTube internal server error or by application's bug. Please, try again later" HorizontalAlignment="Center" HorizontalTextAlignment="Center" TextWrapping="WrapWholeWords"/>
<TextBlock x:Uid="/LoadingPage/err" Text="We are unable to display the page" FontSize="48" HorizontalAlignment="Center" TextWrapping="WrapWholeWords"/>
<TextBlock x:Uid="/LoadingPage/errDescription" Text="It could be caused by YouTube internal server error or by application's bug. Please, try again later" HorizontalAlignment="Center" TextWrapping="WrapWholeWords"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button x:Uid="/LoadingPage/refresh" Name="refresh" Click="wifiRefresh_Click" Content="Refresh page" Margin="5"/>
<Button x:Uid="/LoadingPage/feedback" Content="Leave feedback" Background="Red" Foreground="White" Margin="5" Name="feedback" Click="feedback_Click"/>
+11 -38
View File
@@ -5,7 +5,8 @@
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">
xmlns:ui="using:Microsoft.UI.Xaml.Controls"
xmlns:controls="using:FoxTube.Controls">
<Grid Name="grid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<VisualStateManager.VisualStateGroups>
@@ -34,8 +35,7 @@
Style="{StaticResource CaptionTextBlockStyle}" />
</Border>
<ui:NavigationView FontSize="14" SelectedItem="toHome" BackRequested="Nav_BackRequested" PaneClosing="Nav_PaneClosing" PaneOpened="Nav_PaneOpened" OpenPaneLength="300" Name="nav" SelectionChanged="Nav_SelectionChanged">
<ui:NavigationView SelectedItem="toHome" BackRequested="Nav_BackRequested" PaneClosing="Nav_PaneClosing" PaneOpened="Nav_PaneOpened" OpenPaneLength="300" Name="nav" SelectionChanged="Nav_SelectionChanged">
<ui:NavigationView.Header>
<Grid>
<TextBlock Name="Title" Style="{StaticResource TitleTextBlockStyle}"/>
@@ -66,23 +66,6 @@
</Ellipse>
</Button>
</StackPanel>
<CommandBar HorizontalAlignment="Right" Background="Transparent" OverflowButtonVisibility="Collapsed" Visibility="Collapsed" Margin="100,0">
<AppBarButton Label="Leave feedback">
<AppBarButton.Icon>
<FontIcon Glyph="&#xED15;"/>
</AppBarButton.Icon>
</AppBarButton>
<AppBarButton Label="Add account">
<AppBarButton.Icon>
<FontIcon Glyph="&#xE8FA;"/>
</AppBarButton.Icon>
</AppBarButton>
<AppBarButton>
<AppBarButton.Content>
<PersonPicture/>
</AppBarButton.Content>
</AppBarButton>
</CommandBar>
</Grid>
</ui:NavigationView.Header>
@@ -116,30 +99,20 @@
</ui:NavigationView.MenuItems>
<ui:NavigationView.PaneFooter>
<ui:NavigationViewList>
<ui:NavigationViewList.ItemContainerTransitions>
<TransitionCollection>
<EntranceThemeTransition IsStaggeringEnabled="True"/>
<ReorderThemeTransition/>
</TransitionCollection>
</ui:NavigationViewList.ItemContainerTransitions>
<ui:NavigationViewItem Name="openWeb" Tapped="Web_Tapped" Icon="Globe" Content="Browser" Visibility="Collapsed"/>
<ui:NavigationViewItem Content="Remove ads" Visibility="Collapsed" Tapped="RemoveAds_Tapped" Name="removeAds">
<ui:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE14D;"/>
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
</ui:NavigationViewList>
<ui:NavigationViewItem Content="Remove ads" Visibility="Collapsed" Tapped="RemoveAds_Tapped" Name="removeAds">
<ui:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE14D;"/>
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
</ui:NavigationView.PaneFooter>
<ui:NavigationView.AutoSuggestBox>
<AutoSuggestBox FontSize="14" x:Name="search" QueryIcon="Find" QuerySubmitted="Search_QuerySubmitted" TextChanged="Search_TextChanged" x:Uid="/Main/searchPlaceholder" PlaceholderText="Search"/>
<AutoSuggestBox x:Name="search" QueryIcon="Find" QuerySubmitted="Search_QuerySubmitted" TextChanged="Search_TextChanged" x:Uid="/Main/searchPlaceholder" PlaceholderText="Search"/>
</ui:NavigationView.AutoSuggestBox>
<Grid>
<Frame Name="content" Navigated="Content_Navigated"/>
<Frame Name="videoPlaceholder"/>
<controls:ContentFrame x:Name="content" Navigated="Content_Navigated"/>
<controls:ContentFrame x:Name="videoPlaceholder"/>
</Grid>
</ui:NavigationView>
+141 -200
View File
@@ -6,7 +6,6 @@ using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Navigation;
using System.Diagnostics;
using Windows.UI.Xaml.Media.Imaging;
using System.Xml;
using Google.Apis.YouTube.v3.Data;
@@ -14,11 +13,11 @@ using Windows.ApplicationModel.Core;
using Windows.System;
using FoxTube.Pages;
using Windows.UI.Popups;
using Windows.Networking.Connectivity;
using Windows.ApplicationModel.Resources;
using Microsoft.Services.Store.Engagement;
using Windows.UI.Xaml.Shapes;
using Windows.UI.Xaml.Media;
using FoxTube.Controls;
namespace FoxTube
{
@@ -28,38 +27,14 @@ namespace FoxTube
public sealed partial class MainPage : Page
{
bool wasInvoked = false;
public static event ObjectEventHandler VideoPageSizeChanged;
readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("Main");
Dictionary<Type, string> headers;
public Page PageContent => content.Content as Page;
public ContentFrame PageContent => content;
public ContentFrame VideoContent => videoPlaceholder;
public MainPage()
{
InitializeComponent();
Window.Current.SetTitleBar(AppTitleBar);
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = true;
CoreApplication.GetCurrentView().TitleBar.LayoutMetricsChanged += (s, e) => SetTitleBar(s);
SecretsVault.AuthorizationStateChanged += AuthorizationStateChanged;
SecretsVault.SubscriptionsChanged += SecretsVault_SubscriptionsChanged;
SecretsVault.Purchased += async (sender, e) =>
{
removeAds.Visibility = (e[0] as bool?).Value ? Visibility.Collapsed : Visibility.Visible;
removeAds.Content = $"{resources.GetString("/Main/adsFree/Content")} ({e[1]})";
if (!(bool)e[0])
return;
MessageDialog dialog = new MessageDialog(resources.GetString("/Main/purchaseSuccess"));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/close"), (command) => Methods.CloseApp()));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/delay")));
dialog.CancelCommandIndex = 1;
dialog.DefaultCommandIndex = 0;
await dialog.ShowAsync();
};
SecretsVault.Initialize();
headers = new Dictionary<Type, string>()
{
{ typeof(Settings), resources.GetString("/Main/settings/Content") },
@@ -72,6 +47,33 @@ namespace FoxTube
{ typeof(Downloads), resources.GetString("/Main/downloads/Content") }
};
InitializeComponent();
Window.Current.SetTitleBar(AppTitleBar);
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = true;
CoreApplication.GetCurrentView().TitleBar.LayoutMetricsChanged += (s, e) => SetTitleBar(s);
SecretsVault.AuthorizationStateChanged += AuthorizationStateChanged;
SecretsVault.SubscriptionsChanged += SecretsVault_SubscriptionsChanged;
SecretsVault.Purchased += async (sender, e) =>
{
removeAds.Visibility = (e[0] as bool?).Value ? Visibility.Collapsed : Visibility.Visible;
if (!(bool)e[0])
{
removeAds.Content = $"{resources.GetString("/Main/adsFree/Content")} ({e[1]})";
return;
}
MessageDialog dialog = new MessageDialog(resources.GetString("/Main/purchaseSuccess"));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/close"), (command) => Methods.CloseApp()));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/delay")));
dialog.CancelCommandIndex = 1;
dialog.DefaultCommandIndex = 0;
await dialog.ShowAsync();
};
SecretsVault.Initialize();
if(StoreServicesFeedbackLauncher.IsSupported())
feedback.Visibility = Visibility.Visible;
@@ -121,16 +123,15 @@ namespace FoxTube
}
}
public string GetPlaylist()
{
try { return (videoPlaceholder.Content as VideoPage).playlistId; }
catch { return null; }
}
public void SetTitleBar(CoreApplicationViewTitleBar coreTitleBar = null)
{
if (coreTitleBar != null)
{
bool full = ApplicationView.GetForCurrentView().IsFullScreenMode;
double left = 12 + (full ? 0 : coreTitleBar.SystemOverlayLeftInset);
AppTitle.Margin = new Thickness(left, 8, 0, 0);
AppTitleBar.Height = coreTitleBar.Height;
}
var titleBar = ApplicationView.GetForCurrentView().TitleBar;
@@ -148,7 +149,7 @@ namespace FoxTube
private void SecretsVault_SubscriptionsChanged(object sender, params object[] args)
{
switch(args[0] as string)
switch((string)args[0])
{
case "add":
if (nav.MenuItems.Count < 19)
@@ -156,21 +157,21 @@ namespace FoxTube
break;
case "remove":
Microsoft.UI.Xaml.Controls.NavigationViewItem item = nav.MenuItems.Find(i => ((i as Microsoft.UI.Xaml.Controls.NavigationViewItem).Content as StackPanel).Tag.ToString() == (args[1] as Subscription).Snippet.ResourceId.ChannelId) as Microsoft.UI.Xaml.Controls.NavigationViewItem;
if (item == null)
break;
else
if (nav.MenuItems.Find(i => ((i as Microsoft.UI.Xaml.Controls.NavigationViewItem).Content as StackPanel).Tag.ToString() == (string)args[1]) is Microsoft.UI.Xaml.Controls.NavigationViewItem item)
{
nav.MenuItems.Remove(item);
if (SecretsVault.Subscriptions.Count >= 10)
nav.MenuItems.Add(SecretsVault.Subscriptions[9]);
if (SecretsVault.Subscriptions.Count >= 10)
nav.MenuItems.Add(SecretsVault.Subscriptions[9]);
}
break;
}
}
private async void AuthorizationStateChanged(object sender, params object[] e)
{
switch(e[0] as bool?)
wasInvoked = false;
switch (e[0] as bool?)
{
case true:
account.Visibility = Visibility.Collapsed;
@@ -183,8 +184,7 @@ namespace FoxTube
avatar.Visibility = Visibility.Visible;
if(SecretsVault.UserChannel != null)
toChannel.Visibility = Visibility.Visible;
toChannel.Visibility = Visibility.Visible;
toSubscriptions.Visibility = Visibility.Visible;
libHeader.Visibility = Visibility.Visible;
toHistory.Visibility = Visibility.Visible;
@@ -198,9 +198,16 @@ namespace FoxTube
nav.MenuItems.Add(SecretsVault.Subscriptions[k]);
}
HistorySet.Load();
if (content.Frame.Content != null)
content.Refresh();
else
content.Frame.Navigate(typeof(Home));
break;
case false:
content.Frame.Navigate(typeof(Home));
for (int k = nav.MenuItems.Count - 1; k > 8; k--)
nav.MenuItems.RemoveAt(k);
@@ -216,6 +223,9 @@ namespace FoxTube
subsHeader.Visibility = Visibility.Collapsed;
subsHeader.Visibility = Visibility.Collapsed;
content.Frame.BackStack.Clear();
content.Frame.ForwardStack.Clear();
break;
default:
@@ -237,17 +247,13 @@ namespace FoxTube
break;
}
await Dispatcher.RunIdleAsync((command) => DownloadAgent.Initialize());
if (videoPlaceholder.Frame.Content != null)
{
MaximizeVideo();
videoPlaceholder.Refresh();
}
wasInvoked = false;
if (content.Content != null)
content.Navigate(content.CurrentSourcePageType, (content.Content as NavigationPage).Parameter);
else
content.Navigate(typeof(Home));
if (videoPlaceholder.Content != null)
GoToVideo((videoPlaceholder.Content as VideoPage).videoId, (videoPlaceholder.Content as VideoPage).playlistId);
DownloadAgent.Initialize();
}
private async void Feedback_Click(object sender, RoutedEventArgs e)
@@ -262,120 +268,69 @@ namespace FoxTube
private void Logout_Click(object sender, RoutedEventArgs e)
{
avatar.Flyout.Hide();
SecretsVault.Deauthenticate();
}
public void GoToSearch(SearchParameters args)
{
nav.IsPaneOpen = false;
content.Navigate(typeof(Search), args);
content.Frame.Navigate(typeof(Search), new object[] { args, content });
}
public void GoToChannel(string id)
{
content.Navigate(typeof(ChannelPage), id);
content.Frame.Navigate(typeof(ChannelPage), id);
}
public void GoToHome()
{
content.Navigate(typeof(Home));
content.Frame.Navigate(typeof(Home));
}
public async void GoToVideo(string id, string playlistId = null, bool incognito = false)
public void GoToVideo(string id, string playlistId = null, bool incognito = false)
{
if (SettingsStorage.CheckConnection)
try
{
bool cancel = false;
ConnectionCost connection = NetworkInformation.GetInternetConnectionProfile().GetConnectionCost();
if (connection.NetworkCostType == NetworkCostType.Fixed || connection.NetworkCostType == NetworkCostType.Variable)
{
MessageDialog dialog = new MessageDialog(resources.GetString("/Main/metered"))
{
DefaultCommandIndex = 2,
CancelCommandIndex = 1
};
dialog.Commands.Add(new UICommand(resources.GetString("/Main/yes")));
dialog.Commands.Add(new UICommand(resources.GetString("/Main/no"), (command) => cancel = true));
if (SecretsVault.IsAuthorized)
dialog.Commands.Add(new UICommand(resources.GetString("/Main/addLater"), (command) =>
{
try
{
PlaylistItem item = new PlaylistItem()
{
Snippet = new PlaylistItemSnippet()
{
ResourceId = new ResourceId()
{
Kind = "youtube#video",
VideoId = id
},
PlaylistId = "WL"
}
};
SecretsVault.Service.PlaylistItems.Insert(item, "snippet").Execute();
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
}
cancel = true;
}));
await dialog.ShowAsync();
if (cancel)
return;
}
}
catch { }
if (videoPlaceholder.Content != null)
(videoPlaceholder.Content as VideoPage).CloseVideo();
MaximizeVideo();
nav.IsBackEnabled = true;
nav.ExpandedModeThresholdWidth = short.MaxValue;
nav.IsPaneOpen = false;
VideoPageSizeChanged?.Invoke(this, true);
videoPlaceholder.Navigate(typeof(VideoPage), new object[3] { id, playlistId, incognito });
videoPlaceholder.Frame.Navigate(typeof(VideoPage), new object[3] { id, playlistId, incognito });
Title.Text = resources.GetString("/Main/video");
}
public void GoToDeveloper(string id)
{
content.Navigate(typeof(Settings), id);
content.Frame.Navigate(typeof(Settings), id);
}
public void GoToPlaylist(string id)
{
content.Navigate(typeof(PlaylistPage), id);
content.Frame.Navigate(typeof(PlaylistPage), id);
}
public void GoToHistory()
{
content.Navigate(typeof(History));
content.Frame.Navigate(typeof(History));
}
public void GoToDownloads()
{
content.Navigate(typeof(Downloads));
content.Frame.Navigate(typeof(Downloads));
}
public void MinimizeAsInitializer()
{
if (videoPlaceholder.Content == null)
if (videoPlaceholder.Frame.Content == null)
return;
if ((videoPlaceholder.Content as VideoPage).LoadingPage.State != LoadingState.Loaded)
if (videoPlaceholder.LoadingPage.State != LoadingState.Loaded)
CloseVideo();
else
(videoPlaceholder.Content as VideoPage).Player.Minimize();
(videoPlaceholder.Frame.Content as VideoPage).Player.Minimize();
Title.Text = headers[content.SourcePageType];
Title.Text = headers[content.Frame.SourcePageType];
}
public void MinimizeVideo()
@@ -386,27 +341,28 @@ namespace FoxTube
videoPlaceholder.HorizontalAlignment = HorizontalAlignment.Right;
videoPlaceholder.Margin = new Thickness(0, 0, 25, 50);
if (content.CanGoBack)
if (content.Frame.CanGoBack)
nav.IsBackEnabled = true;
else
nav.IsBackEnabled = false;
VideoPageSizeChanged?.Invoke(this, false);
SetNavigationMenu();
Title.Text = headers[content.SourcePageType];
Title.Text = headers[content.Frame.SourcePageType];
}
void SetNavigationMenu()
{
if (content.SourcePageType == typeof(Home) || content.SourcePageType == typeof(Settings) || content.SourcePageType == typeof(Subscriptions))
if (content.Frame.SourcePageType == typeof(Home) || content.Frame.SourcePageType == typeof(Settings) || content.Frame.SourcePageType == typeof(Subscriptions))
{
nav.ExpandedModeThresholdWidth = 1008;
nav.IsPaneOpen = nav.DisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Expanded ? true : false;
}
else
{
nav.ExpandedModeThresholdWidth = short.MaxValue;
nav.IsPaneOpen = false;
}
}
public void MaximizeVideo()
@@ -417,15 +373,13 @@ namespace FoxTube
videoPlaceholder.HorizontalAlignment = HorizontalAlignment.Stretch;
videoPlaceholder.Margin = new Thickness(0);
if (videoPlaceholder.Content == null)
if (videoPlaceholder.Frame.Content == null)
return;
nav.IsBackEnabled = true;
Title.Text = resources.GetString("/Main/video");
nav.ExpandedModeThresholdWidth = short.MaxValue;
nav.IsPaneOpen = false;
VideoPageSizeChanged?.Invoke(this, true);
}
public void CloseVideo()
@@ -433,20 +387,15 @@ namespace FoxTube
if (ApplicationView.GetForCurrentView().IsFullScreenMode)
ApplicationView.GetForCurrentView().ExitFullScreenMode();
videoPlaceholder.Content = null;
videoPlaceholder.Frame.Content = null;
GC.Collect();
MaximizeVideo();
if (content.CanGoBack)
nav.IsBackEnabled = true;
else
nav.IsBackEnabled = false;
VideoPageSizeChanged?.Invoke(this, false);
nav.IsBackEnabled = content.Frame.CanGoBack;
SetNavigationMenu();
Title.Text = headers[content.SourcePageType];
Title.Text = headers[content.Frame.SourcePageType];
}
private void Search_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
@@ -464,7 +413,7 @@ namespace FoxTube
try
{
XmlDocument doc = new XmlDocument();
doc.Load($"http://suggestqueries.google.com/complete/search?ds=yt&client=toolbar&q={search.Text}");
doc.Load($"http://suggestqueries.google.com/complete/search?ds=yt&client=toolbar&q={search.Text}&hl={SettingsStorage.RelevanceLanguage}");
List<string> suggestions = new List<string>();
@@ -492,7 +441,7 @@ namespace FoxTube
public void Content_Navigated(object sender, NavigationEventArgs e)
{
Title.Text = headers[content.SourcePageType];
Title.Text = headers[content.Frame.SourcePageType];
if (!wasInvoked)
{
@@ -508,12 +457,17 @@ namespace FoxTube
SetNavigationItem(toHome);
else if (e.SourcePageType == typeof(Search))
SetNavigationItem(null);
else if(e.SourcePageType == typeof(ChannelPage) && SecretsVault.IsAuthorized)
else if(e.SourcePageType == typeof(ChannelPage))
{
if (e.Parameter.ToString() == SecretsVault.AccountId)
SetNavigationItem(toChannel);
else if (nav.MenuItems.Contains(SecretsVault.Subscriptions.Find(i => i.Snippet.ResourceId.ChannelId == e.Parameter.ToString())))
SetNavigationItem(SecretsVault.Subscriptions.Find(i => i.Snippet.ResourceId.ChannelId == e.Parameter.ToString()));
if (SecretsVault.IsAuthorized)
{
if (e.Parameter.ToString() == SecretsVault.AccountId)
SetNavigationItem(toChannel);
else if (nav.MenuItems.Contains(SecretsVault.Subscriptions.Find(i => i.Snippet.ResourceId.ChannelId == e.Parameter.ToString())))
SetNavigationItem(SecretsVault.Subscriptions.Find(i => i.Snippet.ResourceId.ChannelId == e.Parameter.ToString()));
else
SetNavigationItem(null);
}
else
SetNavigationItem(null);
}
@@ -521,87 +475,79 @@ namespace FoxTube
SetNavigationItem(toHistory);
else if(e.SourcePageType == typeof(PlaylistPage))
{
if (e.Parameter.ToString() == SecretsVault.UserChannel.ContentDetails.RelatedPlaylists.Likes)
SetNavigationItem(toLiked);
else if (e.Parameter.Equals("WL"))
SetNavigationItem(toLater);
if (SecretsVault.IsAuthorized)
{
if (e.Parameter.ToString() == SecretsVault.UserChannel.ContentDetails.RelatedPlaylists.Likes)
SetNavigationItem(toLiked);
else if (e.Parameter.Equals("WL"))
SetNavigationItem(toLater);
}
else
SetNavigationItem(null);
}
}
else
wasInvoked = false;
if (content.CanGoBack)
nav.IsBackEnabled = true;
else
nav.IsBackEnabled = false;
nav.IsBackEnabled = content.Frame.CanGoBack;
SetNavigationMenu();
if (videoPlaceholder.Content != null && videoPlaceholder.HorizontalAlignment == HorizontalAlignment.Stretch)
if (videoPlaceholder.Frame.Content != null && videoPlaceholder.HorizontalAlignment == HorizontalAlignment.Stretch)
MinimizeAsInitializer();
}
private void OpenContext(object sender, TappedRoutedEventArgs e)
{
((Microsoft.UI.Xaml.Controls.NavigationViewItem)sender).ContextFlyout.ShowAt((Microsoft.UI.Xaml.Controls.NavigationViewItem)sender);
}
private void RemoveAds_Tapped(object sender, TappedRoutedEventArgs e)
{
SecretsVault.GetAdblock();
}
private void Web_Tapped(object sender, TappedRoutedEventArgs e)
{
content.Navigate(typeof(Home1));
}
private void Nav_SelectionChanged(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs args)
{
try
if (!wasInvoked)
{
if (!wasInvoked)
{
wasInvoked = true;
if (args.IsSettingsSelected)
content.Navigate(typeof(Settings));
else
{
if (args.SelectedItem == toHome)
content.Navigate(typeof(Home));
else if (args.SelectedItem == toHistory)
content.Navigate(typeof(History));
else if (args.SelectedItem == toLiked)
content.Navigate(typeof(PlaylistPage), SecretsVault.UserChannel.ContentDetails.RelatedPlaylists.Likes);
else if (args.SelectedItem == toLater)
content.Navigate(typeof(PlaylistPage), "WL");
else if (args.SelectedItem == toSubscriptions)
content.Navigate(typeof(Subscriptions));
else if (args.SelectedItem == toDownloads)
content.Navigate(typeof(Downloads));
else if (args.SelectedItem == toChannel)
content.Navigate(typeof(ChannelPage), SecretsVault.UserChannel.Id);
else
content.Navigate(typeof(ChannelPage), (args.SelectedItem as Subscription).Snippet.ResourceId.ChannelId);
}
}
if (content == null)
return;
wasInvoked = true;
if (args.IsSettingsSelected)
content.Frame.Navigate(typeof(Settings));
else
wasInvoked = false;
{
if (args.SelectedItem == toHome)
content.Frame.Navigate(typeof(Home));
else if (args.SelectedItem == toHistory)
content.Frame.Navigate(typeof(History));
else if (args.SelectedItem == toLiked)
content.Frame.Navigate(typeof(PlaylistPage), SecretsVault.UserChannel.ContentDetails.RelatedPlaylists.Likes);
else if (args.SelectedItem == toLater)
content.Frame.Navigate(typeof(PlaylistPage), "WL");
else if (args.SelectedItem == toSubscriptions)
content.Frame.Navigate(typeof(Subscriptions));
else if (args.SelectedItem == toDownloads)
content.Frame.Navigate(typeof(Downloads));
else if (args.SelectedItem == toChannel)
content.Frame.Navigate(typeof(ChannelPage), SecretsVault.UserChannel.Id);
else
content.Frame.Navigate(typeof(ChannelPage), (args.SelectedItem as Subscription).Snippet.ResourceId.ChannelId);
}
}
catch { }
else
wasInvoked = false;
}
private void Nav_BackRequested(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewBackRequestedEventArgs args)
{
if (videoPlaceholder.Content != null && videoPlaceholder.Width == double.NaN)
if (videoPlaceholder.Frame.Content != null && double.IsNaN(videoPlaceholder.Width))
{
if ((videoPlaceholder.Content as VideoPage).LoadingPage.State != LoadingState.Loaded)
if (videoPlaceholder.Frame.CanGoBack)
videoPlaceholder.Frame.GoBack();
else if (videoPlaceholder.LoadingPage.State != LoadingState.Loaded)
CloseVideo();
else if (videoPlaceholder.HorizontalAlignment == HorizontalAlignment.Stretch)
else
MinimizeAsInitializer();
}
else
content.GoBack();
content.Frame.GoBack();
}
private void Nav_PaneClosing(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewPaneClosingEventArgs args)
@@ -612,11 +558,6 @@ namespace FoxTube
private void Nav_PaneOpened(Microsoft.UI.Xaml.Controls.NavigationView sender, object args)
{
AppTitle.Visibility = Visibility.Visible;
if (sender.DisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Expanded && sender.IsPaneOpen)
AppTitleBar.Margin = new Thickness(40, 0, 0, 0);
else
AppTitleBar.Margin = new Thickness();
}
}
}
+4 -7
View File
@@ -5,7 +5,6 @@
xmlns:local="using:FoxTube.Pages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:foxtube="using:FoxTube"
xmlns:controls="using:FoxTube.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
@@ -40,19 +39,19 @@
<RowDefinition Height="auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="20" Grid.Column="1" x:Name="cover" HorizontalAlignment="Left">
<StackPanel Grid.Row="0" Margin="10" Grid.Column="1" x:Name="cover" HorizontalAlignment="Left">
<Image Source="/Assets/videoThumbSample.png" Name="thumbnail"/>
<TextBlock FontSize="20" Text="[Title]" TextWrapping="WrapWholeWords" Name="title"/>
<TextBlock Foreground="Gray" Text="# videos | # views | Updated at: ##-##-## ##:##:##" TextWrapping="WrapWholeWords" Name="info"/>
<TextBlock Foreground="Gray" Text="description" TextWrapping="WrapWholeWords" Name="description"/>
<Button Margin="0,10,0,0" Background="Transparent" Name="toChannel" Click="toChannel_Click">
<Button Margin="10" Background="Transparent" Name="toChannel" Click="toChannel_Click">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<PersonPicture Height="50" Name="avatar"/>
<TextBlock Grid.Column="1" Text="Channel name" FontSize="18" VerticalAlignment="Center" Margin="10,0,0,0" TextWrapping="WrapWholeWords" Name="channelName"/>
<TextBlock Grid.Column="1" Text="Channel name" FontSize="18" VerticalAlignment="Center" Margin="10,0" TextWrapping="WrapWholeWords" Name="channelName"/>
</Grid>
</Button>
</StackPanel>
@@ -76,7 +75,7 @@
</StackPanel>
</Grid>
</ScrollViewer>
<CommandBar Grid.Row="2">
<AppBarButton x:Uid="/Playlist/openWeb" Icon="Globe" Label="Open in browser" Name="inBrowser" Click="inBrowser_Click"/>
<AppBarButton x:Uid="/Playlist/addTo" Icon="Add" Label="Add to" IsEnabled="False" Visibility="Collapsed">
@@ -89,7 +88,5 @@
<AppBarButton x:Uid="/Playlist/refresh" Icon="Refresh" Label="Refresh" Name="refresh" Click="refresh_Click"/>
<AppBarButton x:Uid="/Playlist/share" Icon="Share" Label="Share" Name="share" Click="share_Click"/>
</CommandBar>
<foxtube:LoadingPage Grid.RowSpan="2" Visibility="Collapsed" RefreshPage="refresh_Click" x:Name="loading"/>
</Grid>
</Page>
+15 -29
View File
@@ -19,7 +19,7 @@ namespace FoxTube.Pages
/// <summary>
/// Playlist page
/// </summary>
public sealed partial class PlaylistPage : Page, NavigationPage
public sealed partial class PlaylistPage : Page, INavigationPage
{
public object Parameter { get; set; } = null;
public string playlistId;
@@ -32,17 +32,14 @@ namespace FoxTube.Pages
public PlaylistPage()
{
InitializeComponent();
DataTransferManager.GetForCurrentView().DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(Share);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
Parameter = e.Parameter;
if (e.Parameter == null)
loading.Error("NullReferenceException", "Unable to initialize page. Playlist ID is not stated.");
else if (e.Parameter as string == "WL")
if (e.Parameter as string == "WL")
LoadWL();
else
Initialize(e.Parameter as string);
@@ -50,8 +47,6 @@ namespace FoxTube.Pages
public async void Initialize(string id)
{
loading.Refresh();
try
{
playlistId = id;
@@ -66,16 +61,10 @@ namespace FoxTube.Pages
info.Text = $"{item.ContentDetails.ItemCount} {ResourceLoader.GetForCurrentView("Playlist").GetString("/Playlist/videos")}";
description.Text = item.Snippet.Localized.Description;
channelName.Text = item.Snippet.ChannelTitle;
ChannelsResource.ListRequest channelRequest = SecretsVault.Service.Channels.List("snippet");
channelRequest.Id = item.Snippet.ChannelId;
Channel channel = (await channelRequest.ExecuteAsync()).Items[0];
channelName.Text = Methods.GuardFromNull(item.Snippet.ChannelTitle);
try { avatar.ProfilePicture = new BitmapImage(channel.Snippet.Thumbnails.Medium.Url.ToUri()) { DecodePixelWidth = 50, DecodePixelHeight = 50 }; }
catch { }
try { thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri()); }
catch { }
avatar.ProfilePicture = new BitmapImage((await new YoutubeExplode.YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelWidth = 50, DecodePixelHeight = 50 };
thumbnail.Source = new BitmapImage(item.Snippet.Thumbnails.Medium.Url.ToUri());
request = SecretsVault.Service.PlaylistItems.List("contentDetails");
request.PlaylistId = id;
@@ -89,15 +78,15 @@ namespace FoxTube.Pages
foreach (PlaylistItem i in response.Items)
list.Add(new VideoCard(i.ContentDetails.VideoId, playlistId));
loading.Close();
Methods.MainPage.PageContent.LoadingPage.Close();
}
catch (System.Net.Http.HttpRequestException)
{
loading.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
Methods.MainPage.PageContent.LoadingPage.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
}
catch (Exception e)
{
loading.Error(e.GetType().ToString(), e.Message);
Methods.MainPage.PageContent.LoadingPage.Error(e.GetType().ToString(), e.Message);
Analytics.TrackEvent("Playlist loading error", new Dictionary<string, string>()
{
{ "Exception", e.GetType().ToString() },
@@ -109,8 +98,6 @@ namespace FoxTube.Pages
public async void LoadWL()
{
loading.Refresh();
try
{
playlistId = "WL";
@@ -125,10 +112,8 @@ namespace FoxTube.Pages
channelName.Text = SecretsVault.UserChannel.Snippet.Title;
try { avatar.ProfilePicture = new BitmapImage(SecretsVault.UserChannel.Snippet.Thumbnails.Medium.Url.ToUri()) { DecodePixelWidth = 50, DecodePixelHeight = 50 }; }
catch { }
try { thumbnail.Source = new BitmapImage((await new YoutubeExplode.YoutubeClient().GetVideoAsync(SecretsVault.WatchLater.First())).Thumbnails.HighResUrl.ToUri()); }
catch { }
avatar.ProfilePicture = new BitmapImage(SecretsVault.UserChannel.Snippet.Thumbnails.Medium.Url.ToUri()) { DecodePixelWidth = 50, DecodePixelHeight = 50 };
thumbnail.Source = new BitmapImage((await new YoutubeExplode.YoutubeClient().GetVideoAsync(SecretsVault.WatchLater.First())).Thumbnails.HighResUrl.ToUri());
for (int k = 0; k < 25 && k < SecretsVault.WatchLater.Count; k++)
list.Add(new VideoCard(SecretsVault.WatchLater[k], "WL"));
@@ -136,11 +121,11 @@ namespace FoxTube.Pages
if (list.Count >= SecretsVault.WatchLater.Count)
more.Visibility = Visibility.Collapsed;
loading.Close();
Methods.MainPage.PageContent.LoadingPage.Close();
}
catch (Exception e)
{
loading.Error(e.GetType().ToString(), e.Message);
Methods.MainPage.PageContent.LoadingPage.Error(e.GetType().ToString(), e.Message);
Analytics.TrackEvent("WL playlist loading error", new Dictionary<string, string>()
{
{ "Exception", e.GetType().ToString() },
@@ -161,11 +146,12 @@ namespace FoxTube.Pages
private void refresh_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.GoToPlaylist(playlistId);
Methods.MainPage.VideoContent.Refresh();
}
private void share_Click(object sender, RoutedEventArgs e)
{
DataTransferManager.GetForCurrentView().DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(Share);
DataTransferManager.ShowShareUI();
}
-2
View File
@@ -76,7 +76,5 @@
<AppBarButton Label="Open in browser" Icon="Globe" Name="inBrowser" Click="InBrowser_Click"/>
<AppBarButton Label="Refresh" Icon="Refresh" Click="AppBarButton_Click"/>
</CommandBar>
<local:LoadingPage x:Name="loading" Grid.RowSpan="2" Visibility="Collapsed" RefreshPage="AppBarButton_Click"/>
</Grid>
</Page>
+10 -16
View File
@@ -16,11 +16,12 @@ namespace FoxTube
/// <summary>
/// Search page
/// </summary>
public sealed partial class Search : Page, NavigationPage
public sealed partial class Search : Page, INavigationPage
{
public object Parameter { get; set; } = null;
readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("Search");
ContentFrame frame;
public SearchParameters Parameters;
SearchResource.ListRequest request;
string nextToken;
@@ -65,21 +66,13 @@ namespace FoxTube
{
base.OnNavigatedTo(e);
Parameter = e.Parameter;
if (e.Parameter == null)
loading.Error("NullReferenceException", "Unable to initialize search. Search term is not stated.");
else
{
if (e.Parameter is SearchParameters)
Initialize(e.Parameter as SearchParameters);
else
loading.Error("ArgumentException", "Wrong parameter");
}
frame = ((object[])e.Parameter)[1] as ContentFrame;
Initialize(((object[])e.Parameter)[0] as SearchParameters);
}
public async void Initialize(SearchParameters arg)
{
loading.Refresh();
try
{
Parameters = arg;
@@ -129,6 +122,7 @@ namespace FoxTube
SearchListResponse response = await request.ExecuteAsync();
searchTerm.Text = $"{resources.GetString("/Search/header")} '{Parameters.Term}'";
resultsCount.Text = $"{resources.GetString("/Search/found")}: {SetResults(response.PageInfo.TotalResults)} {resources.GetString("/Search/items")}";
if (!string.IsNullOrWhiteSpace(response.NextPageToken))
nextToken = response.NextPageToken;
else
@@ -138,15 +132,15 @@ namespace FoxTube
foreach (SearchResult item in response.Items)
AddItem(item);
loading.Close();
frame.LoadingPage.Close();
}
catch (System.Net.Http.HttpRequestException)
{
loading.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
frame.LoadingPage.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
}
catch (Exception e)
{
loading.Error(e.GetType().ToString(), e.Message);
frame.LoadingPage.Error(e.GetType().ToString(), e.Message);
Analytics.TrackEvent("Search loading error", new Dictionary<string, string>()
{
{ "Exception", e.GetType().ToString() },
@@ -172,7 +166,7 @@ namespace FoxTube
private void AppBarButton_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.GoToSearch(Parameters);
frame.Refresh();
}
private async void More_Clicked()
+4 -4
View File
@@ -9,19 +9,19 @@
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Pivot SelectedIndex="0" Name="pivot" IsHeaderItemsCarouselEnabled="False" SelectionChanged="Pivot_SelectionChanged">
<PivotItem Header="General" x:Uid="/Settings/general">
<PivotItem Name="generalTab" Header="General" x:Uid="/Settings/general">
<ScrollViewer>
<settingspages:General/>
</ScrollViewer>
</PivotItem>
<PivotItem Header="About us" x:Uid="/Settings/about">
<PivotItem Name="aboutTab" Header="About us" x:Uid="/Settings/about">
<ScrollViewer>
<settingspages:About/>
</ScrollViewer>
</PivotItem>
<PivotItem Header="Inbox" x:Uid="/Settings/inbox">
<PivotItem Name="inboxTab" Header="Inbox" x:Uid="/Settings/inbox">
<ScrollViewer>
<settingspages:Inbox/>
<settingspages:Inbox x:Name="inbox"/>
</ScrollViewer>
</PivotItem>
</Pivot>
+19 -13
View File
@@ -7,9 +7,10 @@ namespace FoxTube
/// <summary>
/// Settings tabs placeholder
/// </summary>
public sealed partial class Settings : Page, NavigationPage
public sealed partial class Settings : Page, INavigationPage
{
public object Parameter { get; set; } = null;
bool inboxLoaded = false;
string inboxId = null;
public Settings()
@@ -21,24 +22,29 @@ namespace FoxTube
{
base.OnNavigatedTo(e);
Parameter = e.Parameter;
if (!string.IsNullOrWhiteSpace(e.Parameter as string))
{
inboxId = e.Parameter as string;
pivot.SelectedIndex = 2;
}
if(!string.IsNullOrWhiteSpace((string)e.Parameter))
switch((string)e.Parameter)
{
case "about":
pivot.SelectedItem = aboutTab;
break;
default:
inboxId = (string)e.Parameter;
pivot.SelectedItem = inboxTab;
break;
}
Methods.MainPage.PageContent.LoadingPage.Close();
}
private void Pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (pivot.SelectedIndex == 2 && !inboxLoaded)
if (pivot.SelectedItem == inboxTab && !inboxLoaded)
{
(((pivot.Items[2] as PivotItem).Content as ScrollViewer).Content as Inbox).LoadItems();
inbox.LoadItems(inboxId);
inboxLoaded = true;
if(inboxId != null)
{
(((pivot.Items[2] as PivotItem).Content as ScrollViewer).Content as Inbox).Open(inboxId as string);
inboxId = null;
}
inboxId = null;
}
}
}
+20 -41
View File
@@ -7,48 +7,27 @@
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid Name="grid">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="600"/>
</VisualState.StateTriggers>
<StackPanel>
<TextBlock Text="FoxTube" FontSize="28"/>
<TextBlock Name="version" Text="[currentVersion]" FontSize="14" Foreground="Gray" Margin="0,-5,0,0"/>
<VisualState.Setters>
<Setter Target="grid.ColumnDefinitions[1].Width" Value="auto"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="0"/>
</Grid.ColumnDefinitions>
<TextBlock x:Uid="/About/developed" TextWrapping="WrapWholeWords" Text="Developed by Michael &#x22;XFox&#x22; Gordeev" Margin="0,10,0,0"/>
<TextBlock TextWrapping="WrapWholeWords" Visibility="Collapsed" Text="Special thanks to contributors for motivating me, testers and translators for making this app better everyday and you for using this app;)" Margin="0,10,0,0"/>
<StackPanel>
<TextBlock Text="FoxTube" FontSize="28"/>
<TextBlock Name="version" Text="[currentVersion]" FontSize="14" Foreground="Gray" Margin="0,-5,0,10"/>
<TextBlock x:Uid="/About/contacts" Text="Contacts" FontSize="22" FontWeight="SemiBold" Margin="0,10,0,0"/>
<TextBlock><Run x:Uid="/About/twitter">Twitter:</Run> <Hyperlink NavigateUri="https://twitter.com/xfox111">@xfox111</Hyperlink></TextBlock>
<TextBlock><Run x:Uid="/About/vk">Vkontakte:</Run> <Hyperlink NavigateUri="https://vk.com/XFox.Mike">@XFox.Mike</Hyperlink></TextBlock>
<TextBlock Visibility="Collapsed">YouTube: <Hyperlink NavigateUri="https://youtube.com/c/FoxGameStudioChannel">@xfox</Hyperlink></TextBlock>
<TextBlock>E-mail: <Hyperlink NavigateUri="mailto:michael.xfox@outlook.com">michael.xfox@outlook.com</Hyperlink></TextBlock>
<TextBlock Visibility="Collapsed"><Run x:Uid="/About/myBlog">My website:</Run> <Hyperlink NavigateUri="https://michael-xfox.com">https://michael-xfox.com</Hyperlink></TextBlock>
<TextBlock x:Uid="/About/developed" TextWrapping="WrapWholeWords" Text="Developed by Michael Gordeev (also known as XFox)" Margin="0,0,0,10"/>
<TextBlock TextWrapping="WrapWholeWords" Visibility="Collapsed" Text="Special thanks to contributors for motivating me, testers and translators for making this app better everyday and you for using this app;)" Margin="0,0,0,10"/>
<TextBlock x:Uid="/About/contacts" Text="Contacts" FontSize="22" FontWeight="SemiBold"/>
<TextBlock><Run x:Uid="/About/twitter">Twitter:</Run> <Hyperlink NavigateUri="https://twitter.com/XFox_Mike">@XFox_Mike</Hyperlink></TextBlock>
<TextBlock><Run x:Uid="/About/vk">Vkontakte:</Run> <Hyperlink NavigateUri="https://vk.com/XFox.Mike">@XFox.Mike</Hyperlink></TextBlock>
<TextBlock Visibility="Collapsed">YouTube: <Hyperlink NavigateUri="https://youtube.com/c/FoxGameStudioChannel">@FoxGameStudioChannel</Hyperlink></TextBlock>
<TextBlock>E-mail: <Hyperlink NavigateUri="mailto:michael.xfox@outlook.com">michael.xfox@outlook.com</Hyperlink></TextBlock>
<TextBlock Visibility="Collapsed" Margin="0,0,0,10"> <Run x:Uid="/About/myBlog">My blog (Russian language only):</Run> <Hyperlink NavigateUri="https://michael-xfox.com">https://michael-xfox.com</Hyperlink></TextBlock>
<TextBlock x:Uid="/About/legal" Text="Legal stuff" FontSize="22" FontWeight="SemiBold"/>
<HyperlinkButton x:Uid="/About/ourPrivacy" Content="Our Privacy Policy" NavigateUri="https://foxgame-studio.000webhostapp.com/FoxTubeAssets/PrivacyPolicy.txt" Padding="0,5,0,0"/>
<HyperlinkButton x:Uid="/About/ytPrivacy" Content="YouTube Privacy Policy" NavigateUri="https://youtube.com/t/privacy" Padding="0"/>
<HyperlinkButton x:Uid="/About/terms" Content="YouTube Terms of use" NavigateUri="https://youtube.com/t/terms" Padding="0"/>
<HyperlinkButton x:Uid="/About/guides" Content="YouTube Community Guidelines" NavigateUri="https://youtube.com/t/community_guidelines" Padding="0,0,0,10"/>
<TextBlock x:Uid="/About/crMe" Text="© 2018 Michael Gordeev"/>
<TextBlock x:Uid="/About/crYt" Text="© 2018 YouTube, LLC"/>
<Button Name="feedback" x:Uid="/About/feedback" Content="Leave feedback" Margin="0,5" Click="Button_Click" Visibility="Collapsed"/>
</StackPanel>
<Image Grid.Column="1" Source="/Assets/LogoAvatar.png" VerticalAlignment="Top" Width="128"/>
</Grid>
<TextBlock x:Uid="/About/legal" Text="Legal stuff" FontSize="22" FontWeight="SemiBold" Margin="0,10,0,0"/>
<HyperlinkButton x:Uid="/About/ourPrivacy" Content="Our Privacy Policy" NavigateUri="https://foxgame-studio.000webhostapp.com/FoxTubeAssets/PrivacyPolicy.txt" Padding="0"/>
<HyperlinkButton x:Uid="/About/ytPrivacy" Content="YouTube Privacy Policy" NavigateUri="https://youtube.com/t/privacy" Padding="0"/>
<HyperlinkButton x:Uid="/About/terms" Content="YouTube Terms of use" NavigateUri="https://youtube.com/t/terms" Padding="0"/>
<HyperlinkButton x:Uid="/About/guides" Content="YouTube Community Guidelines" NavigateUri="https://youtube.com/t/community_guidelines" Padding="0,0,0,10"/>
<TextBlock Name="crMe" x:Uid="/About/crMe" Text="© Michael Gordeev"/>
<TextBlock Name="crYt" x:Uid="/About/crYt" Text="© YouTube, LLC"/>
<Button Name="feedback" x:Uid="/About/feedback" Content="Leave feedback" Margin="0,10" Click="Button_Click" Visibility="Collapsed"/>
</StackPanel>
</Page>
@@ -17,6 +17,9 @@ namespace FoxTube.Pages.SettingsPages
PackageVersion ver = Package.Current.Id.Version;
version.Text = $"{ver.Major}.{ver.Minor}.{ver.Build}";
crMe.Text = crMe.Text.Insert(1, " " + DateTime.Now.Year);
crYt.Text = crYt.Text.Insert(1, " " + DateTime.Now.Year);
if (StoreServicesFeedbackLauncher.IsSupported())
feedback.Visibility = Visibility.Visible;
}
+20 -19
View File
@@ -26,6 +26,7 @@ namespace FoxTube.Pages.SettingsPages
foreach (VideoQuality i in Enum.GetValues(typeof(VideoQuality)).ToReversedList())
quality.Items.Add(new ComboBoxItem() { Tag = i.GetVideoQualityLabel(), Content = i.GetVideoQualityLabel() });
quality.SelectedItem = quality.Items.ToList().Find(i => ((ComboBoxItem)i).Tag.ToString() == SettingsStorage.VideoQuality);
mobileWarning.IsOn = SettingsStorage.CheckConnection;
autoplay.IsOn = SettingsStorage.Autoplay;
@@ -50,34 +51,34 @@ namespace FoxTube.Pages.SettingsPages
async void InitializeRegions()
{
//Initialize regions
I18nRegionsResource.ListRequest regRequest = SecretsVault.Service.I18nRegions.List("snippet");
regRequest.Hl = SettingsStorage.Language;
I18nRegionListResponse regResponse = await regRequest.ExecuteAsync();
regResponse.Items.ForEach(i => region.Items.Add(new ComboBoxItem
{
Content = i.Snippet.Name,
Tag = i.Snippet.Gl
}));
region.SelectedItem = region.Items.Find(i => ((ComboBoxItem)i).Tag as string == SettingsStorage.Region) ?? region.Items.Find(i => ((ComboBoxItem)i).Tag as string == SettingsStorage.Language.Remove(0, 3));
region.SelectedItem = region.Items.Find(i => ((ComboBoxItem)i).Tag as string == SettingsStorage.Region);
//Initialize relevance language
I18nLanguagesResource.ListRequest langRequest = SecretsVault.Service.I18nLanguages.List("snippet");
langRequest.Hl = SettingsStorage.Language;
I18nLanguageListResponse langResponse = await langRequest.ExecuteAsync();
foreach(I18nLanguage i in langResponse.Items)
langResponse.Items.ForEach(i => relLanguage.Items.Add(new ComboBoxItem
{
relLanguage.Items.Add(new ComboBoxItem
{
Content = i.Snippet.Name,
Tag = i.Snippet.Hl
});
if (SettingsStorage.RelevanceLanguage == i.Snippet.Hl)
relLanguage.SelectedItem = relLanguage.Items.Last();
}
Content = i.Snippet.Name,
Tag = i.Snippet.Hl
}));
relLanguage.SelectedItem = relLanguage.Items.Find(i => ((ComboBoxItem)i).Tag as string == SettingsStorage.RelevanceLanguage);
}
private void language_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (SettingsStorage.Language == (language.SelectedItem as ComboBoxItem).Tag.ToString())
if ((language.SelectedItem as ComboBoxItem).Tag.ToString() == SettingsStorage.Language)
return;
ApplicationLanguages.PrimaryLanguageOverride = (language.SelectedItem as ComboBoxItem).Tag.ToString();
SettingsStorage.Language = (language.SelectedItem as ComboBoxItem).Tag.ToString();
@@ -104,6 +105,11 @@ namespace FoxTube.Pages.SettingsPages
SettingsStorage.VideoNotifications = newVideo.IsOn;
}
private void devNews_Toggled(object sender, RoutedEventArgs e)
{
SettingsStorage.DevNotifications = devNews.IsOn;
}
private void RelLanguage_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SettingsStorage.RelevanceLanguage = ((ComboBoxItem)relLanguage.SelectedItem).Tag.ToString();
@@ -121,17 +127,17 @@ namespace FoxTube.Pages.SettingsPages
private void RadioButton_Checked(object sender, RoutedEventArgs e)
{
if (sender == light && SettingsStorage.Theme != 0)
if (sender == light)
{
SettingsStorage.Theme = 0;
Methods.MainPage.RequestedTheme = ElementTheme.Light;
}
else if (sender == dark && SettingsStorage.Theme != 1)
else if (sender == dark)
{
SettingsStorage.Theme = 1;
Methods.MainPage.RequestedTheme = ElementTheme.Dark;
}
else if (sender == system && SettingsStorage.Theme != 2)
else if (sender == system)
{
SettingsStorage.Theme = 2;
if (new Windows.UI.ViewManagement.UISettings().GetColorValue(Windows.UI.ViewManagement.UIColorType.Background) == Colors.Black)
@@ -146,10 +152,5 @@ namespace FoxTube.Pages.SettingsPages
{
CoreApplication.Exit();
}
private void devNews_Toggled(object sender, RoutedEventArgs e)
{
SettingsStorage.DevNotifications = devNews.IsOn;
}
}
}
+5 -2
View File
@@ -10,7 +10,7 @@
<Grid Name="grid">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualStateGroup CurrentStateChanged="VisualStateGroup_CurrentStateChanged">
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="1500"/>
@@ -20,6 +20,7 @@
<Setter Target="grid.ColumnDefinitions[0].Width" Value="*"/>
<Setter Target="grid.ColumnDefinitions[1].Width" Value="3*"/>
<Setter Target="close.Visibility" Value="Collapsed"/>
<Setter Target="selector.Background" Value="{StaticResource SystemControlBackgroundChromeMediumLowBrush}"/>
</VisualState.Setters>
</VisualState>
@@ -32,6 +33,7 @@
<Setter Target="grid.ColumnDefinitions[0].Width" Value="*"/>
<Setter Target="grid.ColumnDefinitions[1].Width" Value="2*"/>
<Setter Target="close.Visibility" Value="Collapsed"/>
<Setter Target="selector.Background" Value="{StaticResource SystemControlBackgroundChromeMediumLowBrush}"/>
</VisualState.Setters>
</VisualState>
@@ -44,6 +46,7 @@
<Setter Target="grid.ColumnDefinitions[0].Width" Value="*"/>
<Setter Target="grid.ColumnDefinitions[1].Width" Value="*"/>
<Setter Target="close.Visibility" Value="Collapsed"/>
<Setter Target="selector.Background" Value="{StaticResource SystemControlBackgroundChromeMediumLowBrush}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
@@ -53,7 +56,7 @@
<ColumnDefinition Width="0"/>
</Grid.ColumnDefinitions>
<StackPanel VerticalAlignment="Stretch" Background="{StaticResource SystemControlBackgroundChromeMediumLowBrush}">
<StackPanel VerticalAlignment="Stretch" Name="selector">
<ComboBox x:Uid="/Inbox/filter" Header="Filter" Margin="10" HorizontalAlignment="Stretch" SelectedIndex="0" Name="filter" SelectionChanged="filter_SelectionChanged">
<ComboBoxItem x:Uid="/Inbox/all" Content="All"/>
<ComboBoxItem x:Uid="/Inbox/messages" Content="Messages"/>
+54 -58
View File
@@ -7,6 +7,7 @@ using Windows.UI.Xaml.Controls;
using FoxTube.Classes;
using Windows.Storage;
using System.Xml;
using Microsoft.AppCenter.Analytics;
namespace FoxTube.Pages.SettingsPages
{
@@ -15,27 +16,26 @@ namespace FoxTube.Pages.SettingsPages
/// </summary>
public sealed partial class Inbox : Page
{
List<InboxItem> items = new List<InboxItem>();
readonly List<InboxItem> items = new List<InboxItem>();
string open;
public Inbox()
{
InitializeComponent();
}
public async void LoadItems()
public async void LoadItems(string id = null)
{
try
{
XmlDocument doc = new XmlDocument();
StorageFile file = await (await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync(@"Assets\Data")).GetFileAsync("Patchnotes.xml");
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync("ms-appx:///Assets/Data/Patchnotes.xml".ToUri());
doc.Load(await file.OpenStreamForReadAsync());
foreach (XmlElement e in doc["items"].ChildNodes)
items.Add(new InboxItem(
e.GetAttribute("version"),
e["content"][SettingsStorage.Language].InnerText,
e.GetAttribute("time")));
DateTime.Parse(e.GetAttribute("time"))));
doc.Load("http://foxgame-studio.000webhostapp.com/foxtube-messages.xml");
foreach (XmlElement e in doc["posts"].ChildNodes)
@@ -43,59 +43,55 @@ namespace FoxTube.Pages.SettingsPages
e["header"][SettingsStorage.Language].InnerText,
e["content"][SettingsStorage.Language].InnerText,
DateTime.Parse(e.GetAttribute("time")),
e["id"].InnerText
));
items.OrderBy(item => item.TimeStamp);
e["id"].InnerText));
}
catch (Exception e)
{
Analytics.TrackEvent("Failed to load inbox", new Dictionary<string, string>
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message }
});
}
catch { }
items.OrderBy(item => item.TimeStamp);
items.ForEach(i => list.Items.Add(i));
if (!string.IsNullOrWhiteSpace(open))
Open(open);
open = null;
if (!string.IsNullOrWhiteSpace(id))
list.SelectedItem = items.Find(i => i.Id == id);
}
private void filter_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
if (list == null)
return;
list.Items.Clear();
switch (filter.SelectedIndex)
{
list.Items.Clear();
case 0:
items.ForEach(i => list.Items.Add(i));
break;
switch (filter.SelectedIndex)
{
case 0:
items.ForEach(i => list.Items.Add(i));
break;
case 1:
List<InboxItem> messages = items.FindAll(i => i.Type == InboxItemType.Default);
messages.ForEach(i => list.Items.Add(i));
break;
case 1:
List<InboxItem> messages = items.FindAll(i => i.Type == InboxItemType.Default);
messages.ForEach(i => list.Items.Add(i));
break;
case 2:
List<InboxItem> notes = items.FindAll(i => i.Type == InboxItemType.PatchNote);
notes.ForEach(i => list.Items.Add(i));
break;
}
case 2:
List<InboxItem> notes = items.FindAll(i => i.Type == InboxItemType.PatchNote);
notes.ForEach(i => list.Items.Add(i));
break;
}
catch(NullReferenceException) { }
}
private void list_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
InboxItem item = list.SelectedItem as InboxItem;
if(list.SelectedItem != null)
{
OpenView(item.Title, item.Content);
if (grid.ColumnDefinitions[1].Width.Value == 0)
{
grid.ColumnDefinitions[0].Width = new GridLength(0);
grid.ColumnDefinitions[1].Width = new GridLength(1, GridUnitType.Star);
}
}
if (!(list.SelectedItem is InboxItem item))
return;
OpenView(item.Title, item.Content);
}
void CloseView()
@@ -104,6 +100,12 @@ namespace FoxTube.Pages.SettingsPages
title.Text = "";
list.SelectedItem = null;
block.Visibility = Visibility.Visible;
if (grid.ColumnDefinitions[0].Width.Value == 0)
{
grid.ColumnDefinitions[1].Width = new GridLength(0);
grid.ColumnDefinitions[0].Width = new GridLength(1, GridUnitType.Star);
}
}
void OpenView(string header, string body)
@@ -111,29 +113,23 @@ namespace FoxTube.Pages.SettingsPages
content.Text = body;
title.Text = header;
block.Visibility = Visibility.Collapsed;
if (grid.ColumnDefinitions[1].Width.Value == 0)
{
grid.ColumnDefinitions[0].Width = new GridLength(0);
grid.ColumnDefinitions[1].Width = new GridLength(1, GridUnitType.Star);
}
}
private void close_Click(object sender, RoutedEventArgs e)
{
CloseView();
if (grid.ColumnDefinitions[0].Width.Value == 0)
{
grid.ColumnDefinitions[0].Width = new GridLength(1, GridUnitType.Star);
grid.ColumnDefinitions[1].Width = new GridLength(0);
}
}
public void Open(string arg)
private void VisualStateGroup_CurrentStateChanged(object sender, VisualStateChangedEventArgs e)
{
if(items.Count == 0)
{
open = arg;
return;
}
InboxItem item = items.Find(i => i.Id == arg);
if(item != null)
list.SelectedItem = item;
if (e.NewState == null)
CloseView();
}
}
}
+22 -26
View File
@@ -4,34 +4,30 @@
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:foxtube="using:FoxTube"
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<ScrollViewer>
<controls:AdaptiveGridView ItemsSource="{x:Bind list}" DesiredWidth="250" Margin="5,0,0,0">
<controls:AdaptiveGridView.ItemTemplate>
<DataTemplate>
<Button HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Padding="5" Tag="{Binding Path=Snippet.ResourceId.ChannelId}" Click="Button_Click">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="55"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<PersonPicture Height="50" HorizontalAlignment="Left">
<PersonPicture.ProfilePicture>
<BitmapImage UriSource="{Binding Path=Snippet.Thumbnails.Medium.Url}" DecodePixelHeight="50" DecodePixelWidth="50"/>
</PersonPicture.ProfilePicture>
</PersonPicture>
<TextBlock Grid.Column="1" TextWrapping="Wrap" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Text="{Binding Path=Snippet.Title}"/>
</Grid>
</Button>
</DataTemplate>
</controls:AdaptiveGridView.ItemTemplate>
</controls:AdaptiveGridView>
</ScrollViewer>
<foxtube:LoadingPage Visibility="Collapsed"/>
</Grid>
<ScrollViewer>
<controls:AdaptiveGridView ItemsSource="{x:Bind list}" DesiredWidth="250">
<controls:AdaptiveGridView.ItemTemplate>
<DataTemplate>
<Button HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Padding="5" Tag="{Binding Path=Snippet.ResourceId.ChannelId}" Click="Button_Click">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="55"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<PersonPicture Height="50" HorizontalAlignment="Left">
<PersonPicture.ProfilePicture>
<BitmapImage UriSource="{Binding Path=Snippet.Thumbnails.Medium.Url}" DecodePixelHeight="50" DecodePixelWidth="50"/>
</PersonPicture.ProfilePicture>
</PersonPicture>
<TextBlock Grid.Column="1" TextWrapping="Wrap" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Text="{Binding Path=Snippet.Title}"/>
</Grid>
</Button>
</DataTemplate>
</controls:AdaptiveGridView.ItemTemplate>
</controls:AdaptiveGridView>
</ScrollViewer>
</Page>
+3 -1
View File
@@ -8,13 +8,15 @@ namespace FoxTube.Pages
/// <summary>
/// User's subscriptions page
/// </summary>
public sealed partial class Subscriptions : Page, NavigationPage
public sealed partial class Subscriptions : Page, INavigationPage
{
public object Parameter { get; set; } = null;
readonly List<Subscription> list = SecretsVault.Subscriptions;
public Subscriptions()
{
InitializeComponent();
Methods.MainPage.PageContent.LoadingPage.Close();
}
private void Button_Click(object sender, RoutedEventArgs e)
+10 -15
View File
@@ -4,22 +4,17 @@
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:ui="using:Microsoft.Toolkit.Uwp.UI.Controls"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Name="grid" SizeChanged="Grid_SizeChanged">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="0"/>
<ColumnDefinition Width="0"/>
<ColumnDefinition Width="0"/>
<ColumnDefinition Width="0"/>
</Grid.ColumnDefinitions>
<TextBlock Name="empty" Grid.ColumnSpan="5" Text="&#xD8;" FontSize="200" Foreground="Gray" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.RowSpan="2"/>
<StackPanel Grid.Column="0" Name="col0"/>
<StackPanel Grid.Column="1" Name="col1"/>
<StackPanel Grid.Column="2" Name="col2"/>
<StackPanel Grid.Column="3" Name="col3"/>
<StackPanel Grid.Column="4" Name="col4"/>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ui:AdaptiveGridView Name="list" DesiredWidth="400" SelectionMode="None" HorizontalContentAlignment="Left">
<ui:AdaptiveGridView.ItemContainerTransitions>
<TransitionCollection>
<EntranceThemeTransition IsStaggeringEnabled="True"/>
</TransitionCollection>
</ui:AdaptiveGridView.ItemContainerTransitions>
</ui:AdaptiveGridView>
<TextBlock Name="empty" Text="&#xD8;" FontSize="200" Foreground="Gray" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.RowSpan="2"/>
</Grid>
</Page>
+9 -58
View File
@@ -1,5 +1,5 @@
using FoxTube.Controls.Adverts;
using System.Collections.Generic;
using FoxTube.Controls;
using FoxTube.Controls.Adverts;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
@@ -10,19 +10,9 @@ namespace FoxTube.Pages
/// </summary>
public sealed partial class VideoGrid : Page
{
public int Columns
{
get { return cols; }
set
{
cols = value;
UpdateGrid();
}
}
private int cols = 1;
public int Count => Children.Count;
public bool IsRelatedVideos { get; set; } = false;
public List<UIElement> Children { get; } = new List<UIElement>();
public int Count => list.Items.Count;
public ItemCollection Children => list.Items;
private int ItemsCount => Children.FindAll(i => i is VideoCard).Count + Children.FindAll(i => i is ChannelCard).Count + Children.FindAll(i => i is PlaylistCard).Count + Children.FindAll(i => i is CardAdvert).Count;
public VideoGrid()
{
@@ -31,60 +21,21 @@ namespace FoxTube.Pages
public void Add(UIElement card)
{
(grid.Children[Count % cols + 1] as StackPanel).Children.Add(card);
Children.Add(card);
if ((Children.Count - 5) % 25 == 0 && !SecretsVault.AdsDisabled)
{
CardAdvert advert = new CardAdvert(IsRelatedVideos);
(grid.Children[Count % cols + 1] as StackPanel).Children.Add(advert);
Children.Add(advert);
}
list.Items.Add(card);
if ((list.Items.Count - 5) % 25 == 0)
list.Items.Add(new CardAdvert());
empty.Visibility = Visibility.Collapsed;
}
public void Clear()
{
for (int k = 1; k <= 5; k++)
(grid.Children[k] as StackPanel).Children.Clear();
list.Items.Clear();
empty.Visibility = Visibility.Visible;
}
public void DeleteItem(FrameworkElement item)
{
Children.Remove(item);
UpdateGrid();
}
void UpdateGrid()
{
for (int k = 1; k <= 5; k++)
(grid.Children[k] as StackPanel).Children.Clear();
for (int k = 0; k < Count; k++)
(grid.Children[k % cols + 1] as StackPanel).Children.Add(Children[k]);
for (int k = 0; k < cols; k++)
grid.ColumnDefinitions[k].Width = new GridLength(1, GridUnitType.Star);
for (int k = cols; k < 5; k++)
grid.ColumnDefinitions[k].Width = new GridLength(0);
}
private void Grid_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (e.NewSize.Width >= 1600 && Columns != 5)
Columns = 5;
else if (e.NewSize.Width >= 1200 && e.NewSize.Width < 1600 && Columns != 4)
Columns = 4;
else if (e.NewSize.Width >= 900 && e.NewSize.Width < 1200 && Columns != 3)
Columns = 3;
else if (e.NewSize.Width >= 550 && e.NewSize.Width < 900 && Columns != 2)
Columns = 2;
else if (e.NewSize.Width < 550 && Columns != 1)
Columns = 1;
}
}
}
+4 -6
View File
@@ -29,8 +29,8 @@
<ColumnDefinition Width="400"/>
</Grid.ColumnDefinitions>
<ScrollViewer Margin="0,0,0,50" Name="mainScroll" VerticalScrollBarVisibility="Hidden">
<StackPanel Orientation="Vertical" Name="mainContent">
<Border BorderBrush="Red" BorderThickness="5" CornerRadius="10" Margin="0,10" Name="upcoming" Visibility="Collapsed">
<StackPanel Name="mainContent">
<Border BorderBrush="Red" BorderThickness="5" CornerRadius="10" Margin="10" Name="upcoming" Visibility="Collapsed">
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
@@ -125,10 +125,10 @@
</CommandBar>
<Grid Grid.Column="1" Name="tabsPlaceholder">
<Pivot Grid.Row="1" Name="pivot" SelectedIndex="0" IsHeaderItemsCarouselEnabled="False">
<Pivot Name="pivot" SelectedIndex="0" IsHeaderItemsCarouselEnabled="False">
<PivotItem x:Uid="/VideoPage/related" Header="Suggestions">
<ScrollViewer>
<pages:VideoGrid x:Name="relatedVideos" IsRelatedVideos="True"/>
<pages:VideoGrid x:Name="relatedVideos"/>
</ScrollViewer>
</PivotItem>
<PivotItem x:Uid="/VideoPage/comments" Header="Comments" Name="commentsPlaceholder">
@@ -174,7 +174,5 @@
</ComboBox>
</StackPanel>
</ContentDialog>
<local:LoadingPage Grid.ColumnSpan="2" Visibility="Collapsed" x:Name="loading" RefreshPage="refresh_Click"/>
</Grid>
</Page>
+74 -86
View File
@@ -42,11 +42,12 @@ namespace FoxTube.Pages
/// <summary>
/// Video page
/// </summary>
public sealed partial class VideoPage : Page
public sealed partial class VideoPage : Page, INavigationPage
{
ResourceLoader resources = ResourceLoader.GetForCurrentView("VideoPage");
public string videoId;
public object Parameter { get; set; } = null;
public string playlistId = null;
public Video item;
public HistoryItem history;
@@ -59,14 +60,11 @@ namespace FoxTube.Pages
DispatcherTimer liveTimer;
DispatcherTimer countdownTimer;
public LoadingPage LoadingPage => loading;
public VideoPlayer Player => player;
public VideoPage()
{
InitializeComponent();
DataTransferManager.GetForCurrentView().DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(Share);
if (Window.Current.Bounds.Width <= 1000)
{
@@ -78,14 +76,12 @@ namespace FoxTube.Pages
mainContent.Children.Add(pivot);
grid.ColumnDefinitions[1].Width = new GridLength(0);
pivot.SelectedIndex = 0;
}
}
private void Player_NextClicked()
{
if (playlistId != null)
if (playlistId != null && playlistList.SelectedIndex + 1 < playlistList.Items.Count)
playlistList.SelectedIndex++;
else
(relatedVideos.Children[0] as VideoCard).Button_Click(this, null);
@@ -94,19 +90,15 @@ namespace FoxTube.Pages
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.Parameter == null)
loading.Error("NullReferenceException", "Unable to initialize page. Video ID is not stated.");
else
Initialize(e.Parameter as object[]);
Parameter = e.Parameter;
Initialize(e.Parameter as object[]);
}
public async void Initialize(object[] ids)
{
loading.Refresh();
try
{
videoId = ids[0] as string;
incognito = (bool)ids[2];
if (ids[1] != null)
@@ -130,27 +122,26 @@ namespace FoxTube.Pages
LoadInfo();
LoadAddTo();
loading.Close();
Methods.MainPage.VideoContent.LoadingPage.Close();
}
catch (System.Net.Http.HttpRequestException)
{
loading.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
Methods.MainPage.VideoContent.LoadingPage.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
}
catch (Exception e)
{
loading.Error(e.GetType().ToString(), e.Message);
Methods.MainPage.VideoContent.LoadingPage.Error(e.GetType().ToString(), e.Message);
Analytics.TrackEvent("Video loading error", new Dictionary<string, string>()
{
{ "Exception", e.GetType().ToString() },
{ "Message", e.Message },
{ "Video ID", videoId }
{ "Video ID", item.Id }
});
}
}
void SetSchedule()
{
views.Visibility = Visibility.Collapsed;
upcoming.Visibility = Visibility.Visible;
if (item.LiveStreamingDetails.ScheduledEndTime.HasValue || item.LiveStreamingDetails.ScheduledStartTime.HasValue)
schedule.Visibility = Visibility.Visible;
@@ -208,7 +199,6 @@ namespace FoxTube.Pages
}
else
{
//Retrieving data
PlaylistsResource.ListRequest playlistRequest = SecretsVault.Service.Playlists.List("snippet,contentDetails");
playlistRequest.Id = id;
@@ -237,7 +227,7 @@ namespace FoxTube.Pages
//Setting data
playlistName.Text = playlistItem.Snippet.Localized.Title;
playlistChannel.Text = playlistItem.Snippet.ChannelTitle;
playlistChannel.Text = Methods.GuardFromNull(playlistItem.Snippet.ChannelTitle);
playlistCounter.Text = $"{items.IndexOf(selection) + 1}/{playlistItem.ContentDetails.ItemCount}";
}
@@ -249,9 +239,6 @@ namespace FoxTube.Pages
await Task.Delay(500);
playlistScroll.ChangeView(null, playlistList.SelectedIndex * 86 + 89, null, true);
if (playlistList.SelectedIndex == playlistList.Items.Count - 1)
player.Controls.IsNextTrackButtonVisible = false;
}
async void LoadInfo()
@@ -259,7 +246,7 @@ namespace FoxTube.Pages
//Setting meta
title.Text = item.Snippet.Localized.Title;
date.Text = $"{resources.GetString("/VideoPage/publishedAt")}: {item.Snippet.PublishedAt} ({Methods.GetAgo(item.Snippet.PublishedAt.Value)})";
Methods.FormatText(ref description, item.Snippet.Localized.Description);
description.FormatText(item.Snippet.Localized.Description);
//Setting channel button
ChannelsResource.ListRequest channelRequest = SecretsVault.Service.Channels.List("snippet, statistics");
@@ -278,28 +265,24 @@ namespace FoxTube.Pages
//Setting User's rate
if (SecretsVault.IsAuthorized)
{
VideoGetRatingResponse ratingResponse = await SecretsVault.Service.Videos.GetRating(videoId).ExecuteAsync();
if (ratingResponse.Items[0].Rating == "like")
VideoRating rating = (await SecretsVault.Service.Videos.GetRating(item.Id).ExecuteAsync()).Items[0];
if (rating.Rating == "like")
{
userRating = Rating.Like;
like.Foreground = new SolidColorBrush(Colors.Green);
}
else if (ratingResponse.Items[0].Rating == "dislike")
else if (rating.Rating == "dislike")
{
userRating = Rating.Dislike;
dislike.Foreground = new SolidColorBrush(Colors.Red);
}
foreach (Subscription s in SecretsVault.Subscriptions)
if(SecretsVault.Subscriptions.Exists(i => i.Snippet.ResourceId.ChannelId == item.Snippet.ChannelId))
{
if (s.Snippet.ResourceId.ChannelId == item.Snippet.ChannelId)
{
subscribe.Background = new SolidColorBrush(Colors.Transparent);
subscribe.Foreground = new SolidColorBrush(Colors.Gray);
subscribe.Content = resources.GetString("/Cards/unsubscribe");
}
subscribe.Background = new SolidColorBrush(Colors.Transparent);
subscribe.Foreground = new SolidColorBrush(Colors.Gray);
subscribe.Content = resources.GetString("/Cards/unsubscribe");
}
subscribe.Visibility = Visibility.Visible;
}
else
{
@@ -308,9 +291,9 @@ namespace FoxTube.Pages
subscribe.Visibility = Visibility.Collapsed;
}
if(HistorySet.Items.Exists(i => i.Id == videoId) && HistorySet.Items.Find(i => i.Id == videoId).LeftOn.TotalSeconds >= 30 && Methods.GetDuration(item.ContentDetails.Duration).TotalSeconds - HistorySet.Items.Find(i => i.Id == videoId).LeftOn.TotalSeconds >= 30)
if ((history = HistorySet.Items.Find(i => i.Id == item.Id)) != null && history.LeftOn.TotalSeconds >= 30 && Methods.GetDuration(item.ContentDetails.Duration).TotalSeconds - history.LeftOn.TotalSeconds >= 30)
{
history = HistorySet.Items.Find(i => i.Id == videoId);
left.Visibility = Visibility.Visible;
left.Content = $"\xE122 {resources.GetString("/VideoPage/continue")}: {history.LeftOn.ToString(@"hh\:mm\:ss")}";
}
@@ -342,7 +325,7 @@ namespace FoxTube.Pages
private async void LiveStatsUpdate(object sender = null, object e = null)
{
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("liveStreamingDetails");
request.Id = videoId;
request.Id = item.Id;
views.Text = $"{(await request.ExecuteAsync()).Items[0].LiveStreamingDetails.ConcurrentViewers} {resources.GetString("/Cards/viewers")}";
}
@@ -359,7 +342,7 @@ namespace FoxTube.Pages
{
try
{
MediaStreamInfoSet infoSet = await new YoutubeClient().GetVideoMediaStreamInfosAsync(videoId);
MediaStreamInfoSet infoSet = await new YoutubeClient().GetVideoMediaStreamInfosAsync(item.Id);
foreach (MuxedStreamInfo i in infoSet.Muxed)
{
MenuFlyoutItem menuItem = new MenuFlyoutItem()
@@ -395,7 +378,7 @@ namespace FoxTube.Pages
SearchResource.ListRequest request = SecretsVault.Service.Search.List("snippet");
request.RegionCode = SettingsStorage.Region;
request.RelevanceLanguage = SettingsStorage.RelevanceLanguage;
request.RelatedToVideoId = videoId;
request.RelatedToVideoId = item.Id;
request.SafeSearch = (SearchResource.ListRequest.SafeSearchEnum)SettingsStorage.SafeSearch;
request.MaxResults = 20;
request.Type = "video";
@@ -408,9 +391,6 @@ namespace FoxTube.Pages
private void Player_Minimize(object sender, params object[] e)
{
if (isExtended == (bool)e[0])
return;
isExtended = (bool)e[0];
if(isExtended)
{
@@ -447,12 +427,12 @@ namespace FoxTube.Pages
string timecode = player.Player.Position.TotalSeconds > 10 ?
"&t=" + (int)player.Player.Position.TotalSeconds + "s" : string.Empty;
await Launcher.LaunchUriAsync($"https://www.youtube.com/watch?v={videoId}{timecode}".ToUri());
await Launcher.LaunchUriAsync($"https://www.youtube.com/watch?v={item.Id}{timecode}".ToUri());
}
public void refresh_Click(object sender, RoutedEventArgs e)
{
Methods.MainPage.GoToVideo(videoId, playlistId);
Methods.MainPage.VideoContent.Refresh();
}
public void CloseVideo()
@@ -497,12 +477,13 @@ namespace FoxTube.Pages
Methods.Share(args,
item.Snippet.Thumbnails.Medium.Url,
item.Snippet.Title,
$"https://www.youtube.com/watch?v={videoId}",
$"https://www.youtube.com/watch?v={item.Id}",
resources.GetString("/Cards/videoShare"));
}
private void share_Click(object sender, RoutedEventArgs e)
{
DataTransferManager.GetForCurrentView().DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(Share);
DataTransferManager.ShowShareUI();
}
@@ -518,7 +499,7 @@ namespace FoxTube.Pages
dislike.Foreground = new SolidColorBrush(Colors.Red);
dislikes.Text = (int.Parse(dislikes.Text, NumberStyles.AllowThousands) + 1).ToString("0,0");
rating.Value--;
await SecretsVault.Service.Videos.Rate(videoId, VideosResource.RateRequest.RatingEnum.Dislike).ExecuteAsync();
await SecretsVault.Service.Videos.Rate(item.Id, VideosResource.RateRequest.RatingEnum.Dislike).ExecuteAsync();
userRating = Rating.Dislike;
break;
@@ -527,7 +508,7 @@ namespace FoxTube.Pages
dislike.Foreground = new SolidColorBrush(Colors.Red);
dislikes.Text = (int.Parse(dislikes.Text, NumberStyles.AllowThousands) + 1).ToString("0,0");
rating.Maximum++;
await SecretsVault.Service.Videos.Rate(videoId, VideosResource.RateRequest.RatingEnum.Dislike).ExecuteAsync();
await SecretsVault.Service.Videos.Rate(item.Id, VideosResource.RateRequest.RatingEnum.Dislike).ExecuteAsync();
userRating = Rating.Dislike;
break;
@@ -536,7 +517,7 @@ namespace FoxTube.Pages
dislike.Foreground = new SolidColorBrush(Colors.Gray);
dislikes.Text = (int.Parse(dislikes.Text, NumberStyles.AllowThousands) - 1).ToString("0,0");
rating.Maximum--;
await SecretsVault.Service.Videos.Rate(videoId, VideosResource.RateRequest.RatingEnum.None).ExecuteAsync();
await SecretsVault.Service.Videos.Rate(item.Id, VideosResource.RateRequest.RatingEnum.None).ExecuteAsync();
break;
}
}
@@ -553,7 +534,7 @@ namespace FoxTube.Pages
like.Foreground = new SolidColorBrush(Colors.Green);
likes.Text = (int.Parse(likes.Text, NumberStyles.AllowThousands) + 1).ToString("0,0");
rating.Value++;
await SecretsVault.Service.Videos.Rate(videoId, VideosResource.RateRequest.RatingEnum.Like).ExecuteAsync();
await SecretsVault.Service.Videos.Rate(item.Id, VideosResource.RateRequest.RatingEnum.Like).ExecuteAsync();
userRating = Rating.Like;
break;
@@ -563,7 +544,7 @@ namespace FoxTube.Pages
likes.Text = (int.Parse(likes.Text, NumberStyles.AllowThousands) + 1).ToString("0,0");
rating.Maximum++;
rating.Value++;
await SecretsVault.Service.Videos.Rate(videoId, VideosResource.RateRequest.RatingEnum.Like).ExecuteAsync();
await SecretsVault.Service.Videos.Rate(item.Id, VideosResource.RateRequest.RatingEnum.Like).ExecuteAsync();
userRating = Rating.Like;
break;
@@ -573,7 +554,7 @@ namespace FoxTube.Pages
likes.Text = (int.Parse(likes.Text, NumberStyles.AllowThousands) - 1).ToString("0,0");
rating.Maximum--;
rating.Value--;
await SecretsVault.Service.Videos.Rate(videoId, VideosResource.RateRequest.RatingEnum.None).ExecuteAsync();
await SecretsVault.Service.Videos.Rate(item.Id, VideosResource.RateRequest.RatingEnum.None).ExecuteAsync();
break;
}
}
@@ -582,7 +563,7 @@ namespace FoxTube.Pages
{
try
{
if ((e.AddedItems[0] as VideoPlaylistItem).Id != videoId)
if ((e.AddedItems[0] as VideoPlaylistItem).Id != item.Id)
Methods.MainPage.GoToVideo((e.AddedItems[0] as VideoPlaylistItem).Id, playlistId);
}
catch { }
@@ -606,39 +587,46 @@ namespace FoxTube.Pages
async void LoadAddTo()
{
if(SecretsVault.UserChannel == null)
try
{
if (SecretsVault.UserChannel == null)
{
addTo.Visibility = Visibility.Collapsed;
return;
}
if (SecretsVault.WatchLater.Contains(item.Id))
(addList.Items[1] as ToggleMenuFlyoutItem).IsChecked = true;
PlaylistsResource.ListRequest request = SecretsVault.Service.Playlists.List("snippet");
request.Mine = true;
request.MaxResults = 50;
PlaylistListResponse response = await request.ExecuteAsync();
PlaylistItemsResource.ListRequest itemRequest = SecretsVault.Service.PlaylistItems.List("snippet");
itemRequest.VideoId = item.Id;
foreach (Playlist i in response.Items)
{
itemRequest.PlaylistId = i.Id;
ToggleMenuFlyoutItem menuItem = new ToggleMenuFlyoutItem
{
Text = i.Snippet.Title,
IsChecked = (await itemRequest.ExecuteAsync()).Items.Count > 0,
Tag = i,
Icon = new FontIcon
{
Glyph = "\xE728"
}
};
menuItem.Click += Item_Click;
addList.Items.Add(menuItem);
}
}
catch
{
addTo.Visibility = Visibility.Collapsed;
return;
}
if (SecretsVault.WatchLater.Contains(item.Id))
(addList.Items[1] as ToggleMenuFlyoutItem).IsChecked = true;
PlaylistsResource.ListRequest request = SecretsVault.Service.Playlists.List("snippet");
request.Mine = true;
request.MaxResults = 50;
PlaylistListResponse response = await request.ExecuteAsync();
PlaylistItemsResource.ListRequest itemRequest = SecretsVault.Service.PlaylistItems.List("snippet");
itemRequest.VideoId = item.Id;
foreach (Playlist i in response.Items)
{
itemRequest.PlaylistId = i.Id;
ToggleMenuFlyoutItem menuItem = new ToggleMenuFlyoutItem
{
Text = i.Snippet.Title,
IsChecked = (await itemRequest.ExecuteAsync()).Items.Count > 0,
Tag = i,
Icon = new FontIcon
{
Glyph = "\xE728"
}
};
menuItem.Click += Item_Click;
addList.Items.Add(menuItem);
}
}
+3 -3
View File
@@ -124,10 +124,10 @@
<value>Contacts</value>
</data>
<data name="crMe.Text" xml:space="preserve">
<value>© 2018 Michael Gordeev</value>
<value>© Michael Gordeev</value>
</data>
<data name="crYt.Text" xml:space="preserve">
<value>© 2018 YouTube, LLC</value>
<value>© YouTube, LLC</value>
</data>
<data name="developed.Text" xml:space="preserve">
<value>Developed by Michael Gordeev (also known as XFox)</value>
@@ -142,7 +142,7 @@
<value>Legal stuff</value>
</data>
<data name="myBlog.Text" xml:space="preserve">
<value>My blog (Russian language only)</value>
<value>My website</value>
</data>
<data name="ourPrivacy.Content" xml:space="preserve">
<value>Our Privacy Policy</value>
+2 -2
View File
@@ -139,7 +139,7 @@
<value>Windows color settings</value>
</data>
<data name="en.Content" xml:space="preserve">
<value>English (United States of America)</value>
<value>English (United States)</value>
</data>
<data name="interfaceLang.Header" xml:space="preserve">
<value>App interface language</value>
@@ -187,7 +187,7 @@
<value>Reopen the app to apply changes (otherwise some elements may not be displayed correctly)</value>
</data>
<data name="ru.Content" xml:space="preserve">
<value>Russian (Russian Federation)</value>
<value>Russian (Russia)</value>
</data>
<data name="safeSearch.Header" xml:space="preserve">
<value>SafeSearch</value>
+3 -3
View File
@@ -124,10 +124,10 @@
<value>Контакты</value>
</data>
<data name="crMe.Text" xml:space="preserve">
<value>© 2018 Михаил Гордеев</value>
<value>© Михаил Гордеев</value>
</data>
<data name="crYt.Text" xml:space="preserve">
<value>© 2018 YouTube, LLC</value>
<value>© YouTube, LLC</value>
</data>
<data name="developed.Text" xml:space="preserve">
<value>Разработано Михаилом Гордеевым (также известным как XFox)</value>
@@ -142,7 +142,7 @@
<value>Юридический материал</value>
</data>
<data name="myBlog.Text" xml:space="preserve">
<value>Мой блог</value>
<value>Мой веб-сайт</value>
</data>
<data name="ourPrivacy.Content" xml:space="preserve">
<value>Наша политика конфиденциальности</value>
+2 -2
View File
@@ -139,7 +139,7 @@
<value>Цветовые настройки Windows</value>
</data>
<data name="en.Content" xml:space="preserve">
<value>Английский (Соединенные Штаты Америки)</value>
<value>Английский (США)</value>
</data>
<data name="interfaceLang.Header" xml:space="preserve">
<value>Язык интерфейса</value>
@@ -187,7 +187,7 @@
<value>Перезапустите приложение, чтобы применить настройки (в противном случае некоторые элементы могут неправильно отображаться)</value>
</data>
<data name="ru.Content" xml:space="preserve">
<value>Русский (Российская Федерация)</value>
<value>Русский (Россия)</value>
</data>
<data name="safeSearch.Header" xml:space="preserve">
<value>Безопасный поиск</value>
+1 -1
View File
@@ -41,6 +41,6 @@
<Style TargetType="ui:NavigationViewItemHeader">
<Setter Property="FontSize" Value="14"/>
</Style>
<Duration x:Key="CardOpacityDuration">0:0:0.5</Duration>
<Duration x:Key="CardOpacityDuration">0:0:0.3</Duration>
</ResourceDictionary>