Compare commits

..

2 commits

Author SHA1 Message Date
ce21244517 Making refresh behaviour more solid 2024-06-24 14:52:45 +10:00
65276d4984 Loading app settings
and putting app name and version number in the app class on startup
2024-06-24 14:51:48 +10:00
21 changed files with 103 additions and 26 deletions

View file

@ -1,5 +1,9 @@
namespace Neighbourhood.omg.lol { namespace Neighbourhood.omg.lol {
public partial class App : Application { public partial class App : Application {
public static string Name { get; set; }
public static string Version { get; set; }
public App(NavigatorService navigatorService) { public App(NavigatorService navigatorService) {
InitializeComponent(); InitializeComponent();

View file

@ -72,6 +72,7 @@
if (!string.IsNullOrEmpty(Pic?.Id)) { if (!string.IsNullOrEmpty(Pic?.Id)) {
await api.DeletePic(State.SelectedAddressName, Pic.Id); await api.DeletePic(State.SelectedAddressName, Pic.Id);
await State.RefreshPics(); await State.RefreshPics();
State.SendRefresh();
await InvokeAsync(StateHasChanged); await InvokeAsync(StateHasChanged);
} }
@ -92,6 +93,7 @@
if(!string.IsNullOrEmpty(Pic?.Id)) { if(!string.IsNullOrEmpty(Pic?.Id)) {
await api.PostPicDescription(State.SelectedAddressName, Pic.Id, Description); await api.PostPicDescription(State.SelectedAddressName, Pic.Id, Description);
await State.RefreshPics(); await State.RefreshPics();
State.SendRefresh();
await InvokeAsync(StateHasChanged); await InvokeAsync(StateHasChanged);
} }

View file

@ -91,6 +91,7 @@
if (!string.IsNullOrEmpty(Status?.Id)) { if (!string.IsNullOrEmpty(Status?.Id)) {
await api.DeleteStatus(State.SelectedAddressName, Status.Id); await api.DeleteStatus(State.SelectedAddressName, Status.Id);
await State.RefreshStatuses(); await State.RefreshStatuses();
State.SendRefresh();
await InvokeAsync(StateHasChanged); await InvokeAsync(StateHasChanged);
} }
@ -112,6 +113,7 @@
if (!string.IsNullOrEmpty(Status?.Id)) { if (!string.IsNullOrEmpty(Status?.Id)) {
await api.PatchStatus(State.SelectedAddressName, Status.Id, Content, Emoji); await api.PatchStatus(State.SelectedAddressName, Status.Id, Content, Emoji);
await State.RefreshStatuses(); await State.RefreshStatuses();
State.SendRefresh();
await InvokeAsync(StateHasChanged); await InvokeAsync(StateHasChanged);
} }

View file

@ -19,13 +19,13 @@
} }
public async Task Reload() { public async Task Reload() {
if (Html == null){ // if (Html == null){
Html = await api.GetHtml(Url); Html = await api.GetHtml(Url);
string? HtmlString = Html?.ToString(); string? HtmlString = Html?.ToString();
HtmlString = HtmlString?.Replace("</head>", "<base target='_blank'></head>"); HtmlString = HtmlString?.Replace("</head>", "<base target='_blank'></head>");
HtmlString = HtmlString?.Replace("</body>", "<script src='https://cdn.jsdelivr.net/npm/@iframe-resizer/child'></script></body>"); HtmlString = HtmlString?.Replace("</body>", "<script src='https://cdn.jsdelivr.net/npm/@iframe-resizer/child'></script></body>");
Html = (MarkupString)HtmlString; Html = (MarkupString)HtmlString;
} // }
await InvokeAsync(StateHasChanged); await InvokeAsync(StateHasChanged);
} }
} }

View file

@ -1,5 +1,4 @@
@using System.Reflection @inject CustomAuthenticationStateProvider AuthStateProvider;
@inject CustomAuthenticationStateProvider AuthStateProvider;
@inject State State; @inject State State;
<AuthorizeView> <AuthorizeView>
<Authorized> <Authorized>
@ -45,7 +44,7 @@
</a> </a>
<a class="row medium-opacity"> <a class="row medium-opacity">
<i class="fa-solid fa-circle-info tiny"></i> <i class="fa-solid fa-circle-info tiny"></i>
<small>@assembly.Name - @assembly.Version?.ToString()</small> <small>@App.Name - @App.Version</small>
</a> </a>
</menu> </menu>
</button> </button>
@ -81,7 +80,6 @@
</AuthorizeView> </AuthorizeView>
@code { @code {
AssemblyName assembly = Assembly.GetExecutingAssembly().GetName();
public void changeAddress(AddressResponseData address) { public void changeAddress(AddressResponseData address) {
State.SelectedAddress = address; State.SelectedAddress = address;
} }

View file

@ -78,6 +78,7 @@
if(!string.IsNullOrEmpty(Description) && response != null && !string.IsNullOrEmpty(response.Id)) { if(!string.IsNullOrEmpty(Description) && response != null && !string.IsNullOrEmpty(response.Id)) {
await api.PostPicDescription(State.SelectedAddressName, response.Id, Description); await api.PostPicDescription(State.SelectedAddressName, response.Id, Description);
await State.RefreshPics(); await State.RefreshPics();
State.SendRefresh();
await InvokeAsync(StateHasChanged); await InvokeAsync(StateHasChanged);
} }

View file

@ -81,8 +81,9 @@
var result = await api.StatusPost(State!.SelectedAddressName!, post); var result = await api.StatusPost(State!.SelectedAddressName!, post);
if(result != null){ if(result != null){
await State.RefreshStatuses(); await State.RefreshStatuses();
State.SendRefresh();
await InvokeAsync(StateHasChanged); await InvokeAsync(StateHasChanged);
navigationManager.NavigateTo("/statuslog/latest"); // navigationManager.NavigateTo("/statuslog/latest");
} }
this.Active = false; this.Active = false;

View file

@ -65,9 +65,11 @@ else {
private async void StateChanged(object? sender, PropertyChangedEventArgs e) { private async void StateChanged(object? sender, PropertyChangedEventArgs e) {
if (e.PropertyName == nameof(State.IsRefreshing) && State.IsRefreshing) { if (e.PropertyName == nameof(State.IsRefreshing) && State.IsRefreshing) {
addresses = await State.GetDirectory(true); using (State.GetRefreshToken()) {
GroupAddresses(); addresses = await State.GetDirectory(true);
State.IsRefreshing = false; GroupAddresses();
await InvokeAsync(StateHasChanged);
}
} }
} }

View file

@ -36,8 +36,10 @@
private async void StateChanged(object? sender, PropertyChangedEventArgs e) { private async void StateChanged(object? sender, PropertyChangedEventArgs e) {
if (e.PropertyName == nameof(State.IsRefreshing) && State.IsRefreshing) { if (e.PropertyName == nameof(State.IsRefreshing) && State.IsRefreshing) {
messages = await State.GetEphemeralMessages(true); using (State.GetRefreshToken()) {
State.IsRefreshing = false; messages = await State.GetEphemeralMessages(true);
await InvokeAsync(StateHasChanged);
}
} }
} }

View file

@ -43,8 +43,10 @@
private async void StateChanged(object? sender, PropertyChangedEventArgs e) { private async void StateChanged(object? sender, PropertyChangedEventArgs e) {
if (e.PropertyName == nameof(State.IsRefreshing) && State.IsRefreshing) { if (e.PropertyName == nameof(State.IsRefreshing) && State.IsRefreshing) {
garden = await State.GetNowGarden(true); using (State.GetRefreshToken()){
State.IsRefreshing = false; garden = await State.GetNowGarden(true);
await InvokeAsync(StateHasChanged);
}
} }
} }

View file

@ -129,6 +129,18 @@
if (fragment.EndsWith("now")) await ReloadNow(); if (fragment.EndsWith("now")) await ReloadNow();
else if (fragment.EndsWith("profile")) await ReloadProfile(); else if (fragment.EndsWith("profile")) await ReloadProfile();
bio = await State.GetBio(Address); bio = await State.GetBio(Address);
State.PropertyChanged += StateChanged;
}
private async void StateChanged(object? sender, PropertyChangedEventArgs e) {
if (e.PropertyName == nameof(State.IsRefreshing) && State.IsRefreshing) {
using (State.GetRefreshToken()){
await ReloadNow();
await ReloadProfile();
bio = await State.GetBio(Address);
await InvokeAsync(StateHasChanged);
}
}
} }
private async Task ReloadNow() { private async Task ReloadNow() {

View file

@ -34,8 +34,10 @@
private async void StateChanged(object? sender, PropertyChangedEventArgs e) { private async void StateChanged(object? sender, PropertyChangedEventArgs e) {
if (e.PropertyName == nameof(State.IsRefreshing) && State.IsRefreshing) { if (e.PropertyName == nameof(State.IsRefreshing) && State.IsRefreshing) {
pics = await PicsFunc(true); using (State.GetRefreshToken()){
State.IsRefreshing = false; pics = await PicsFunc(true);
await InvokeAsync(StateHasChanged);
}
} }
} }

View file

@ -1,6 +1,6 @@
@inject State State @inject State State
<button id="refreshButton" class="absolute transparent circle top right margin" @onclick="() => State.IsRefreshing = true"> <button id="refreshButton" class="absolute transparent circle top right margin" @onclick="() => State.SendRefresh()">
<i class="fa-solid fa-arrow-rotate-right @(State.IsRefreshing ? "fa-spin" : "")"></i> <i class="fa-solid fa-arrow-rotate-right @(State.IsRefreshing ? "fa-spin" : "")"></i>
</button> </button>

View file

@ -33,8 +33,10 @@
private async void StateChanged(object? sender, PropertyChangedEventArgs e) { private async void StateChanged(object? sender, PropertyChangedEventArgs e) {
if (e.PropertyName == nameof(State.IsRefreshing) && State.IsRefreshing) { if (e.PropertyName == nameof(State.IsRefreshing) && State.IsRefreshing) {
statuses = await StatusFunc(true); using (State.GetRefreshToken()) {
State.IsRefreshing = false; statuses = await StatusFunc(true);
await InvokeAsync(StateHasChanged);
}
} }
} }

View file

@ -33,6 +33,7 @@ public partial class LoginWebViewPage : ContentPage
if ( client_id != null if ( client_id != null
&& client_secret != null && client_secret != null
&& redirect_uri != null) { && redirect_uri != null) {
//this.loginwebview.UserAgent = new System.Net.Http.Headers.ProductInfoHeaderValue(App.Name, App.Version).ToString();
this.loginwebview.Source = $"https://home.omg.lol/oauth/authorize?client_id={client_id}&scope=everything&redirect_uri={redirect_uri}&response_type=code"; this.loginwebview.Source = $"https://home.omg.lol/oauth/authorize?client_id={client_id}&scope=everything&redirect_uri={redirect_uri}&response_type=code";
} }
} }

View file

@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Neighbourhood.omg.lol.Models; using Neighbourhood.omg.lol.Models;
using System.Reflection;
namespace Neighbourhood.omg.lol { namespace Neighbourhood.omg.lol {
public static class MauiProgram { public static class MauiProgram {
@ -22,8 +23,16 @@ namespace Neighbourhood.omg.lol {
builder.Services.AddSingleton<State>(); builder.Services.AddSingleton<State>();
builder.Services.AddSingleton<NavigatorService>(); builder.Services.AddSingleton<NavigatorService>();
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); //ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
builder.Services.AddSingleton<IConfiguration>(configurationBuilder.AddUserSecrets<App>().Build()); //builder.Services.AddSingleton<IConfiguration>(configurationBuilder.AddUserSecrets<App>().Build());
var assembly = Assembly.GetExecutingAssembly();
var details = assembly.GetName();
App.Name = details.Name!;
App.Version = details.Version!.ToString();
var appSettings = $"{App.Name}.appsettings.json";
using var stream = assembly.GetManifestResourceStream(appSettings);
var config = new ConfigurationBuilder().AddJsonStream(stream).Build();
builder.Configuration.AddConfiguration(config);
builder.Services.AddAuthorizationCore(); builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<CustomAuthenticationStateProvider>(); builder.Services.AddScoped<CustomAuthenticationStateProvider>();

View file

@ -70,12 +70,37 @@ namespace Neighbourhood.omg.lol.Models {
private bool _isRefreshing; private bool _isRefreshing;
public bool IsRefreshing { public bool IsRefreshing {
get => _isRefreshing; get => _isRefreshing;
set { private set {
_isRefreshing = value; _isRefreshing = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsRefreshing))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsRefreshing)));
} }
} }
public void SendRefresh() => IsRefreshing = true;
private static int _refresherCount = 0;
private static Mutex mutex = new Mutex();
public class RefreshToken : IDisposable {
public event EventHandler? Disposed;
public void Dispose() => Disposed?.Invoke(this, EventArgs.Empty);
}
public RefreshToken GetRefreshToken() {
mutex.WaitOne();
_refresherCount++;
mutex.ReleaseMutex();
RefreshToken token = new RefreshToken();
token.Disposed += RefreshToken_Disposed;
return token;
}
private void RefreshToken_Disposed(object? sender, EventArgs e) {
mutex.WaitOne();
_refresherCount--;
if (_refresherCount == 0) IsRefreshing = false;
mutex.ReleaseMutex();
}
private bool _canRefresh; private bool _canRefresh;
public bool CanRefresh { public bool CanRefresh {
get => _canRefresh; get => _canRefresh;

View file

@ -90,6 +90,10 @@
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" /> <MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Remove="appsettings.json" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<MauiFont Remove="Resources\Fonts\fa-brands-400.ttf" /> <MauiFont Remove="Resources\Fonts\fa-brands-400.ttf" />
<MauiFont Remove="Resources\Fonts\fa-brands-400.woff2" /> <MauiFont Remove="Resources\Fonts\fa-brands-400.woff2" />
@ -195,6 +199,10 @@
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup>
<EmbeddedResource Include="appsettings.json" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Markdig" Version="0.37.0" /> <PackageReference Include="Markdig" Version="0.37.0" />

View file

@ -19,8 +19,7 @@ namespace Neighbourhood.omg.lol {
public RestService(string? token = null) { public RestService(string? token = null) {
_client = new HttpClient(); _client = new HttpClient();
_client.BaseAddress = new Uri(BaseUrl); _client.BaseAddress = new Uri(BaseUrl);
AssemblyName name = Assembly.GetExecutingAssembly().GetName(); _client.DefaultRequestHeaders.UserAgent.Add(new System.Net.Http.Headers.ProductInfoHeaderValue(App.Name, App.Version));
_client.DefaultRequestHeaders.UserAgent.Add(new System.Net.Http.Headers.ProductInfoHeaderValue(name.Name ?? "Neighbourhood.omg.lol", name.Version?.ToString()));
_serializerOptions = new JsonSerializerOptions { _serializerOptions = new JsonSerializerOptions {
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
WriteIndented = true WriteIndented = true

5
appsettings.json Normal file
View file

@ -0,0 +1,5 @@
{
"client_id": "08656a9ade42b8ff16b868c4bb33379e",
"client_secret": "529e47904ca8900a4fb187ecee2f6221",
"redirect_uri": "https://auth.neighbourhood.omg.lol/"
}

View file

@ -461,7 +461,7 @@ article {
overflow-wrap: anywhere; overflow-wrap: anywhere;
} }
nav label { nav label:is(.checkbox, .radio, .switch) {
white-space: break-spaces; white-space: break-spaces;
flex: 1 1 100%; flex: 1 1 100%;
} }