diff --git a/Iceshrimp.Backend/Components/PublicPreview/Renderers/NoteRenderer.cs b/Iceshrimp.Backend/Components/PublicPreview/Renderers/NoteRenderer.cs index 06964374..e4d1728e 100644 --- a/Iceshrimp.Backend/Components/PublicPreview/Renderers/NoteRenderer.cs +++ b/Iceshrimp.Backend/Components/PublicPreview/Renderers/NoteRenderer.cs @@ -28,13 +28,15 @@ public class NoteRenderer( var emoji = await GetEmojiAsync(allNotes); var users = await GetUsersAsync(allNotes); var attachments = await GetAttachmentsAsync(allNotes); + var polls = await GetPollsAsync(allNotes); - return await RenderAsync(note, users, mentions, emoji, attachments); + return await RenderAsync(note, users, mentions, emoji, attachments, polls); } private async Task RenderAsync( Note note, List users, Dictionary> mentions, - Dictionary> emoji, Dictionary?> attachments + Dictionary> emoji, Dictionary?> attachments, + Dictionary polls ) { var renderedText = await mfm.RenderAsync(note.Text, note.User.Host, mentions[note.Id], emoji[note.Id], "span", attachments[note.Id]); @@ -49,6 +51,7 @@ public class NoteRenderer( QuoteUrl = note.Renote?.Url ?? note.Renote?.Uri ?? note.Renote?.GetPublicUriOrNull(instance.Value), QuoteInaccessible = note.Renote?.VisibilityIsPublicOrHome == false, Attachments = attachments[note.Id]?.Where(p => !inlineMediaUrls.Contains(p.Url)).ToList(), + Poll = polls.GetValueOrDefault(note.Id), CreatedAt = note.CreatedAt.ToDisplayStringTz(), UpdatedAt = note.UpdatedAt?.ToDisplayStringTz() }; @@ -112,6 +115,24 @@ public class NoteRenderer( .ToList()); } + private async Task> GetPollsAsync(List notes) + { + if (notes is []) return new Dictionary(); + var ids = notes.Select(p => p.Id).ToList(); + var polls = await db.Polls + .Where(p => ids.Contains(p.NoteId)) + .ToListAsync(); + + return polls.ToDictionary(p => p.NoteId, + p => new PreviewPoll + { + ExpiresAt = p.ExpiresAt, + Multiple = p.Multiple, + Choices = p.Choices.Zip(p.Votes).Select(c => (c.First, c.Second)).ToList(), + VotersCount = p.VotersCount + }); + } + public async Task> RenderManyAsync(List notes) { if (notes is []) return []; @@ -120,7 +141,8 @@ public class NoteRenderer( var mentions = await GetMentionsAsync(allNotes); var emoji = await GetEmojiAsync(allNotes); var attachments = await GetAttachmentsAsync(allNotes); - return await notes.Select(p => RenderAsync(p, users, mentions, emoji, attachments)) + var polls = await GetPollsAsync(allNotes); + return await notes.Select(p => RenderAsync(p, users, mentions, emoji, attachments, polls)) .AwaitAllAsync() .ToListAsync(); } diff --git a/Iceshrimp.Backend/Components/PublicPreview/Schemas/PreviewNote.cs b/Iceshrimp.Backend/Components/PublicPreview/Schemas/PreviewNote.cs index 8e07ce45..c688756d 100644 --- a/Iceshrimp.Backend/Components/PublicPreview/Schemas/PreviewNote.cs +++ b/Iceshrimp.Backend/Components/PublicPreview/Schemas/PreviewNote.cs @@ -11,6 +11,7 @@ public class PreviewNote public required string? QuoteUrl; public required bool QuoteInaccessible; public required List? Attachments; + public required PreviewPoll? Poll; public required string CreatedAt; public required string? UpdatedAt; } @@ -22,4 +23,12 @@ public class PreviewAttachment public required string Name; public required string? Alt; public required bool Sensitive; +} + +public class PreviewPoll +{ + public required DateTime? ExpiresAt { get; set; } + public required bool Multiple { get; set; } + public required List<(string Value, int Votes)> Choices { get; set; } + public required int? VotersCount { get; set; } } \ No newline at end of file diff --git a/Iceshrimp.Backend/Pages/NotePreview.razor b/Iceshrimp.Backend/Pages/NotePreview.razor index ab60acab..a5528d46 100644 --- a/Iceshrimp.Backend/Pages/NotePreview.razor +++ b/Iceshrimp.Backend/Pages/NotePreview.razor @@ -30,6 +30,33 @@ else

} + if (_note.Poll != null) + { + var total = _note.Poll.Choices.Sum(p => p.Votes); + +
+ @foreach (var choice in _note.Poll.Choices) + { + var percentage = Math.Floor(choice.Votes / (double)total * 100); + + + @choice.Value + @choice.Votes votes@percentage% + + } +
+ + List footerText = []; + @if (_note.Poll.Multiple) + footerText.Add("Multiple choice"); + @if (_note.Poll.VotersCount != null) + footerText.Add($"{_note.Poll.VotersCount} users voted"); + @if (_note.Poll.ExpiresAt != null) + footerText.Add(_note.Poll.ExpiresAt <= DateTime.UtcNow ? "Ended" : $"Ends at {_note.Poll.ExpiresAt.Value.ToLocalTime():G}"); + + @string.Join(" - ", footerText) + } + if (!ShowMedia && _note.Attachments != null) {

diff --git a/Iceshrimp.Backend/Pages/NotePreview.razor.css b/Iceshrimp.Backend/Pages/NotePreview.razor.css new file mode 100644 index 00000000..85a1b137 --- /dev/null +++ b/Iceshrimp.Backend/Pages/NotePreview.razor.css @@ -0,0 +1,49 @@ +.poll-results { + display: flex; + flex-direction: column; + gap: 0.5rem; + width: 100%; + margin-top: 0.5em; +} + +.poll-result { + --percentage: 0%; + display: flex; + align-items: center; + gap: 0.25rem; + padding: 0.2rem 0.5rem; + border-radius: 0.5rem; + background: linear-gradient(to right, var(--selection) var(--percentage), var(--background) var(--percentage), var(--background)); +} + +.poll-value { + flex-grow: 1; + text-wrap: wrap; + word-break: break-word; +} + +.poll-info { + flex-shrink: 0; +} + +.vote-count, .vote-percentage { + display: inline-block; + vertical-align: middle; + text-wrap: nowrap; +} + +.vote-count { + margin-right: 0.5rem; + font-size: 0.7em; +} + +@media (max-width: 768px) { + .poll-result { + flex-direction: column; + align-items: start; + } + + .vote-count { + font-size: 1em; + } +} \ No newline at end of file