Simplify and standardise a lot of the loading
I was overcomplicating everything trying to reduce the render lag. Just simplify it a bit. It works.
This commit is contained in:
parent
08788db7fa
commit
0c3836b0c8
16 changed files with 205 additions and 261 deletions
|
@ -1,11 +1,9 @@
|
||||||
@inject IJSRuntime JS
|
@inject IJSRuntime JS
|
||||||
|
@inject State State
|
||||||
@if(Html != null) {
|
@if(Html != null) {
|
||||||
<iframe id="@id" frameborder="0" scrolling="no" srcdoc="@Html" onload="() => iframeResize({ license: 'GPLv3' })"></iframe>
|
<iframe id="@id" frameborder="0" scrolling="no" srcdoc="@Html" onload="() => iframeResize({ license: 'GPLv3' })"></iframe>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Url { get; set; }
|
public string Url { get; set; }
|
||||||
|
@ -13,6 +11,11 @@
|
||||||
public string id { get; set; }
|
public string id { get; set; }
|
||||||
public MarkupString? Html { get; set; }
|
public MarkupString? Html { get; set; }
|
||||||
|
|
||||||
|
protected override void OnInitialized() {
|
||||||
|
base.OnInitialized();
|
||||||
|
State.CurrentPage = Page.Other;
|
||||||
|
}
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender) {
|
protected override async Task OnAfterRenderAsync(bool firstRender) {
|
||||||
if(firstRender){
|
if(firstRender){
|
||||||
await Reload();
|
await Reload();
|
||||||
|
|
16
Components/LoadingCard.razor
Normal file
16
Components/LoadingCard.razor
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<article id="@id" class="@(string.IsNullOrEmpty(@class)?"":@class) middle-align center-align">
|
||||||
|
<div>
|
||||||
|
<i class="extra square @icon"></i>
|
||||||
|
<div class="space"></div>
|
||||||
|
<progress class="circle"></progress>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public string id { get; set; }
|
||||||
|
[Parameter]
|
||||||
|
public string? @class { get; set; }
|
||||||
|
[Parameter]
|
||||||
|
public string icon { get; set; }
|
||||||
|
}
|
18
Components/PageHeading.razor
Normal file
18
Components/PageHeading.razor
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<div class="row center-align">
|
||||||
|
<h3>
|
||||||
|
<i class="@icon extra"></i> @title
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div class="row center-align">
|
||||||
|
<p>@Description</p>
|
||||||
|
</div>
|
||||||
|
<div class="space"></div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public string icon { get; set; }
|
||||||
|
[Parameter]
|
||||||
|
public string title { get; set; }
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment Description { get; set; }
|
||||||
|
}
|
|
@ -1,27 +1,30 @@
|
||||||
@page "/ephemeral"
|
@page "/ephemeral"
|
||||||
|
@inject IJSRuntime JS
|
||||||
|
@inject State State
|
||||||
|
<PageHeading title="Eph.emer.al" icon="fa-light fa-comment-dots">
|
||||||
|
<Description><a href="https://eph.emer.al">Eph.emer.al</a> is a place for fleeting thoughts. Everything on this page will disappear after a while.</Description>
|
||||||
|
</PageHeading>
|
||||||
|
|
||||||
<div class="row center-align">
|
<div id="ephemeral" class="responsive">
|
||||||
<h3>
|
|
||||||
<i class="fa-light fa-comment-dots"></i> Ephemeral
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div class="row center-align">
|
|
||||||
<p><a href="https://eph.emer.al">Ephemeral</a> is a place for fleeting thoughts. Everything on this page will disappear after a while.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
@if (messages != null) {
|
||||||
<Virtualize Items="messages" Context="message">
|
foreach (MarkupString message in messages) {
|
||||||
<article class="ephemeral center">
|
<article class="ephemeral center">
|
||||||
@message
|
@message
|
||||||
</article>
|
</article>
|
||||||
</Virtualize>
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<LoadingCard id="ephemeral-loading" icon="fa-light fa-comment-dots"></LoadingCard>
|
||||||
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<MarkupString> messages = new List<MarkupString>();
|
private List<MarkupString>? messages;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync() {
|
protected override async Task OnInitializedAsync() {
|
||||||
//await Shell.Current.GoToAsync(nameof(EphemeralWebPage));
|
await base.OnInitializedAsync();
|
||||||
RestService api = new RestService();
|
if (messages == null || messages.Count == 0) messages = await State.GetEphemeralMessages();
|
||||||
messages = await api.Ephemeral();
|
await InvokeAsync(StateHasChanged);
|
||||||
|
await JS.InvokeVoidAsync("removeElementById", "ephemeral-loading");
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,17 +1,14 @@
|
||||||
@page "/now"
|
@page "/now"
|
||||||
|
@inject IJSRuntime JS
|
||||||
@inject State State
|
@inject State State
|
||||||
<div class="row center-align">
|
<PageHeading title="Now.garden" icon="fa-duotone fa-seedling">
|
||||||
<h3>
|
<Description>Feel free to stroll through the <a href="now.garden">now.garden</a> and take a look at what people are up to.</Description>
|
||||||
<i class="fa-duotone fa-seedling"></i> Now.garden
|
</PageHeading>
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div class="row center-align">
|
|
||||||
<p>Feel free to stroll through the <a href="now.garden">now.garden</a> and take a look at what people are up to.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="now-garden" class="responsive card-grid">
|
<div id="now-garden" class="responsive card-grid">
|
||||||
<Virtualize ItemsProvider="State.VirtualNowGarden" Context="now" ItemSize="180">
|
|
||||||
<ItemContent>
|
@if (garden != null) {
|
||||||
|
foreach (NowData now in garden) {
|
||||||
<article class="now">
|
<article class="now">
|
||||||
<nav>
|
<nav>
|
||||||
<a class="author" href="/person/@now.Address#now">
|
<a class="author" href="/person/@now.Address#now">
|
||||||
|
@ -22,13 +19,19 @@
|
||||||
<small>@now.UpdatedRelative</small>
|
<small>@now.UpdatedRelative</small>
|
||||||
</nav>
|
</nav>
|
||||||
</article>
|
</article>
|
||||||
</ItemContent>
|
}
|
||||||
<Placeholder>
|
}
|
||||||
<StatusCardSkeleton></StatusCardSkeleton>
|
<LoadingCard id="now-loading" icon="fa-duotone fa-seedling" class="now"></LoadingCard>
|
||||||
</Placeholder>
|
|
||||||
</Virtualize>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
private List<NowData>? garden;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync() {
|
||||||
|
await base.OnInitializedAsync();
|
||||||
|
if (garden == null || garden.Count == 0) garden = await State.GetNowGarden();
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
await JS.InvokeVoidAsync("removeElementById", "now-loading");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
@page "/person/{Address}"
|
@page "/person/{Address}"
|
||||||
@inject State State
|
@inject State State
|
||||||
@inject IJSRuntime JS
|
@inject IJSRuntime JS
|
||||||
|
@inject NavigationManager Nav
|
||||||
|
|
||||||
|
|
||||||
<div class="row center-align">
|
<div class="row center-align">
|
||||||
|
@ -10,16 +11,6 @@
|
||||||
<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>
|
||||||
|
|
||||||
<div id="bio" class="center-align max">
|
|
||||||
@if (bio == null)
|
|
||||||
{
|
|
||||||
<p><em>Getting Bio...</em></p>
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
@bio
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="responsive">
|
<div class="responsive">
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<a data-ui="#profile" @onclick="ReloadProfile">
|
<a data-ui="#profile" @onclick="ReloadProfile">
|
||||||
|
@ -42,6 +33,7 @@
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="responsive page-container">
|
<div class="responsive page-container">
|
||||||
<div id="profile" class="page no-padding">
|
<div id="profile" class="page no-padding">
|
||||||
<a href="@ProfileUrl" target="_blank" class="hover absolute top right chip fill large-elevate">Open in browser <i class="fa-solid fa-arrow-up-right-from-square tiny"></i></a>
|
<a href="@ProfileUrl" target="_blank" class="hover absolute top right chip fill large-elevate">Open in browser <i class="fa-solid fa-arrow-up-right-from-square tiny"></i></a>
|
||||||
|
@ -49,14 +41,21 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="statuses" class="page padding active">
|
<div id="statuses" class="page padding active">
|
||||||
<StatusList StatusFunc="@State.VirtualStatusesFunc(Address)"></StatusList>
|
<div id="info" class="box basis transparent">
|
||||||
|
<article id="bio" class="container shadowed blue-2-bg gray-9-fg">
|
||||||
|
@if (bio == null) {
|
||||||
|
<p><progress class="circle small"></progress></p>
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
@bio
|
||||||
|
}
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
<StatusList StatusFunc="@(async() => await State.GetStatuses(Address))"></StatusList>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="pics" class="page padding">
|
<div id="pics" class="page padding">
|
||||||
@if(Editable){
|
<PicList PicsFunc="@(async() => await State.GetPics(Address))" Editable="@Editable"></PicList>
|
||||||
<EditPicDialog @ref="editPicDialog" id="EditPicModal"></EditPicDialog>
|
|
||||||
}
|
|
||||||
<PicList PicsFunc="@(async() => await State.GetPics(Address))" Editable="@Editable" Dialog="@editPicDialog"></PicList>
|
|
||||||
</div>
|
</div>
|
||||||
@if(now != null){
|
@if(now != null){
|
||||||
<div id="now" class="page no-padding">
|
<div id="now" class="page no-padding">
|
||||||
|
@ -73,7 +72,6 @@
|
||||||
get => $"https://{Address}.omg.lol/";
|
get => $"https://{Address}.omg.lol/";
|
||||||
}
|
}
|
||||||
|
|
||||||
private EditPicDialog? editPicDialog { get; set; }
|
|
||||||
public ExternalPageComponent? NowPage { get; set; }
|
public ExternalPageComponent? NowPage { get; set; }
|
||||||
public ExternalPageComponent? ProfilePage { get; set; }
|
public ExternalPageComponent? ProfilePage { get; set; }
|
||||||
|
|
||||||
|
@ -86,9 +84,14 @@
|
||||||
private NowData? now;
|
private NowData? now;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync() {
|
protected override async Task OnInitializedAsync() {
|
||||||
|
List<NowData>? garden = await State.GetNowGarden();
|
||||||
|
now = garden?.FirstOrDefault(n => n.Address == Address);
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
string fragment = new Uri(Nav.Uri).Fragment;
|
||||||
|
await JS.InvokeVoidAsync("ui", fragment);
|
||||||
|
if (fragment.EndsWith("now")) await ReloadNow();
|
||||||
|
else if (fragment.EndsWith("profile")) await ReloadProfile();
|
||||||
bio = await State.GetBio(Address);
|
bio = await State.GetBio(Address);
|
||||||
List<NowData> garden = await State.GetNowGarden();
|
|
||||||
now = garden.FirstOrDefault(n => n.Address == Address);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ReloadNow() {
|
private async Task ReloadNow() {
|
||||||
|
|
|
@ -1,16 +1,9 @@
|
||||||
@page "/pics"
|
@page "/pics"
|
||||||
@inject State State
|
@inject State State
|
||||||
@inject IJSRuntime JS
|
|
||||||
|
|
||||||
|
<PageHeading title="Some.pics" icon="fa-solid fa-images">
|
||||||
<div class="row center-align">
|
<Description>Sit back, relax, and look at <a href="https://some.pics/">some.pics</a></Description>
|
||||||
<h3>
|
</PageHeading>
|
||||||
<i class="fa-solid fa-images"></i> Some.pics
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div class="row center-align">
|
|
||||||
<p>Sit back, relax, and look at <a href="https://some.pics/">some.pics</a></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<AuthorizeView>
|
<AuthorizeView>
|
||||||
<Authorized>
|
<Authorized>
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
@page "/statuslog/latest"
|
@page "/statuslog/latest"
|
||||||
@inject State State
|
@inject State State
|
||||||
|
|
||||||
<div class="row center-align">
|
<PageHeading title="Status.lol" icon="fa-solid fa-message-smile">
|
||||||
<h3>
|
<Description>The latest posts from everyone at <a href="https://status.lol">status.lol</a></Description>
|
||||||
<i class="fa-solid fa-message-smile"></i> Statuslog
|
</PageHeading>
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div class="row center-align">
|
|
||||||
<p>The latest posts from everyone at <a href="https://status.lol">status.lol</a></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<AuthorizeView>
|
<AuthorizeView>
|
||||||
<Authorized>
|
<Authorized>
|
||||||
|
@ -20,10 +15,9 @@
|
||||||
</AuthorizeView>
|
</AuthorizeView>
|
||||||
|
|
||||||
<div id="statuses" class="responsive">
|
<div id="statuses" class="responsive">
|
||||||
<StatusList StatusFunc="@State.VirtualStatusesFunc()"></StatusList>
|
<StatusList StatusFunc="@(async() => await State.GetStatuses())"></StatusList>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<i class="fa fa-clock"></i> @Pic.RelativeTime
|
<i class="fa fa-clock"></i> @Pic.RelativeTime
|
||||||
</a>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
<p>@Pic.Description</p>
|
<p>@((MarkupString)Pic.DescriptionHtml)</p>
|
||||||
<nav>
|
<nav>
|
||||||
<div class="max"></div>
|
<div class="max"></div>
|
||||||
@if(Editable) {
|
@if(Editable) {
|
||||||
|
|
|
@ -2,112 +2,30 @@
|
||||||
@inject State State
|
@inject State State
|
||||||
|
|
||||||
@if (Editable) {
|
@if (Editable) {
|
||||||
<PicCardEditableTemplate></PicCardEditableTemplate>
|
<EditPicDialog @ref="Dialog" id="EditPicModal"></EditPicDialog>
|
||||||
}
|
|
||||||
else {
|
|
||||||
<PicCardTemplate></PicCardTemplate>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<article id="pics-loading" class="middle-align center-align">
|
@if (pics != null) foreach (Pic pic in pics) {
|
||||||
<div>
|
<PicCard Pic="pic" Editable="Editable" Dialog="Dialog"></PicCard>
|
||||||
<i class="extra fa-solid fa-images"></i>
|
}
|
||||||
<div class="space"></div>
|
|
||||||
<progress class="circle"></progress>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
|
<LoadingCard id="pics-loading" icon="fa-solid fa-images"></LoadingCard>
|
||||||
<script>
|
|
||||||
async function renderPics(somePics) {
|
|
||||||
const template = document.getElementById("PicCard")
|
|
||||||
if(template) for (const pic of somePics) {
|
|
||||||
const clone = template.content.cloneNode(true)
|
|
||||||
let html = clone.children[0].innerHTML
|
|
||||||
|
|
||||||
html = html.replaceAll("{{Pic.Id}}", pic.id)
|
|
||||||
html = html.replaceAll("{{Pic.Url}}", pic.url ?? `https://cdn.some.pics/${pic.address}/${pic.id}.${JSON.parse(pic.exif)["File Type Extension"]}`) //TODO: temporary fix
|
|
||||||
html = html.replaceAll("{{Pic.Address}}", pic.address)
|
|
||||||
html = html.replaceAll("{{Pic.RelativeTime}}", pic.relativeTime)
|
|
||||||
html = html.replaceAll("{{Pic.Description}}", pic.description)
|
|
||||||
html = html.replaceAll("{{Pic.DescriptionHtml}}", pic.descriptionHtml)
|
|
||||||
|
|
||||||
clone.children[0].innerHTML = html
|
|
||||||
|
|
||||||
clone.querySelector('.share-button').addEventListener('click', shareClick)
|
|
||||||
clone.querySelector('.edit-button')?.addEventListener('click', editClick)
|
|
||||||
document.getElementById("pics").insertBefore(clone, document.getElementById("pics-loading"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async function clearLoading() {
|
|
||||||
document.getElementById("pics-loading").remove()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function shareClick(e) {
|
|
||||||
const url = this.querySelector('input[name="url"]')?.value
|
|
||||||
const desc = this.querySelector('input[name="description"]')?.value
|
|
||||||
if (CSHARP) CSHARP.invokeMethodAsync('ShareClick', url, desc)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function editClick(e) {
|
|
||||||
const id = this.querySelector('input[name="id"]')?.value
|
|
||||||
if (CSHARP) CSHARP.invokeMethodAsync('EditClick', id)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Func<Task<List<Pic>?>> PicsFunc { get; set; }
|
public Func<Task<List<Pic>?>> PicsFunc { get; set; }
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool Editable { get; set; } = false;
|
public bool Editable { get; set; } = false;
|
||||||
[Parameter]
|
|
||||||
public EditPicDialog? Dialog { get; set; }
|
public EditPicDialog? Dialog { get; set; }
|
||||||
|
|
||||||
private List<Pic> pics;
|
private List<Pic>? pics;
|
||||||
private DotNetObjectReference<PicList>? objRef;
|
|
||||||
|
|
||||||
protected override void OnInitialized() {
|
// TODO: There is a noticable rendering delay between the pics loading and the page rendering
|
||||||
base.OnInitialized();
|
protected override async Task OnInitializedAsync() {
|
||||||
objRef = DotNetObjectReference.Create(this);
|
await base.OnInitializedAsync();
|
||||||
State.CurrentPage = Page.Pics;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender) {
|
|
||||||
await base.OnAfterRenderAsync(firstRender);
|
|
||||||
if (firstRender) {
|
|
||||||
await JS.InvokeVoidAsync("injectCSharp", objRef);
|
|
||||||
if (State.CurrentPage != Page.Pics) return;
|
|
||||||
if (pics == null || pics.Count == 0) pics = await PicsFunc();
|
if (pics == null || pics.Count == 0) pics = await PicsFunc();
|
||||||
if (State.CurrentPage != Page.Pics) return;
|
await InvokeAsync(StateHasChanged);
|
||||||
|
await JS.InvokeVoidAsync("removeElementById", "pics-loading");
|
||||||
int page_size = 1;
|
|
||||||
for (int i = 0; i < pics.Count; i += page_size) {
|
|
||||||
if (State.CurrentPage != Page.Pics) return;
|
|
||||||
await JS.InvokeVoidAsync("renderPics", pics.Skip(i * page_size).Take(page_size));
|
|
||||||
if (State.CurrentPage != Page.Pics) return;
|
|
||||||
}
|
|
||||||
if (State.CurrentPage != Page.Pics) return;
|
|
||||||
await JS.InvokeVoidAsync("clearLoading");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[JSInvokable]
|
|
||||||
public async Task EditClick(string? id) {
|
|
||||||
if (Editable && Dialog != null)
|
|
||||||
{
|
|
||||||
Pic? pic = pics.FirstOrDefault(p => p.Id == id);
|
|
||||||
Dialog.Pic = pic;
|
|
||||||
// await InvokeAsync(StateHasChanged);
|
|
||||||
await JS.InvokeVoidAsync("ui", "#" + Dialog?.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[JSInvokable]
|
|
||||||
public async Task ShareClick(string? url, string? description) {
|
|
||||||
await Share.Default.RequestAsync(new ShareTextRequest {
|
|
||||||
Uri = url,
|
|
||||||
Text = description,
|
|
||||||
Title = "I saw this on some.pics",
|
|
||||||
Subject = "I saw this on some.pics"
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,12 @@
|
||||||
<article class="status">
|
<article class="status">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="large emoji" data-emoji="@status.EmojiOrDefault">@status.EmojiOrDefault</div>
|
<div class="emoji" data-emoji="@status.EmojiOrDefault">@status.EmojiOrDefault</div>
|
||||||
<div class="max">
|
<div class="max">
|
||||||
<a class="author" href="/person/@status.Address">
|
<a class="author" href="/person/@status.Address">
|
||||||
<i class="fa-solid fa-fw fa-at"></i>@status.Address
|
<i class="fa-solid fa-fw fa-at"></i>@status.Address
|
||||||
</a>
|
</a>
|
||||||
@status.HtmlContent
|
@status.HtmlContent
|
||||||
</div>
|
<nav class="no-margin">
|
||||||
</div>
|
|
||||||
<nav>
|
|
||||||
<a class="chip transparent-border">
|
<a class="chip transparent-border">
|
||||||
<i class="fa fa-clock"></i> @status.RelativeTime
|
<i class="fa fa-clock"></i> @status.RelativeTime
|
||||||
</a>
|
</a>
|
||||||
|
@ -20,6 +18,8 @@
|
||||||
<i class="fa-solid fa-share-nodes"></i>
|
<i class="fa-solid fa-share-nodes"></i>
|
||||||
</button>
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
|
|
@ -1,23 +1,24 @@
|
||||||
@inject State State
|
@inject IJSRuntime JS
|
||||||
<Virtualize ItemsProvider="GetStatuses" Context="status" ItemSize="180">
|
@inject State State
|
||||||
<ItemContent>
|
|
||||||
|
@if(statuses != null) foreach(Status status in statuses) {
|
||||||
<StatusCard status="@status"></StatusCard>
|
<StatusCard status="@status"></StatusCard>
|
||||||
</ItemContent>
|
}
|
||||||
<Placeholder>
|
|
||||||
<StatusCardSkeleton></StatusCardSkeleton>
|
<LoadingCard id="statusLoading" icon="fa-solid fa-message-smile"></LoadingCard>
|
||||||
</Placeholder>
|
|
||||||
</Virtualize>
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Func<ItemsProviderRequest, ValueTask<ItemsProviderResult<Status>>> StatusFunc { get; set; }
|
public Func<Task<List<Status>?>> StatusFunc { get; set; }
|
||||||
|
[Parameter]
|
||||||
|
public bool Editable { get; set; } = false;
|
||||||
|
|
||||||
private async ValueTask<ItemsProviderResult<Status>> GetStatuses(ItemsProviderRequest request) {
|
private List<Status>? statuses;
|
||||||
return await this.StatusFunc(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnInitialized() {
|
protected override async Task OnInitializedAsync() {
|
||||||
base.OnInitialized();
|
await base.OnInitializedAsync();
|
||||||
State.CurrentPage = Page.Status;
|
if (statuses == null || statuses.Count == 0) statuses = await StatusFunc();
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
await JS.InvokeVoidAsync("removeElementById", "statusLoading");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace Neighbourhood.omg.lol.Models {
|
||||||
public long Size { get; set; }
|
public long Size { get; set; }
|
||||||
public string Mime { get; set; }
|
public string Mime { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public string DescriptionHtml { get => Markdown.ToHtml(Description); }
|
public string DescriptionHtml { get => Description == null ? string.Empty : Markdown.ToHtml(Description); }
|
||||||
|
|
||||||
[JsonPropertyName("exif")]
|
[JsonPropertyName("exif")]
|
||||||
public JsonElement ExifJson { get; set; }
|
public JsonElement ExifJson { get; set; }
|
||||||
|
|
|
@ -25,6 +25,7 @@ namespace Neighbourhood.omg.lol.Models {
|
||||||
public List<Status>? Statuses { get; set; }
|
public List<Status>? Statuses { get; set; }
|
||||||
public List<Pic>? Pics { get; set; }
|
public List<Pic>? Pics { get; set; }
|
||||||
public List<NowData>? NowGarden { get; set; }
|
public List<NowData>? NowGarden { get; set; }
|
||||||
|
public List<MarkupString>? EphemeralMessages { get; set; }
|
||||||
|
|
||||||
public List<Status>? CachedAddressStatuses { get; set; }
|
public List<Status>? CachedAddressStatuses { get; set; }
|
||||||
public List<Pic>? CachedAddressPics { get; set; }
|
public List<Pic>? CachedAddressPics { get; set; }
|
||||||
|
@ -88,6 +89,14 @@ namespace Neighbourhood.omg.lol.Models {
|
||||||
return CachedAddressBio;
|
return CachedAddressBio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<List<MarkupString>?> GetEphemeralMessages(bool forceRefresh = false) {
|
||||||
|
RestService api = new RestService();
|
||||||
|
if(forceRefresh || this.EphemeralMessages == null || this.EphemeralMessages.Count == 0) {
|
||||||
|
this.EphemeralMessages = await api.Ephemeral();
|
||||||
|
}
|
||||||
|
return this.EphemeralMessages;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<List<Status>?> GetStatuses(bool forceRefresh = false) {
|
public async Task<List<Status>?> GetStatuses(bool forceRefresh = false) {
|
||||||
RestService api = new RestService();
|
RestService api = new RestService();
|
||||||
if (forceRefresh || this.Statuses == null || this.Statuses.Count == 0) {
|
if (forceRefresh || this.Statuses == null || this.Statuses.Count == 0) {
|
||||||
|
@ -96,27 +105,13 @@ namespace Neighbourhood.omg.lol.Models {
|
||||||
return this.Statuses;
|
return this.Statuses;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask<ItemsProviderResult<Status>> VirtualStatuses(ItemsProviderRequest request) {
|
public async Task<List<Status>?> GetStatuses(string address, bool forceRefresh = false) {
|
||||||
// TODO: request.cancellationToken
|
this.CachedAddress = address;
|
||||||
var statuses = (await this.GetStatuses()) ?? new List<Status>();
|
|
||||||
var numStatuses = Math.Min(request.Count, statuses.Count - request.StartIndex);
|
|
||||||
return new ItemsProviderResult<Status>(statuses.Skip(request.StartIndex).Take(numStatuses), statuses.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async ValueTask<ItemsProviderResult<Status>> VirtualStatuses(ItemsProviderRequest request, string address) {
|
|
||||||
// TODO: request.cancellationToken
|
|
||||||
RestService api = new RestService();
|
RestService api = new RestService();
|
||||||
CachedAddressStatuses = (await api.Statuslog(address)) ?? new List<Status>();
|
if (forceRefresh || this.CachedAddressStatuses == null || this.CachedAddressStatuses.Count == 0) {
|
||||||
var numStatuses = Math.Min(request.Count, CachedAddressStatuses.Count - request.StartIndex);
|
this.CachedAddressStatuses = await api.Statuslog(address);
|
||||||
return new ItemsProviderResult<Status>(CachedAddressStatuses.Skip(request.StartIndex).Take(numStatuses), CachedAddressStatuses.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Func<ItemsProviderRequest, ValueTask<ItemsProviderResult<Status>>> VirtualStatusesFunc(string? address = null) {
|
|
||||||
if (address == null) return VirtualStatuses;
|
|
||||||
else {
|
|
||||||
CachedAddress = address;
|
|
||||||
return async (ItemsProviderRequest request) => await VirtualStatuses(request, CachedAddress);
|
|
||||||
}
|
}
|
||||||
|
return this.CachedAddressStatuses;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<NowData>?> GetNowGarden(bool forceRefresh = false) {
|
public async Task<List<NowData>?> GetNowGarden(bool forceRefresh = false) {
|
||||||
|
@ -126,16 +121,6 @@ namespace Neighbourhood.omg.lol.Models {
|
||||||
}
|
}
|
||||||
return this.NowGarden;
|
return this.NowGarden;
|
||||||
}
|
}
|
||||||
public async ValueTask<ItemsProviderResult<NowData>> VirtualNowGarden(ItemsProviderRequest request) {
|
|
||||||
// TODO: request.cancellationToken
|
|
||||||
var garden = (await this.GetNowGarden()) ?? new List<NowData>();
|
|
||||||
var numSeedlings = Math.Min(request.Count, garden.Count - request.StartIndex);
|
|
||||||
return new ItemsProviderResult<NowData>(garden.Skip(request.StartIndex).Take(numSeedlings), garden.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Func<ItemsProviderRequest, ValueTask<ItemsProviderResult<NowData>>> VirtualNowGardenFunc() {
|
|
||||||
return VirtualNowGarden;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<Pic>?> GetPics(bool forceRefresh = false) {
|
public async Task<List<Pic>?> GetPics(bool forceRefresh = false) {
|
||||||
if(forceRefresh || this.Pics == null || this.Pics.Count == 0) {
|
if(forceRefresh || this.Pics == null || this.Pics.Count == 0) {
|
||||||
|
@ -147,36 +132,13 @@ namespace Neighbourhood.omg.lol.Models {
|
||||||
|
|
||||||
public async Task<List<Pic>?> GetPics(string address, bool forceRefresh = false) {
|
public async Task<List<Pic>?> GetPics(string address, bool forceRefresh = false) {
|
||||||
CachedAddress = address;
|
CachedAddress = address;
|
||||||
if (forceRefresh || this.Pics == null || this.Pics.Count == 0) {
|
if (forceRefresh || this.CachedAddressPics == null || this.CachedAddressPics.Count == 0) {
|
||||||
RestService api = new RestService();
|
RestService api = new RestService();
|
||||||
CachedAddressPics = (await api.SomePics(address)) ?? new List<Pic>();
|
CachedAddressPics = (await api.SomePics(address)) ?? new List<Pic>();
|
||||||
}
|
}
|
||||||
return CachedAddressPics;
|
return CachedAddressPics;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask<ItemsProviderResult<Pic>> VirtualPics(ItemsProviderRequest request) {
|
|
||||||
// TODO: request.cancellationToken
|
|
||||||
var pics = (await this.GetPics()) ?? new List<Pic>();
|
|
||||||
var numPics = Math.Min(request.Count, pics.Count - request.StartIndex);
|
|
||||||
return new ItemsProviderResult<Pic>(pics.Skip(request.StartIndex).Take(numPics), pics.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async ValueTask<ItemsProviderResult<Pic>> VirtualPics(ItemsProviderRequest request, string address) {
|
|
||||||
// TODO: request.cancellationToken
|
|
||||||
RestService api = new RestService();
|
|
||||||
CachedAddressPics = (await api.SomePics(address)) ?? new List<Pic>();
|
|
||||||
var numPics = Math.Min(request.Count, CachedAddressPics.Count - request.StartIndex);
|
|
||||||
return new ItemsProviderResult<Pic>(CachedAddressPics.Skip(request.StartIndex).Take(numPics), CachedAddressPics.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Func<ItemsProviderRequest, ValueTask<ItemsProviderResult<Pic>>> VirtualPicsFunc(string? address = null) {
|
|
||||||
if (address == null) return VirtualPics;
|
|
||||||
else {
|
|
||||||
CachedAddress = address;
|
|
||||||
return async (ItemsProviderRequest request) => await VirtualPics(request, CachedAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<long> FileSize(FileResult file) {
|
public async Task<long> FileSize(FileResult file) {
|
||||||
using var fileStream = await file.OpenReadAsync();
|
using var fileStream = await file.OpenReadAsync();
|
||||||
return fileStream.Length;
|
return fileStream.Length;
|
||||||
|
@ -213,7 +175,7 @@ namespace Neighbourhood.omg.lol.Models {
|
||||||
Status,
|
Status,
|
||||||
Pics,
|
Pics,
|
||||||
Ephemeral,
|
Ephemeral,
|
||||||
Person,
|
NowGarden,
|
||||||
Other
|
Other
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,13 @@
|
||||||
--emoji-font: SegoeUIEmoji, 'Segoe UI Emoji', 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Symbol';
|
--emoji-font: SegoeUIEmoji, 'Segoe UI Emoji', 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Symbol';
|
||||||
--font: 'Lato', 'Helvetica Neue', Helvetica, Arial, sans-serif, var(--emoji-font);
|
--font: 'Lato', 'Helvetica Neue', Helvetica, Arial, sans-serif, var(--emoji-font);
|
||||||
--prami-svg: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><path fill="%23FF6BAE" stroke="none" d="M250 450C211.612 450 173.225 435.354 143.934 406.066L43.9346 306.066C-14.6446 247.487 -14.6446 152.513 43.9346 93.9341C100.533 37.3361 191.104 35.421 250 88.1907C308.898 35.4229 399.47 37.3379 456.066 93.9341C514.645 152.513 514.645 247.487 456.066 306.066L356.066 406.066C326.778 435.354 288.389 450 250 450" /><path fill="none" stroke="%23471036" stroke-width="19" stroke-linecap="round" d="M208.749 223.817C227.625 254.619 272.376 254.619 291.251 223.817" /><circle fill="%23471036" cx="291.3" cy="176.6" r="17.75" /><circle fill="%23471036" cx="208.6" cy="176.6" r="17.75" /><circle fill="%23E24097" cx="120.3" cy="212" r="59.2" /><circle fill="%23E24097" cx="379.7" cy="212" r="59.2" /></svg>');
|
--prami-svg: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><path fill="%23FF6BAE" stroke="none" d="M250 450C211.612 450 173.225 435.354 143.934 406.066L43.9346 306.066C-14.6446 247.487 -14.6446 152.513 43.9346 93.9341C100.533 37.3361 191.104 35.421 250 88.1907C308.898 35.4229 399.47 37.3379 456.066 93.9341C514.645 152.513 514.645 247.487 456.066 306.066L356.066 406.066C326.778 435.354 288.389 450 250 450" /><path fill="none" stroke="%23471036" stroke-width="19" stroke-linecap="round" d="M208.749 223.817C227.625 254.619 272.376 254.619 291.251 223.817" /><circle fill="%23471036" cx="291.3" cy="176.6" r="17.75" /><circle fill="%23471036" cx="208.6" cy="176.6" r="17.75" /><circle fill="%23E24097" cx="120.3" cy="212" r="59.2" /><circle fill="%23E24097" cx="379.7" cy="212" r="59.2" /></svg>');
|
||||||
|
--spacing: 1.5rem;
|
||||||
|
--radius: .75em;
|
||||||
|
--small-radius: .13em;
|
||||||
|
/* --color: var(--white);
|
||||||
|
--background: var(--gray-8);
|
||||||
|
--shadow: var(--black);
|
||||||
|
--button-shadow: var(--gray-7);*/
|
||||||
}
|
}
|
||||||
html, body {
|
html, body {
|
||||||
font-family: var(--font);
|
font-family: var(--font);
|
||||||
|
@ -53,13 +60,17 @@ img {
|
||||||
|
|
||||||
.status .emoji, #status-emoji {
|
.status .emoji, #status-emoji {
|
||||||
margin-bottom: auto;
|
margin-bottom: auto;
|
||||||
inline-size: 3.5rem;
|
inline-size: 5.5rem;
|
||||||
block-size: 3.5rem;
|
block-size: 5.5rem;
|
||||||
font-size: 3.1rem;
|
font-size: 5rem;
|
||||||
line-height: 3.5rem;
|
line-height: 5.5rem;
|
||||||
text-indent: -6px;
|
text-indent: -6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status nav .chip, .status nav {
|
||||||
|
color: var(--gray-7);
|
||||||
|
}
|
||||||
|
|
||||||
.profile.avatar {
|
.profile.avatar {
|
||||||
border-radius: .75rem;
|
border-radius: .75rem;
|
||||||
max-block-size: 10rem;
|
max-block-size: 10rem;
|
||||||
|
@ -269,3 +280,11 @@ main {
|
||||||
.hover {
|
.hover {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#info :is(ul, ol) {
|
||||||
|
margin-left: var(--spacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#info :is(p, ul, ol) {
|
||||||
|
margin-bottom: var(--spacing);
|
||||||
|
}
|
|
@ -1,3 +1,14 @@
|
||||||
window.injectCSharp = async function (helper) {
|
window.injectCSharp = async function (helper) {
|
||||||
window.CSHARP = helper
|
window.CSHARP = helper
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function delay(t) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, t);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeElementById(id) {
|
||||||
|
document.getElementById(id)?.remove()
|
||||||
|
}
|
Loading…
Reference in a new issue