Fixes and editing statuses
This commit is contained in:
parent
ed5ad5a7b1
commit
8c8634f3c3
11 changed files with 193 additions and 68 deletions
88
Components/EditStatusDialog.razor
Normal file
88
Components/EditStatusDialog.razor
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
@inject IJSRuntime JS
|
||||||
|
@inject State State
|
||||||
|
|
||||||
|
<div class="overlay" data-ui="#@id"></div>
|
||||||
|
<dialog id="@id">
|
||||||
|
<div class="row">
|
||||||
|
<div class="min square extra">
|
||||||
|
<button class="transparent square extra no-margin">
|
||||||
|
<object id="status-emoji" class="large emoji @(Emoji == null ? "animated" : string.Empty)" data-emoji="@(Emoji ?? "🫥")">@(Emoji ?? "🫥")</object>
|
||||||
|
<menu class="no-wrap">
|
||||||
|
<script type="module" src="https://cdn.jsdelivr.net/npm/emoji-picker-element@@^1/index.js"></script>
|
||||||
|
<emoji-picker emoji-version="15.1"></emoji-picker>
|
||||||
|
<script>
|
||||||
|
document.querySelector('emoji-picker')
|
||||||
|
.addEventListener('emoji-click', event => {
|
||||||
|
document.getElementById('status-emoji').setAttribute('data-emoji', event.detail.unicode)
|
||||||
|
const input = document.getElementById('status-emoji-input')
|
||||||
|
input.value = event.detail.unicode
|
||||||
|
var event = new Event('change');
|
||||||
|
input.dispatchEvent(event);
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</menu>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="field textarea label border max">
|
||||||
|
<InputTextArea @bind-Value="Content"></InputTextArea>
|
||||||
|
<label>Status</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<nav class="right-align no-space">
|
||||||
|
<InputText id="status-emoji-input" class="invisible" @bind-Value="Emoji"></InputText>
|
||||||
|
<button class="transparent link" data-ui="#@id" disabled="@loading">Cancel</button>
|
||||||
|
<button @onclick="PatchStatus" disabled="@loading">
|
||||||
|
@if (loading) {
|
||||||
|
<span>Saving...</span>
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
<i class="fa-solid fa-floppy-disk"></i> <span>Save</span>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private Status? _status;
|
||||||
|
|
||||||
|
public Status? Status {
|
||||||
|
get => _status;
|
||||||
|
set {
|
||||||
|
_status = value;
|
||||||
|
Content = _status?.Content;
|
||||||
|
Emoji = _status?.Emoji;
|
||||||
|
InvokeAsync(StateHasChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string? Content { get; set; }
|
||||||
|
public string? Emoji { get; set; }
|
||||||
|
private bool loading = false;
|
||||||
|
[Parameter]
|
||||||
|
public string id { get; set; }
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync() {
|
||||||
|
Content = Status?.Content;
|
||||||
|
Emoji = Status?.Emoji;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task PatchStatus() {
|
||||||
|
loading = true;
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|
||||||
|
RestService api = new RestService();
|
||||||
|
if (!string.IsNullOrEmpty(Status?.Id)) {
|
||||||
|
await api.PatchStatus(State.SelectedAddressName, Status.Id, Content, Emoji);
|
||||||
|
await State.RefreshStatuses();
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
await JS.InvokeVoidAsync("ui", "#" + id);
|
||||||
|
// clear input
|
||||||
|
Content = string.Empty;
|
||||||
|
Emoji = string.Empty;
|
||||||
|
Status = null;
|
||||||
|
loading = false;
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -51,11 +51,11 @@
|
||||||
}
|
}
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
<StatusList StatusFunc="@(async() => await State.GetStatuses(Address))"></StatusList>
|
<StatusList @ref="StatusList" StatusFunc="@(async() => await State.GetStatuses(Address))" Editable="@Editable"></StatusList>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="pics" class="page padding">
|
<div id="pics" class="page padding">
|
||||||
<PicList PicsFunc="@(async() => await State.GetPics(Address))" Editable="@Editable"></PicList>
|
<PicList @ref="PicList" PicsFunc="@(async() => await State.GetPics(Address))" Editable="@Editable"></PicList>
|
||||||
</div>
|
</div>
|
||||||
@if(now != null){
|
@if(now != null){
|
||||||
<div id="now" class="page no-padding">
|
<div id="now" class="page no-padding">
|
||||||
|
@ -66,8 +66,16 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
private string _address;
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Address { get; set; }
|
public string Address {
|
||||||
|
get => _address;
|
||||||
|
set {
|
||||||
|
_address = value;
|
||||||
|
if(StatusList != null) StatusList.StatusFunc = async () => await State.GetStatuses(_address);
|
||||||
|
if(PicList != null) PicList.PicsFunc = async () => await State.GetPics(_address);
|
||||||
|
}
|
||||||
|
}
|
||||||
public string ProfileUrl {
|
public string ProfileUrl {
|
||||||
get => $"https://{Address}.omg.lol/";
|
get => $"https://{Address}.omg.lol/";
|
||||||
}
|
}
|
||||||
|
@ -75,6 +83,9 @@
|
||||||
public ExternalPageComponent? NowPage { get; set; }
|
public ExternalPageComponent? NowPage { get; set; }
|
||||||
public ExternalPageComponent? ProfilePage { get; set; }
|
public ExternalPageComponent? ProfilePage { get; set; }
|
||||||
|
|
||||||
|
private StatusList StatusList { get; set; }
|
||||||
|
private PicList PicList { get; set; }
|
||||||
|
|
||||||
private bool Editable {
|
private bool Editable {
|
||||||
get => Address == State.SelectedAddressName;
|
get => Address == State.SelectedAddressName;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
@code {
|
@code {
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Pic Pic {get; set;}
|
public Pic Pic {get; set;}
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool Editable { get; set; } = false;
|
public bool Editable { get; set; } = false;
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public EditPicDialog? Dialog { get; set; }
|
public EditPicDialog? Dialog { get; set; }
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
<template id="PicCard">
|
|
||||||
<article class="no-padding">
|
|
||||||
<img src="{{Pic.Url}}" loading="lazy">
|
|
||||||
<div class="padding">
|
|
||||||
<nav>
|
|
||||||
<a class="author" href="/person/{{Pic.Address}}#pics">
|
|
||||||
<i class="fa-solid fa-fw fa-at"></i>{{Pic.Address}}
|
|
||||||
</a>
|
|
||||||
<span class="max"></span>
|
|
||||||
<a class="chip transparent-border right">
|
|
||||||
<i class="fa fa-clock"></i> {{Pic.RelativeTime}}
|
|
||||||
</a>
|
|
||||||
<span class="max"></span>
|
|
||||||
<button class="share-button transparent circle">
|
|
||||||
<input type="hidden" name="url" value="{{Pic.Url}}" />
|
|
||||||
<input type="hidden" name="description" value="{{Pic.Description}}" />
|
|
||||||
<i class="fa-solid fa-share-nodes"></i>
|
|
||||||
</button>
|
|
||||||
</nav>
|
|
||||||
<p class="description">{{Pic.Description}}</p>
|
|
||||||
<nav>
|
|
||||||
<div class="max"></div>
|
|
||||||
<button class="edit-button">
|
|
||||||
<input type="hidden" name="id" value="{{Pic.Id}}" />
|
|
||||||
<i class="fa-solid fa-pencil"></i> Edit
|
|
||||||
</button>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</template>
|
|
|
@ -1,23 +0,0 @@
|
||||||
<template id="PicCard">
|
|
||||||
<article class="no-padding">
|
|
||||||
<img src="{{Pic.Url}}" loading="lazy">
|
|
||||||
<div class="padding">
|
|
||||||
<nav>
|
|
||||||
<a class="author" href="/person/{{Pic.Address}}#pics">
|
|
||||||
<i class="fa-solid fa-fw fa-at"></i>{{Pic.Address}}
|
|
||||||
</a>
|
|
||||||
<span class="max"></span>
|
|
||||||
<a class="chip transparent-border right">
|
|
||||||
<i class="fa fa-clock"></i> {{Pic.RelativeTime}}
|
|
||||||
</a>
|
|
||||||
<span class="max"></span>
|
|
||||||
<button class="share-button transparent circle">
|
|
||||||
<input type="hidden" name="url" value="{{Pic.Url}}" />
|
|
||||||
<input type="hidden" name="description" value="{{Pic.Description}}" />
|
|
||||||
<i class="fa-solid fa-share-nodes"></i>
|
|
||||||
</button>
|
|
||||||
</nav>
|
|
||||||
<p class="description">{{Pic.DescriptionHtml}}</p>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</template>
|
|
|
@ -1,19 +1,24 @@
|
||||||
<article class="status gray-9-fg" style="background-color:@status.Background">
|
@inject IJSRuntime JS
|
||||||
|
|
||||||
|
<article class="status gray-9-fg" style="background-color:@Status.Background">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="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
|
||||||
<nav class="no-margin">
|
<nav class="no-margin">
|
||||||
<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>
|
||||||
<a class="chip transparent-border" href="@status.ExternalUrl" target="_blank">
|
<a class="chip transparent-border" href="@Status.ExternalUrl" target="_blank">
|
||||||
<i class="fa fa-message-dots"></i> Respond
|
<i class="fa fa-message-dots"></i> Respond
|
||||||
</a>
|
</a>
|
||||||
<div class="max"></div>
|
<div class="max"></div>
|
||||||
|
@if (Editable) {
|
||||||
|
<button @onclick="EditStatus"><i class="fa-solid fa-pencil"></i> Edit</button>
|
||||||
|
}
|
||||||
<button class="transparent circle" @onclick="ShareClick">
|
<button class="transparent circle" @onclick="ShareClick">
|
||||||
<i class="fa-solid fa-share-nodes"></i>
|
<i class="fa-solid fa-share-nodes"></i>
|
||||||
</button>
|
</button>
|
||||||
|
@ -24,11 +29,21 @@
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Status status { get; set; }
|
public Status Status { get; set; }
|
||||||
|
[Parameter]
|
||||||
|
public bool Editable { get; set; } = false;
|
||||||
|
[Parameter]
|
||||||
|
public EditStatusDialog? Dialog { get; set; }
|
||||||
|
|
||||||
|
private async Task EditStatus(EventArgs e) {
|
||||||
|
Dialog.Status = Status;
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
await JS.InvokeVoidAsync("ui", "#" + Dialog?.id);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task ShareClick(EventArgs e){
|
public async Task ShareClick(EventArgs e){
|
||||||
await Share.Default.RequestAsync(new ShareTextRequest{
|
await Share.Default.RequestAsync(new ShareTextRequest{
|
||||||
Text = $"{status.Content}\n- from [@{status.Address}]({status.Url})",
|
Text = $"{Status.Content}\n- from [@{Status.Address}]({Status.Url})",
|
||||||
Title = "I saw this on status.lol",
|
Title = "I saw this on status.lol",
|
||||||
Subject = "I saw this on status.lol"
|
Subject = "I saw this on status.lol"
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
@inject IJSRuntime JS
|
@inject IJSRuntime JS
|
||||||
@inject State State
|
@inject State State
|
||||||
|
|
||||||
|
@if (Editable) {
|
||||||
|
<EditStatusDialog @ref="Dialog" id="EditStatusModal"></EditStatusDialog>
|
||||||
|
}
|
||||||
|
|
||||||
@if(statuses != null) foreach(Status status in statuses) {
|
@if(statuses != null) foreach(Status status in statuses) {
|
||||||
<StatusCard status="@status"></StatusCard>
|
<StatusCard Status="@status" Editable="Editable" Dialog="Dialog"></StatusCard>
|
||||||
}
|
}
|
||||||
|
|
||||||
<LoadingCard id="statusLoading" icon="fa-solid fa-message-smile"></LoadingCard>
|
<LoadingCard id="statusLoading" icon="fa-solid fa-message-smile"></LoadingCard>
|
||||||
|
@ -13,6 +17,8 @@
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool Editable { get; set; } = false;
|
public bool Editable { get; set; } = false;
|
||||||
|
|
||||||
|
public EditStatusDialog? Dialog { get; set; }
|
||||||
|
|
||||||
private List<Status>? statuses;
|
private List<Status>? statuses;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync() {
|
protected override async Task OnInitializedAsync() {
|
||||||
|
|
13
Models/PatchStatus.cs
Normal file
13
Models/PatchStatus.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Neighbourhood.omg.lol.Models {
|
||||||
|
public class PatchStatus {
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Content { get; set; }
|
||||||
|
public string? Emoji { get; set; }
|
||||||
|
}
|
||||||
|
}
|
13
Models/PatchStatusResponseData.cs
Normal file
13
Models/PatchStatusResponseData.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Neighbourhood.omg.lol.Models {
|
||||||
|
public class PatchStatusResponseData : IOmgLolResponseData {
|
||||||
|
public string Message { get; set; }
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Url { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -94,6 +94,25 @@ namespace Neighbourhood.omg.lol {
|
||||||
return responseData;
|
return responseData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<TResponse?> Patch<TResponse, TData>(string uri, TData data, CancellationToken cancellationToken = default) where TResponse : IOmgLolResponseData {
|
||||||
|
TResponse? responseData = default(TResponse);
|
||||||
|
try {
|
||||||
|
HttpResponseMessage response = await _client.PatchAsJsonAsync(uri, data, _serializerOptions, cancellationToken: cancellationToken);
|
||||||
|
string str = await response.Content.ReadAsStringAsync();
|
||||||
|
if (response.IsSuccessStatusCode) {
|
||||||
|
OmgLolResponse<TResponse>? responseObj = await response.Content.ReadFromJsonAsync<OmgLolResponse<TResponse>>(_serializerOptions, cancellationToken: cancellationToken);
|
||||||
|
if (responseObj != null && responseObj.Request.Success) {
|
||||||
|
responseData = responseObj.Response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
Debug.WriteLine(@"\tERROR {0}", ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return responseData;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<List<Status>> StatuslogLatest() =>
|
public async Task<List<Status>> StatuslogLatest() =>
|
||||||
(await Get<StatusResponseData>("/statuslog/latest"))?.Statuses ?? new List<Status>();
|
(await Get<StatusResponseData>("/statuslog/latest"))?.Statuses ?? new List<Status>();
|
||||||
|
|
||||||
|
@ -148,6 +167,10 @@ namespace Neighbourhood.omg.lol {
|
||||||
public async Task<PutPicResponseData?> PostPicDescription(string address, string id, string description) =>
|
public async Task<PutPicResponseData?> PostPicDescription(string address, string id, string description) =>
|
||||||
(await Post<PutPicResponseData, PostPic>($"/address/{address}/pics/{id}", new PostPic { Description = description }));
|
(await Post<PutPicResponseData, PostPic>($"/address/{address}/pics/{id}", new PostPic { Description = description }));
|
||||||
|
|
||||||
|
public async Task<PatchStatusResponseData?> PatchStatus(string address, string id, string content, string? emoji) =>
|
||||||
|
(await Patch<PatchStatusResponseData, PatchStatus>($"/address/{address}/statuses/", new PatchStatus { Id = id, Content = content, Emoji = emoji }));
|
||||||
|
|
||||||
|
|
||||||
public async Task<List<NowData>?> NowGarden() =>
|
public async Task<List<NowData>?> NowGarden() =>
|
||||||
(await Get<NowResponseData>($"/now/garden"))?.Garden ?? new List<NowData>();
|
(await Get<NowResponseData>($"/now/garden"))?.Garden ?? new List<NowData>();
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ img {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status .emoji, #status-emoji {
|
.status .emoji {
|
||||||
margin-bottom: auto;
|
margin-bottom: auto;
|
||||||
inline-size: 5.5rem;
|
inline-size: 5.5rem;
|
||||||
block-size: 5.5rem;
|
block-size: 5.5rem;
|
||||||
|
@ -91,6 +91,15 @@ img {
|
||||||
text-indent: -10px;
|
text-indent: -10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#status-emoji {
|
||||||
|
margin-bottom: auto;
|
||||||
|
inline-size: 3.5rem;
|
||||||
|
block-size: 3.5rem;
|
||||||
|
font-size: 3.1rem;
|
||||||
|
line-height: 3.5rem;
|
||||||
|
text-indent: -6px;
|
||||||
|
}
|
||||||
|
|
||||||
:is(h1, h2, h3, h4, h5, h6) :is(i.fa-at, i:only-child){
|
:is(h1, h2, h3, h4, h5, h6) :is(i.fa-at, i:only-child){
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
inline-size: auto;
|
inline-size: auto;
|
||||||
|
|
Loading…
Reference in a new issue