- Implement unread state sync for SSE

This commit is contained in:
Infi
2025-10-01 21:30:41 +02:00
parent ad77299e9e
commit c54efe5420
6 changed files with 64 additions and 14 deletions
+2 -1
View File
@@ -251,6 +251,7 @@ internal class Tab
public bool HideWhenInactive; public bool HideWhenInactive;
[NonSerialized] public uint Unread; [NonSerialized] public uint Unread;
[NonSerialized] public uint LastSendUnread;
[NonSerialized] public long LastActivity; [NonSerialized] public long LastActivity;
[NonSerialized] public MessageList Messages = new(); [NonSerialized] public MessageList Messages = new();
@@ -265,8 +266,8 @@ internal class Tab
Messages.AddPrune(message, MessageManager.MessageDisplayLimit); Messages.AddPrune(message, MessageManager.MessageDisplayLimit);
if (!unread) if (!unread)
return; return;
Unread += 1;
Unread += 1;
if (message.Matches(Plugin.Config.InactivityHideChannels!, Plugin.Config.InactivityHideExtraChatAll, Plugin.Config.InactivityHideExtraChatChannels)) if (message.Matches(Plugin.Config.InactivityHideChannels!, Plugin.Config.InactivityHideExtraChatAll, Plugin.Config.InactivityHideExtraChatChannels))
LastActivity = Environment.TickCount64; LastActivity = Environment.TickCount64;
} }
+24 -5
View File
@@ -45,6 +45,7 @@ interface ChannelList {
export interface ChatTab { export interface ChatTab {
name: string; name: string;
index: number; index: number;
unreadCount: number;
} }
// ref `DataStructure.ChatTabList` // ref `DataStructure.ChatTabList`
@@ -52,6 +53,12 @@ interface ChatTabList {
tabs: ChatTab[]; tabs: ChatTab[];
} }
// ref `DataStructure.ChatTabUnreadState`
interface ChatTabUnreadState {
index: number;
unreadCount: number;
}
export class ChatTwoWeb { export class ChatTwoWeb {
elements!: ChatElements; elements!: ChatElements;
maxTimestampWidth: number = 0; maxTimestampWidth: number = 0;
@@ -297,7 +304,7 @@ export class ChatTwoWeb {
this.connection = source('/sse') this.connection = source('/sse')
this.connection.select('close').subscribe((data: string) => { this.connection.select('close').subscribe((data: string) => {
console.log(`Data received: ${data}`) console.log(`close: ${data}`)
if (data) { if (data) {
console.log('Closing SSE connection.'); console.log('Closing SSE connection.');
this.connection.close(); this.connection.close();
@@ -306,7 +313,7 @@ export class ChatTwoWeb {
// new messages to be appended to the message list // new messages to be appended to the message list
this.connection.select('new-message').subscribe((data: string) => { this.connection.select('new-message').subscribe((data: string) => {
console.log(`Data received: ${data}`) console.log(`new-message: ${data}`)
if (data) { if (data) {
try { try {
let message: MessageResponse = JSON.parse(data); let message: MessageResponse = JSON.parse(data);
@@ -319,7 +326,7 @@ export class ChatTwoWeb {
// a bulk of new messages, with a clear of the message list beforehand // a bulk of new messages, with a clear of the message list beforehand
this.connection.select('bulk-messages').subscribe((data: string) => { this.connection.select('bulk-messages').subscribe((data: string) => {
console.log(`Data received: ${data}`) console.log(`bulk-messages: ${data}`)
if (data) { if (data) {
this.clearAllMessages(); this.clearAllMessages();
try { try {
@@ -334,7 +341,7 @@ export class ChatTwoWeb {
}); });
this.connection.select('channel-switched').subscribe((data: string) => { this.connection.select('channel-switched').subscribe((data: string) => {
console.log(`Data received: ${data}`) console.log(`channel-switched: ${data}`)
if (data) { if (data) {
try { try {
let channel: SwitchChannel = JSON.parse(data); let channel: SwitchChannel = JSON.parse(data);
@@ -347,7 +354,7 @@ export class ChatTwoWeb {
// list of all channels // list of all channels
this.connection.select('channel-list').subscribe((data: string) => { this.connection.select('channel-list').subscribe((data: string) => {
console.log(`Data received: ${data}`) console.log(`channel-list: ${data}`)
if (data) { if (data) {
try { try {
let channelList: ChannelList = JSON.parse(data); let channelList: ChannelList = JSON.parse(data);
@@ -386,5 +393,17 @@ export class ChatTwoWeb {
} }
} }
}); });
// the unread state of a specific tab has changed
this.connection.select('tab-unread-state').subscribe((data: string) => {
console.log(`tab-unread-state: ${data}`)
if (data) {
try {
const chatTabUnreadState: ChatTabUnreadState = JSON.parse(data);
} catch (error) {
console.error(error);
}
}
});
} }
} }
+11 -1
View File
@@ -7,10 +7,11 @@ namespace ChatTwo.Http.MessageProtocol;
/// <summary> /// <summary>
/// Contains a valid tab with its assigned index /// Contains a valid tab with its assigned index
/// </summary> /// </summary>
public struct ChatTab(string name, int index) public struct ChatTab(string name, int index, uint unreadCount)
{ {
[JsonProperty("name")] public string Name = name; [JsonProperty("name")] public string Name = name;
[JsonProperty("index")] public int Index = index; [JsonProperty("index")] public int Index = index;
[JsonProperty("unreadCount")] public uint UnreadCount = unreadCount;
} }
/// <summary> /// <summary>
@@ -21,6 +22,15 @@ public struct ChatTabList(ChatTab[] tabs)
[JsonProperty("tabs")] public ChatTab[] Tabs = tabs; [JsonProperty("tabs")] public ChatTab[] Tabs = tabs;
} }
/// <summary>
/// Contains a valid tab index and the current unread state as a number unread of messages
/// </summary>
public struct ChatTabUnreadState(int index, uint unreadCount)
{
[JsonProperty("index")] public int Index = index;
[JsonProperty("unreadCount")] public uint UnreadCount = unreadCount;
}
/// <summary> /// <summary>
/// Contains the current channel name /// Contains the current channel name
/// </summary> /// </summary>
@@ -9,6 +9,7 @@ public class CloseEvent() : BaseEvent("close");
// Tab related // Tab related
public class ChatTabListEvent(ChatTabList list) : BaseEvent("tab-list", JsonConvert.SerializeObject(list)); public class ChatTabListEvent(ChatTabList list) : BaseEvent("tab-list", JsonConvert.SerializeObject(list));
public class ChatTabSwitchedEvent(ChatTab chatTab) : BaseEvent("tab-switched", JsonConvert.SerializeObject(chatTab)); public class ChatTabSwitchedEvent(ChatTab chatTab) : BaseEvent("tab-switched", JsonConvert.SerializeObject(chatTab));
public class ChatTabUnreadStateEvent(ChatTabUnreadState unreadState) : BaseEvent("tab-unread-state", JsonConvert.SerializeObject(unreadState));
// Input channel related // Input channel related
public class ChannelListEvent(ChannelList channelList) : BaseEvent("channel-list", JsonConvert.SerializeObject(channelList)); public class ChannelListEvent(ChannelList channelList) : BaseEvent("channel-list", JsonConvert.SerializeObject(channelList));
+3 -2
View File
@@ -104,12 +104,13 @@ public class Processing
public ChatTab GetCurrentTab() public ChatTab GetCurrentTab()
{ {
return new ChatTab(HostContext.Core.Plugin.CurrentTab.Name, HostContext.Core.Plugin.LastTab); var currentTab = HostContext.Core.Plugin.CurrentTab;
return new ChatTab(currentTab.Name, HostContext.Core.Plugin.LastTab, currentTab.Unread);
} }
public ChatTabList GetAllTabs() public ChatTabList GetAllTabs()
{ {
var tabs = Plugin.Config.Tabs.Select((tab, idx) => new ChatTab(tab.Name, idx)).ToArray(); var tabs = Plugin.Config.Tabs.Select((tab, idx) => new ChatTab(tab.Name, idx, tab.Unread)).ToArray();
return new ChatTabList(tabs); return new ChatTabList(tabs);
} }
} }
+23 -5
View File
@@ -1,4 +1,6 @@
using ChatTwo.Http.MessageProtocol; using ChatTwo.Http.MessageProtocol;
using ChatTwo.Util;
using Dalamud.Plugin.Services;
namespace ChatTwo.Http; namespace ChatTwo.Http;
@@ -11,6 +13,27 @@ public class ServerCore : IAsyncDisposable
{ {
Plugin = plugin; Plugin = plugin;
HostContext = new HostContext(this); HostContext = new HostContext(this);
Plugin.Framework.Update += FrameworkUpdate;
}
public async ValueTask DisposeAsync()
{
Plugin.Framework.Update -= FrameworkUpdate;
await HostContext.DisposeAsync();
}
private void FrameworkUpdate(IFramework _)
{
foreach (var (tab, idx) in Plugin.Config.Tabs.WithIndex())
{
if (tab.Unread == tab.LastSendUnread)
continue;
tab.LastSendUnread = tab.Unread;
foreach (var eventServer in HostContext.EventConnections)
eventServer.OutboundQueue.Enqueue(new ChatTabUnreadStateEvent(new ChatTabUnreadState(idx, tab.Unread)));
}
} }
#region SSE Helper #region SSE Helper
@@ -165,9 +188,4 @@ public class ServerCore : IAsyncDisposable
{ {
return await HostContext.Stop(); return await HostContext.Stop();
} }
public async ValueTask DisposeAsync()
{
await HostContext.DisposeAsync();
}
} }