Compare commits

...

2 commits

Author SHA1 Message Date
fd0300fccb More reorg, cleaning up some warnings 2024-07-02 10:01:06 +10:00
1b0155d064 Some basic project tidy up 2024-07-02 09:19:37 +10:00
52 changed files with 134 additions and 182 deletions

View file

@ -1,8 +1,5 @@
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Authorization;
using Neighbourhood.omg.lol.Models;
using System.Security.Claims; using System.Security.Claims;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Neighbourhood.omg.lol { namespace Neighbourhood.omg.lol {
public class CustomAuthenticationStateProvider : AuthenticationStateProvider { public class CustomAuthenticationStateProvider : AuthenticationStateProvider {

5
Classes/FeatureFlags.cs Normal file
View file

@ -0,0 +1,5 @@
namespace Neighbourhood.omg.lol {
public static class FeatureFlags {
public static bool Following { get; } = false;
}
}

View file

@ -0,0 +1,7 @@
using Microsoft.AspNetCore.Components;
namespace Neighbourhood.omg.lol {
public class NavigatorService {
internal NavigationManager? NavigationManager { get; set; }
}
}

View file

@ -1,16 +1,14 @@
using Markdig; using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms; using Microsoft.AspNetCore.Components.Forms;
using Neighbourhood.omg.lol.Models; using Neighbourhood.omg.lol.Models;
using System.Diagnostics; using System.Diagnostics;
using System.Net;
using System.Net.Http.Json; using System.Net.Http.Json;
using System.Reflection;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace Neighbourhood.omg.lol { namespace Neighbourhood.omg.lol
{
public class RestService { public class RestService {
HttpClient _client; HttpClient _client;
JsonSerializerOptions _serializerOptions; JsonSerializerOptions _serializerOptions;

View file

@ -2,11 +2,21 @@
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
using System.Text.Json; using System.Text.Json;
using Neighbourhood.omg.lol.Models;
using System.Runtime.CompilerServices;
namespace Neighbourhood.omg.lol.Models { namespace Neighbourhood.omg.lol {
public class State : INotifyPropertyChanged { public class State : INotifyPropertyChanged {
// Feature flags
public bool FeatureFollowing { get; } = false; // Events
public event EventHandler<EventArgs>? IntentReceived;
public event PropertyChangedEventHandler? PropertyChanged;
// Create the OnPropertyChanged method to raise the event
// The calling member's name will be used as the parameter.
protected void OnPropertyChanged([CallerMemberName] string? name = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
protected void OnIntentRecieved() => IntentReceived?.Invoke(this, EventArgs.Empty);
// Main data lists // Main data lists
public List<Status>? Statuses { get; set; } public List<Status>? Statuses { get; set; }
public List<Pic>? Pics { get; set; } public List<Pic>? Pics { get; set; }
@ -42,6 +52,7 @@ namespace Neighbourhood.omg.lol.Models {
else { else {
string selectedAddressJson = JsonSerializer.Serialize(_selectedAddress); string selectedAddressJson = JsonSerializer.Serialize(_selectedAddress);
Preferences.Default.Set("selectedaddress", selectedAddressJson); Preferences.Default.Set("selectedaddress", selectedAddressJson);
OnPropertyChanged();
} }
} }
} }
@ -68,13 +79,12 @@ namespace Neighbourhood.omg.lol.Models {
} }
// share intent stuff // share intent stuff
public event EventHandler<EventArgs>? IntentReceived;
private string? _shareString; private string? _shareString;
public string? ShareString { public string? ShareString {
get => _shareString; get => _shareString;
set { set {
_shareString = value; _shareString = value;
IntentReceived?.Invoke(this, EventArgs.Empty); OnIntentRecieved();
} }
} }
public string? ShareStringSubject { get; set; } public string? ShareStringSubject { get; set; }
@ -84,7 +94,7 @@ namespace Neighbourhood.omg.lol.Models {
get => _sharePhoto; get => _sharePhoto;
set { set {
_sharePhoto = value; _sharePhoto = value;
IntentReceived?.Invoke(this, EventArgs.Empty); OnIntentRecieved();
} }
} }
public long? SharePhotoSize { get; set; } public long? SharePhotoSize { get; set; }
@ -92,13 +102,12 @@ namespace Neighbourhood.omg.lol.Models {
public string? SharePhotoText { get; set; } public string? SharePhotoText { get; set; }
// refreshing // refreshing
public event PropertyChangedEventHandler? PropertyChanged;
private bool _isRefreshing; private bool _isRefreshing;
public bool IsRefreshing { public bool IsRefreshing {
get => _isRefreshing; get => _isRefreshing;
private set { private set {
_isRefreshing = value; _isRefreshing = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsRefreshing))); OnPropertyChanged();
} }
} }
@ -132,7 +141,7 @@ namespace Neighbourhood.omg.lol.Models {
get => _canRefresh; get => _canRefresh;
set { set {
_canRefresh = value; _canRefresh = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CanRefresh))); OnPropertyChanged();
} }
} }
@ -178,12 +187,14 @@ namespace Neighbourhood.omg.lol.Models {
} }
public async Task RemoveAccountDetails() { public async Task RemoveAccountDetails() {
await Task.Run(() => {
Preferences.Default.Clear(); Preferences.Default.Clear();
AccountInfo = null; AccountInfo = null;
AddressList = null; AddressList = null;
SelectedAddress = null; SelectedAddress = null;
Following = null; Following = null;
api.RemoveToken(); api.RemoveToken();
});
} }
public bool IsFollowing(string address) => Following?.Contains(address) ?? false; public bool IsFollowing(string address) => Following?.Contains(address) ?? false;
@ -217,7 +228,7 @@ namespace Neighbourhood.omg.lol.Models {
public async Task<List<string>?> GetDirectory(bool forceRefresh = false) { public async Task<List<string>?> GetDirectory(bool forceRefresh = false) {
if (forceRefresh || this.AddressDirectory == null || this.AddressDirectory.Count == 0) { if (forceRefresh || this.AddressDirectory == null || this.AddressDirectory.Count == 0) {
IdnMapping idn = new IdnMapping(); IdnMapping idn = new IdnMapping();
this.AddressDirectory = (await api.Directory()).Select(s => { this.AddressDirectory = (await api.Directory())?.Select(s => {
if (s.StartsWith("xn--")) return idn.GetUnicode(s); if (s.StartsWith("xn--")) return idn.GetUnicode(s);
else return s; else return s;
}).ToList(); }).ToList();

51
Classes/Utilities.cs Normal file
View file

@ -0,0 +1,51 @@
using Markdig;
using Microsoft.AspNetCore.Components;
namespace Neighbourhood.omg.lol
{
public static class Utilities
{
private static MarkdownPipeline markdownPipeline { get; }
= new MarkdownPipelineBuilder().UseAutoLinks().Build();
public static string MdToHtml(string markdown) =>
Markdown.ToHtml(markdown, markdownPipeline);
public static MarkupString MdToHtmlMarkup(string markdown) =>
(MarkupString)MdToHtml(markdown);
public static async Task<long> FileSize(FileResult file)
{
using var fileStream = await file.OpenReadAsync();
return fileStream.Length;
}
public static async Task<string> Base64FromFile(FileResult file)
{
using var memoryStream = new MemoryStream();
using var fileStream = await file.OpenReadAsync();
await fileStream.CopyToAsync(memoryStream);
byte[] bytes = memoryStream.ToArray();
return Convert.ToBase64String(bytes);
}
public static string RelativeTimeFromUnix(long unix)
{
DateTimeOffset createdTime = DateTimeOffset.UnixEpoch.AddSeconds(unix);
TimeSpan offset = DateTimeOffset.UtcNow - createdTime;
var offsetString = string.Empty;
if (Math.Floor(offset.TotalDays) == 1) offsetString = $"{Math.Floor(offset.TotalDays)} day ago";
else if (offset.TotalDays > 1) offsetString = $"{Math.Floor(offset.TotalDays)} days ago";
else if (Math.Floor(offset.TotalHours) == 1) offsetString = $"{Math.Floor(offset.TotalHours)} hour ago";
else if (offset.TotalHours > 1) offsetString = $"{Math.Floor(offset.TotalHours)} hours ago";
else if (Math.Floor(offset.TotalMinutes) == 1) offsetString = $"{Math.Floor(offset.TotalMinutes)} minute ago";
else if (offset.TotalMinutes > 1) offsetString = $"{Math.Floor(offset.TotalMinutes)} minutes ago";
else if (Math.Floor(offset.TotalSeconds) == 1) offsetString = $"{Math.Floor(offset.TotalSeconds)} second ago";
else offsetString = $"{Math.Floor(offset.TotalSeconds)} seconds ago";
return offsetString;
}
}
}

View file

@ -32,7 +32,7 @@
</a> </a>
} }
} }
@if (State.FeatureFollowing && State.IsAuthorized) { @if (FeatureFlags.Following && State.IsAuthorized) {
<a class="m s row" href="/feed"> <a class="m s row" href="/feed">
<i class="square fa-solid fa-list-timeline"></i> <i class="square fa-solid fa-list-timeline"></i>
<span>Feed</span> <span>Feed</span>

View file

@ -21,7 +21,7 @@
<i class="square fa-duotone fa-address-book"></i> <i class="square fa-duotone fa-address-book"></i>
<div class="label">Address Directory</div> <div class="label">Address Directory</div>
</NavLink> </NavLink>
@if (State.FeatureFollowing) { @if (FeatureFlags.Following) {
<AuthorizeView> <AuthorizeView>
<Authorized> <Authorized>
<NavLink class="l nav-link" href="/feed"> <NavLink class="l nav-link" href="/feed">

View file

@ -11,8 +11,7 @@
<div class="row center-align"> <div class="row center-align">
<img class="profile avatar" src="https://profiles.cache.lol/@Address/picture" alt="@Address" /> <img class="profile avatar" src="https://profiles.cache.lol/@Address/picture" alt="@Address" />
</div> </div>
@if (State.FeatureFollowing) @if (FeatureFlags.Following) {
{
<div class="row center-align"> <div class="row center-align">
@if (State.IsFollowing(Address)) { @if (State.IsFollowing(Address)) {
<button id="follow-button" @onclick="() => {State.Unfollow(Address);InvokeAsync(StateHasChanged);}"> <button id="follow-button" @onclick="() => {State.Unfollow(Address);InvokeAsync(StateHasChanged);}">

View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Neighbourhood.omg.lol.EphemeralWebPage"
BackgroundColor="{DynamicResource PageBackgroundColor}">
<Grid>
<WebView x:Name="webview"
Navigating="webview_Navigating"
VerticalOptions="Fill"
HorizontalOptions="Fill" />
</Grid>
</ContentPage>

View file

@ -1,18 +0,0 @@
namespace Neighbourhood.omg.lol;
public partial class EphemeralWebPage : ContentPage
{
private NavigatorService NavigatorService { get; set; }
public EphemeralWebPage(NavigatorService navigatorService)
{
this.NavigatorService = navigatorService;
InitializeComponent();
this.webview.Source = $"https://home.omg.lol/ephemeral";
}
public async void webview_Navigating(object sender, WebNavigatingEventArgs e) {
var cookies = this.webview.Cookies;
Uri uri = new Uri(e.Url);
}
}

View file

@ -1,9 +1,6 @@
using Markdig; using Microsoft.AspNetCore.Components.Authorization;
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 System.Reflection;
namespace Neighbourhood.omg.lol { namespace Neighbourhood.omg.lol {
public static class MauiProgram { public static class MauiProgram {
@ -17,20 +14,12 @@ namespace Neighbourhood.omg.lol {
builder.Services.AddMauiBlazorWebView(); builder.Services.AddMauiBlazorWebView();
builder.Services.AddTransient<LoginWebViewPage>(); builder.Services.AddTransient<LoginWebViewPage>();
builder.Services.AddTransient<EphemeralWebPage>();
builder.Services.AddSingleton<RestService>(); builder.Services.AddSingleton<RestService>();
builder.Services.AddSingleton<State>(); builder.Services.AddSingleton<State>();
builder.Services.AddSingleton<NavigatorService>(); builder.Services.AddSingleton<NavigatorService>();
//ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); using Stream stream = App.Assembly.GetManifestResourceStream($"{App.Name}.appsettings.json")!;
//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(); var config = new ConfigurationBuilder().AddJsonStream(stream).Build();
builder.Configuration.AddConfiguration(config); builder.Configuration.AddConfiguration(config);

View file

@ -1,10 +1,5 @@
using System; namespace Neighbourhood.omg.lol.Models
using System.Collections.Generic; {
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models {
public class NowData { public class NowData {
public string Address { get; set; } public string Address { get; set; }
public string Url { get; set; } public string Url { get; set; }

View file

@ -1,14 +1,8 @@
using Markdig; using System.Text.Json;
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol.Models { namespace Neighbourhood.omg.lol.Models
{
public class Pic { public class Pic {
public string Id { get; set; } public string Id { get; set; }
public string Url { get; set; } public string Url { get; set; }

View file

@ -1,7 +1,7 @@
using Markdig; using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components;
namespace Neighbourhood.omg.lol.Models { namespace Neighbourhood.omg.lol.Models
{
public class Status { public class Status {
public string Id { get; set; } public string Id { get; set; }
public string Address { get; set; } public string Address { get; set; }

View file

@ -1,51 +0,0 @@
using Markdig;
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol {
public static class Utilities {
private static MarkdownPipeline markdownPipeline { get; }
= new MarkdownPipelineBuilder().UseAutoLinks().Build();
public static string MdToHtml(string markdown) =>
Markdown.ToHtml(markdown, markdownPipeline);
public static MarkupString MdToHtmlMarkup(string markdown) =>
(MarkupString)MdToHtml(markdown);
public static async Task<long> FileSize(FileResult file) {
using var fileStream = await file.OpenReadAsync();
return fileStream.Length;
}
public static async Task<string> Base64FromFile(FileResult file) {
using var memoryStream = new MemoryStream();
using var fileStream = await file.OpenReadAsync();
await fileStream.CopyToAsync(memoryStream);
byte[] bytes = memoryStream.ToArray();
return Convert.ToBase64String(bytes);
}
public static string RelativeTimeFromUnix(long unix) {
DateTimeOffset createdTime = DateTimeOffset.UnixEpoch.AddSeconds(unix);
TimeSpan offset = DateTimeOffset.UtcNow - createdTime;
var offsetString = string.Empty;
if (Math.Floor(offset.TotalDays) == 1) offsetString = $"{Math.Floor(offset.TotalDays)} day ago";
else if (offset.TotalDays > 1) offsetString = $"{Math.Floor(offset.TotalDays)} days ago";
else if (Math.Floor(offset.TotalHours) == 1) offsetString = $"{Math.Floor(offset.TotalHours)} hour ago";
else if (offset.TotalHours > 1) offsetString = $"{Math.Floor(offset.TotalHours)} hours ago";
else if (Math.Floor(offset.TotalMinutes) == 1) offsetString = $"{Math.Floor(offset.TotalMinutes)} minute ago";
else if (offset.TotalMinutes > 1) offsetString = $"{Math.Floor(offset.TotalMinutes)} minutes ago";
else if (Math.Floor(offset.TotalSeconds) == 1) offsetString = $"{Math.Floor(offset.TotalSeconds)} second ago";
else offsetString = $"{Math.Floor(offset.TotalSeconds)} seconds ago";
return offsetString;
}
}
}

View file

@ -1,12 +0,0 @@
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Neighbourhood.omg.lol {
public class NavigatorService {
internal NavigationManager NavigationManager { get; set; }
}
}

View file

@ -241,13 +241,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<MauiXaml Update="AppShell.xaml"> <MauiXaml Update="XamlComponents\AppShell.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</MauiXaml> </MauiXaml>
<MauiXaml Update="EphemeralWebPage.xaml"> <MauiXaml Update="XamlComponents\LoginWebViewPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="LoginWebViewPage.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</MauiXaml> </MauiXaml>
</ItemGroup> </ItemGroup>

View file

@ -2,10 +2,9 @@
using Android.App; using Android.App;
using Android.Content; using Android.Content;
using Android.Content.PM; using Android.Content.PM;
using Android.Net;
using Android.OS; using Android.OS;
using Microsoft.Extensions.DependencyInjection; using Android.Views;
using Neighbourhood.omg.lol.Models; using System.Diagnostics;
namespace Neighbourhood.omg.lol { namespace Neighbourhood.omg.lol {
[Activity(Exported = true, Theme = "@style/Maui.SplashTheme", LaunchMode = LaunchMode.SingleTop, MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] [Activity(Exported = true, Theme = "@style/Maui.SplashTheme", LaunchMode = LaunchMode.SingleTop, MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
@ -13,14 +12,13 @@ namespace Neighbourhood.omg.lol {
[IntentFilter([Intent.ActionSend], Categories = [Intent.CategoryDefault], DataMimeType = "*/*")] [IntentFilter([Intent.ActionSend], Categories = [Intent.CategoryDefault], DataMimeType = "*/*")]
public class MainActivity : MauiAppCompatActivity { public class MainActivity : MauiAppCompatActivity {
protected override void OnCreate(Bundle savedInstanceState) { protected override void OnCreate(Bundle? savedInstanceState) {
base.OnCreate(savedInstanceState); base.OnCreate(savedInstanceState);
// In case the app was opened (on first load) with an `ActionView` intent // In case the app was opened (on first load) with an `ActionView` intent
OnNewIntent(this.Intent); OnNewIntent(this.Intent);
} }
protected override void OnNewIntent(Intent? intent) { protected override void OnNewIntent(Intent? intent) {
base.OnNewIntent(intent); base.OnNewIntent(intent);
if (intent != null && intent.Type != null) { if (intent != null && intent.Type != null) {
@ -37,9 +35,13 @@ namespace Neighbourhood.omg.lol {
else if (intent.Type.StartsWith("image/")) //image else if (intent.Type.StartsWith("image/")) //image
{ {
string? shareString = intent.GetStringExtra(Intent.ExtraText); string? shareString = intent.GetStringExtra(Intent.ExtraText);
var extra = intent.GetParcelableExtra(Intent.ExtraStream); Java.Lang.Object? extra;
if (OperatingSystem.IsAndroidVersionAtLeast(33))
extra = intent.GetParcelableExtra(Intent.ExtraStream, Java.Lang.Class.FromType(typeof(Android.Net.Uri)));
else extra = intent.GetParcelableExtra(Intent.ExtraStream);
if (extra is Android.Net.Uri) { if (extra is Android.Net.Uri) {
Stream? stream = ContentResolver?.OpenInputStream(extra as Android.Net.Uri); Stream? stream = ContentResolver?.OpenInputStream((Android.Net.Uri)extra);
byte[] bytes = new byte[stream?.Length ?? 0]; byte[] bytes = new byte[stream?.Length ?? 0];
stream?.Read(bytes, 0, bytes.Length); stream?.Read(bytes, 0, bytes.Length);
string base64String = Convert.ToBase64String(bytes); string base64String = Convert.ToBase64String(bytes);
@ -52,9 +54,13 @@ namespace Neighbourhood.omg.lol {
} }
} }
} }
else if (intent.Type.Equals(Intent.ActionSendMultiple)) //Multiple file else if (intent.Type.Equals(Intent.ActionSendMultiple)) //Multiple files
{ {
var uriList = intent.GetParcelableArrayListExtra(Intent.ExtraStream); // TODO: we don't really support this at the moment.
//System.Collections.IList? uriList;
//if (OperatingSystem.IsAndroidVersionAtLeast(33))
// uriList = intent.GetParcelableArrayListExtra(Intent.ExtraStream, Java.Lang.Class.FromType(typeof(Android.Net.Uri)));
//else uriList = intent.GetParcelableArrayListExtra(Intent.ExtraStream);
} }
} }
} }

View file

@ -1,13 +1,15 @@
namespace Neighbourhood.omg.lol { using System.Reflection;
namespace Neighbourhood.omg.lol {
public partial class App : Application { public partial class App : Application {
public static string Name { get; set; } public static Assembly Assembly { get; } = Assembly.GetExecutingAssembly();
public static string Version { get; set; } public static string Name { get; } = App.Assembly.GetName().Name!;
public static string Version { get; } = App.Assembly.GetName().Version!.ToString();
public App(NavigatorService navigatorService) { public App(NavigatorService navigatorService) {
InitializeComponent(); InitializeComponent();
//MainPage = new AppShell();
NavigatorService = navigatorService; NavigatorService = navigatorService;
} }

View file

@ -7,6 +7,5 @@ public partial class AppShell : Shell
InitializeComponent(); InitializeComponent();
Routing.RegisterRoute(nameof(LoginWebViewPage), typeof(LoginWebViewPage)); Routing.RegisterRoute(nameof(LoginWebViewPage), typeof(LoginWebViewPage));
Routing.RegisterRoute(nameof(EphemeralWebPage), typeof(EphemeralWebPage));
} }
} }

View file

@ -1,6 +1,4 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Authorization;
using System.Diagnostics;
using System.Web; using System.Web;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;

View file

@ -1,7 +1,4 @@
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.WebView;
using Microsoft.AspNetCore.Components.WebView;
using Microsoft.AspNetCore.Components.WebView.Maui;
using System.Diagnostics;
namespace Neighbourhood.omg.lol { namespace Neighbourhood.omg.lol {
public partial class MainPage : ContentPage { public partial class MainPage : ContentPage {