diff --git a/FoxTube.Core/FoxTube.Core.csproj b/FoxTube.Core/FoxTube.Core.csproj index 9f76189..a6b2923 100644 --- a/FoxTube.Core/FoxTube.Core.csproj +++ b/FoxTube.Core/FoxTube.Core.csproj @@ -132,26 +132,28 @@ + + - + + + - - @@ -163,22 +165,25 @@ 1.0.3 - 1.45.0 + 1.46.0 + + + 1.46.0.1986 1.44.1.1869 - 1.45.0.1929 + 1.46.0.1987 10.1811.22001 - 3.2.1 + 3.2.2 - 3.2.1 + 3.2.2 6.2.10 @@ -186,11 +191,14 @@ 10.1901.28001 - - 2.4.0 + + 6.0.0 + + + 1.0.0 - 5.0.4 + 5.0.5 diff --git a/FoxTube.Core/Models/Collections/InboxCollection.cs b/FoxTube.Core/Models/Collections/InboxCollection.cs index c3a5089..2c87f6a 100644 --- a/FoxTube.Core/Models/Collections/InboxCollection.cs +++ b/FoxTube.Core/Models/Collections/InboxCollection.cs @@ -1,45 +1,49 @@ -using Newtonsoft.Json; +using System; using System.Threading.Tasks; -using System.Net.Http; using Windows.UI.Xaml.Data; using FoxTube.Services; using FoxTube.Utils; +using Google.Apis.Blogger.v3; +using Google.Apis.Blogger.v3.Data; namespace FoxTube.Models.Collections { - public class InboxCollection : ViewCollection + public class InboxCollection : ViewCollection { - private int _pageNumber = 0; - private HttpClient _httpClient = new HttpClient(); + private string nextPageToken; public override async Task LoadItems() { - // TODO: Add backend - HttpResponseMessage response = await _httpClient.GetAsync($"https://xfox111.net/API/FoxTube/Inbox?" + - $"lang={Storage.GetValue(Storage.Settings.UILanguage)}&" + - $"currentVersion={Metrics.CurrentVersion}&" + - $"itemsCount={ItemsPerRequest}&" + - $"iteration={_pageNumber}"); - - if (!response.IsSuccessStatusCode || response.StatusCode == System.Net.HttpStatusCode.NoContent) + try { + PostsResource.ListRequest request = InboxService.Service.Posts.List(SecretConstants.BlogId); + request.FetchImages = false; + request.PageToken = nextPageToken; + request.Labels = "FoxTube"; + request.MaxResults = ItemsPerRequest; + request.OrderBy = PostsResource.ListRequest.OrderByEnum.UPDATED; + + PostList response = await request.ExecuteAsync(); + + foreach (Post post in response.Items) + Items.Add(post); + + HasMoreItems = !string.IsNullOrWhiteSpace(nextPageToken = response.NextPageToken); + + return new LoadMoreItemsResult + { + Count = (uint)response.Items.Count + }; + } + catch (Exception e) + { + Metrics.SendReport(new Exception("Unable to load inbox", e)); HasMoreItems = false; return new LoadMoreItemsResult { Count = 0 }; } - - InboxItem[] newItems = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); - foreach (InboxItem item in newItems) - Items.Add(item); - - _pageNumber++; - - return new LoadMoreItemsResult - { - Count = (uint)newItems.Length - }; } } } diff --git a/FoxTube.Core/Models/Collections/ViewCollection.cs b/FoxTube.Core/Models/Collections/ViewCollection.cs index 1ee2372..94cbfa9 100644 --- a/FoxTube.Core/Models/Collections/ViewCollection.cs +++ b/FoxTube.Core/Models/Collections/ViewCollection.cs @@ -8,7 +8,7 @@ namespace FoxTube.Models.Collections { public abstract class ViewCollection : ObservableCollection, ISupportIncrementalLoading { - public int ItemsPerRequest { get; set; } + public int ItemsPerRequest { get; set; } = 25; public bool HasMoreItems { get; protected set; } = true; public IAsyncOperation LoadMoreItemsAsync(uint count) => diff --git a/FoxTube.Core/Models/InboxItem.cs b/FoxTube.Core/Models/InboxItem.cs deleted file mode 100644 index ab281b9..0000000 --- a/FoxTube.Core/Models/InboxItem.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; - -namespace FoxTube.Models -{ - public class InboxItem - { - public string Id { get; set; } - - public string DefaultIcon { get; set; } - public string Avatar { get; set; } - - public string Title { get; set; } - public string Description { get; set; } - public string Content { get; set; } - - public DateTime TimeStamp { get; set; } - public string Type { get; set; } - - public string ShortTimeStamp => $"{TimeStamp.ToShortDateString()} {TimeStamp.ToShortTimeString()}"; - } -} \ No newline at end of file diff --git a/FoxTube.Core/Models/SearchParameters.cs b/FoxTube.Core/Models/SearchParameters.cs new file mode 100644 index 0000000..2f4e77f --- /dev/null +++ b/FoxTube.Core/Models/SearchParameters.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FoxTube.Models +{ + public class SearchParameters + { + public SearchParameters(string query) + { + + } + } +} \ No newline at end of file diff --git a/FoxTube.Core/Models/User.cs b/FoxTube.Core/Models/User.cs index 11f0850..4c4ea92 100644 --- a/FoxTube.Core/Models/User.cs +++ b/FoxTube.Core/Models/User.cs @@ -5,12 +5,10 @@ using Google.Apis.Oauth2.v2.Data; using Google.Apis.Services; using Google.Apis.YouTube.v3; using Google.Apis.YouTube.v3.Data; -using Newtonsoft.Json; using System; using System.Linq; using System.Collections.Generic; using System.Threading.Tasks; -using Windows.Storage; using YouTube; using YoutubeExplode; using FoxTube.Services; @@ -42,8 +40,6 @@ namespace FoxTube.Models UserService.SubscriptionsChangedInvoker(this, subscription); Subscriptions.Remove(subscription); - - SaveSubscriptions(); return false; } else @@ -79,21 +75,10 @@ namespace FoxTube.Models Subscriptions.Add(subscription); UserService.SubscriptionsChangedInvoker(this, subscription); - - SaveSubscriptions(); return true; } } - private void SaveSubscriptions() - { - Dictionary subs = Subscriptions.Select(i => - new KeyValuePair(i.ChannelId, i.Avatar.Default__?.Url)) - as Dictionary; - - ApplicationData.Current.RoamingSettings.Values[$"Subscriptions.{UserInfo.Id}"] = JsonConvert.SerializeObject(subs); - } - public static async Task GetUser(UserCredential credential) { User user = new User diff --git a/FoxTube.Core/Models/VideoItem.cs b/FoxTube.Core/Models/VideoItem.cs deleted file mode 100644 index a86caff..0000000 --- a/FoxTube.Core/Models/VideoItem.cs +++ /dev/null @@ -1,49 +0,0 @@ -using FoxTube.Services; -using Google.Apis.YouTube.v3.Data; -using System; -using YoutubeExplode; - -namespace FoxTube.Models -{ - public class VideoItem - { - public Video Meta { get; set; } - public YoutubeExplode.Videos.Video AdditionalMeta { get; set; } - public YoutubeExplode.Channels.Channel ChannelMeta { get; set; } - - public string TimeLabel { get; set; } - public string ViewsLabel { get; set; } - public int LiveLabelOpacity => Meta?.LiveStreamingDetails == null ? 0 : 1; - public string LiveLabel - { - get - { - if (Meta?.LiveStreamingDetails == null) - return ""; - else if (Meta.LiveStreamingDetails.ActualStartTime.HasValue) - return "LIVE"; - else if (Meta.LiveStreamingDetails.ScheduledStartTime.HasValue) - return $"Live in {Meta.LiveStreamingDetails.ScheduledStartTime - DateTime.Now}"; - else - return "Upcoming"; - } - } - - public VideoItem(Video meta) - { - Meta = meta; - LoadInfo(); - } - - private async void LoadInfo() - { - YoutubeClient client = new YoutubeClient(UserService.Service.HttpClient); - - AdditionalMeta = await client.Videos.GetAsync(Meta.Id); - ChannelMeta = await client.Channels.GetByVideoAsync(Meta.Id); - - TimeLabel = $"{AdditionalMeta?.Duration} • {AdditionalMeta.UploadDate.DateTime.GetFriendlyDate()}"; - ViewsLabel = $"{AdditionalMeta?.Engagement.ViewCount} views"; - } - } -} \ No newline at end of file diff --git a/FoxTube.Core/Services/DownloadsService.cs b/FoxTube.Core/Services/DownloadsService.cs index 221b434..7f307ed 100644 --- a/FoxTube.Core/Services/DownloadsService.cs +++ b/FoxTube.Core/Services/DownloadsService.cs @@ -18,12 +18,9 @@ namespace FoxTube.Services public static List History { get; } = new List(); public static List Queue { get; } = new List(); - static DownloadsService() => - Initialize(); - - private static async void Initialize() + public static async Task Initialize() { - StorageFile file = await Storage.Folder.CreateFileAsync("DownloadHistory.json", CreationCollisionOption.OpenIfExists); + StorageFile file = await StorageService.Folder.CreateFileAsync("DownloadHistory.json", CreationCollisionOption.OpenIfExists); try { List savedVideos = JsonConvert.DeserializeObject>(File.ReadAllText(file.Path) ?? "") ?? new List(); @@ -70,7 +67,7 @@ namespace FoxTube.Services History.Add(savedItem); - StorageFile file = await Storage.Folder.CreateFileAsync("DownloadHistory.json", CreationCollisionOption.OpenIfExists); + StorageFile file = await StorageService.Folder.CreateFileAsync("DownloadHistory.json", CreationCollisionOption.OpenIfExists); File.WriteAllText(file.Path, JsonConvert.SerializeObject(History)); } catch (OperationCanceledException) { } @@ -112,7 +109,7 @@ namespace FoxTube.Services public static async Task GetDefaultDownloadsFolder() { - if (Storage.GetValue(Storage.Settings.DefaultDownloadsFolder) is string token && !string.IsNullOrWhiteSpace(token)) + if (SettingsService.DefaultDownloadsFolder is string token && !string.IsNullOrWhiteSpace(token)) return await StorageApplicationPermissions.FutureAccessList.GetFolderAsync(token) ?? await KnownFolders.VideosLibrary.CreateFolderAsync("FoxTube", CreationCollisionOption.OpenIfExists); else @@ -137,12 +134,12 @@ namespace FoxTube.Services if (folder != null) { - if (Storage.GetValue(Storage.Settings.DefaultDownloadsFolder) is string token && !string.IsNullOrWhiteSpace(token)) + if (SettingsService.DefaultDownloadsFolder is string token && !string.IsNullOrWhiteSpace(token)) StorageApplicationPermissions.FutureAccessList.AddOrReplace(token, folder); else { token = StorageApplicationPermissions.FutureAccessList.Add(folder); - Storage.SetValue(Storage.Settings.DefaultDownloadsFolder, token); + SettingsService.DefaultDownloadsFolder = token; } } diff --git a/FoxTube.Core/Services/InboxService.cs b/FoxTube.Core/Services/InboxService.cs index ef3cbc4..3338ec6 100644 --- a/FoxTube.Core/Services/InboxService.cs +++ b/FoxTube.Core/Services/InboxService.cs @@ -1,18 +1,24 @@ using FoxTube.Models.Collections; using FoxTube.Utils; -using Newtonsoft.Json; using System; -using System.Net; using System.Net.Http; using System.Threading.Tasks; using Windows.UI.Notifications; +using Google.Apis.Blogger.v3; +using Google.Apis.Blogger.v3.Data; +using System.Linq; +using System.Globalization; +using Windows.Foundation.Metadata; namespace FoxTube.Services { public static class InboxService { - public const string lastChangelogVersionKey = "Inbox.lastChangelogVersion"; - public const string lastCheckKey = "Inbox.lastChangelogVersion"; + public static BloggerService Service { get; } = new BloggerService(new Google.Apis.Services.BaseClientService.Initializer + { + ApplicationName = "FoxTube", + ApiKey = SecretConstants.BloggerApiKey + }); private static readonly HttpClient client = new HttpClient(); @@ -23,22 +29,19 @@ namespace FoxTube.Services { try { - // TODO: Add backend - HttpResponseMessage response = await client.GetAsync($"https://xfox111.net/FoxTube/Inbox?" + - $"toast=true&" + - $"publishedAfter={Storage.Registry.Values[lastCheckKey]}&" + - $"lang={Storage.GetValue(Storage.Settings.UILanguage)}&" + - $"appVersion={Metrics.CurrentVersion}"); + PostsResource.ListRequest request = Service.Posts.List(SecretConstants.BlogId); + request.FetchImages = false; + request.Labels = "FoxTube"; + request.MaxResults = 500; + request.StartDate = StorageService.LastInboxCheck.ToString("yyyy-MM-dd'T'HH:mm:ss.fffK", DateTimeFormatInfo.InvariantInfo); + request.OrderBy = PostsResource.ListRequest.OrderByEnum.UPDATED; - Storage.Registry.Values[lastCheckKey] = DateTime.UtcNow.Ticks; + PostList response = await request.ExecuteAsync(); - if (response.StatusCode == HttpStatusCode.NoContent) - return; + foreach(Post post in response.Items.Where(i => !i.Labels.Contains("Changelog"))) + ToastNotificationManager.CreateToastNotifier().Show(ToastTemplates.GetBlogpostToast(post)); - string[] toasts = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); - - foreach (string toast in toasts) - ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toast.ToXml())); + StorageService.LastInboxCheck = DateTime.UtcNow; } catch (Exception e) { @@ -46,27 +49,33 @@ namespace FoxTube.Services } } + // TODO: Add changelog retrieval /// /// Fires toast notification with the last changelog content /// + [Deprecated("In future versions it will be replaced with splash screen full size message", DeprecationType.Deprecate, 1)] public static async void PushChangelog() { try { - // TODO: Add backend - if ((string)Storage.Registry.Values[lastChangelogVersionKey] == Metrics.CurrentVersion) + if (string.IsNullOrWhiteSpace(StorageService.LastOpenedVersion)) + StorageService.LastOpenedVersion = Metrics.CurrentVersion; + + string lastVersion = StorageService.LastOpenedVersion; + if (string.IsNullOrWhiteSpace(lastVersion) || lastVersion == Metrics.CurrentVersion) return; - Storage.Registry.Values[lastChangelogVersionKey] = Metrics.CurrentVersion; + PostsResource.ListRequest request = Service.Posts.List(SecretConstants.BlogId); + request.FetchImages = false; + request.Labels = $"FoxTube,Changelog,v{Metrics.CurrentVersion}"; + request.MaxResults = 1; - HttpResponseMessage response = await client.GetAsync($"https://xfox111.net/API/FoxTube/Changelog?" + - $"lang={Storage.GetValue(Storage.Settings.UILanguage)}&" + - $"version={Metrics.CurrentVersion}"); + PostList response = await request.ExecuteAsync(); - if (response.StatusCode == HttpStatusCode.NoContent) - return; + if (response.Items.Count > 0) + ToastNotificationManager.CreateToastNotifier().Show(ToastTemplates.GetBlogpostToast(response.Items.First())); - ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification((await response.Content.ReadAsStringAsync()).ToXml())); + StorageService.LastOpenedVersion = Metrics.CurrentVersion; } catch (Exception e) { diff --git a/FoxTube.Core/Services/Search.cs b/FoxTube.Core/Services/Search.cs index 17f3d00..7b48e6b 100644 --- a/FoxTube.Core/Services/Search.cs +++ b/FoxTube.Core/Services/Search.cs @@ -16,7 +16,12 @@ namespace FoxTube.Services try { using HttpClient client = new HttpClient(); - string results = await client.GetStringAsync($"http://suggestqueries.google.com/complete/search?ds=yt&client=toolbar&q={term}&hl={Storage.GetValue(Storage.Settings.RelevanceLanguage)}"); + string results = await client.GetStringAsync($"http://suggestqueries.google.com/complete/search?" + + $"ds=yt&" + + $"client=toolbar&" + + $"q={term}&" + + $"hl={SettingsService.RelevanceLanguage}"); + XmlDocument doc = new XmlDocument(); doc.LoadXml(results); diff --git a/FoxTube.Core/Services/SettingsService.cs b/FoxTube.Core/Services/SettingsService.cs new file mode 100644 index 0000000..41cb82d --- /dev/null +++ b/FoxTube.Core/Services/SettingsService.cs @@ -0,0 +1,67 @@ +using Windows.Storage; +using System.Globalization; +using System.Diagnostics; +using Windows.UI.Xaml; + +namespace FoxTube.Services +{ + public class SettingsService + { + private static readonly ApplicationDataContainer storage = ApplicationData.Current.RoamingSettings; + + public static void ResetSettings() => + storage.Values.Clear(); + + public static ElementTheme Theme + { + get => (ElementTheme)GetValue(ElementTheme.Default); + set => SetValue((int)value); + } + + public static string RelevanceLanguage + { + get => GetValue(GetDefaultLanguage()); + set => SetValue(value); + } + + public static bool AllowAnalytics + { + get => GetValue(true); + set => SetValue(value); + } + + public static bool AskEveryDownload + { + get => GetValue(true); + set => SetValue(value); + } + + public static string DefaultDownloadsFolder + { + get => GetValue(null); + set => SetValue(value); + } + + #region Service methods + private static string GetDefaultLanguage() + { + if (CultureInfo.InstalledUICulture.TwoLetterISOLanguageName.Belongs("ua", "ru", "by", "kz", "kg", "md", "lv", "ee")) // Languages for Russian-speaking countries + return "ru-RU"; + else + return "en-US"; + } + + private static dynamic GetValue(object defaultValue) + { + string propName = (new StackTrace()).GetFrame(1).GetMethod().Name.Substring(4); + return storage.Values[$"Settings.{propName}"] ?? defaultValue; + } + + private static void SetValue(object value) + { + string propName = (new StackTrace()).GetFrame(1).GetMethod().Name.Substring(4); + storage.Values[$"Settings.{propName}"] = value; + } + #endregion + } +} \ No newline at end of file diff --git a/FoxTube.Core/Services/Storage.cs b/FoxTube.Core/Services/Storage.cs deleted file mode 100644 index 3f373e6..0000000 --- a/FoxTube.Core/Services/Storage.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Threading.Tasks; -using Windows.Storage; -using Windows.UI.Xaml; - -namespace FoxTube.Services -{ - public static class Storage - { - public static event EventHandler SettingsChanged; - - public static StorageFolder Folder => ApplicationData.Current.RoamingFolder; - public static ApplicationDataContainer Registry { get; } = ApplicationData.Current.RoamingSettings; - - private static readonly Dictionary _defaultSettings = new Dictionary - { - { Settings.Theme, ElementTheme.Default }, - { Settings.UILanguage, GetDefaultLanguage() }, - { Settings.RelevanceLanguage, GetDefaultLanguage() }, - { Settings.PromptFeedback, true }, - { Settings.PromptReview, true }, - { Settings.AllowAnalytics, true } - }; - - public enum Settings - { - /// - /// ElementTheme - /// - Theme, - /// - /// string - /// - UILanguage, - /// - /// string - /// - RelevanceLanguage, - /// - /// string - /// - DefaultDownloadsFolder, - /// - /// bool - /// - PromptFeedback, - /// - /// bool - /// - PromptReview, - /// - /// bool - /// - AllowAnalytics, - /// - /// string - /// - Region - } - - public enum Metrics - { - /// - /// TimeSpan - /// - Uptime - } - - - public static void SetValue(Settings key, object value) - { - Registry.Values[$"{key.GetType().Name}.{key}"] = value; - SettingsChanged?.Invoke(value, key); - } - public static void SetValue(Metrics key, object value) => - Registry.Values[$"{key.GetType().Name}.{key}"] = value; - - public static T GetValue(Settings key) => - (T)(Registry.Values[$"{key.GetType().Name}.{key}"] ?? (_defaultSettings.ContainsKey(key) ? _defaultSettings[key] : null)); - public static T GetValue(Metrics key) => - (T)Registry.Values[$"{key.GetType().Name}.{key}"]; - - - private static string GetDefaultLanguage() - { - if (CultureInfo.InstalledUICulture.TwoLetterISOLanguageName.Belongs("ua", "ru", "by", "kz", "kg", "md", "lv", "ee")) //Languages for Russian-speaking countries - return "ru-RU"; - else - return "en-US"; - } - - public static async Task ResetStorage() - { - Registry.Values.Clear(); - foreach (IStorageItem i in await Folder.GetItemsAsync()) - await i.DeleteAsync(); - } - } -} \ No newline at end of file diff --git a/FoxTube.Core/Services/StorageService.cs b/FoxTube.Core/Services/StorageService.cs new file mode 100644 index 0000000..1ff8286 --- /dev/null +++ b/FoxTube.Core/Services/StorageService.cs @@ -0,0 +1,77 @@ +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using Windows.Storage; + +namespace FoxTube.Services +{ + public static class StorageService + { + private static readonly ApplicationDataContainer storage = ApplicationData.Current.RoamingSettings; + + public static StorageFolder Folder => ApplicationData.Current.RoamingFolder; + + public static bool PromptFeedback + { + get => GetValue(Uptime.TotalHours > 12); + set => SetValue(value); + } + + public static bool PromptReview + { + get => GetValue(Uptime.TotalHours > 24); + set => SetValue(value); + } + + public static TimeSpan Uptime + { + get => GetValue(TimeSpan.FromSeconds(0)); + set => SetValue(value); + } + + public static DateTime LastInboxCheck + { + get => GetValue(DateTime.UtcNow); + set => SetValue(value); + } + + public static string LastOpenedVersion + { + get => GetValue(null); + set => SetValue(value); + } + + public static int? LastUserIndex + { + get => GetValue(null); + set => SetValue(value); + } + + public static string UserInfos + { + get => GetValue(null); + set => SetValue(value); + } + + public static async Task ClearStorage() + { + storage.Values.Clear(); + foreach (IStorageItem i in await Folder.GetItemsAsync()) + await i.DeleteAsync(); + } + + #region Service methods + private static dynamic GetValue(object defaultValue) + { + string propName = (new StackTrace()).GetFrame(1).GetMethod().Name.Substring(4); + return storage.Values[$"Storage.{propName}"] ?? defaultValue; + } + + private static void SetValue(object value) + { + string propName = (new StackTrace()).GetFrame(1).GetMethod().Name.Substring(4); + storage.Values[$"Storage.{propName}"] = value; + } + #endregion + } +} \ No newline at end of file diff --git a/FoxTube.Core/Services/UserService.cs b/FoxTube.Core/Services/UserService.cs index d0b4aec..66384e4 100644 --- a/FoxTube.Core/Services/UserService.cs +++ b/FoxTube.Core/Services/UserService.cs @@ -20,9 +20,6 @@ namespace FoxTube.Services { public static class UserService { - public const string UsersStorageKey = "UserService.Users"; - public const string LastUserInfoKey = "UserService.LastUser"; - public const int MaxUsersCount = 1; #region Private members @@ -74,9 +71,6 @@ namespace FoxTube.Services public static event EventHandler UserStateUpdated; public static event EventHandler SubscriptionsChanged; - static UserService() => - Initialize(); - public static async Task AddUser() { int queueIndex = Users.ToList().FindIndex(i => i == null); @@ -117,10 +111,10 @@ namespace FoxTube.Services return false; } - public static async void Initialize() + public static async Task Initialize() { - Users = JsonConvert.DeserializeObject(Storage.Registry.Values[UsersStorageKey] as string ?? "") ?? new Userinfoplus[MaxUsersCount]; - int? lastUserIndex = Storage.Registry.Values[LastUserInfoKey] as int?; + Users = JsonConvert.DeserializeObject(StorageService.UserInfos ?? "") ?? new Userinfoplus[MaxUsersCount]; + int? lastUserIndex = StorageService.LastUserIndex; if (lastUserIndex.HasValue && Users[lastUserIndex.Value] != null || (lastUserIndex = Users.ToList().FindIndex(i => i != null)) > -1) @@ -146,12 +140,11 @@ namespace FoxTube.Services await CurrentUser.Credential.RevokeTokenAsync(CancellationToken.None); - Storage.Registry.Values.Remove($"Subscriptions.{CurrentUser.UserInfo.Id}"); CurrentUser = null; Users[Users.ToList().FindIndex(i => i.Id == userId)] = null; - Storage.Registry.Values[UsersStorageKey] = JsonConvert.SerializeObject(Users); - Storage.Registry.Values[LastUserInfoKey] = null; + StorageService.UserInfos = JsonConvert.SerializeObject(Users); + StorageService.LastUserIndex = null; if (Users.Any(i => i != null)) await SwitchUser(Users.ToList().FindIndex(i => i != null)); @@ -202,8 +195,8 @@ namespace FoxTube.Services CurrentUser = await User.GetUser(credential); Users[userIndex] = CurrentUser.UserInfo; - Storage.Registry.Values[UsersStorageKey] = JsonConvert.SerializeObject(Users); - Storage.Registry.Values[LastUserInfoKey] = userIndex; + StorageService.UserInfos = JsonConvert.SerializeObject(Users); + StorageService.LastUserIndex = userIndex; credential.RefreshTokenUpdated += (s, e) => UpdateToken(CurrentUser.UserInfo.Id, credential.Token.RefreshToken); UpdateToken(CurrentUser.UserInfo.Id, credential.Token.RefreshToken); diff --git a/FoxTube.Core/Utils/AddonsInterop.cs b/FoxTube.Core/Utils/AddonsInterop.cs index 66a13d1..1fd8d86 100644 --- a/FoxTube.Core/Utils/AddonsInterop.cs +++ b/FoxTube.Core/Utils/AddonsInterop.cs @@ -18,18 +18,15 @@ namespace FoxTube.Utils public static NativeAdsManagerV2 AdsManager => new NativeAdsManagerV2(ApplicationId, AdsId); - static AddonsInterop() => - UpdateStoreState(); - - public static async void UpdateStoreState() + public static async Task UpdateProPurchasedState() { StoreProductQueryResult requset = await StoreContext.GetDefault().GetAssociatedStoreProductsAsync(new[] { "Durable" }); if (requset.Products[ProProductId].IsInUserCollection) - return; + return AdsDisabled = true; Price = requset.Products[ProProductId].Price.FormattedPrice; - AdsDisabled = false; + return AdsDisabled = false; } public static async Task PurchaseApp() diff --git a/FoxTube.Core/Utils/BackgroundManager.cs b/FoxTube.Core/Utils/BackgroundManager.cs new file mode 100644 index 0000000..94a953a --- /dev/null +++ b/FoxTube.Core/Utils/BackgroundManager.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.ApplicationModel.Activation; +using Windows.ApplicationModel.Background; +using Windows.System.Power; +using Windows.UI.Notifications; + +namespace FoxTube.Utils +{ + // TODO: Complete class + public static class BackgroundManager + { + public static async Task PerformBackgroundTask(BackgroundActivatedEventArgs args) + { + + if (args.TaskInstance.TriggerDetails is ToastNotificationActionTriggerDetail details) + { + ProcessBackgroundAction(details.Argument); + // TODO: Process toast parameters + } + else + { + // TODO: Restore user + // TODO: Update subscriptions + // TODO: Update homepage cache + } + + //var saverRequest = PowerManager.EnergySaverStatus; + } + + public static async Task ProcessBackgroundAction(string uri) + { + + } + + public static async void RegisterBackgroundTasks() + { + var backgroundRequest = await BackgroundExecutionManager.RequestAccessAsync(); + if (backgroundRequest == BackgroundAccessStatus.DeniedBySystemPolicy || backgroundRequest == BackgroundAccessStatus.DeniedByUser) + return; + + RegisterBackgoundTask("FoxtubeToastBackground", new ToastNotificationActionTrigger()); + RegisterBackgoundTask("FoxtubeBackground", new TimeTrigger(15, false)); + } + + private static void RegisterBackgoundTask(string taskName, IBackgroundTrigger trigger) + { + if (BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals(taskName))) + return; + + BackgroundTaskBuilder builder = new BackgroundTaskBuilder + { + Name = taskName, + IsNetworkRequested = true + }; + builder.SetTrigger(trigger); + + BackgroundTaskRegistration registration = builder.Register(); + } + } +} diff --git a/FoxTube.Core/Utils/FeedbackIterop.cs b/FoxTube.Core/Utils/FeedbackIterop.cs index 8820bc9..bae5b12 100644 --- a/FoxTube.Core/Utils/FeedbackIterop.cs +++ b/FoxTube.Core/Utils/FeedbackIterop.cs @@ -33,7 +33,7 @@ namespace FoxTube.Utils { if (!HasFeedbackHub) { - Storage.SetValue(Storage.Settings.PromptFeedback, false); + StorageService.PromptFeedback = false; return; } @@ -53,7 +53,7 @@ namespace FoxTube.Utils ContentDialogResult result = await dialog.ShowAsync(); if (result != ContentDialogResult.None) - Storage.SetValue(Storage.Settings.PromptFeedback, false); + StorageService.PromptFeedback = false; if (result == ContentDialogResult.Primary) OpenFeedbackHub(); @@ -80,7 +80,7 @@ namespace FoxTube.Utils ContentDialogResult result = await dialog.ShowAsync(); if (result != ContentDialogResult.None) - Storage.SetValue(Storage.Settings.PromptReview, false); + StorageService.PromptFeedback = false; if (result == ContentDialogResult.Primary) RequestStoreReview(); diff --git a/FoxTube.Core/Utils/Metrics.cs b/FoxTube.Core/Utils/Metrics.cs index 8e296cd..c08227f 100644 --- a/FoxTube.Core/Utils/Metrics.cs +++ b/FoxTube.Core/Utils/Metrics.cs @@ -5,8 +5,8 @@ using System; using System.Linq; using System.Collections.Generic; using System.Diagnostics; -using Windows.ApplicationModel; using FoxTube.Services; +using System.Globalization; namespace FoxTube.Utils { @@ -15,26 +15,29 @@ namespace FoxTube.Utils static readonly Stopwatch sw = new Stopwatch(); public static TimeSpan Uptime { - get => Storage.GetValue(Storage.Metrics.Uptime) ?? TimeSpan.FromSeconds(0); - set => Storage.SetValue(Storage.Metrics.Uptime, value); + get => StorageService.Uptime; + set => StorageService.Uptime = value; } + public static string CurrentVersion { get { - PackageVersion v = Package.Current.Id.Version; - return $"{v.Major}.{v.Minor}.{v.Revision}.{v.Build}"; + // TODO: Remove on release + /*PackageVersion v = Package.Current.Id.Version; + return $"{v.Major}.{v.Minor}.{v.Revision}.{v.Build}";*/ + return "2.0 Preview 1"; } } static Metrics() { sw.Start(); - if (!Storage.GetValue(Storage.Settings.AllowAnalytics)) + if (!SettingsService.AllowAnalytics) return; AppCenter.Start(SecretConstants.MetricsId, typeof(Analytics), typeof(Crashes)); - AppCenter.SetCountryCode(Storage.GetValue(Storage.Settings.Region)); + AppCenter.SetCountryCode(RegionInfo.CurrentRegion.TwoLetterISORegionName); AppCenter.LogLevel = LogLevel.Verbose; } @@ -43,7 +46,7 @@ namespace FoxTube.Utils sw.Stop(); Uptime += sw.Elapsed; - if (Storage.GetValue(Storage.Settings.AllowAnalytics)) + if (SettingsService.AllowAnalytics) AddEvent("Session closed", ("Duration", sw.Elapsed.ToString()), ("Spend time total", Uptime.ToString())); @@ -51,7 +54,7 @@ namespace FoxTube.Utils public static void AddEvent(string eventName, params (string key, string value)[] details) { - if (Storage.GetValue(Storage.Settings.AllowAnalytics)) + if (SettingsService.AllowAnalytics) Analytics.TrackEvent(eventName, details.Length < 1 ? null : details.Select(i => new KeyValuePair(i.key, i.value)) as Dictionary); @@ -59,7 +62,7 @@ namespace FoxTube.Utils public static void SendReport(Exception exception, ErrorAttachmentLog[] logs = null, params (string key, string value)[] details) { - if (Storage.GetValue(Storage.Settings.AllowAnalytics)) + if (SettingsService.AllowAnalytics) { logs ??= new ErrorAttachmentLog[0]; Crashes.TrackError(exception ?? new Exception("Unknown exception"), diff --git a/FoxTube.Core/Utils/SecretConstants.cs b/FoxTube.Core/Utils/SecretConstants.cs index b3d3b39..3ff1d7d 100644 --- a/FoxTube.Core/Utils/SecretConstants.cs +++ b/FoxTube.Core/Utils/SecretConstants.cs @@ -11,6 +11,8 @@ namespace FoxTube.Utils public static class SecretConstants { public const string YoutubeApiKey = "AIzaSyBgHrCnrlzlVmk0cJKL8RqP9Y8x6XSuk_0"; + public const string BloggerApiKey = "AIzaSyD7tpbuvmYDv9h4udo9L_g3r0sLPFAnN00"; + public const string BlogId = "8566398713922921363"; public const string MetricsId = "45774462-9ea7-438a-96fc-03982666f39e"; public const string ProAddonId = "9NP1QK556625"; public const string AdsUnitId = "1100044398"; diff --git a/FoxTube.Core/Utils/ToastTemplates.cs b/FoxTube.Core/Utils/ToastTemplates.cs new file mode 100644 index 0000000..b658e60 --- /dev/null +++ b/FoxTube.Core/Utils/ToastTemplates.cs @@ -0,0 +1,51 @@ +using System.Linq; +using Microsoft.Toolkit.Uwp.Notifications; +using Google.Apis.Blogger.v3.Data; +using Windows.UI.Notifications; +using AngleSharp.Html.Parser; + +namespace FoxTube.Utils +{ + public static class ToastTemplates + { + public static ToastNotification GetBlogpostToast(Post post) + { + ToastContent toastContent = new ToastContent + { + Visual = new ToastVisual + { + BindingGeneric = new ToastBindingGeneric + { + Children = + { + new AdaptiveText + { + Text = new BindableString(post.Title) + }, + new AdaptiveText() + { + Text = new BindableString(new HtmlParser().ParseDocument(post.Content).QuerySelector("p").TextContent), + HintMaxLines = 2 + } + }, + AppLogoOverride = new ToastGenericAppLogo + { + Source = post.Author.Image.Url, + HintCrop = ToastGenericAppLogoCrop.Circle + } + } + }, + Launch = $"Settings/Inbox/{post.Id}", + ActivationType = ToastActivationType.Foreground + }; + + if (post.Images.Count > 0) + toastContent.Visual.BindingGeneric.HeroImage = new ToastGenericHeroImage + { + Source = post.Images.FirstOrDefault()?.Url + }; + + return new ToastNotification(toastContent.GetXml()); + } + } +} diff --git a/FoxTube.Core/Utils/Utils.cs b/FoxTube.Core/Utils/Utils.cs index c3de3c6..089222a 100644 --- a/FoxTube.Core/Utils/Utils.cs +++ b/FoxTube.Core/Utils/Utils.cs @@ -2,6 +2,8 @@ using System; using Windows.ApplicationModel.Core; using Windows.Security.Credentials; +using Windows.UI; +using Windows.UI.ViewManagement; namespace FoxTube.Utils { @@ -29,11 +31,36 @@ namespace FoxTube.Utils public static async void InitializeFailsafeProtocol() { Metrics.AddEvent("Failsafe protocol initiated"); - await Storage.ResetStorage(); + await StorageService.ClearStorage(); PasswordVault passwordVault = new PasswordVault(); foreach (PasswordCredential credential in passwordVault.RetrieveAll()) passwordVault.Remove(credential); RestartApp(); } + + public static void UpdateTitleBarTheme(bool isDark) + { + ApplicationViewTitleBar titleBar = ApplicationView.GetForCurrentView().TitleBar; + titleBar.ButtonBackgroundColor = Colors.Transparent; + titleBar.ButtonInactiveBackgroundColor = Colors.Transparent; + titleBar.ButtonInactiveForegroundColor = Colors.Gray; + + if (isDark) + { + titleBar.ButtonForegroundColor = + titleBar.ButtonHoverForegroundColor = + titleBar.ButtonPressedForegroundColor = Colors.White; + titleBar.ButtonHoverBackgroundColor = Color.FromArgb(50, 255, 255, 255); + titleBar.ButtonPressedBackgroundColor = Color.FromArgb(30, 255, 255, 255); + } + else + { + titleBar.ButtonForegroundColor = + titleBar.ButtonHoverForegroundColor = + titleBar.ButtonPressedForegroundColor = Colors.Black; + titleBar.ButtonHoverBackgroundColor = Color.FromArgb(50, 0, 0, 0); + titleBar.ButtonPressedBackgroundColor = Color.FromArgb(70, 0, 0, 0); + } + } } } diff --git a/FoxTube/App.xaml b/FoxTube/App.xaml index be15912..1a5c405 100644 --- a/FoxTube/App.xaml +++ b/FoxTube/App.xaml @@ -1,13 +1,22 @@  + x:Class="FoxTube.App" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + + + + + + + + + Red diff --git a/FoxTube/App.xaml.cs b/FoxTube/App.xaml.cs index 1ff2f5f..d4fa856 100644 --- a/FoxTube/App.xaml.cs +++ b/FoxTube/App.xaml.cs @@ -1,11 +1,8 @@ using System; -using FoxTube.Services; using FoxTube.Utils; using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; -using Windows.UI; -using Windows.UI.Popups; -using Windows.UI.ViewManagement; +using Windows.ApplicationModel.Background; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -21,52 +18,50 @@ namespace FoxTube UnhandledException += ErrorOccured; } - protected override async void OnLaunched(LaunchActivatedEventArgs args) + protected override void OnLaunched(LaunchActivatedEventArgs args) { - try - { - await UserManagement.Initialize(); - await StoreInterop.UpdateStoreState(); - if (Settings.LastReviewedVersion != Metrics.CurrentVersion) - Inbox.PushChangelog(); - } - catch (Exception e) - { - Metrics.SendReport(new Exception("OuLaunch initialization failed", e)); - await new MessageDialog("There can be a temporary server issue or weak internet connection. Please, check your internet connection and try again later", "Something went wrong...").ShowAsync(); - Utils.Utils.CloseApp(); - } - - if (!args.PrelaunchActivated && Window.Current.Content == null) - Window.Current.Content = new MainPage(); - Window.Current.Activate(); + base.OnLaunched(args); + ActivateWindow(args); } - protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args) + protected override async void OnBackgroundActivated(BackgroundActivatedEventArgs args) { - var deferral = args.TaskInstance.GetDeferral(); base.OnBackgroundActivated(args); + BackgroundTaskDeferral defferal = args.TaskInstance.GetDeferral(); - deferral.Complete(); + await BackgroundManager.PerformBackgroundTask(args); + + defferal.Complete(); } protected override void OnActivated(IActivatedEventArgs e) { base.OnActivated(e); - - if (Window.Current.Content == null) - Window.Current.Content = new MainPage(); - Window.Current.Activate(); + ActivateWindow(e); } - void OnSuspending(object sender, SuspendingEventArgs e) + private void ActivateWindow(IActivatedEventArgs args) + { + if (!(Window.Current.Content is Frame rootFrame)) + Window.Current.Content = rootFrame = new Frame(); + + if (rootFrame.Content == null && args.PreviousExecutionState != ApplicationExecutionState.Running) + { + if (rootFrame.Content == null) + rootFrame.Content = new SplashScreen(args); + Window.Current.Activate(); + } + } + + private void OnSuspending(object sender, SuspendingEventArgs e) { Metrics.EndSession(); + // TODO: Perform suspending tasks (complete downloads or cancel closing) } - async void ErrorOccured(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e) + private async void ErrorOccured(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e) { - Metrics.AddEvent("Application chrashed", + Metrics.AddEvent("Application crashed", ("Exception", e.Exception.GetType().ToString()), ("Message", e.Message), ("StackTrace", e.Exception.StackTrace)); @@ -79,46 +74,22 @@ namespace FoxTube Content = "It may be a bug or temporary server issues. Please, try again later\n\nIf this happens again try to reset your app settings or report the problem", PrimaryButtonText = "Report the problem", SecondaryButtonText = "Reset application", - CloseButtonText = "Close", + CloseButtonText = "Restart the app", DefaultButton = ContentDialogButton.Primary }.ShowAsync(); switch (result) { case ContentDialogResult.Primary: - Feedback.OpenFeedbackHub(); + FeedbackInterop.OpenFeedbackHub(); break; case ContentDialogResult.Secondary: Utils.Utils.InitializeFailsafeProtocol(); break; default: + Utils.Utils.RestartApp(); break; } } - - public static void UpdateTitleBar(bool isDark) - { - ApplicationViewTitleBar titleBar = ApplicationView.GetForCurrentView().TitleBar; - titleBar.ButtonBackgroundColor = Colors.Transparent; - titleBar.ButtonInactiveBackgroundColor = Colors.Transparent; - titleBar.ButtonInactiveForegroundColor = Colors.Gray; - - if (isDark) - { - titleBar.ButtonForegroundColor = - titleBar.ButtonHoverForegroundColor = - titleBar.ButtonPressedForegroundColor = Colors.White; - titleBar.ButtonHoverBackgroundColor = Color.FromArgb(50, 255, 255, 255); - titleBar.ButtonPressedBackgroundColor = Color.FromArgb(30, 255, 255, 255); - } - else - { - titleBar.ButtonForegroundColor = - titleBar.ButtonHoverForegroundColor = - titleBar.ButtonPressedForegroundColor = Colors.Black; - titleBar.ButtonHoverBackgroundColor = Color.FromArgb(50, 0, 0, 0); - titleBar.ButtonPressedBackgroundColor = Color.FromArgb(70, 0, 0, 0); - } - } } } \ No newline at end of file diff --git a/FoxTube/Classes/MenuItemsList.cs b/FoxTube/Classes/MenuItemsList.cs index 4bf0d3a..a818327 100644 --- a/FoxTube/Classes/MenuItemsList.cs +++ b/FoxTube/Classes/MenuItemsList.cs @@ -3,6 +3,9 @@ using Windows.UI.Xaml.Controls; using Windows.UI.Xaml; using Windows.UI.Xaml.Media.Imaging; using FoxTube.Models; +using FoxTube.Views.HomeSections; +using FoxTube.Views; +using FoxTube.Services; namespace FoxTube.Utils { @@ -10,26 +13,25 @@ namespace FoxTube.Utils { public static List GetMenuItems(bool authorized) { - List list = new List - { - GetItem("Home", new SymbolIcon(Symbol.Home), "home") - }; + List list = new List(); if (authorized) list.AddRange(new Microsoft.UI.Xaml.Controls.NavigationViewItemBase[] { - GetItem("Subscriptions", new SymbolIcon(Symbol.People), "subscriptions"), + GetItem("Home", new SymbolIcon(Symbol.Home), typeof(HomeView)), new Microsoft.UI.Xaml.Controls.NavigationViewItemHeader { Content = "Library" }, - GetItem("History", new FontIcon { Glyph = "\xE81C" }, "history"), - GetItem("Liked videos", new SymbolIcon(Symbol.Like), "liked"), - GetItem("Watch later", new SymbolIcon(Symbol.Clock), "watchLater"), - GetItem("Downloads", new SymbolIcon(Symbol.Download), "downloads") + GetItem("History", new FontIcon { Glyph = "\xE81C" }, typeof(HistoryView)), + GetItem("Liked videos", new SymbolIcon(Symbol.Like), (typeof(PlaylistView), UserService.CurrentUser.Channel.ContentDetails.RelatedPlaylists.Likes)), + GetItem("Watch later", new SymbolIcon(Symbol.Clock), (typeof(PlaylistView), "WL")), + GetItem("Downloads", new SymbolIcon(Symbol.Download), typeof(DownloadsView)), + GetItem("Subscriptions", new SymbolIcon(Symbol.People), typeof(SubscriptionsView)) }); else list.AddRange(new Microsoft.UI.Xaml.Controls.NavigationViewItemBase[] { - GetItem("Downloads", new SymbolIcon(Symbol.Download), "downloads"), + GetItem("Home", new SymbolIcon(Symbol.Home), typeof(TrendingView)), + GetItem("Downloads", new SymbolIcon(Symbol.Download), typeof(DownloadsView)), new Microsoft.UI.Xaml.Controls.NavigationViewItemHeader { Content = "Best of YouTube" }, GetItem("Music", new FontIcon { Glyph = "\xE189" }, "UC-9-kyTW8ZkZNDHQJ6FgpwQ"), @@ -44,7 +46,7 @@ namespace FoxTube.Utils return list; } - private static Microsoft.UI.Xaml.Controls.NavigationViewItem GetItem(string content, IconElement icon, string tag) => + private static Microsoft.UI.Xaml.Controls.NavigationViewItem GetItem(string content, IconElement icon, object tag) => new Microsoft.UI.Xaml.Controls.NavigationViewItem { Content = content, @@ -54,29 +56,29 @@ namespace FoxTube.Utils public static Microsoft.UI.Xaml.Controls.NavigationViewItem GenerateItemFromSubscription(Subscription subscription) { - StackPanel stack = new StackPanel + Grid grid = new Grid { - Orientation = Orientation.Horizontal, - Padding = new Thickness(5), - Margin = new Thickness(-5, 0, 0, 0) + ColumnSpacing = 12 }; - stack.Children.Add(new Microsoft.UI.Xaml.Controls.PersonPicture + grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(20) }); + grid.ColumnDefinitions.Add(new ColumnDefinition()); + + grid.Children.Add(new Microsoft.UI.Xaml.Controls.PersonPicture { - Height = 20, - Margin = new Thickness(-5, 0, 15, 0), + Width = 20, ProfilePicture = new BitmapImage().LoadImage(subscription.Avatar.Default__.Url, 20, 20) }); - stack.Children.Add(new TextBlock + grid.Children.Add(new TextBlock { - FontSize = 14, Text = subscription.Title }); + Grid.SetColumn(grid.Children[1] as TextBlock, 1); return new Microsoft.UI.Xaml.Controls.NavigationViewItem { - Content = stack, + Content = grid, Tag = subscription.ChannelId }; } diff --git a/FoxTube/Classes/Navigation.cs b/FoxTube/Classes/Navigation.cs deleted file mode 100644 index e092b08..0000000 --- a/FoxTube/Classes/Navigation.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace FoxTube -{ - public enum NavigationTarget - { - Home, - Settings, - Downloads, - Subscriptions - } - - public static class Navigation - { - public static NavigationTarget CurrentPage { get; private set; } - public static object CurrentParameter { get; private set; } - - public static void NavigateTo(NavigationTarget destination) => - NavigateTo(destination, null); - - public static void NavigateTo(NavigationTarget destination, object parameters) - { - MainPage.Current.Navigate(destination switch - { - NavigationTarget.Settings => typeof(Views.Settings), - NavigationTarget.Downloads => typeof(Views.Downloads), - NavigationTarget.Subscriptions => typeof(Views.Subscriptions), - _ => UserManagement.Authorized ? typeof(Views.Home) : typeof(Views.HomeSections.Trending), - }, - parameters); - - CurrentPage = destination; - CurrentParameter = parameters; - } - - public static void RefreshCurrentPage() => - MainPage.Current.Refresh(); - } -} \ No newline at end of file diff --git a/FoxTube/Classes/ScrollPage.cs b/FoxTube/Classes/ScrollPage.cs new file mode 100644 index 0000000..8a74999 --- /dev/null +++ b/FoxTube/Classes/ScrollPage.cs @@ -0,0 +1,16 @@ +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace FoxTube.Pages +{ + public class ScrollPage : Page + { + public ScrollViewer ScrollViewer => base.Content as ScrollViewer; + public new UIElement Content => ScrollViewer.Content as UIElement; + + public ScrollPage() + { + base.Content = new ScrollViewer(); + } + } +} \ No newline at end of file diff --git a/FoxTube/Classes/TemplatedPage.cs b/FoxTube/Classes/TemplatedPage.cs new file mode 100644 index 0000000..f99379b --- /dev/null +++ b/FoxTube/Classes/TemplatedPage.cs @@ -0,0 +1,27 @@ +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace FoxTube.Classes +{ + public class TemplatedPage : Page + { + public UIElement PageDummy + { + get => ((base.Content as Grid).Children[0] as ContentPresenter).Content as UIElement; + set => ((base.Content as Grid).Children[0] as ContentPresenter).Content = value; + } + public new UIElement Content + { + get => ((base.Content as Grid).Children[1] as ContentPresenter).Content as UIElement; + set => ((base.Content as Grid).Children[1] as ContentPresenter).Content = value; + } + + public TemplatedPage() + { + Grid grid = new Grid(); + grid.Children.Add(new ContentPresenter()); + grid.Children.Add(new ContentPresenter()); + base.Content = grid; + } + } +} diff --git a/FoxTube/Controls/AccountManager.xaml b/FoxTube/Controls/AccountManager.xaml index c9e0dc3..5fe73f0 100644 --- a/FoxTube/Controls/AccountManager.xaml +++ b/FoxTube/Controls/AccountManager.xaml @@ -2,94 +2,164 @@ x:Class="FoxTube.Controls.AccountManager" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:data="using:Google.Apis.Oauth2.v2.Data" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" xmlns:ui="using:Microsoft.UI.Xaml.Controls" - xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls" xmlns:data="using:Google.Apis.Oauth2.v2.Data" - Loaded="AppBarButton_Loaded" - Style="{StaticResource ButtonRevealStyle}" - CornerRadius="0" - Padding="10,0" - Height="32" - Background="Transparent"> + Height="32" + Padding="10,0" + Background="Transparent" + CornerRadius="0" + Loaded="AppBarButton_Loaded" + Style="{StaticResource ButtonRevealStyle}" + mc:Ignorable="d"> - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/FoxTube/Controls/AccountManager.xaml.cs b/FoxTube/Controls/AccountManager.xaml.cs index 21a26b0..962a6e1 100644 --- a/FoxTube/Controls/AccountManager.xaml.cs +++ b/FoxTube/Controls/AccountManager.xaml.cs @@ -4,6 +4,9 @@ using FoxTube.Models; using System.Linq; using Google.Apis.Oauth2.v2.Data; using Windows.UI.Xaml.Media.Imaging; +using FoxTube.Services; +using System; +using FoxTube.Utils; namespace FoxTube.Controls { @@ -14,27 +17,40 @@ namespace FoxTube.Controls public AccountManager() => InitializeComponent(); - private async void Flyout_Opening(object sender, object e) + private async void Flyout_Opening(object sender, object args) { - if (UserManagement.Authorized) + if (UserService.Authorized) return; (sender as Flyout).Hide(); - await UserManagement.AddUser(); + try + { + await UserService.AddUser(); + } + catch (Exception e) + { + await new ContentDialog + { + Title = "Failed to add account", + Content = "Something went wrong. Please, try again later" + }.ShowAsync(); + + Metrics.SendReport(new Exception("Failed to login", e)); + } } private void AppBarButton_Loaded(object sender, RoutedEventArgs e) { - if (UserManagement.Authorized) + if (UserService.Authorized) UpdateData(true); - UserManagement.UserStateUpdated += (s, e) => UpdateData(e); + UserService.UserStateUpdated += (s, e) => UpdateData(e); } private void UpdateData(bool authorized) { - CurrentUser = UserManagement.CurrentUser; + CurrentUser = UserService.CurrentUser; if (authorized) { buttonLabel.Text = name.Text = CurrentUser.Channel.Snippet.Title; @@ -49,11 +65,11 @@ namespace FoxTube.Controls icon.Visibility = Visibility.Collapsed; profileIcon.Visibility = Visibility.Visible; - var users = UserManagement.Users.Where(i => i.Id != CurrentUser.UserInfo.Id).ToList(); + var users = UserService.Users.Where(i => i.Id != CurrentUser.UserInfo.Id).ToList(); usersList.ItemsSource = users; usersList.Visibility = users.Count > 0 ? Visibility.Visible : Visibility.Visible; - addAccountButton.Visibility = UserManagement.CanAddAccounts ? Visibility.Visible : Visibility.Collapsed; + addAccountButton.Visibility = UserService.CanAddAccounts ? Visibility.Visible : Visibility.Collapsed; } else { @@ -68,7 +84,7 @@ namespace FoxTube.Controls private async void SwitchUser(object sender, ItemClickEventArgs e) { Userinfoplus user = (Userinfoplus)e.ClickedItem; - await UserManagement.SwitchUser(UserManagement.Users.ToList().IndexOf(user)); + await UserService.SwitchUser(UserService.Users.ToList().IndexOf(user)); } private async void NavigationViewItem_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) @@ -81,10 +97,10 @@ namespace FoxTube.Controls case "creatorStudio": break; case "addUser": - await UserManagement.AddUser(); + await UserService.AddUser(); break; case "logout": - await UserManagement.Logout(); + await UserService.Logout(); break; } } @@ -98,22 +114,17 @@ namespace FoxTube.Controls MainPage.Current.RequestedTheme = ElementTheme.Dark; else { - if (Settings.Theme == 0) - MainPage.Current.RequestedTheme = ElementTheme.Light; - else if (Settings.Theme == 1) - MainPage.Current.RequestedTheme = ElementTheme.Dark; - else - MainPage.Current.RequestedTheme = ElementTheme.Default; + MainPage.Current.RequestedTheme = ElementTheme.Default; - if (RequestedTheme == ElementTheme.Default) - App.UpdateTitleBar(Application.Current.RequestedTheme == ApplicationTheme.Dark); + if ((Window.Current.Content as Frame).RequestedTheme == ElementTheme.Default) + Utils.Utils.UpdateTitleBarTheme(Application.Current.RequestedTheme == ApplicationTheme.Dark); else - App.UpdateTitleBar(MainPage.Current.RequestedTheme == ElementTheme.Dark); + Utils.Utils.UpdateTitleBarTheme(MainPage.Current.RequestedTheme == ElementTheme.Dark); } - UpdateLayout(); + MainPage.Current.UpdateLayout(); - UserManagement.IncognitoMode = incognito.IsOn; + UserService.IncognitoMode = incognito.IsOn; } } } \ No newline at end of file diff --git a/FoxTube/Controls/Cards/AdvertCard.xaml.cs b/FoxTube/Controls/Cards/AdvertCard.xaml.cs index 06e683c..dcbb4a5 100644 --- a/FoxTube/Controls/Cards/AdvertCard.xaml.cs +++ b/FoxTube/Controls/Cards/AdvertCard.xaml.cs @@ -1,4 +1,5 @@ -using FoxTube.Utils; +using FoxTube.Services; +using FoxTube.Utils; using Microsoft.Advertising.WinRT.UI; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -10,7 +11,7 @@ namespace FoxTube.Controls.Cards /// public sealed partial class AdvertCard : UserControl { - readonly NativeAdsManagerV2 manager = new NativeAdsManagerV2(StoreInterop.ApplicationId, StoreInterop.AdsId); + readonly NativeAdsManagerV2 manager = AddonsInterop.AdsManager; NativeAdV2 advert; public AdvertCard() @@ -23,6 +24,7 @@ namespace FoxTube.Controls.Cards void ErrorOccurred(object sender, NativeAdErrorEventArgs e) { + UnsubscribeEvents(); (Parent as Panel)?.Children.Remove(this); Metrics.AddEvent("Error has occured while loading ad", ("Code", e.ErrorCode.ToString()), @@ -32,6 +34,7 @@ namespace FoxTube.Controls.Cards void AdReady(object sender, NativeAdReadyEventArgs e) { + UnsubscribeEvents(); advert = e.NativeAd; if (string.IsNullOrWhiteSpace(advert.CallToActionText)) @@ -43,9 +46,15 @@ namespace FoxTube.Controls.Cards Opacity = 1; - Metrics.AddEvent("Advert loaded", - ("Region", Settings.Region), - ("Version", Metrics.CurrentVersion)); + /*Metrics.AddEvent("Advert loaded", + ("Region", Storage.GetValue(Storage.Settings.Region)), + ("Version", Metrics.CurrentVersion));*/ + } + + private void UnsubscribeEvents() + { + manager.AdReady -= AdReady; + manager.ErrorOccurred -= ErrorOccurred; } void UserControl_SizeChanged(object sender, SizeChangedEventArgs e) => diff --git a/FoxTube/Controls/Dialogs/DownloadVideoDialog.xaml.cs b/FoxTube/Controls/Dialogs/DownloadVideoDialog.xaml.cs index 1fa3f1a..2a9b012 100644 --- a/FoxTube/Controls/Dialogs/DownloadVideoDialog.xaml.cs +++ b/FoxTube/Controls/Dialogs/DownloadVideoDialog.xaml.cs @@ -1,4 +1,5 @@ -using System; +using FoxTube.Services; +using System; using System.Linq; using Windows.Storage; using Windows.Storage.Pickers; @@ -41,7 +42,7 @@ namespace FoxTube.Controls.Dialogs private async void ContentDialog_Loading(FrameworkElement sender, object args) { - StorageFolder defaultFolder = await Services.DownloadsCenter.GetDefaultDownloadsFolder(); + StorageFolder defaultFolder = await DownloadsService.GetDefaultDownloadsFolder(); location.Text = $"{defaultFolder.Name}\\{Meta.Title.ReplaceInvalidChars('_')}.mp4"; } diff --git a/FoxTube/Controls/Dialogs/ProOfferDialog.xaml.cs b/FoxTube/Controls/Dialogs/ProOfferDialog.xaml.cs index fb36796..cd220d7 100644 --- a/FoxTube/Controls/Dialogs/ProOfferDialog.xaml.cs +++ b/FoxTube/Controls/Dialogs/ProOfferDialog.xaml.cs @@ -10,7 +10,7 @@ namespace FoxTube.Controls.Dialogs public ProOfferDialog() { InitializeComponent(); - getButton.Content = $"Get for {StoreInterop.Price}!"; + getButton.Content = $"Get for {AddonsInterop.Price}!"; } private async void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) => @@ -24,7 +24,7 @@ namespace FoxTube.Controls.Dialogs private async Task Purchase() { - if (!await StoreInterop.PurchaseApp()) + if (!await AddonsInterop.PurchaseApp()) return; ContentDialog dialog = new ContentDialog diff --git a/FoxTube/Controls/ItemsGrid.xaml b/FoxTube/Controls/ItemsGrid.xaml index 199cdf2..aa2aeb7 100644 --- a/FoxTube/Controls/ItemsGrid.xaml +++ b/FoxTube/Controls/ItemsGrid.xaml @@ -1,18 +1,18 @@  + d:DesignHeight="1080" + d:DesignWidth="1920"> - + @@ -21,7 +21,7 @@ - + diff --git a/FoxTube/Controls/LoadingScreen.xaml.cs b/FoxTube/Controls/LoadingScreen.xaml.cs index 2af8faf..3e3aed9 100644 --- a/FoxTube/Controls/LoadingScreen.xaml.cs +++ b/FoxTube/Controls/LoadingScreen.xaml.cs @@ -18,7 +18,7 @@ namespace FoxTube.Controls private void UserControl_Loaded(object sender, RoutedEventArgs e) { - feedbackButton.Visibility = Feedback.HasFeedbackHub ? Visibility.Visible : Visibility.Collapsed; + feedbackButton.Visibility = FeedbackInterop.HasFeedbackHub ? Visibility.Visible : Visibility.Collapsed; for (int i = 0; i < 25; i++) grid.Items.Add(new Image @@ -102,13 +102,13 @@ namespace FoxTube.Controls } private void LeaveFeedback(object sender, RoutedEventArgs e) => - Feedback.OpenFeedbackHub(); + FeedbackInterop.OpenFeedbackHub(); private async void OpenNetworkSettings(object sender, RoutedEventArgs e) => await Launcher.LaunchUriAsync("ms-settings:network".ToUri()); private void RefreshPage(object sender, RoutedEventArgs e) => - Navigation.RefreshCurrentPage(); + Navigation.Refresh(); private async void OpenTroubleshooter(object sender, RoutedEventArgs e) => await Launcher.LaunchUriAsync("ms-settings:troubleshoot".ToUri()); diff --git a/FoxTube/FoxTube.csproj b/FoxTube/FoxTube.csproj index 7bf020c..54b5d66 100644 --- a/FoxTube/FoxTube.csproj +++ b/FoxTube/FoxTube.csproj @@ -106,7 +106,8 @@ App.xaml - + + DownloadVideoDialog.xaml @@ -143,11 +144,20 @@ MainPage.xaml - - Downloads.xaml + + SplashScreen.xaml - - Home.xaml + + ChannelView.xaml + + + DownloadsView.xaml + + + HistoryView.xaml + + + HomeView.xaml Recommended.xaml @@ -155,26 +165,29 @@ Subscriptions.xaml - - Trending.xaml + + TrendingView.xaml - - Search.xaml + + PlaylistView.xaml - - Settings.xaml + + SearchView.xaml - - About.xaml + + AboutSection.xaml - - General.xaml + + PreferencesSection.xaml - - Inbox.xaml + + SettingsView.xaml - - Subscriptions.xaml + + InboxSection.xaml + + + SubscriptionsView.xaml @@ -296,11 +309,27 @@ Designer MSBuild:Compile - + Designer MSBuild:Compile - + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + Designer MSBuild:Compile @@ -312,31 +341,35 @@ Designer MSBuild:Compile - + Designer MSBuild:Compile - + Designer MSBuild:Compile - + Designer MSBuild:Compile - + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + Designer MSBuild:Compile - + Designer MSBuild:Compile - - Designer - MSBuild:Compile - - + Designer MSBuild:Compile @@ -352,7 +385,7 @@ 6.0.0 - 2.4.0 + 2.4.2 diff --git a/FoxTube/MainPage.xaml b/FoxTube/MainPage.xaml index cb0c28c..265d85c 100644 --- a/FoxTube/MainPage.xaml +++ b/FoxTube/MainPage.xaml @@ -1,38 +1,75 @@  - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - + + + + + + + + + + - - - - + BackRequested="NavigationViewControl_BackRequested" + ItemInvoked="NavigationViewControl_ItemInvoked" + IsPaneToggleButtonVisible="False" + IsPaneOpen="True"> @@ -55,28 +92,13 @@ - + - - + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +