📄 src/App/ViewModels/Panels/SmhiPanelViewModel.cs
using System;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.Extensions.DependencyInjection;
using MMirror.Integrations.Smhi;

namespace MMirror.App.ViewModels.Panels;

public partial class SmhiPanelViewModel(TimeProvider timeProvider, SmhiClient smhiClient) : ViewModelBase
{
    private readonly TimeProvider timeProvider = timeProvider;
    private readonly SmhiClient smhiClient = smhiClient;

    private CancellationTokenSource cts = new();

    [ObservableProperty]
    public partial PointForecast? Forecast { get; private set; }

    [ObservableProperty]
    public required partial string LocationName { get; set; }

    public double Latitude { get; set; }
    public double Longitude { get; set; }

    public void SubscribeToUpdates()
    {
        Task.Run(() => FetchLoop(cts.Token));
    }

    private async Task FetchLoop(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            Forecast = await smhiClient.GetPointForecast(Longitude, Latitude, cancellationToken);

            await Task.Delay(TimeSpan.FromMinutes(15), timeProvider, cancellationToken);
        }
    }

    public void UnsubscribeFromUpdates()
    {
        cts.Cancel();
        cts = new();
    }
}

internal static class SmhiPanelViewModelServiceCollectionExtensions
{
    extension(IServiceCollection services)
    {
        public IServiceCollection AddSmhiPanelViewModel()
        {
            services.AddTransient<SmhiPanelViewModel>();
            return services;
        }
    }
}