Archived
1
0

Refactored core

UI navigation framework

Related Work Items: #408, #414, #416
This commit is contained in:
Michael Gordeev
2020-06-15 15:46:38 +03:00
parent c58d846057
commit 787a6e9f48
72 changed files with 2002 additions and 1227 deletions
+18 -10
View File
@@ -132,26 +132,28 @@
<ItemGroup>
<Compile Include="Models\Collections\InboxCollection.cs" />
<Compile Include="Models\Collections\ViewCollection.cs" />
<Compile Include="Models\SearchParameters.cs" />
<Compile Include="Models\Subscription.cs" />
<Compile Include="Services\DownloadsService.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="Services\SettingsService.cs" />
<Compile Include="Utils\FeedbackIterop.cs" />
<Compile Include="Services\History.cs" />
<Compile Include="Services\InboxService.cs" />
<Compile Include="Utils\Metrics.cs" />
<Compile Include="Services\Search.cs" />
<Compile Include="Services\Storage.cs" />
<Compile Include="Services\StorageService.cs" />
<Compile Include="Utils\AddonsInterop.cs" />
<Compile Include="Services\UserService.cs" />
<Compile Include="Utils\SecretConstants.cs" />
<Compile Include="Utils\BackgroundManager.cs" />
<Compile Include="Utils\ToastTemplates.cs" />
<Compile Include="Utils\Utils.cs" />
<Compile Include="Models\DownloadItem.cs" />
<Compile Include="Models\InboxItem.cs" />
<Compile Include="Models\Attributes.cs" />
<Compile Include="Models\SavedVideo.cs" />
<Compile Include="Models\SearchSuggestion.cs" />
<Compile Include="Models\User.cs" />
<Compile Include="Models\VideoItem.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="Properties\FoxTube.Core.rd.xml" />
</ItemGroup>
@@ -163,22 +165,25 @@
<Version>1.0.3</Version>
</PackageReference>
<PackageReference Include="Google.Apis.Auth">
<Version>1.45.0</Version>
<Version>1.46.0</Version>
</PackageReference>
<PackageReference Include="Google.Apis.Blogger.v3">
<Version>1.46.0.1986</Version>
</PackageReference>
<PackageReference Include="Google.Apis.Oauth2.v2">
<Version>1.44.1.1869</Version>
</PackageReference>
<PackageReference Include="Google.Apis.YouTube.v3">
<Version>1.45.0.1929</Version>
<Version>1.46.0.1987</Version>
</PackageReference>
<PackageReference Include="Microsoft.Advertising.XAML">
<Version>10.1811.22001</Version>
</PackageReference>
<PackageReference Include="Microsoft.AppCenter.Analytics">
<Version>3.2.1</Version>
<Version>3.2.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.AppCenter.Crashes">
<Version>3.2.1</Version>
<Version>3.2.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.2.10</Version>
@@ -186,11 +191,14 @@
<PackageReference Include="Microsoft.Services.Store.Engagement">
<Version>10.1901.28001</Version>
</PackageReference>
<PackageReference Include="Microsoft.UI.Xaml">
<Version>2.4.0</Version>
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications">
<Version>6.0.0</Version>
</PackageReference>
<PackageReference Include="QueryString.NET">
<Version>1.0.0</Version>
</PackageReference>
<PackageReference Include="YoutubeExplode">
<Version>5.0.4</Version>
<Version>5.0.5</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
@@ -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<InboxItem>
public class InboxCollection : ViewCollection<Post>
{
private int _pageNumber = 0;
private HttpClient _httpClient = new HttpClient();
private string nextPageToken;
public override async Task<LoadMoreItemsResult> LoadItems()
{
// TODO: Add backend
HttpResponseMessage response = await _httpClient.GetAsync($"https://xfox111.net/API/FoxTube/Inbox?" +
$"lang={Storage.GetValue<string>(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<InboxItem[]>(await response.Content.ReadAsStringAsync());
foreach (InboxItem item in newItems)
Items.Add(item);
_pageNumber++;
return new LoadMoreItemsResult
{
Count = (uint)newItems.Length
};
}
}
}
@@ -8,7 +8,7 @@ namespace FoxTube.Models.Collections
{
public abstract class ViewCollection<T> : ObservableCollection<T>, ISupportIncrementalLoading
{
public int ItemsPerRequest { get; set; }
public int ItemsPerRequest { get; set; } = 25;
public bool HasMoreItems { get; protected set; } = true;
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count) =>
-21
View File
@@ -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()}";
}
}
+16
View File
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FoxTube.Models
{
public class SearchParameters
{
public SearchParameters(string query)
{
}
}
}
-15
View File
@@ -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<string, string> subs = Subscriptions.Select(i =>
new KeyValuePair<string, string>(i.ChannelId, i.Avatar.Default__?.Url))
as Dictionary<string, string>;
ApplicationData.Current.RoamingSettings.Values[$"Subscriptions.{UserInfo.Id}"] = JsonConvert.SerializeObject(subs);
}
public static async Task<User> GetUser(UserCredential credential)
{
User user = new User
-49
View File
@@ -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";
}
}
}
+6 -9
View File
@@ -18,12 +18,9 @@ namespace FoxTube.Services
public static List<SavedVideo> History { get; } = new List<SavedVideo>();
public static List<DownloadItem> Queue { get; } = new List<DownloadItem>();
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<SavedVideo> savedVideos = JsonConvert.DeserializeObject<List<SavedVideo>>(File.ReadAllText(file.Path) ?? "") ?? new List<SavedVideo>();
@@ -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<StorageFolder> GetDefaultDownloadsFolder()
{
if (Storage.GetValue<string>(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<string>(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;
}
}
+35 -26
View File
@@ -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<string>(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<string[]>(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
/// <summary>
/// Fires toast notification with the last changelog content
/// </summary>
[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<string>(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)
{
+6 -1
View File
@@ -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<string>(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);
+67
View File
@@ -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
}
}
-101
View File
@@ -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<Settings> SettingsChanged;
public static StorageFolder Folder => ApplicationData.Current.RoamingFolder;
public static ApplicationDataContainer Registry { get; } = ApplicationData.Current.RoamingSettings;
private static readonly Dictionary<Settings, object> _defaultSettings = new Dictionary<Settings, object>
{
{ Settings.Theme, ElementTheme.Default },
{ Settings.UILanguage, GetDefaultLanguage() },
{ Settings.RelevanceLanguage, GetDefaultLanguage() },
{ Settings.PromptFeedback, true },
{ Settings.PromptReview, true },
{ Settings.AllowAnalytics, true }
};
public enum Settings
{
/// <summary>
/// ElementTheme
/// </summary>
Theme,
/// <summary>
/// string
/// </summary>
UILanguage,
/// <summary>
/// string
/// </summary>
RelevanceLanguage,
/// <summary>
/// string
/// </summary>
DefaultDownloadsFolder,
/// <summary>
/// bool
/// </summary>
PromptFeedback,
/// <summary>
/// bool
/// </summary>
PromptReview,
/// <summary>
/// bool
/// </summary>
AllowAnalytics,
/// <summary>
/// string
/// </summary>
Region
}
public enum Metrics
{
/// <summary>
/// TimeSpan
/// </summary>
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<T>(Settings key) =>
(T)(Registry.Values[$"{key.GetType().Name}.{key}"] ?? (_defaultSettings.ContainsKey(key) ? _defaultSettings[key] : null));
public static T GetValue<T>(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();
}
}
}
+77
View File
@@ -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
}
}
+7 -14
View File
@@ -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<bool> UserStateUpdated;
public static event EventHandler<Subscription> SubscriptionsChanged;
static UserService() =>
Initialize();
public static async Task<bool> 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<Userinfoplus[]>(Storage.Registry.Values[UsersStorageKey] as string ?? "") ?? new Userinfoplus[MaxUsersCount];
int? lastUserIndex = Storage.Registry.Values[LastUserInfoKey] as int?;
Users = JsonConvert.DeserializeObject<Userinfoplus[]>(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);
+3 -6
View File
@@ -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<bool> 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<bool> PurchaseApp()
+64
View File
@@ -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();
}
}
}
+3 -3
View File
@@ -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();
+13 -10
View File
@@ -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<TimeSpan?>(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<bool>(Storage.Settings.AllowAnalytics))
if (!SettingsService.AllowAnalytics)
return;
AppCenter.Start(SecretConstants.MetricsId, typeof(Analytics), typeof(Crashes));
AppCenter.SetCountryCode(Storage.GetValue<string>(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<bool>(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<bool>(Storage.Settings.AllowAnalytics))
if (SettingsService.AllowAnalytics)
Analytics.TrackEvent(eventName,
details.Length < 1 ? null :
details.Select(i => new KeyValuePair<string, string>(i.key, i.value)) as Dictionary<string, string>);
@@ -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<bool>(Storage.Settings.AllowAnalytics))
if (SettingsService.AllowAnalytics)
{
logs ??= new ErrorAttachmentLog[0];
Crashes.TrackError(exception ?? new Exception("Unknown exception"),
+2
View File
@@ -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";
+51
View File
@@ -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());
}
}
}
+28 -1
View File
@@ -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);
}
}
}
}