adjust visibility flags, improve chat functionality, bump submodules
This commit is contained in:
@@ -146,12 +146,19 @@ public class StandaloneProfileUi : WindowMediatorSubscriberBase
|
||||
if (string.IsNullOrEmpty(hashedCid))
|
||||
return LightfinderDisplayName;
|
||||
|
||||
var (name, address) = dalamudUtilService.FindPlayerByNameHash(hashedCid);
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return LightfinderDisplayName;
|
||||
try
|
||||
{
|
||||
var (name, address) = dalamudUtilService.FindPlayerByNameHash(hashedCid);
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return LightfinderDisplayName;
|
||||
|
||||
var world = dalamudUtilService.GetWorldNameFromPlayerAddress(address);
|
||||
return string.IsNullOrEmpty(world) ? name : $"{name} ({world})";
|
||||
var world = dalamudUtilService.GetWorldNameFromPlayerAddress(address);
|
||||
return string.IsNullOrEmpty(world) ? name : $"{name} ({world})";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return LightfinderDisplayName;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DrawInternal()
|
||||
|
||||
@@ -95,13 +95,6 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
.Apply();
|
||||
|
||||
Mediator.Subscribe<ChatChannelMessageAdded>(this, OnChatChannelMessageAdded);
|
||||
Mediator.Subscribe<ChatChannelHistoryCleared>(this, msg =>
|
||||
{
|
||||
if (_selectedChannelKey is not null && string.Equals(_selectedChannelKey, msg.ChannelKey, StringComparison.Ordinal))
|
||||
{
|
||||
_scrollToBottom = true;
|
||||
}
|
||||
});
|
||||
Mediator.Subscribe<ChatChannelsUpdated>(this, _ => _scrollToBottom = true);
|
||||
}
|
||||
|
||||
@@ -250,6 +243,21 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
for (var i = 0; i < channel.Messages.Count; i++)
|
||||
{
|
||||
var message = channel.Messages[i];
|
||||
ImGui.PushID(i);
|
||||
|
||||
if (message.IsSystem)
|
||||
{
|
||||
DrawSystemEntry(message);
|
||||
ImGui.PopID();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (message.Payload is not { } payload)
|
||||
{
|
||||
ImGui.PopID();
|
||||
continue;
|
||||
}
|
||||
|
||||
var timestampText = string.Empty;
|
||||
if (showTimestamps)
|
||||
{
|
||||
@@ -257,24 +265,21 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
var color = message.FromSelf ? UIColors.Get("LightlessBlue") : ImGuiColors.DalamudWhite;
|
||||
|
||||
ImGui.PushID(i);
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, color);
|
||||
ImGui.TextWrapped($"{timestampText}{message.DisplayName}: {message.Payload.Message}");
|
||||
ImGui.TextWrapped($"{timestampText}{message.DisplayName}: {payload.Message}");
|
||||
ImGui.PopStyleColor();
|
||||
|
||||
if (ImGui.BeginPopupContextItem($"chat_msg_ctx##{channel.Key}_{i}"))
|
||||
{
|
||||
var contextLocalTimestamp = message.Payload.SentAtUtc.ToLocalTime();
|
||||
var contextLocalTimestamp = payload.SentAtUtc.ToLocalTime();
|
||||
var contextTimestampText = contextLocalTimestamp.ToString("yyyy-MM-dd HH:mm:ss 'UTC'z", CultureInfo.InvariantCulture);
|
||||
ImGui.TextDisabled(contextTimestampText);
|
||||
ImGui.Separator();
|
||||
|
||||
var actionIndex = 0;
|
||||
foreach (var action in GetContextMenuActions(channel, message))
|
||||
{
|
||||
if (ImGui.MenuItem(action.Label, string.Empty, false, action.IsEnabled))
|
||||
{
|
||||
action.Execute();
|
||||
}
|
||||
DrawContextMenuAction(action, actionIndex++);
|
||||
}
|
||||
|
||||
ImGui.EndPopup();
|
||||
@@ -538,6 +543,13 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.Payload is not { } payload)
|
||||
{
|
||||
CloseReportPopup();
|
||||
ImGui.EndPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_reportSubmissionResult is { } pendingResult)
|
||||
{
|
||||
_reportSubmissionResult = null;
|
||||
@@ -563,11 +575,11 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
|
||||
ImGui.TextUnformatted(channelLabel);
|
||||
ImGui.TextUnformatted($"Sender: {message.DisplayName}");
|
||||
ImGui.TextUnformatted($"Sent: {message.Payload.SentAtUtc.ToLocalTime().ToString("g", CultureInfo.CurrentCulture)}");
|
||||
ImGui.TextUnformatted($"Sent: {payload.SentAtUtc.ToLocalTime().ToString("g", CultureInfo.CurrentCulture)}");
|
||||
|
||||
ImGui.Separator();
|
||||
ImGui.PushTextWrapPos(ImGui.GetWindowContentRegionMax().X);
|
||||
ImGui.TextWrapped(message.Payload.Message);
|
||||
ImGui.TextWrapped(payload.Message);
|
||||
ImGui.PopTextWrapPos();
|
||||
ImGui.Separator();
|
||||
|
||||
@@ -633,9 +645,15 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
|
||||
private void OpenReportPopup(ChatChannelSnapshot channel, ChatMessageEntry message)
|
||||
{
|
||||
if (message.Payload is not { } payload)
|
||||
{
|
||||
_logger.LogDebug("Ignoring report popup request for non-message entry in {ChannelKey}", channel.Key);
|
||||
return;
|
||||
}
|
||||
|
||||
_reportTargetChannel = channel;
|
||||
_reportTargetMessage = message;
|
||||
_logger.LogDebug("Opening report popup for channel {ChannelKey}, message {MessageId}", channel.Key, message.Payload.MessageId);
|
||||
_logger.LogDebug("Opening report popup for channel {ChannelKey}, message {MessageId}", channel.Key, payload.MessageId);
|
||||
_reportReason = string.Empty;
|
||||
_reportAdditionalContext = string.Empty;
|
||||
_reportError = null;
|
||||
@@ -650,6 +668,12 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
if (_reportSubmitting)
|
||||
return;
|
||||
|
||||
if (message.Payload is not { } payload)
|
||||
{
|
||||
_reportError = "Unable to report this message.";
|
||||
return;
|
||||
}
|
||||
|
||||
var trimmedReason = _reportReason.Trim();
|
||||
if (trimmedReason.Length == 0)
|
||||
{
|
||||
@@ -666,7 +690,7 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
_reportSubmissionResult = null;
|
||||
|
||||
var descriptor = channel.Descriptor;
|
||||
var messageId = message.Payload.MessageId;
|
||||
var messageId = payload.MessageId;
|
||||
if (string.IsNullOrWhiteSpace(messageId))
|
||||
{
|
||||
_reportSubmitting = false;
|
||||
@@ -743,25 +767,33 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
|
||||
private IEnumerable<ChatMessageContextAction> GetContextMenuActions(ChatChannelSnapshot channel, ChatMessageEntry message)
|
||||
{
|
||||
if (TryCreateCopyMessageAction(message, out var copyAction))
|
||||
if (message.IsSystem || message.Payload is not { } payload)
|
||||
yield break;
|
||||
|
||||
if (TryCreateCopyMessageAction(message, payload, out var copyAction))
|
||||
{
|
||||
yield return copyAction;
|
||||
}
|
||||
|
||||
if (TryCreateViewProfileAction(channel, message, out var viewProfile))
|
||||
if (TryCreateViewProfileAction(channel, message, payload, out var viewProfile))
|
||||
{
|
||||
yield return viewProfile;
|
||||
}
|
||||
|
||||
if (TryCreateReportMessageAction(channel, message, out var reportAction))
|
||||
if (TryCreateMuteParticipantAction(channel, message, payload, out var muteAction))
|
||||
{
|
||||
yield return muteAction;
|
||||
}
|
||||
|
||||
if (TryCreateReportMessageAction(channel, message, payload, out var reportAction))
|
||||
{
|
||||
yield return reportAction;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryCreateCopyMessageAction(ChatMessageEntry message, out ChatMessageContextAction action)
|
||||
private static bool TryCreateCopyMessageAction(ChatMessageEntry message, ChatMessageDto payload, out ChatMessageContextAction action)
|
||||
{
|
||||
var text = message.Payload.Message;
|
||||
var text = payload.Message;
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
action = default;
|
||||
@@ -769,20 +801,21 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
|
||||
action = new ChatMessageContextAction(
|
||||
FontAwesomeIcon.Clipboard,
|
||||
"Copy Message",
|
||||
true,
|
||||
() => ImGui.SetClipboardText(text));
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryCreateViewProfileAction(ChatChannelSnapshot channel, ChatMessageEntry message, out ChatMessageContextAction action)
|
||||
private bool TryCreateViewProfileAction(ChatChannelSnapshot channel, ChatMessageEntry message, ChatMessageDto payload, out ChatMessageContextAction action)
|
||||
{
|
||||
action = default;
|
||||
switch (channel.Type)
|
||||
{
|
||||
case ChatChannelType.Group:
|
||||
{
|
||||
var user = message.Payload.Sender.User;
|
||||
var user = payload.Sender.User;
|
||||
if (user?.UID is not { Length: > 0 })
|
||||
return false;
|
||||
|
||||
@@ -790,6 +823,7 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
if (snapshot.PairsByUid.TryGetValue(user.UID, out var pair) && pair is not null)
|
||||
{
|
||||
action = new ChatMessageContextAction(
|
||||
FontAwesomeIcon.User,
|
||||
"View Profile",
|
||||
true,
|
||||
() => Mediator.Publish(new ProfileOpenStandaloneMessage(pair)));
|
||||
@@ -797,41 +831,64 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
|
||||
action = new ChatMessageContextAction(
|
||||
FontAwesomeIcon.User,
|
||||
"View Profile",
|
||||
true,
|
||||
() => RunContextAction(() => OpenStandardProfileAsync(user)));
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
case ChatChannelType.Zone:
|
||||
if (!message.Payload.Sender.CanResolveProfile)
|
||||
if (!payload.Sender.CanResolveProfile)
|
||||
return false;
|
||||
|
||||
if (string.IsNullOrEmpty(message.Payload.Sender.Token))
|
||||
var hashedCid = payload.Sender.HashedCid;
|
||||
if (string.IsNullOrEmpty(hashedCid))
|
||||
return false;
|
||||
|
||||
action = new ChatMessageContextAction(
|
||||
FontAwesomeIcon.User,
|
||||
"View Profile",
|
||||
true,
|
||||
() => RunContextAction(() => OpenZoneParticipantProfileAsync(channel.Descriptor, message.Payload.Sender.Token)));
|
||||
() => RunContextAction(() => OpenLightfinderProfileInternalAsync(hashedCid)));
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryCreateReportMessageAction(ChatChannelSnapshot channel, ChatMessageEntry message, out ChatMessageContextAction action)
|
||||
private bool TryCreateMuteParticipantAction(ChatChannelSnapshot channel, ChatMessageEntry message, ChatMessageDto payload, out ChatMessageContextAction action)
|
||||
{
|
||||
action = default;
|
||||
if (message.FromSelf)
|
||||
return false;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(message.Payload.MessageId))
|
||||
if (string.IsNullOrEmpty(payload.Sender.Token))
|
||||
return false;
|
||||
|
||||
var safeName = string.IsNullOrWhiteSpace(message.DisplayName)
|
||||
? "Participant"
|
||||
: message.DisplayName;
|
||||
|
||||
action = new ChatMessageContextAction(
|
||||
FontAwesomeIcon.VolumeMute,
|
||||
$"Mute '{safeName}'",
|
||||
true,
|
||||
() => RunContextAction(() => _zoneChatService.SetParticipantMuteAsync(channel.Descriptor, payload.Sender.Token!, true)));
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryCreateReportMessageAction(ChatChannelSnapshot channel, ChatMessageEntry message, ChatMessageDto payload, out ChatMessageContextAction action)
|
||||
{
|
||||
action = default;
|
||||
if (message.FromSelf)
|
||||
return false;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(payload.MessageId))
|
||||
return false;
|
||||
|
||||
action = new ChatMessageContextAction(
|
||||
FontAwesomeIcon.ExclamationTriangle,
|
||||
"Report Message",
|
||||
true,
|
||||
() => OpenReportPopup(channel, message));
|
||||
@@ -863,24 +920,12 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
});
|
||||
}
|
||||
|
||||
private async Task OpenZoneParticipantProfileAsync(ChatChannelDescriptor descriptor, string token)
|
||||
private void OnChatChannelMessageAdded(ChatChannelMessageAdded message)
|
||||
{
|
||||
var result = await _zoneChatService.ResolveParticipantAsync(descriptor, token).ConfigureAwait(false);
|
||||
if (result is null)
|
||||
if (_selectedChannelKey is not null && string.Equals(_selectedChannelKey, message.ChannelKey, StringComparison.Ordinal))
|
||||
{
|
||||
Mediator.Publish(new NotificationMessage("Zone Chat", "Participant is no longer available.", NotificationType.Warning, TimeSpan.FromSeconds(3)));
|
||||
return;
|
||||
_scrollToBottom = true;
|
||||
}
|
||||
|
||||
var resolved = result.Value;
|
||||
var hashedCid = resolved.Sender.HashedCid;
|
||||
if (string.IsNullOrEmpty(hashedCid))
|
||||
{
|
||||
Mediator.Publish(new NotificationMessage("Zone Chat", "This participant remains anonymous.", NotificationType.Warning, TimeSpan.FromSeconds(3)));
|
||||
return;
|
||||
}
|
||||
|
||||
await OpenLightfinderProfileInternalAsync(hashedCid).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task OpenLightfinderProfileInternalAsync(string hashedCid)
|
||||
@@ -901,14 +946,6 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
Mediator.Publish(new OpenLightfinderProfileMessage(sanitizedUser, profile.Value.ProfileData, hashedCid));
|
||||
}
|
||||
|
||||
private void OnChatChannelMessageAdded(ChatChannelMessageAdded message)
|
||||
{
|
||||
if (_selectedChannelKey is not null && string.Equals(_selectedChannelKey, message.ChannelKey, StringComparison.Ordinal))
|
||||
{
|
||||
_scrollToBottom = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureSelectedChannel(IReadOnlyList<ChatChannelSnapshot> channels)
|
||||
{
|
||||
if (_selectedChannelKey is not null && channels.Any(channel => string.Equals(channel.Key, _selectedChannelKey, StringComparison.Ordinal)))
|
||||
@@ -1374,5 +1411,86 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - style.ItemSpacing.Y * 0.3f);
|
||||
}
|
||||
|
||||
private readonly record struct ChatMessageContextAction(string Label, bool IsEnabled, Action Execute);
|
||||
private void DrawSystemEntry(ChatMessageEntry entry)
|
||||
{
|
||||
var system = entry.SystemMessage;
|
||||
if (system is null)
|
||||
return;
|
||||
|
||||
switch (system.Type)
|
||||
{
|
||||
case ChatSystemEntryType.ZoneSeparator:
|
||||
DrawZoneSeparatorEntry(system, entry.ReceivedAtUtc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawZoneSeparatorEntry(ChatSystemEntry systemEntry, DateTime timestampUtc)
|
||||
{
|
||||
ImGui.Spacing();
|
||||
|
||||
var zoneName = string.IsNullOrWhiteSpace(systemEntry.ZoneName) ? "Zone" : systemEntry.ZoneName;
|
||||
var localTime = timestampUtc.ToLocalTime();
|
||||
var label = $"{localTime.ToString("HH:mm", CultureInfo.CurrentCulture)} - {zoneName}";
|
||||
var availableWidth = ImGui.GetContentRegionAvail().X;
|
||||
var textSize = ImGui.CalcTextSize(label);
|
||||
var cursor = ImGui.GetCursorPos();
|
||||
var textPosX = cursor.X + MathF.Max(0f, (availableWidth - textSize.X) * 0.5f);
|
||||
|
||||
ImGui.SetCursorPos(new Vector2(textPosX, cursor.Y));
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey2);
|
||||
ImGui.TextUnformatted(label);
|
||||
ImGui.PopStyleColor();
|
||||
|
||||
var nextY = ImGui.GetCursorPosY() + ImGui.GetStyle().ItemSpacing.Y * 0.35f;
|
||||
ImGui.SetCursorPos(new Vector2(cursor.X, nextY));
|
||||
ImGui.Separator();
|
||||
ImGui.Spacing();
|
||||
}
|
||||
|
||||
private void DrawContextMenuAction(ChatMessageContextAction action, int index)
|
||||
{
|
||||
ImGui.PushID(index);
|
||||
using var disabled = ImRaii.Disabled(!action.IsEnabled);
|
||||
|
||||
var availableWidth = Math.Max(1f, ImGui.GetContentRegionAvail().X);
|
||||
var clicked = ImGui.Selectable("##chat_ctx_action", false, ImGuiSelectableFlags.None, new Vector2(availableWidth, 0f));
|
||||
|
||||
var drawList = ImGui.GetWindowDrawList();
|
||||
var itemMin = ImGui.GetItemRectMin();
|
||||
var itemMax = ImGui.GetItemRectMax();
|
||||
var itemHeight = itemMax.Y - itemMin.Y;
|
||||
var style = ImGui.GetStyle();
|
||||
var textColor = ImGui.GetColorU32(action.IsEnabled ? ImGuiCol.Text : ImGuiCol.TextDisabled);
|
||||
|
||||
var textSize = ImGui.CalcTextSize(action.Label);
|
||||
var textPos = new Vector2(itemMin.X + style.FramePadding.X, itemMin.Y + (itemHeight - textSize.Y) * 0.5f);
|
||||
|
||||
if (action.Icon.HasValue)
|
||||
{
|
||||
var iconSize = _uiSharedService.GetIconSize(action.Icon.Value);
|
||||
var iconPos = new Vector2(
|
||||
itemMin.X + style.FramePadding.X,
|
||||
itemMin.Y + (itemHeight - iconSize.Y) * 0.5f);
|
||||
|
||||
using (_uiSharedService.IconFont.Push())
|
||||
{
|
||||
drawList.AddText(iconPos, textColor, action.Icon.Value.ToIconString());
|
||||
}
|
||||
|
||||
textPos.X = iconPos.X + iconSize.X + style.ItemSpacing.X;
|
||||
}
|
||||
|
||||
drawList.AddText(textPos, textColor, action.Label);
|
||||
|
||||
if (clicked && action.IsEnabled)
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
action.Execute();
|
||||
}
|
||||
|
||||
ImGui.PopID();
|
||||
}
|
||||
|
||||
private readonly record struct ChatMessageContextAction(FontAwesomeIcon? Icon, string Label, bool IsEnabled, Action Execute);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user