Implement dynamic channel list
This commit is contained in:
@@ -7,6 +7,11 @@ public struct SwitchChannel(string name)
|
||||
[JsonProperty("channel")] public string Name = name;
|
||||
}
|
||||
|
||||
public struct ChannelList(Dictionary<string, uint> channels)
|
||||
{
|
||||
[JsonProperty("channels")] public Dictionary<string, uint> Channels = channels;
|
||||
}
|
||||
|
||||
public struct Messages(MessageResponse[] set)
|
||||
{
|
||||
[JsonProperty("messages")] public MessageResponse[] Set = set;
|
||||
|
||||
@@ -5,6 +5,8 @@ namespace ChatTwo.Http.MessageProtocol;
|
||||
|
||||
public class CloseEvent() : BaseEvent("close");
|
||||
|
||||
public class ChannelListEvent(ChannelList channelList) : BaseEvent("channel-list", JsonConvert.SerializeObject(channelList));
|
||||
|
||||
public class SwitchChannelEvent(SwitchChannel switchChannel) : BaseEvent("switch-channel", JsonConvert.SerializeObject(switchChannel));
|
||||
|
||||
public class NewMessageEvent(Messages messages) : BaseEvent("new-message", JsonConvert.SerializeObject(messages));
|
||||
|
||||
@@ -165,9 +165,11 @@ public class RouteController
|
||||
|
||||
// TODO Check if reconnect or new connection
|
||||
var messages = await WebserverUtil.FrameworkWrapper(Core.Processing.ReadMessageList);
|
||||
var channels = await Plugin.Framework.RunOnTick(Plugin.ChatLogWindow.GetAvailableChannels);
|
||||
var channelName = await Plugin.Framework.RunOnTick(() => Core.Processing.ReadChannelName(Plugin.ChatLogWindow.PreviousChannel));
|
||||
sse.OutboundQueue.Enqueue(new NewMessageEvent(new Messages(messages)));
|
||||
sse.OutboundQueue.Enqueue(new SwitchChannelEvent(new SwitchChannel(channelName)));
|
||||
sse.OutboundQueue.Enqueue(new ChannelListEvent(new ChannelList(channels.ToDictionary(pair => pair.Key, pair => (uint)pair.Value))));
|
||||
|
||||
await sse.HandleEventLoop(ctx);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using ChatTwo.Http.MessageProtocol;
|
||||
using ChatTwo.Code;
|
||||
using ChatTwo.Http.MessageProtocol;
|
||||
using WatsonWebserver.Core;
|
||||
using WatsonWebserver.Lite;
|
||||
using ExceptionEventArgs = WatsonWebserver.Core.ExceptionEventArgs;
|
||||
@@ -72,6 +73,24 @@ public class ServerCore : IAsyncDisposable
|
||||
Plugin.Log.Error(ex, "Sending channel switch over SSE failed.");
|
||||
}
|
||||
}
|
||||
|
||||
internal void SendChannelList()
|
||||
{
|
||||
try
|
||||
{
|
||||
Plugin.Framework.RunOnTick(() =>
|
||||
{
|
||||
var channels = Plugin.ChatLogWindow.GetAvailableChannels();
|
||||
var bundledResponse = new ChannelListEvent(new ChannelList(channels.ToDictionary(pair => pair.Key, pair => (uint)pair.Value)));
|
||||
foreach (var eventServer in EventConnections)
|
||||
eventServer.OutboundQueue.Enqueue(bundledResponse);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Plugin.Log.Error(ex, "Sending channel switch over SSE failed.");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region GeneralHandlers
|
||||
|
||||
@@ -13,12 +13,15 @@ class SSEConnection {
|
||||
});
|
||||
|
||||
this.socket.addEventListener('new-message', (event) => {
|
||||
let eventData = JSON.parse(event.data);
|
||||
for (let message of eventData.messages)
|
||||
for (let message of JSON.parse(event.data).messages)
|
||||
{
|
||||
addMessage(message);
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.addEventListener('channel-list', (event) => {
|
||||
updateChannelOptions(JSON.parse(event.data).channels)
|
||||
});
|
||||
}
|
||||
|
||||
send(message) {
|
||||
@@ -31,14 +34,30 @@ const sse = new SSEConnection();
|
||||
|
||||
// channel switcher
|
||||
function updateChannelHint(label) {
|
||||
document.getElementById('channel-hint').innerText = label;
|
||||
document.getElementById('channel-hint').innerHTML = label;
|
||||
}
|
||||
|
||||
document.getElementById('channel-select').addEventListener('change', (event) => {
|
||||
updateChannelHint(event.target.value);
|
||||
// TODO: send new channel to "backend"
|
||||
// ws.send(...);
|
||||
});
|
||||
|
||||
function updateChannelOptions(channels) {
|
||||
let select = document.getElementById('channel-select');
|
||||
|
||||
// clear existing channels
|
||||
select.innerHTML = '';
|
||||
|
||||
for (let [ name, channel ] of Object.entries(channels))
|
||||
{
|
||||
let option = document.createElement('option');
|
||||
option.text = name;
|
||||
option.value = channel;
|
||||
|
||||
select.appendChild(option)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// functions for handling the message list
|
||||
function scrollMessagesToBottom() {
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,20 @@
|
||||
using Lumina.Excel;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
|
||||
namespace ChatTwo;
|
||||
|
||||
public static class Sheets
|
||||
{
|
||||
public static readonly ExcelSheet<Item> ItemSheet;
|
||||
public static readonly ExcelSheet<World> WorldSheet;
|
||||
public static readonly ExcelSheet<LogFilter> LogFilterSheet;
|
||||
public static readonly ExcelSheet<TextCommand> TextCommandSheet;
|
||||
|
||||
static Sheets()
|
||||
{
|
||||
ItemSheet = Plugin.DataManager.GetExcelSheet<Item>()!;
|
||||
WorldSheet = Plugin.DataManager.GetExcelSheet<World>()!;
|
||||
LogFilterSheet = Plugin.DataManager.GetExcelSheet<LogFilter>()!;
|
||||
TextCommandSheet = Plugin.DataManager.GetExcelSheet<TextCommand>()!;
|
||||
}
|
||||
}
|
||||
+46
-45
@@ -20,7 +20,6 @@ using Dalamud.Interface.Windowing;
|
||||
using Dalamud.Memory;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using ImGuiNET;
|
||||
using Lumina.Excel;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
|
||||
namespace ChatTwo.Ui;
|
||||
@@ -91,10 +90,6 @@ public sealed class ChatLogWindow : Window
|
||||
private long FrameTime; // set every frame
|
||||
internal long LastActivityTime = Environment.TickCount64;
|
||||
|
||||
private readonly ExcelSheet<World> WorldSheet;
|
||||
private readonly ExcelSheet<LogFilter> LogFilterSheet;
|
||||
private readonly ExcelSheet<TextCommand> TextCommandSheet;
|
||||
|
||||
internal ChatLogWindow(Plugin plugin) : base($"{Plugin.PluginName}###chat2")
|
||||
{
|
||||
Plugin = plugin;
|
||||
@@ -118,10 +113,6 @@ public sealed class ChatLogWindow : Window
|
||||
Plugin.Commands.Register("/clearlog2", "Clear the Chat 2 chat log").Execute += ClearLog;
|
||||
Plugin.Commands.Register("/chat2").Execute += ToggleChat;
|
||||
|
||||
WorldSheet = Plugin.DataManager.GetExcelSheet<World>()!;
|
||||
LogFilterSheet = Plugin.DataManager.GetExcelSheet<LogFilter>()!;
|
||||
TextCommandSheet = Plugin.DataManager.GetExcelSheet<TextCommand>()!;
|
||||
|
||||
Plugin.ClientState.Login += Login;
|
||||
Plugin.ClientState.Logout += Logout;
|
||||
|
||||
@@ -302,7 +293,7 @@ public sealed class ChatLogWindow : Window
|
||||
AddTextCommandChannel(command, type);
|
||||
}
|
||||
|
||||
var echo = Plugin.DataManager.GetExcelSheet<TextCommand>()?.GetRow(116);
|
||||
var echo = Sheets.TextCommandSheet.GetRow(116);
|
||||
if (echo != null)
|
||||
AddTextCommandChannel(echo, ChatType.Echo);
|
||||
}
|
||||
@@ -573,41 +564,12 @@ public sealed class ChatLogWindow : Window
|
||||
{
|
||||
if (popup)
|
||||
{
|
||||
foreach (var channel in Enum.GetValues<InputChannel>())
|
||||
{
|
||||
var name = LogFilterSheet.FirstOrDefault(row => row.LogKind == (byte) channel.ToChatType())?.Name?.RawString ?? channel.ToChatType().Name();
|
||||
if (channel.IsLinkshell())
|
||||
{
|
||||
var lsName = Plugin.Functions.Chat.GetLinkshellName(channel.LinkshellIndex());
|
||||
if (string.IsNullOrWhiteSpace(lsName))
|
||||
continue;
|
||||
|
||||
name += $": {lsName}";
|
||||
}
|
||||
|
||||
if (channel.IsCrossLinkshell())
|
||||
{
|
||||
var lsName = Plugin.Functions.Chat.GetCrossLinkshellName(channel.LinkshellIndex());
|
||||
if (string.IsNullOrWhiteSpace(lsName))
|
||||
continue;
|
||||
|
||||
name += $": {lsName}";
|
||||
}
|
||||
|
||||
// Check if the linkshell with this index is registered in
|
||||
// the ExtraChat plugin by seeing if the command is
|
||||
// registered. The command gets registered only if a
|
||||
// linkshell is assigned (and even gets unassigned if the
|
||||
// index changes!).
|
||||
if (channel.IsExtraChatLinkshell())
|
||||
if (!Plugin.CommandManager.Commands.ContainsKey(channel.Prefix()))
|
||||
continue;
|
||||
|
||||
var channels = GetAvailableChannels();
|
||||
foreach (var (name, channel) in channels)
|
||||
if (ImGui.Selectable(name))
|
||||
SetChannel(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
var afterIcon = ImGui.GetCursorPos();
|
||||
@@ -753,6 +715,45 @@ public sealed class ChatLogWindow : Window
|
||||
GameFunctions.GameFunctions.ClickNoviceNetworkButton();
|
||||
}
|
||||
|
||||
internal Dictionary<string, InputChannel> GetAvailableChannels()
|
||||
{
|
||||
var channels = new Dictionary<string, InputChannel>();
|
||||
foreach (var channel in Enum.GetValues<InputChannel>())
|
||||
{
|
||||
var name = Sheets.LogFilterSheet.FirstOrDefault(row => row.LogKind == (byte) channel.ToChatType())?.Name?.RawString ?? channel.ToChatType().Name();
|
||||
if (channel.IsLinkshell())
|
||||
{
|
||||
var lsName = Plugin.Functions.Chat.GetLinkshellName(channel.LinkshellIndex());
|
||||
if (string.IsNullOrWhiteSpace(lsName))
|
||||
continue;
|
||||
|
||||
name += $": {lsName}";
|
||||
}
|
||||
|
||||
if (channel.IsCrossLinkshell())
|
||||
{
|
||||
var lsName = Plugin.Functions.Chat.GetCrossLinkshellName(channel.LinkshellIndex());
|
||||
if (string.IsNullOrWhiteSpace(lsName))
|
||||
continue;
|
||||
|
||||
name += $": {lsName}";
|
||||
}
|
||||
|
||||
// Check if the linkshell with this index is registered in
|
||||
// the ExtraChat plugin by seeing if the command is
|
||||
// registered. The command gets registered only if a
|
||||
// linkshell is assigned (and even gets unassigned if the
|
||||
// index changes!).
|
||||
if (channel.IsExtraChatLinkshell())
|
||||
if (!Plugin.CommandManager.Commands.ContainsKey(channel.Prefix()))
|
||||
continue;
|
||||
|
||||
channels.Add(name, channel);
|
||||
}
|
||||
|
||||
return channels;
|
||||
}
|
||||
|
||||
private void DrawChannelName(Tab? activeTab)
|
||||
{
|
||||
var currentChannel = ReadChannelName(activeTab);
|
||||
@@ -776,7 +777,7 @@ public sealed class ChatLogWindow : Window
|
||||
// Note: don't use HidePlayerInString here because
|
||||
// abbreviation settings do not affect this.
|
||||
playerName = HashPlayer(TellTarget.Name, TellTarget.World);
|
||||
var world = WorldSheet.GetRow(TellTarget.World)?.Name?.RawString ?? "???";
|
||||
var world = Sheets.WorldSheet.GetRow(TellTarget.World)?.Name?.RawString ?? "???";
|
||||
|
||||
channelNameChunks =
|
||||
[
|
||||
@@ -829,7 +830,7 @@ public sealed class ChatLogWindow : Window
|
||||
// Note: don't use HidePlayerInString here because
|
||||
// abbreviation settings do not affect this.
|
||||
var playerName = HashPlayer(tellPlayerName, tellWorldId);
|
||||
var world = WorldSheet.GetRow(tellWorldId)?.Name?.RawString ?? "???";
|
||||
var world = Sheets.WorldSheet.GetRow(tellWorldId)?.Name?.RawString ?? "???";
|
||||
|
||||
channelNameChunks =
|
||||
[
|
||||
@@ -909,7 +910,7 @@ public sealed class ChatLogWindow : Window
|
||||
{
|
||||
var target = TellTarget;
|
||||
var reason = target.Reason;
|
||||
var world = WorldSheet.GetRow(target.World);
|
||||
var world = Sheets.WorldSheet.GetRow(target.World);
|
||||
if (world is { IsPublic: true })
|
||||
{
|
||||
if (reason == TellReason.Reply && GameFunctions.GameFunctions.GetFriends().Any(friend => friend.ContentId == target.ContentId))
|
||||
@@ -1571,7 +1572,7 @@ public sealed class ChatLogWindow : Window
|
||||
if (text.StartsWith('/'))
|
||||
{
|
||||
var command = text.Split(' ')[0];
|
||||
var cmd = TextCommandSheet.FirstOrDefault(cmd =>
|
||||
var cmd = Sheets.TextCommandSheet.FirstOrDefault(cmd =>
|
||||
cmd.Command.RawString == command || cmd.Alias.RawString == command ||
|
||||
cmd.ShortCommand.RawString == command || cmd.ShortAlias.RawString == command);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user