using Blahblah.FlowerApp.Controls; using Blahblah.FlowerApp.Data; using Blahblah.FlowerApp.Data.Model; using Blahblah.FlowerApp.Views.Garden; using Microsoft.Extensions.Logging; using System.Net; using static Blahblah.FlowerApp.Extensions; namespace Blahblah.FlowerApp; public partial class HomePage : AppContentPage { static readonly BindableProperty SearchKeyProperty = CreateProperty(nameof(SearchKey), propertyChanged: OnSearchKeyPropertyChanged); static readonly BindableProperty FlowersProperty = CreateProperty(nameof(Flowers)); static readonly BindableProperty IsRefreshingProperty = CreateProperty(nameof(IsRefreshing)); static readonly BindableProperty CurrentCountProperty = CreateProperty(nameof(CurrentCount)); static void OnSearchKeyPropertyChanged(BindableObject bindable, object old, object @new) { if (bindable is HomePage home && @new is string) { if (home.IsRefreshing) { home.changed = true; } else { home.IsRefreshing = true; } } } public string SearchKey { get => GetValue(SearchKeyProperty); set => SetValue(SearchKeyProperty, value); } public FlowerClientItem[] Flowers { get => GetValue(FlowersProperty); set => SetValue(FlowersProperty, value); } public bool IsRefreshing { get => GetValue(IsRefreshingProperty); set => SetValue(IsRefreshingProperty, value); } public string? CurrentCount { get => GetValue(CurrentCountProperty); set => SetValue(CurrentCountProperty, value); } bool logined = false; bool loaded = false; bool? setup; double pageWidth; bool changed = false; const int margin = 12; const int cols = 2; double[] ys = null!; int yIndex; int itemWidth; int emptyHeight; public HomePage(FlowerDatabase database, ILogger logger) : base(database, logger) { InitializeComponent(); Task.Run(async () => { try { await Database.Setup(); } catch (Exception ex) { this.LogError(ex, $"error occurs when setting up database, {ex.Message}"); } finally { setup = true; } }); } protected override void OnSizeAllocated(double width, double height) { base.OnSizeAllocated(width, height); if (!logined) { logined = true; IsRefreshing = true; Task.Run(async () => { while (setup == null) { await Task.Delay(100); } await DoValidationAsync(); if (!loaded) { loaded = true; await DoRefreshAsync(); } }); } pageWidth = width - margin * 2; if (loaded && Flowers?.Length > 0) { DoInitSize(); foreach (var item in Flowers) { DoResizeItem(item); } } } private async Task DoValidationAsync() { bool invalid = true; var oAuth = Constants.Authorization; if (!string.IsNullOrEmpty(oAuth)) { try { var user = await FetchAsync("api/user/profile"); if (user != null) { invalid = false; AppResources.SetUser(user); } } catch (Exception ex) { this.LogError(ex, $"token is invalid, token: {oAuth}, {ex.Message}"); } } if (invalid) { var source = new TaskCompletionSource(); MainThread.BeginInvokeOnMainThread(() => { var login = new LoginPage(Database, Logger); var sheet = this.ShowBottomSheet(login); login.AfterLogined += (sender, user) => { sheet.CloseBottomSheet(); source.TrySetResult(true); }; }); return await source.Task; } return true; } private void DoInitSize() { ys = new double[cols]; yIndex = 0; itemWidth = (int)(pageWidth / cols) - margin * (cols - 1) / 2; emptyHeight = (int)(itemWidth * AppResources.EmptySize.Height / AppResources.EmptySize.Width); } private void DoResizeItem(FlowerClientItem item) { int height; if (item.Width > 0 && item.Height > 0) { height = itemWidth * item.Height.Value / item.Width.Value; } else { height = emptyHeight; } height += 46; double yMin = double.MaxValue; for (var i = 0; i < cols; i++) { if (ys[i] < yMin) { yMin = ys[i]; yIndex = i; } } ys[yIndex] += height + margin; item.Bounds = new Rect( yIndex, yMin, itemWidth, height); } private async Task DoRefreshAsync() { try { var url = "api/flower/latest?photo=true"; var key = SearchKey; if (!string.IsNullOrWhiteSpace(key)) { url += "&key=" + WebUtility.UrlEncode(key); } var result = await FetchAsync(url); if (result?.Count > 0) { CurrentCount = L("currentCount", "There are currently {count} plants").Replace("{count}", result.Count.ToString()); await Database.UpdateFlowers(result.Flowers); DoInitSize(); var daystring = L("daysPlanted", "{count} days planted"); var flowers = result.Flowers.Select(f => { var item = new FlowerClientItem(f); var days = (DateTimeOffset.UtcNow - DateTimeOffset.FromUnixTimeMilliseconds(f.DateBuyUnixTime)).TotalDays; item.Days = daystring.Replace("{count}", ((int)days).ToString()); if (f.Photos?.Length > 0 && f.Photos[0] is PhotoItem cover) { item.Cover = new UriImageSource { Uri = new Uri($"{Constants.BaseUrl}/{cover.Url}") }; } else { item.Cover = "empty_flower.jpg"; } DoResizeItem(item); return item; }); Flowers = flowers.ToArray(); } else { CurrentCount = null; Flowers = Array.Empty(); } } catch (Exception ex) { await this.AlertError(L("failedGetFlowers", "Failed to get flowers, please try again.")); this.LogError(ex, $"error occurs in HomePage, {ex.Message}"); } finally { IsRefreshing = false; if (changed) { changed = false; await Task.Delay(100); IsRefreshing = true; } } } private void RefreshView_Refreshing(object sender, EventArgs e) { if (loaded) { Task.Run(DoRefreshAsync); } } private async void AddFlower_Clicked(object sender, EventArgs e) { //await Shell.Current.GoToAsync("AddFlower"); var addPage = new AddFlowerPage(Database, Logger); await Navigation.PushAsync(addPage); } } public record FlowerResult { public FlowerItem[] Flowers { get; init; } = null!; public int Count { get; init; } }