using Blahblah.FlowerApp.Controls; using Blahblah.FlowerApp.Data; using Blahblah.FlowerApp.Data.Model; using Microsoft.Extensions.Logging; using static Blahblah.FlowerApp.PropertyExtension; namespace Blahblah.FlowerApp; public partial class HomePage : AppContentPage { static readonly BindableProperty FlowersProperty = CreateProperty(nameof(Flowers)); static readonly BindableProperty IsRefreshingProperty = CreateProperty(nameof(IsRefreshing)); public FlowerClientItem[] Flowers { get => GetValue(FlowersProperty); set => SetValue(FlowersProperty, value); } public bool IsRefreshing { get => GetValue(IsRefreshingProperty); set => SetValue(IsRefreshingProperty, value); } readonly FlowerDatabase database; readonly ILogger logger; bool loaded = false; double pageWidth; const int margin = 12; const int cols = 2; double[] ys = null!; int yIndex; int itemWidth; int emptyHeight; public HomePage(FlowerDatabase database, ILogger logger) { this.database = database; this.logger = logger; InitializeComponent(); } protected override void OnSizeAllocated(double width, double height) { base.OnSizeAllocated(width, height); pageWidth = width - margin * 2; if (!loaded) { loaded = true; IsRefreshing = true; } else if (Flowers?.Length > 0) { DoInitSize(); foreach (var item in Flowers) { DoResizeItem(item); } } } 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 += 36; 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 void DoRefreshSquare() { try { var result = await FetchAsync("api/flower/latest?photo=true"); if (result?.Count > 0) { DoInitSize(); var flowers = result.Flowers.Select(f => { var item = new FlowerClientItem(f); 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; }); logger.LogInformation("got {count} flowers.", result.Count); Flowers = flowers.ToArray(); } else { Flowers = Array.Empty(); logger.LogInformation("no flowers."); } } catch (Exception ex) { await AlertError(L("failedGetFlowers", "Failed to get flowers, please try again.")); logger.LogError("error occurs in HomePage, {exception}", ex); } finally { IsRefreshing = false; } } private void RefreshView_Refreshing(object sender, EventArgs e) { Task.Run(DoRefreshSquare); } } public record FlowerResult { public FlowerItem[] Flowers { get; init; } = null!; public int Count { get; init; } }