From 073b6752ce3729c50bb2763ec7411b9b6cea90ab Mon Sep 17 00:00:00 2001 From: SkyJoshua Date: Sun, 12 Jul 2026 19:11:36 +0100 Subject: [PATCH] Big Emojis --- .../Edit/EditDevicePreferencesComponent.razor | 16 ++++++ .../Messages/MessageComponent.razor | 4 +- Valour/Client/Device/DevicePreferences.cs | 17 ++++++ .../Messages/Rendering/MarkdownManager.cs | 53 +++++++++++++++++++ Valour/Client/wwwroot/css/globals.css | 4 ++ 5 files changed, 93 insertions(+), 1 deletion(-) diff --git a/Valour/Client/Components/Menus/Modals/Users/Edit/EditDevicePreferencesComponent.razor b/Valour/Client/Components/Menus/Modals/Users/Edit/EditDevicePreferencesComponent.razor index c0288d0ef..4e3d1b1cb 100644 --- a/Valour/Client/Components/Menus/Modals/Users/Edit/EditDevicePreferencesComponent.razor +++ b/Valour/Client/Components/Menus/Modals/Users/Edit/EditDevicePreferencesComponent.razor @@ -22,6 +22,17 @@

When enabled, faces like :) will be automatically turned into emojis in chat.

+
+
+ Big Emojis + +
+

When enabled, messages containing only a few emojis (5 or less) are shown larger.

+
+
Error Reporting @@ -398,6 +409,11 @@ MarkdownManager.RegenPipeline(); } + private async Task SwitchBigEmojiMessages() + { + await DevicePreferences.SetBigEmojiMessages(!DevicePreferences.BigEmojiMessages, LocalStorage); + } + private string GetMicPermissionDescription() { if (string.Equals(_micPermissionState, "granted", StringComparison.OrdinalIgnoreCase)) diff --git a/Valour/Client/Components/Messages/MessageComponent.razor b/Valour/Client/Components/Messages/MessageComponent.razor index 6cf65c718..b867984b8 100644 --- a/Valour/Client/Components/Messages/MessageComponent.razor +++ b/Valour/Client/Components/Messages/MessageComponent.razor @@ -201,8 +201,10 @@ else if (string.IsNullOrWhiteSpace(_message.Content)) return; + var isBigEmoji = DevicePreferences.BigEmojiMessages && MarkdownManager.IsEmojiOnlyMessage(_message.Content); + builder.OpenElement(0, "div"); - builder.AddAttribute(1, "class", "fragments"); + builder.AddAttribute(1, "class", isBigEmoji ? "fragments big-emoji" : "fragments"); try { Markdown.RenderToFragment( diff --git a/Valour/Client/Device/DevicePreferences.cs b/Valour/Client/Device/DevicePreferences.cs index 5a01bb127..1dc3aa357 100644 --- a/Valour/Client/Device/DevicePreferences.cs +++ b/Valour/Client/Device/DevicePreferences.cs @@ -17,6 +17,12 @@ public static class DevicePreferences /// public static bool AutoEmoji { get; set; } = false; // Default it to off + /// + /// True if messages containing only a few emojis (and no other text) should + /// be rendered larger, the same size as a heading. + /// + public static bool BigEmojiMessages { get; set; } = true; + public static string? MicrophoneDeviceId { get; set; } public static string? CameraDeviceId { get; set; } public static bool ErrorReportingEnabled { get; private set; } @@ -46,6 +52,12 @@ public static async Task SetErrorReportingEnabled(bool isEnabled, IAppStorage lo await localStorage.SetAsync(ErrorReportingEnabledStorageKey, isEnabled); } + public static async Task SetBigEmojiMessages(bool isEnabled, IAppStorage localStorage) + { + BigEmojiMessages = isEnabled; + await localStorage.SetAsync("BigEmojiMessages", isEnabled); + } + public static async Task LoadPreferences(IAppStorage localStorage) { if (await localStorage.ContainsKeyAsync("AutoEmoji")) @@ -53,6 +65,11 @@ public static async Task LoadPreferences(IAppStorage localStorage) AutoEmoji = await localStorage.GetAsync("AutoEmoji"); } + if (await localStorage.ContainsKeyAsync("BigEmojiMessages")) + { + BigEmojiMessages = await localStorage.GetAsync("BigEmojiMessages"); + } + if (await localStorage.ContainsKeyAsync("MicrophoneDeviceId")) { MicrophoneDeviceId = await localStorage.GetAsync("MicrophoneDeviceId"); diff --git a/Valour/Client/Messages/Rendering/MarkdownManager.cs b/Valour/Client/Messages/Rendering/MarkdownManager.cs index 0cd61733a..3703a3544 100644 --- a/Valour/Client/Messages/Rendering/MarkdownManager.cs +++ b/Valour/Client/Messages/Rendering/MarkdownManager.cs @@ -1,6 +1,8 @@ using Markdig; using Markdig.Blazor; using Markdig.Extensions.AutoLinks; +using Markdig.Syntax; +using Markdig.Syntax.Inlines; using Valour.Client.Device; using Valour.Client.Markdig; using Markdown = Markdig.Markdown; @@ -51,6 +53,57 @@ public static void RegenPipeline() Renderer.ObjectRenderers.Insert(0, new ValourLinkRenderer()); } + /// + /// Maximum number of emojis a message can contain and still be rendered + /// large by the "big emoji" preference. + /// + public const int MaxBigEmojiCount = 5; + + /// + /// Returns true if the given message content is made up of nothing but + /// 1-5 emojis (no other text), and is therefore eligible to be rendered + /// at heading size when the big-emoji preference is enabled. + /// + public static bool IsEmojiOnlyMessage(string content) + { + if (string.IsNullOrWhiteSpace(content)) + return false; + + MarkdownDocument document; + + try + { + document = Markdown.Parse(content, Pipeline); + } + catch + { + return false; + } + + if (document.Count != 1 || document[0] is not ParagraphBlock { Inline: { } inline }) + return false; + + var emojiCount = 0; + + foreach (var item in inline) + { + switch (item) + { + case ValourEmojiInline: + emojiCount++; + break; + case LineBreakInline: + break; + case LiteralInline literal when literal.Content.IsEmptyOrWhitespace(): + break; + default: + return false; + } + } + + return emojiCount is > 0 and <= MaxBigEmojiCount; + } + public static string GetHtml(string content) { if (content is null) diff --git a/Valour/Client/wwwroot/css/globals.css b/Valour/Client/wwwroot/css/globals.css index fc533e481..ab2a61c75 100644 --- a/Valour/Client/wwwroot/css/globals.css +++ b/Valour/Client/wwwroot/css/globals.css @@ -1169,6 +1169,10 @@ img.emoji { max-height: 900px !important; } +.message .fragments.big-emoji { + font-size: var(--font-size-5xl); +} + .message .fragments a:not(.nohide) { font-size: 0; }