From 0e1f734f9f311fd3fa18e720257378d5ec833a7d Mon Sep 17 00:00:00 2001 From: Gordon Pedersen Date: Fri, 7 Jun 2024 14:25:21 +1000 Subject: [PATCH] Editing pics and other changes --- Components/EditPicDialog.razor | 64 ++++++++++++++++++++++++++++++++++ Components/NewPicDialog.razor | 49 +++++++++++++++++++++----- Components/Pages/Person.razor | 11 +++++- Components/Pages/Pics.razor | 2 +- Components/PicCard.razor | 50 ++++++++++++++++++++++++++ Components/PicList.razor | 25 +++++-------- Components/StatusCard.razor | 12 +++++++ Models/State.cs | 15 +++++++- RestService.cs | 10 ++++++ wwwroot/css/style.css | 33 ++++++++++++++++++ 10 files changed, 242 insertions(+), 29 deletions(-) create mode 100644 Components/EditPicDialog.razor create mode 100644 Components/PicCard.razor diff --git a/Components/EditPicDialog.razor b/Components/EditPicDialog.razor new file mode 100644 index 0000000..5006c61 --- /dev/null +++ b/Components/EditPicDialog.razor @@ -0,0 +1,64 @@ +@inject IJSRuntime JS +@inject State State + +
+ + +
+
+ + +
+
+ +
+ +@code { + private Pic? _pic; + [Parameter] + public Pic? Pic { + get => _pic; + set { + _pic = value; + Description = _pic?.Description; + } + } + public string? Description { get; set; } + private bool loading = false; + [Parameter] + public string id { get; set; } + + protected override async Task OnInitializedAsync() { + Description = Pic?.Description; + } + + public async Task PostPic() { + loading = true; + await InvokeAsync(StateHasChanged); + + RestService api = new RestService(); + if(!string.IsNullOrEmpty(Pic.Id)) { + await api.PostPicDescription(State.SelectedAddressName, Pic.Id, Description); + await State.RefreshPics(); + await InvokeAsync(StateHasChanged); + } + + await JS.InvokeVoidAsync("ui", "#" + id); + // clear input + Description = string.Empty; + Pic = null; + loading = false; + await InvokeAsync(StateHasChanged); + + } +} diff --git a/Components/NewPicDialog.razor b/Components/NewPicDialog.razor index 5fd3bd9..1f41c03 100644 --- a/Components/NewPicDialog.razor +++ b/Components/NewPicDialog.razor @@ -5,15 +5,18 @@
Share a picture
-
+ + + @*
-
- @if(File != null){ +
*@ + @if(File != null && Base64File != null && FileSize != null){ + - @File.ContentType (@formatSizeUnits(File.Size)) + @File.ContentType (@formatSizeUnits(FileSize)) } @@ -38,7 +41,17 @@
@code { - private IBrowserFile? File { get; set; } + // private IBrowserFile? File { get; set; } + private FileResult? File { get; set; } + private string? Base64File { get; set; } + private long? FileSize { get; set; } + private string? Base64Url { + get { + if(File == null || Base64File == null) return null; + + return $"data:{File.ContentType};base64,{Base64File}"; + } + } private string Description { get; set; } private bool loading = false; [Parameter] @@ -65,11 +78,12 @@ } - private async Task ChangeFile(InputFileChangeEventArgs e){ - File = e.File; - } + // private async Task ChangeFile(InputFileChangeEventArgs e){ + // File = e.File; + // } - private string formatSizeUnits(long bytes){ + private string formatSizeUnits(long? bytes){ + if(bytes == null) return "?? bytes"; string formatted = "0 bytes"; if (bytes >= 1073741824) { formatted = $"{(bytes / 1073741824):.##} GB"; } else if (bytes >= 1048576) { formatted = $"{(bytes / 1048576):.##} MB"; } @@ -79,4 +93,21 @@ return formatted; } + + + + private async Task PicFromMedia(EventArgs e) { + File = await MediaPicker.Default.PickPhotoAsync(); + await PopulateFileDetails(); + } + + private async Task PicFromPhoto(EventArgs e) { + File = await MediaPicker.Default.CapturePhotoAsync(); + await PopulateFileDetails(); + } + + private async Task PopulateFileDetails() { + FileSize = await State.FileSize(File); + Base64File = await State.Base64FromFile(File); + } } diff --git a/Components/Pages/Person.razor b/Components/Pages/Person.razor index e06fdd6..e4aaaab 100644 --- a/Components/Pages/Person.razor +++ b/Components/Pages/Person.razor @@ -34,7 +34,10 @@
- + @if(Editable){ + + } +
@@ -42,6 +45,12 @@ [Parameter] public string Address { get; set; } + private EditPicDialog? editPicDialog { get; set; } + + private bool Editable { + get => Address == State.SelectedAddressName; + } + private MarkupString? bio; protected override async Task OnInitializedAsync() { diff --git a/Components/Pages/Pics.razor b/Components/Pages/Pics.razor index 5c7186b..3e59321 100644 --- a/Components/Pages/Pics.razor +++ b/Components/Pages/Pics.razor @@ -19,7 +19,7 @@ -
+
diff --git a/Components/PicCard.razor b/Components/PicCard.razor new file mode 100644 index 0000000..a25aa6a --- /dev/null +++ b/Components/PicCard.razor @@ -0,0 +1,50 @@ +@inject IJSRuntime JS + + + +@code { + [Parameter] + public Pic Pic {get; set;} + [Parameter] + public bool Editable { get; set; } = false; + [Parameter] + public EditPicDialog? Dialog { get; set; } + + private async Task EditPic(EventArgs e){ + Dialog.Pic = Pic; + // await InvokeAsync(StateHasChanged); + await JS.InvokeVoidAsync("ui", "#" + Dialog?.id); + } + + public async Task ShareClick(EventArgs e){ + await Share.Default.RequestAsync(new ShareTextRequest{ + Uri = Pic.Url, + Text = Pic.Description, + Title = "I saw this on some.pics", + Subject = "I saw this on some.pics" + }); + } +} diff --git a/Components/PicList.razor b/Components/PicList.razor index f6e9d8f..2919ecd 100644 --- a/Components/PicList.razor +++ b/Components/PicList.razor @@ -1,29 +1,20 @@ - +@inject IJSRuntime JS + - + -
+
@code { [Parameter] public Func>> PicsFunc { get; set; } + [Parameter] + public bool Editable { get; set; } = false; + [Parameter] + public EditPicDialog? Dialog { get; set; } private async ValueTask> GetPics(ItemsProviderRequest request) { return await this.PicsFunc(request); diff --git a/Components/StatusCard.razor b/Components/StatusCard.razor index 377c379..8e909a0 100644 --- a/Components/StatusCard.razor +++ b/Components/StatusCard.razor @@ -15,10 +15,22 @@ Respond +
+ @code { [Parameter] public Status status { get; set; } + + public async Task ShareClick(EventArgs e){ + await Share.Default.RequestAsync(new ShareTextRequest{ + Text = $"{status.Content}\n- from [@{status.Address}]({status.Url})", + Title = "I saw this on status.lol", + Subject = "I saw this on status.lol" + }); + } } diff --git a/Models/State.cs b/Models/State.cs index 4f6b5f7..acb435c 100644 --- a/Models/State.cs +++ b/Models/State.cs @@ -117,8 +117,8 @@ namespace Neighbourhood.omg.lol.Models { } public async Task?> GetPics(bool forceRefresh = false) { - RestService api = new RestService(); if(forceRefresh || this.Pics == null || this.Pics.Count == 0) { + RestService api = new RestService(); this.Pics = await api.SomePics(); } return this.Pics; @@ -147,6 +147,19 @@ namespace Neighbourhood.omg.lol.Models { } } + public async Task FileSize(FileResult file) { + using var fileStream = await file.OpenReadAsync(); + return fileStream.Length; + } + + public async Task 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 async Task RefreshStatuses() => await GetStatuses(forceRefresh: true); public async Task RefreshPics() => await GetPics(forceRefresh: true); } diff --git a/RestService.cs b/RestService.cs index c0a9991..a5a8d35 100644 --- a/RestService.cs +++ b/RestService.cs @@ -132,6 +132,16 @@ namespace Neighbourhood.omg.lol { return await PutPic(address, bytes); } + public async Task PutPic(string address, FileResult file) { + byte[] bytes; + using var memoryStream = new MemoryStream(); + using var fileStream = await file.OpenReadAsync(); + await fileStream.CopyToAsync(memoryStream); + bytes = memoryStream.ToArray(); + + return await PutPic(address, bytes); + } + public async Task PutPic(string address, byte[] bytes) => await PutPic(address, Convert.ToBase64String(bytes)); diff --git a/wwwroot/css/style.css b/wwwroot/css/style.css index f90da68..8566cb6 100644 --- a/wwwroot/css/style.css +++ b/wwwroot/css/style.css @@ -197,4 +197,37 @@ article.ephemeral { .tabs a { text-decoration:none; +} + +.card-grid{ + display: flex; + flex-direction:row; + flex-wrap:wrap; + gap: .5rem; +} + +#pics article { + margin: 1rem auto; + text-align:center; +} + +#pics article>:not(:first-child) { + text-align: left; +} + +#pics article nav { + flex-wrap: wrap; +} + +#pics.card-grid article{ + max-width: 24rem; +} +@media only screen and (max-width: 895px) { + #pics.card-grid article { + max-width: calc(100% - 1rem); + } +} + +#pics article > img:first-child { + text-align: center; } \ No newline at end of file