Archived
1
0

Anyway, I am the only one who reads this, right?

This commit is contained in:
Michael Gordeev
2019-12-02 16:52:49 +03:00
parent acd63a948e
commit bfc8689136
40 changed files with 8722 additions and 178 deletions
+49
View File
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.Data.Xml.Dom;
using Windows.UI;
namespace FoxTube
{
public static class Extensions
{
public static Uri ToUri(this string url)
{
try { return new Uri(url); }
catch { return null; }
}
public static XmlDocument ToXml(this string text)
{
XmlDocument doc = new XmlDocument();
try
{
doc.LoadXml(text);
return doc;
}
catch { return null; }
}
public static bool Belongs<T>(this T obj, params T[] args) =>
args.Contains(obj);
public static string ToHex(this Color color) =>
$"#{color.R:X}{color.G:X}{color.B:X}";
public static Color FromHex(this Color parent, string hex)
{
hex = hex.Replace("#", "");
List<byte> values = new List<byte>();
for(int k = 0; k < hex.Length; k++)
values.Add(byte.Parse(string.Join("", hex[k], hex[++k]), System.Globalization.NumberStyles.HexNumber));
return hex.Length switch
{
6 => Color.FromArgb(255, values[0], values[1], values[2]),
8 => Color.FromArgb(values[0], values[1], values[2], values[3]),
_ => Colors.Black
};
}
}
}
+52
View File
@@ -0,0 +1,52 @@
using System;
using Microsoft.Services.Store.Engagement;
using Windows.UI.Popups;
namespace FoxTube.Core.Helpers
{
public static class Feedback
{
public static bool HasFeedbackHub => StoreServicesFeedbackLauncher.IsSupported();
public static async void OpenFeedbackHub()
{
if (HasFeedbackHub)
await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync();
}
public static async void PromptFeedback()
{
if (!HasFeedbackHub)
{
Settings.PromptFeedback = false;
return;
}
MessageDialog dialog = new MessageDialog("Have some thoughts to share about the app or any suggestions? Leave feedback!");
dialog.Commands.Add(new UICommand("Don't ask me anymore", (command) => Settings.PromptFeedback = false));
dialog.Commands.Add(new UICommand("Maybe later"));
dialog.Commands.Add(new UICommand("Sure!", (command) =>
{
Settings.PromptFeedback = false;
OpenFeedbackHub();
}));
dialog.DefaultCommandIndex = 2;
dialog.CancelCommandIndex = 1;
await dialog.ShowAsync();
}
public static async void PromptReview()
{
MessageDialog dialog = new MessageDialog("Like our app? Review it on Microsoft Store!");
dialog.Commands.Add(new UICommand("Don't ask me anymore", (command) => Settings.PromptReview = false));
dialog.Commands.Add(new UICommand("Maybe later"));
dialog.Commands.Add(new UICommand("Sure!", (command) =>
{
StoreInterop.RequestReview();
Settings.PromptReview = false;
}));
dialog.DefaultCommandIndex = 2;
dialog.CancelCommandIndex = 1;
await dialog.ShowAsync();
}
}
}
+137
View File
@@ -0,0 +1,137 @@
using FoxTube.Core.Models;
using FoxTube.Core.Models.Inbox;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Windows.Data.Xml.Dom;
using Windows.Storage;
using Windows.UI.Notifications;
namespace FoxTube.Core.Helpers
{
public static class Inbox
{
static HttpClient client = new HttpClient();
static ApplicationDataContainer storage = ApplicationData.Current.RoamingSettings;
public static async Task<List<InboxItem>>GetInbox()
{
List<InboxItem> list = new List<InboxItem>();
list.AddRange(await GetMessages());
list.AddRange(await GetChangelogs());
list.OrderByDescending(i => i.TimeStamp);
return list;
}
public static async void PushNew()
{
try
{
// TODO: Add backend
HttpResponseMessage response = await client.GetAsync("https://xfox111.net/FoxTube/Messages?toast=true&publishedAfter=" + storage.Values["Inbox.lastCheck"] + "&lang=" + Settings.Language);
if (response.StatusCode == System.Net.HttpStatusCode.NoContent)
return;
XmlDocument doc = new XmlDocument();
doc.LoadXml(await response.Content.ReadAsStringAsync());
foreach (IXmlNode toast in doc.LastChild.ChildNodes)
ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toast.GetXml().ToXml()));
storage.Values["Inbox.lastCheck"] = DateTime.Now;
}
catch (Exception e)
{
Metrics.AddEvent("Unable to retrieve developers' messages",
("Exception", e.GetType().ToString()),
("Message", e.Message),
("App version", Metrics.CurrentVersion),
("StackTrace", e.StackTrace));
}
}
static async Task<List<Changelog>>GetChangelogs()
{
List<Changelog> list = new List<Changelog>();
try
{
// TODO: Add backend
HttpResponseMessage response = await client.GetAsync("https://xfox111.net/FoxTube/Changelogs?lang=" + Settings.Language);
if (response.StatusCode == System.Net.HttpStatusCode.NoContent)
return list;
dynamic responseObj = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync());
foreach (var item in responseObj)
list.Add(new Changelog(item.Version, item.Content, item.Description, item.TimeStamp));
}
catch (Exception e)
{
Metrics.AddEvent("Unable to retrieve changelogs",
("Exception", e.GetType().ToString()),
("Message", e.Message),
("App version", Metrics.CurrentVersion),
("StackTrace", e.StackTrace));
}
return list;
}
static async Task<List<DeveloperMessage>>GetMessages()
{
List<DeveloperMessage> list = new List<DeveloperMessage>();
try
{
// TODO: Add backend
HttpResponseMessage response = await client.GetAsync("https://xfox111.net/FoxTube/Messages?lang=" + Settings.Language);
if (response.StatusCode == System.Net.HttpStatusCode.NoContent)
return list;
dynamic responseObj = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync());
foreach (var item in responseObj)
list.Add(new DeveloperMessage(item.Id, item.Title, item.Content, item.TimeStamp, item.Avatar));
}
catch (Exception e)
{
Metrics.AddEvent("Unable to retrieve developers' messages",
("Exception", e.GetType().ToString()),
("Message", e.Message),
("App version", Metrics.CurrentVersion),
("StackTrace", e.StackTrace));
}
return list;
}
/// <summary>
/// Fires toast notification with the last changelog content
/// </summary>
public static async void PushChangelog()
{
try
{
// TODO: Add backend
Settings.LastReviewedVersion = Metrics.CurrentVersion;
HttpResponseMessage response = await client.GetAsync("https://xfox111.net/FoxTube/Changelogs?toast=true&lang=" + Settings.Language + "&version=" + Metrics.CurrentVersion);
if (response.StatusCode == System.Net.HttpStatusCode.NoContent)
return;
ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification((await response.Content.ReadAsStringAsync()).ToXml()));
}
catch (Exception e)
{
Metrics.AddEvent("Unable to retrieve changelog",
("Exception", e.GetType().ToString()),
("Message", e.Message),
("App version", Metrics.CurrentVersion),
("StackTrace", e.StackTrace));
}
}
}
}
+76
View File
@@ -0,0 +1,76 @@
using Microsoft.AppCenter;
using Microsoft.AppCenter.Analytics;
using Microsoft.AppCenter.Crashes;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.Storage;
namespace FoxTube
{
public static class Metrics
{
static readonly ApplicationDataContainer storage = ApplicationData.Current.RoamingSettings;
static readonly Stopwatch sw = new Stopwatch();
public static TimeSpan Uptime
{
get => (TimeSpan?)storage.Values["Metrics.SpentTime"] ?? TimeSpan.FromSeconds(0);
set => storage.Values["Metrics.SpentTime"] = value;
}
public static string CurrentVersion
{
get
{
PackageVersion v = Package.Current.Id.Version;
return $"{v.Major}.{v.Minor}.{v.Revision}.{v.Build}";
}
}
public static void StartSession()
{
sw.Start();
AppCenter.Start("45774462-9ea7-438a-96fc-03982666f39e", typeof(Analytics), typeof(Crashes));
AppCenter.SetCountryCode(Settings.Region);
}
public static void EndSession()
{
sw.Stop();
Uptime += sw.Elapsed;
AddEvent("Session closed",
("Duration", sw.Elapsed.ToString()),
("Spend time total", Uptime.ToString()));
}
public static void AddEvent(string eventName, params (string key, string value)[] details)
{
Dictionary<string, string> parameters = new Dictionary<string, string>();
foreach (var (key, value) in details)
parameters.Add(key, value);
Analytics.TrackEvent(eventName, parameters.Count > 0 ? parameters : null);
}
public static async Task<string> SendExtendedData(string packageTitle, string content)
{
// TODO: Add backend
using(HttpClient client = new HttpClient())
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://xfox111.net/FoxTube/AddMetrics");
Dictionary<string, string> body = new Dictionary<string, string>
{
{ "Title", packageTitle },
{ "Content", content },
{ "Version", CurrentVersion }
};
request.Content = new FormUrlEncodedContent(body);
HttpResponseMessage response = await client.SendAsync(request);
return await response.Content.ReadAsStringAsync();
}
}
}
}
+118
View File
@@ -0,0 +1,118 @@
using System.Globalization;
using Windows.Storage;
namespace FoxTube
{
public static class Settings
{
static readonly ApplicationDataContainer settings = ApplicationData.Current.RoamingSettings;
public static string DesiredVideoQuality
{
get => (string)settings.Values["DesiredVideoQuality"] ?? "auto";
set => settings.Values["DesiredVideoQuality"] = value;
}
public static string RememberedQuality
{
get => (string)settings.Values["RememberedVideoQuality"] ?? "1080p";
set => settings.Values["RememberedVideoQuality"] = value;
}
public static bool VideoNotifications
{
get => (bool?)settings.Values["NewVideosNotificationsAll"] ?? true;
set => settings.Values["NewVideosNotificationsAll"] = value;
}
public static bool DevNotifications
{
get => (bool?)settings.Values["DevelopersNewsNotifications"] ?? true;
set => settings.Values["DevelopersNewsNotifications"] = value;
}
public static bool CheckConnection
{
get => (bool?)settings.Values["WarnIfOnMeteredConnection"] ?? false;
set => settings.Values["WarnIfOnMeteredConnection"] = value;
}
public static bool Autoplay
{
get => (bool?)settings.Values["VideoAutoplay"] ?? true;
set => settings.Values["VideoAutoplay"] = value;
}
public static double Volume
{
get => (double?)settings.Values["Volume"] ?? 1;
set => settings.Values["Volume"] = value;
}
public static string Language
{
get => (string)settings.Values["InterfaceLanguage"] ?? GetDefaultLanguage();
set => settings.Values["InterfaceLanguage"] = value;
}
public static string RelevanceLanguage
{
get => (string)settings.Values["DesiredContentLanguage"] ?? CultureInfo.InstalledUICulture.TwoLetterISOLanguageName;
set => settings.Values["DesiredContentLanguage"] = value;
}
public static string Region
{
get => (string)settings.Values["Region"] ?? CultureInfo.InstalledUICulture.Name.Split('-')[1];
set => settings.Values["Region"] = value;
}
public static int SafeSearch
{
get => (int?)settings.Values["SafeSearch"] ?? 0; //Moderate
set => settings.Values["SafeSearch"] = value;
}
public static bool BlockExplicitContent
{
get => (bool?)settings.Values["BlockExplicitContent"] ?? true;
set => settings.Values["BlockExplicitContent"] = value;
}
public static bool HasAccount
{
get => (bool?)settings.Values["HasAccount"] ?? false;
set => settings.Values["HasAccount"] = value;
}
public static int Theme
{
get => (int?)settings.Values["PreferedUITheme"] ?? 2; //System
set => settings.Values["PreferedUITheme"] = value;
}
public static bool PromptReview
{
get => (bool?)settings.Values["PromptReview"] ?? Metrics.Uptime.TotalHours > 24;
set => settings.Values["PromptReview"] = value;
}
public static bool PromptFeedback
{
get => (bool?)settings.Values["PromptFeedback"] ?? Metrics.Uptime.TotalHours > 12;
set => settings.Values["PromptFeedback"] = value;
}
public static bool ProcessClipboard
{
get => (bool?)settings.Values["ProcessClipboardEntry"] ?? true;
set => settings.Values["ProcessClipboardEntry"] = value;
}
public static string LastReviewedVersion
{
get
{
if (settings.Values["LastReviewedVersion"] == null)
settings.Values["LastReviewedVersion"] = Metrics.CurrentVersion;
return (string)settings.Values["LastReviewedVersion"];
}
set => settings.Values["LastReviewedVersion"] = value;
}
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 void ResetSettings() =>
settings.Values.Clear();
}
}
+48
View File
@@ -0,0 +1,48 @@
using System;
using System.Threading.Tasks;
using Windows.Services.Store;
namespace FoxTube.Core.Helpers
{
public static class StoreInterop
{
public static bool AdsDisabled { get; private set; } = true;
public static string Price { get; private set; }
public static async Task UpdateStoreState()
{
StoreProductQueryResult requset = await StoreContext.GetDefault().GetAssociatedStoreProductsAsync(new[] { "Durable" });
if (requset.Products["9NP1QK556625"].IsInUserCollection)
return;
Price = requset.Products["9NP1QK556625"].Price.FormattedPrice;
AdsDisabled = false;
}
public static async Task<bool> PurchaseApp()
{
StorePurchaseResult request = await StoreContext.GetDefault().RequestPurchaseAsync("9NP1QK556625");
switch (request.Status)
{
case StorePurchaseStatus.AlreadyPurchased:
case StorePurchaseStatus.Succeeded:
AdsDisabled = true;
return true;
default:
return false;
}
}
public static async void RequestReview()
{
StoreRateAndReviewResult result = await StoreContext.GetDefault().RequestRateAndReviewAppAsync();
string attachedPackageId = result.Status == StoreRateAndReviewStatus.Error ? await Metrics.SendExtendedData("StoreReviewRequestError", result.ExtendedJsonData) : "Success";
Metrics.AddEvent("Store review request has been recieved",
("Result", result.Status.ToString()),
("ErrorData", attachedPackageId));
}
}
}
+36
View File
@@ -0,0 +1,36 @@
using System;
using System.Net.Http;
using Windows.ApplicationModel.Core;
using Windows.UI.Notifications;
namespace FoxTube.Core.Helpers
{
public static class Utils
{
/// <summary>
/// Terminates current application session
/// </summary>
public static void CloseApp() =>
CoreApplication.Exit();
/// <summary>
/// Restarts application
/// </summary>
public static void RestartApp() =>
RestartApp(null);
/// <summary>
/// Restarts application with specified parameters
/// </summary>
/// <param name="args">Parameters which will be provided to new application instance</param>
public static async void RestartApp(string args) =>
await CoreApplication.RequestRestartAsync(args);
public static void InitializeFailsafeProtocol()
{
Metrics.AddEvent("Failsafe protocol initiated");
Settings.ResetSettings();
RestartApp();
}
}
}