Added auth.
Unfortunately it does not refresh at first login. Have to figure out how to make it refresh
This commit is contained in:
parent
ebd109d850
commit
5fb95c8305
23 changed files with 406 additions and 32 deletions
|
@ -3,7 +3,7 @@
|
|||
public App() {
|
||||
InitializeComponent();
|
||||
|
||||
MainPage = new MainPage();
|
||||
MainPage = new AppShell();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
14
AppShell.xaml
Normal file
14
AppShell.xaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<Shell
|
||||
x:Class="Neighbourhood.omg.lol.AppShell"
|
||||
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:local="clr-namespace:Neighbourhood.omg.lol"
|
||||
Shell.FlyoutBehavior="Disabled"
|
||||
Title="omg.lol Neighbourhood">
|
||||
|
||||
<ShellContent
|
||||
ContentTemplate="{DataTemplate local:MainPage}"
|
||||
Route="MainPage" />
|
||||
|
||||
</Shell>
|
11
AppShell.xaml.cs
Normal file
11
AppShell.xaml.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Neighbourhood.omg.lol;
|
||||
|
||||
public partial class AppShell : Shell
|
||||
{
|
||||
public AppShell()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Routing.RegisterRoute(nameof(LoginWebViewPage), typeof(LoginWebViewPage));
|
||||
}
|
||||
}
|
|
@ -1,18 +1,39 @@
|
|||
<nav class="left drawer l">
|
||||
@inject CustomAuthenticationStateProvider AuthStateProvider;
|
||||
<nav class="left drawer l">
|
||||
<header>
|
||||
<nav>
|
||||
<img src="https://cdn.cache.lol/img/prami.svg" class="circle">
|
||||
<h6>Hey there</h6>
|
||||
<AuthorizeView>
|
||||
<Authorized>
|
||||
<img class="circle medium" src="https://profiles.cache.lol/@FirstAddress/picture" alt="@FirstAddress" />
|
||||
<div>Hey, @Name.</div>
|
||||
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
<img src="https://cdn.cache.lol/img/prami.svg" class="medium">
|
||||
<div>
|
||||
Hey there. <br/>
|
||||
<a href="/login">Login?</a>
|
||||
</div>
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
|
||||
</nav>
|
||||
</header>
|
||||
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
|
||||
<NavLink class="row nav-link" href="" Match="NavLinkMatch.All">
|
||||
<i class="fa-solid fa-fw fa-home"></i>
|
||||
<div>Home</div>
|
||||
</NavLink>
|
||||
<NavLink class="nav-link" href="/statuslog/latest">
|
||||
<NavLink class="row nav-link" href="/statuslog/latest">
|
||||
<i><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><path fill="#4dabf7" d="M250 450c-38.388 0-76.775-14.646-106.066-43.934l-100-100c-58.579-58.58-58.579-153.553 0-212.132C100.534 37.336 191.105 35.421 250 88.191c58.898-52.768 149.47-50.853 206.066 5.743 58.58 58.58 58.58 153.553 0 212.132l-100 100C326.778 435.354 288.39 450 250 450" /><path fill="#228be6" d="M220.52 176.634a11.792 11.792 0 1 1-23.586 0 11.792 11.792 0 0 1 23.585 0" /><path fill="#1864ab" stroke="#1864ab" stroke-miterlimit="10" stroke-width="11.32074" d="M220.52 176.634a11.792 11.792 0 1 1-23.586 0 11.792 11.792 0 0 1 23.585 0Z" /><path fill="#228be6" d="M303.066 176.634a11.792 11.792 0 1 1-23.585 0 11.792 11.792 0 0 1 23.585 0" /><path fill="#1864ab" stroke="#1864ab" stroke-miterlimit="10" stroke-width="11.32074" d="M303.066 176.634a11.792 11.792 0 1 1-23.585 0 11.792 11.792 0 0 1 23.585 0Z" /><path fill="#228be6" stroke="#461036" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="18.8679" d="M208.75 223.817c18.875 30.802 63.626 30.802 82.501 0" /><path fill="#1971c2" d="M438.68 212.011c0-32.564-26.399-58.962-58.963-58.962s-58.962 26.398-58.962 58.962 26.398 58.963 58.962 58.963 58.962-26.399 58.962-58.963m-259.433 0c0-32.564-26.398-58.962-58.962-58.962S61.32 179.447 61.32 212.011s26.398 58.963 58.963 58.963 58.962-26.399 58.962-58.963" /><path fill="#4dabf7" stroke="#1864ab" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="18.8679" d="M208.75 223.817c18.875 30.802 63.626 30.802 82.501 0" /></svg></i>
|
||||
<div>Statuslog</div>
|
||||
</NavLink>
|
||||
<div class="row max"></div>
|
||||
<footer>
|
||||
<AuthorizeView>
|
||||
<Authorized><a class="button small" @onclick='() => AuthStateProvider.Logout()'>Logout</a></Authorized>
|
||||
<NotAuthorized><a class="button small" href="/login">Login</a></NotAuthorized>
|
||||
</AuthorizeView>
|
||||
</footer>
|
||||
</nav>
|
||||
|
||||
<nav class="left m">
|
||||
|
@ -39,3 +60,18 @@
|
|||
<div>Statuslog</div>
|
||||
</NavLink>
|
||||
</nav>
|
||||
|
||||
@code {
|
||||
private string? Name = null;
|
||||
private List<string> Addresses = new List<string>();
|
||||
private string FirstAddress { get => this.Addresses.FirstOrDefault() ?? string.Empty; }
|
||||
|
||||
protected override async Task OnInitializedAsync() {
|
||||
var state = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||
var identity = state.User.Identity;
|
||||
|
||||
Name = identity?.Name ?? string.Empty;
|
||||
Addresses = state.User.FindFirst("addresses")?.Value?.Split(',')?.ToList() ?? new List<string>();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
@page "/"
|
||||
@inject CustomAuthenticationStateProvider AuthStateProvider;
|
||||
|
||||
<h1>Hello, lol!</h1>
|
||||
<h1><i data-emoji="👋"></i> Hello, lol!</h1>
|
||||
<a @onclick='() => AuthStateProvider.Logout()'>Logout</a>
|
||||
|
||||
Welcome to your new app.
|
29
Components/Pages/Login.razor
Normal file
29
Components/Pages/Login.razor
Normal file
|
@ -0,0 +1,29 @@
|
|||
@page "/login"
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using System.Security.Claims
|
||||
@inject NavigationManager navigationManager
|
||||
@inject AuthenticationStateProvider AuthStateProvider
|
||||
<h3>Login</h3>
|
||||
|
||||
@code {
|
||||
|
||||
protected override async Task OnInitializedAsync() {
|
||||
await checkLogin();
|
||||
}
|
||||
|
||||
// protected override async Task OnAfterRenderAsync(bool firstRender) {
|
||||
// await checkLogin();
|
||||
// }
|
||||
|
||||
private async Task checkLogin() {
|
||||
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||
var user = authState.User;
|
||||
|
||||
if (user.Identity is not null && user.Identity.IsAuthenticated) {
|
||||
navigationManager.NavigateTo("/");
|
||||
}
|
||||
else {
|
||||
await Shell.Current.GoToAsync(nameof(LoginWebViewPage));
|
||||
}
|
||||
}
|
||||
}
|
8
Components/RedirectToLogin.razor
Normal file
8
Components/RedirectToLogin.razor
Normal file
|
@ -0,0 +1,8 @@
|
|||
@inject NavigationManager navigationManager
|
||||
<div class="loader loader-bouncing"><span>Redirecting...</span></div>
|
||||
|
||||
@code {
|
||||
protected override void OnInitialized() {
|
||||
navigationManager.NavigateTo("/login");
|
||||
}
|
||||
}
|
|
@ -1,6 +1,18 @@
|
|||
<Router AppAssembly="@typeof(MauiProgram).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" />
|
||||
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
|
||||
</Found>
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
<Router AppAssembly="@typeof(MauiProgram).Assembly">
|
||||
<Found Context="routeData">
|
||||
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)">
|
||||
<Authorizing>Logging in...</Authorizing>
|
||||
<NotAuthorized><RedirectToLogin /></NotAuthorized>
|
||||
</AuthorizeRouteView>
|
||||
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
|
||||
</Found>
|
||||
<NotFound>
|
||||
<CascadingAuthenticationState>
|
||||
<LayoutView Layout="@typeof(Layout.MainLayout)">
|
||||
<img data-emoji="🦒" />
|
||||
<p>Sorry, there's nothing here.</p>
|
||||
</LayoutView>
|
||||
</CascadingAuthenticationState>
|
||||
</NotFound>
|
||||
</Router>
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@using Microsoft.JSInterop
|
||||
@using Neighbourhood.omg.lol
|
||||
@using Neighbourhood.omg.lol.Components
|
||||
|
|
70
CustomAuthenticationStateProvider.cs
Normal file
70
CustomAuthenticationStateProvider.cs
Normal file
|
@ -0,0 +1,70 @@
|
|||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Neighbourhood.omg.lol.Models;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace Neighbourhood.omg.lol {
|
||||
public class CustomAuthenticationStateProvider : AuthenticationStateProvider {
|
||||
public CustomAuthenticationStateProvider() {
|
||||
}
|
||||
|
||||
public async Task Login(string token) {
|
||||
await SecureStorage.SetAsync("accounttoken", token);
|
||||
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||||
}
|
||||
|
||||
public async Task Logout() {
|
||||
SecureStorage.Remove("accounttoken");
|
||||
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||||
}
|
||||
|
||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync() {
|
||||
var identity = new ClaimsIdentity();
|
||||
try {
|
||||
var token = await SecureStorage.GetAsync("accounttoken");
|
||||
if (token != null) {
|
||||
var name = await SecureStorage.GetAsync("accountname");
|
||||
var email = await SecureStorage.GetAsync("accountemail");
|
||||
var addresses = await SecureStorage.GetAsync("accountaddresses");
|
||||
|
||||
RestService api = new RestService(token);
|
||||
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(email)) {
|
||||
AccountResponseData? accountInfo = await api.AccountInfo();
|
||||
if (accountInfo != null) {
|
||||
name = accountInfo.Name;
|
||||
email = accountInfo.Email;
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(email)) {
|
||||
if (string.IsNullOrEmpty(addresses)) {
|
||||
AddressResponseList? addressList = await api.Addresses();
|
||||
List<string> addressStrings = new List<string>();
|
||||
if (addressList != null) foreach (var address in addressList) {
|
||||
if (!address.Expiration.Expired && !string.IsNullOrEmpty(address.Address)) {
|
||||
addressStrings.Add(address.Address);
|
||||
}
|
||||
}
|
||||
addresses = string.Join(',', addressStrings);
|
||||
}
|
||||
if(!string.IsNullOrEmpty(addresses)) {
|
||||
await SecureStorage.SetAsync("accountname", name);
|
||||
await SecureStorage.SetAsync("accountemail", email);
|
||||
await SecureStorage.SetAsync("accountaddresses", addresses);
|
||||
}
|
||||
|
||||
var claims = new[] {
|
||||
new Claim(ClaimTypes.Name, name),
|
||||
new Claim(ClaimTypes.Email, email),
|
||||
new Claim("addresses", addresses)
|
||||
};
|
||||
identity = new ClaimsIdentity(claims, "Server authentication");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex) {
|
||||
Console.WriteLine("Request failed:" + ex.ToString());
|
||||
}
|
||||
|
||||
return new AuthenticationState(new ClaimsPrincipal(identity));
|
||||
}
|
||||
}
|
||||
}
|
12
LoginWebViewPage.xaml
Normal file
12
LoginWebViewPage.xaml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?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.LoginWebViewPage"
|
||||
BackgroundColor="{DynamicResource PageBackgroundColor}">
|
||||
<Grid>
|
||||
<WebView x:Name="loginwebview"
|
||||
Navigating="loginwebview_Navigating"
|
||||
VerticalOptions="Fill"
|
||||
HorizontalOptions="Fill" />
|
||||
</Grid>
|
||||
</ContentPage>
|
37
LoginWebViewPage.xaml.cs
Normal file
37
LoginWebViewPage.xaml.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using System.Diagnostics;
|
||||
using System.Web;
|
||||
|
||||
namespace Neighbourhood.omg.lol;
|
||||
|
||||
public partial class LoginWebViewPage : ContentPage
|
||||
{
|
||||
private AuthenticationStateProvider AuthStateProvider { get; set; }
|
||||
|
||||
public LoginWebViewPage(AuthenticationStateProvider authStateProvider)
|
||||
{
|
||||
this.AuthStateProvider = authStateProvider;
|
||||
InitializeComponent();
|
||||
this.loginwebview.Source = "https://home.omg.lol/oauth/authorize?client_id=ea14dafd3e92cbcf93750c35cd81a031&scope=everything&redirect_uri=https://neatnik.net/adam/bucket/omgloloauth/&response_type=code";
|
||||
|
||||
}
|
||||
|
||||
public async void loginwebview_Navigating(object sender, WebNavigatingEventArgs e) {
|
||||
if(e.Url.StartsWith("https://neatnik.net/adam/bucket/omgloloauth/")) {
|
||||
Debug.WriteLine("And here we go...");
|
||||
Uri uri = new Uri(e.Url);
|
||||
var query = HttpUtility.ParseQueryString(uri.Query);
|
||||
string? code = query.Get("code");
|
||||
if (!string.IsNullOrEmpty(code)) {
|
||||
RestService api = new RestService();
|
||||
string? token = await api.OAuth(code);
|
||||
if (!string.IsNullOrEmpty(token)) {
|
||||
Debug.WriteLine($"Fuck yeah, a token! {token}");
|
||||
await ((CustomAuthenticationStateProvider)this.AuthStateProvider).Login(token);
|
||||
await Shell.Current.GoToAsync("..");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
x:Class="Neighbourhood.omg.lol.MainPage"
|
||||
BackgroundColor="{DynamicResource PageBackgroundColor}">
|
||||
|
||||
<BlazorWebView x:Name="blazorWebView" HostPage="wwwroot/index.html">
|
||||
<BlazorWebView x:Name="blazorWebView" HostPage="wwwroot/index.html" UrlLoading="BlazorUrlLoading">
|
||||
<BlazorWebView.RootComponents>
|
||||
<RootComponent Selector="#app" ComponentType="{x:Type local:Components.Routes}" />
|
||||
</BlazorWebView.RootComponents>
|
||||
|
|
|
@ -1,7 +1,17 @@
|
|||
namespace Neighbourhood.omg.lol {
|
||||
using Microsoft.AspNetCore.Components.WebView;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Neighbourhood.omg.lol {
|
||||
public partial class MainPage : ContentPage {
|
||||
public MainPage() {
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void BlazorUrlLoading(object? sender, UrlLoadingEventArgs e) {
|
||||
if(e.Url.Host == "home.omg.lol" && e.Url.AbsolutePath == "/oauth/authorize") {
|
||||
e.UrlLoadingStrategy = UrlLoadingStrategy.CancelLoad;
|
||||
Shell.Current.GoToAsync(nameof(LoginWebViewPage));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Neighbourhood.omg.lol {
|
||||
public static class MauiProgram {
|
||||
|
@ -11,9 +12,14 @@ namespace Neighbourhood.omg.lol {
|
|||
});
|
||||
|
||||
builder.Services.AddMauiBlazorWebView();
|
||||
builder.Services.AddTransient<LoginWebViewPage>();
|
||||
|
||||
builder.Services.AddAuthorizationCore();
|
||||
builder.Services.AddScoped<CustomAuthenticationStateProvider>();
|
||||
builder.Services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<CustomAuthenticationStateProvider>());
|
||||
|
||||
#if DEBUG
|
||||
builder.Services.AddBlazorWebViewDeveloperTools();
|
||||
builder.Services.AddBlazorWebViewDeveloperTools();
|
||||
builder.Logging.AddDebug();
|
||||
#endif
|
||||
|
||||
|
|
14
Models/AccountResponseData.cs
Normal file
14
Models/AccountResponseData.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Neighbourhood.omg.lol.Models {
|
||||
public class AccountResponseData : IOmgLolResponseData {
|
||||
public string Message { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string Name { get; set; }
|
||||
// created, api_key and settings
|
||||
}
|
||||
}
|
41
Models/AddressResponseData.cs
Normal file
41
Models/AddressResponseData.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Neighbourhood.omg.lol.Models {
|
||||
public class AddressResponseData : IOmgLolResponseData {
|
||||
public string Address { get; set; }
|
||||
public string Message { get; set; }
|
||||
public RegistrationData Registration { get; set; }
|
||||
public ExpirationData Expiration { get; set; }
|
||||
|
||||
public class TimeData {
|
||||
public long? UnixEpochTime { get; set; }
|
||||
public string? Iso8601Time { get; set; }
|
||||
public string? Rfc2822Time { get; set; }
|
||||
public string? RelativeTime { get; set; }
|
||||
public PreferenceData? Preferences { get; set; }
|
||||
}
|
||||
|
||||
public class RegistrationData : TimeData {
|
||||
public string? Message { get; set; }
|
||||
}
|
||||
|
||||
public class ExpirationData : TimeData {
|
||||
public bool Expired { get; set; }
|
||||
public bool WillExpire { get; set; }
|
||||
}
|
||||
|
||||
public class PreferenceData {
|
||||
public string? IncludeInDirectory { get; set; }
|
||||
public string? ShowOnDashboard { get; set; }
|
||||
public StatuslogPreferenceData? Statuslog { get; set; }
|
||||
}
|
||||
|
||||
public class StatuslogPreferenceData {
|
||||
public bool? MastodonPosting { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
10
Models/AddressResponseList.cs
Normal file
10
Models/AddressResponseList.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Neighbourhood.omg.lol.Models {
|
||||
public class AddressResponseList : List<AddressResponseData>, IOmgLolResponseList<AddressResponseData> {
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
namespace Neighbourhood.omg.lol.Models {
|
||||
public interface IOmgLolResponseData {
|
||||
string Message { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
10
Models/IOmgLolResponseList.cs
Normal file
10
Models/IOmgLolResponseList.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Neighbourhood.omg.lol.Models {
|
||||
public interface IOmgLolResponseList<T> : IList<T>, IOmgLolResponseData where T : IOmgLolResponseData {
|
||||
}
|
||||
}
|
7
Models/TokenResponseData.cs
Normal file
7
Models/TokenResponseData.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Neighbourhood.omg.lol.Models {
|
||||
public class TokenResponseData {
|
||||
public string AccessToken { get; set; }
|
||||
public string TokenType { get; set; }
|
||||
public string Scope { get; set; }
|
||||
}
|
||||
}
|
|
@ -60,10 +60,23 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
||||
<PackageReference Include="Markdig" Version="0.37.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="8.0.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.6" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="$(MauiVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Maui.Essentials" Version="8.0.40" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<MauiXaml Update="AppShell.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</MauiXaml>
|
||||
<MauiXaml Update="LoginWebViewPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</MauiXaml>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -1,29 +1,31 @@
|
|||
using Markdig;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Neighbourhood.omg.lol.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Neighbourhood.omg.lol {
|
||||
public class RestService {
|
||||
HttpClient _client;
|
||||
JsonSerializerOptions _serializerOptions;
|
||||
public const string BaseUrl = "https://api.omg.lol";
|
||||
|
||||
public RestService() {
|
||||
public RestService(string? token = null) {
|
||||
_client = new HttpClient();
|
||||
_serializerOptions = new JsonSerializerOptions {
|
||||
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
|
||||
WriteIndented = true
|
||||
};
|
||||
addToken(token);
|
||||
}
|
||||
|
||||
private async Task<T?> GetResponse<T>(Uri uri) where T:IOmgLolResponseData {
|
||||
private void addToken(string? token = null) {
|
||||
if (token == null) token = Task.Run(() => SecureStorage.GetAsync("accounttoken")).GetAwaiter().GetResult();
|
||||
if (token != null) _client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
|
||||
}
|
||||
|
||||
private async Task<T?> Get<T>(Uri uri) where T:IOmgLolResponseData {
|
||||
T? responseData = default(T);
|
||||
try {
|
||||
HttpResponseMessage response = await _client.GetAsync(uri);
|
||||
|
@ -42,20 +44,49 @@ namespace Neighbourhood.omg.lol {
|
|||
}
|
||||
|
||||
public async Task<List<Status>> StatuslogLatest() {
|
||||
Uri uri = new Uri("https://api.omg.lol/statuslog/latest");
|
||||
return (await GetResponse<StatusResponseData>(uri))?.Statuses ?? new List<Status>();
|
||||
Uri uri = new Uri($"{BaseUrl}/statuslog/latest");
|
||||
return (await Get<StatusResponseData>(uri))?.Statuses ?? new List<Status>();
|
||||
}
|
||||
|
||||
public async Task<List<Status>> Statuslog(string address) {
|
||||
Uri uri = new Uri($"https://api.omg.lol/address/{address}/statuses");
|
||||
return (await GetResponse<StatusResponseData>(uri))?.Statuses ?? new List<Status>();
|
||||
Uri uri = new Uri($"{BaseUrl}/address/{address}/statuses");
|
||||
return (await Get<StatusResponseData>(uri))?.Statuses ?? new List<Status>();
|
||||
}
|
||||
|
||||
public async Task<MarkupString> StatuslogBio(string address) {
|
||||
Uri uri = new Uri($"https://api.omg.lol/address/{address}/statuses/bio");
|
||||
StatusBioResponseData? responseData = await GetResponse<StatusBioResponseData>(uri);
|
||||
Debug.WriteLine(responseData?.Bio);
|
||||
Uri uri = new Uri($"{BaseUrl}/address/{address}/statuses/bio");
|
||||
StatusBioResponseData? responseData = await Get<StatusBioResponseData>(uri);
|
||||
return (MarkupString)Markdown.ToHtml(responseData?.Bio ?? "");
|
||||
}
|
||||
|
||||
public async Task<AccountResponseData?> AccountInfo() {
|
||||
Uri uri = new Uri($"{BaseUrl}/account/application/info");
|
||||
AccountResponseData? responseData = await Get<AccountResponseData>(uri);
|
||||
return responseData;
|
||||
}
|
||||
|
||||
public async Task<AddressResponseList?> Addresses() {
|
||||
Uri uri = new Uri($"{BaseUrl}/account/application/addresses");
|
||||
AddressResponseList? responseData = await Get<AddressResponseList>(uri);
|
||||
return responseData;
|
||||
}
|
||||
|
||||
public async Task<string?> OAuth(string code) {
|
||||
string? token = null;
|
||||
Uri uri = new Uri($"{BaseUrl}/oauth/?code={code}&client_id=ea14dafd3e92cbcf93750c35cd81a031&client_secret=ec28b8653f1d98b4eef3f7a20858c43b&redirect_uri=https://neatnik.net/adam/bucket/omgloloauth/&scope=everything");
|
||||
try {
|
||||
HttpResponseMessage response = await _client.GetAsync(uri);
|
||||
if (response.IsSuccessStatusCode) {
|
||||
TokenResponseData? responseObj = await response.Content.ReadFromJsonAsync<TokenResponseData>(_serializerOptions);
|
||||
if (responseObj != null && !string.IsNullOrEmpty(responseObj.AccessToken)) {
|
||||
token = responseObj.AccessToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Debug.WriteLine(@"\tERROR {0}", ex.Message);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue