using Blahblah.FlowerApp.Data; using Microsoft.Extensions.Logging; using System.Net.Http.Json; namespace Blahblah.FlowerApp; internal sealed class Extensions { public static string L(string key, string defaultValue = "") { return LocalizationResource.GetText(key, defaultValue); } public static BindableProperty CreateProperty(string propertyName, T? defaultValue = default, BindingMode defaultBindingMode = BindingMode.OneWay, BindableProperty.BindingPropertyChangedDelegate? propertyChanged = null) { return BindableProperty.Create(propertyName, typeof(T), typeof(V), defaultValue, defaultBindingMode, propertyChanged: propertyChanged); } public static async Task FetchAsync(string url, CancellationToken cancellation = default) { using var client = new HttpClient(); var authorization = Constants.Authorization; if (authorization != null) { client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", authorization); } return await client.GetFromJsonAsync($"{Constants.BaseUrl}/{url}", cancellation); } public static async Task PostAsync(string url, T data, CancellationToken cancellation = default) { using var client = new HttpClient(); var authorization = Constants.Authorization; if (authorization != null) { client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", authorization); } using var response = await client.PostAsJsonAsync($"{Constants.BaseUrl}/{url}", data, cancellation); if (response != null) { response.EnsureSuccessStatusCode(); var content = response.Content; var result = await content.ReadFromJsonAsync(cancellationToken: cancellation); return result; } return default; } public static async Task UploadAsync(string url, MultipartFormDataContent data, CancellationToken cancellation = default) { using var client = new HttpClient(); var authorization = Constants.Authorization; if (authorization != null) { client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", authorization); } using var response = await client.PostAsJsonAsync($"{Constants.BaseUrl}/{url}", data, cancellation); if (response != null) { response.EnsureSuccessStatusCode(); var content = response.Content; var result = await content.ReadFromJsonAsync(cancellationToken: cancellation); return result; } return default; } public static async Task CacheFileAsync(FileResult file) { string cache = Path.Combine(FileSystem.CacheDirectory, file.FileName); using Stream source = await file.OpenReadAsync(); using FileStream fs = File.OpenWrite(cache); await source.CopyToAsync(fs); return cache; } } internal static class LoggerExtension { const LogLevel MinimumLogLevel = #if DEBUG LogLevel.Information; #else LogLevel.Warning; #endif public static void LogInformation(this ILoggerContent content, string message) { Log(content, LogLevel.Information, null, message); } public static void LogWarning(this ILoggerContent content, string message) { Log(content, LogLevel.Warning, null, message); } public static void LogError(this ILoggerContent content, Exception? exception, string message) { Log(content, LogLevel.Error, exception, message); } private static void Log(ILoggerContent content, LogLevel level, Exception? exception, string message) { if (content?.Logger is ILogger logger) { logger.Log(level, exception, "[{time:MM/dd HH:mm:ss}] - {message}", DateTime.UtcNow, message); if (level >= MinimumLogLevel && content.Database is FlowerDatabase database) { _ = database.AddLog(new Data.Model.LogItem { OwnerId = AppResources.User.Id, LogType = level.ToString(), LogUnixTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), Category = "logger", ClientAgent = Constants.UserAgent, Message = message, Description = exception?.ToString(), Source = exception?.Source }); } } } } internal static class PageExtension { public static T Binding(this T obj, BindableProperty property, string path, BindingMode mode = BindingMode.Default, IValueConverter? converter = null) where T : BindableObject { obj.SetBinding(property, path, mode, converter); return obj; } public static T AppThemeBinding(this T obj, BindableProperty property, Color light, Color dark) where T : BindableObject { obj.SetAppThemeColor(property, light, dark); return obj; } public static T GridColumn(this T view, int column) where T : BindableObject { Grid.SetColumn(view, column); return view; } public static T GridRow(this T view, int row) where T : BindableObject { Grid.SetRow(view, row); return view; } public static T GridColumnSpan(this T view, int columnSpan) where T : BindableObject { Grid.SetColumnSpan(view, columnSpan); return view; } public static T GridRowSpan(this T view, int rowSpan) where T : BindableObject { Grid.SetRowSpan(view, rowSpan); return view; } public static Task AlertError(this ContentPage page, string error) { return Alert(page, LocalizationResource.GetText("error", "Error"), error); } public static Task Alert(this ContentPage page, string title, string message, string? cancel = null) { cancel ??= LocalizationResource.GetText("ok", "Ok"); if (MainThread.IsMainThread) { return page.DisplayAlert(title, message, cancel); } var taskSource = new TaskCompletionSource(); MainThread.BeginInvokeOnMainThread(async () => { await page.DisplayAlert(title, message, cancel); taskSource.TrySetResult(); }); return taskSource.Task; } public static Task Confirm(this ContentPage page, string title, string question, string? accept = null, string? cancel = null) { accept ??= LocalizationResource.GetText("yes", "Yes"); cancel ??= LocalizationResource.GetText("no", "No"); if (MainThread.IsMainThread) { return page.DisplayAlert(title, question, accept, cancel); } var taskSource = new TaskCompletionSource(); MainThread.BeginInvokeOnMainThread(async () => { var result = await page.DisplayAlert(title, question, accept, cancel); taskSource.TrySetResult(result); }); return taskSource.Task; } } internal static class Res { public const string Filter = "\ue17c"; public const string Camera = "\uf030"; public const string Image = "\uf03e"; public const string Heart = "\uf004"; public const string Right = "\uf105"; public const string XMarkLarge = "\ue59b"; public const string Gear = "\uf013"; public const string List = "\uf0ae"; public const string Flag = "\uf2b4"; public const string Check = "\uf00c"; public static readonly Color Gray900 = Color.FromRgb(0x21, 0x21, 0x21); public static readonly Color Red100 = Color.FromRgb(0xF4, 0x43, 0x36); public static readonly Color Red300 = Color.FromRgb(0xFF, 0xCD, 0xD2); }