RunicVTT Open Source Virtual Tabletop for TTRPG using P2P
Loading...
Searching...
No Matches
ChatManager.h
Go to the documentation of this file.
1#pragma once
2
3#include <cstdint>
4#include <string>
5#include <vector>
6#include <deque>
7#include <set>
8#include <unordered_map>
9#include <filesystem>
10#include <optional>
11#include <memory>
12#include <array>
13#include <cmath>
14#include "imgui.h"
15#include "IdentityManager.h"
16#include "Message.h"
17#include "UiTypingGuard.h"
18
19class NetworkManager; // fwd
20class PeerLink; // fwd
21
22// ---- Chat model ----
24{
25 enum class Kind
26 {
27 TEXT,
28 IMAGE,
29 LINK
30 };
32 std::string senderUniqueId; // <— NEW: stable sender id
33 std::string username; // display label
34 std::string content; // text / url
35 double ts = 0.0;
36};
37
39{
40 uint64_t id = 0; // 1 reserved for General
41 std::string name; // unique per table
42 std::set<std::string> participants; // peer ids (can duplicate across groups)
43 std::string ownerUniqueId; // who can delete/rename
44 std::deque<ChatMessageModel> messages; // local history
45 uint32_t unread = 0; // UI badge
46};
47
48// Pure UI+logic+wire sync manager (groups-based)
49class ChatManager : public std::enable_shared_from_this<ChatManager>
50{
51public:
52 explicit ChatManager(std::weak_ptr<NetworkManager> nm, std::shared_ptr<IdentityManager> identity_manager);
53
54 // Attach/replace network
55 void setNetwork(std::weak_ptr<NetworkManager> nm);
56
57 // Change active GameTable context
58 void setActiveGameTable(uint64_t tableId, const std::string& gameTableName);
59 bool hasCurrent() const
60 {
61 return currentTableId_ != 0;
62 }
63
64 // Persistence (local file per table)
65 bool saveCurrent();
66 bool loadCurrent();
67
68 // Incoming (GameTableManager -> processReceivedMessages)
69 void applyReady(const msg::ReadyMessage& m); // handles group create/update/delete & message
70 void pushMessageLocal(uint64_t groupId,
71 const std::string& fromPeer,
72 const std::string& username,
73 const std::string& text,
74 double ts,
75 bool incoming);
76
77 // UI render
78 void render();
79
80 // Snapshot helpers (unchanged file layout can be separate if you already use them)
81 void writeGroupsToSnapshotGT(std::vector<unsigned char>& buf) const;
82 void readGroupsFromSnapshotGT(const std::vector<unsigned char>& buf, size_t& off);
83
85 //void replaceUsernameEverywhere(const std::string& oldUsername,
86 // const std::string& newUsername);
87
88 // Exposed current table data
89 static constexpr uint64_t generalGroupId_ = 1;
90 uint64_t currentTableId_ = 0;
91 std::string currentTableName_;
92
93 // Utility
94 ChatGroupModel* getGroup(uint64_t id);
95
96 void replaceUsernameForUnique(const std::string& uniqueId, const std::string& newUsername);
97 void tryHandleSlashCommand(uint64_t threadId, const std::string& input);
98 std::set<std::string> resolvePeerIdsForParticipants(const std::set<std::string>& participantUids) const;
99 bool isMeParticipantOf(const ChatGroupModel& g) const;
100
101private:
102 std::shared_ptr<IdentityManager> identity_manager;
103
104 // === runtime store ===
105 std::unordered_map<uint64_t, ChatGroupModel> groups_;
107
108 // UI state
109 std::array<char, 512> input_{};
110 bool focusInput_ = false;
111 bool followScroll_ = true;
112 bool jumpToBottom_ = false;
113 bool chatWindowFocused_ = false;
114
115 // Popups
116 bool openCreatePopup_ = false;
117 bool openDeletePopup_ = false;
118 bool openDicePopup_ = false;
119
120 // Create popup state
121 std::array<char, 128> newGroupName_{};
122 std::set<std::string> newGroupSel_; // peer ids selected
123 float leftWidth_ = 170.0f;
124 // --- Edit popup state ---
125 bool openEditPopup_ = false;
126 uint64_t editGroupId_ = 0;
127 std::array<char, 128> editGroupName_{};
128 std::set<std::string> editGroupSel_; // peer ids selected for edit
129
130 // Dice popup state
131 int diceN_ = 1;
132 int diceSides_ = 20;
133 int diceMod_ = 0;
134 bool diceModPerDie_ = false;
135
136 // wiring
137 std::weak_ptr<NetworkManager> network_;
138
139 // === helpers ===
140 static ChatMessageModel::Kind classifyMessage(const std::string& s);
141 static double nowSec();
142
143 mutable std::unordered_map<std::string, ImU32> nameColorCache_;
144 static ImVec4 HSVtoRGB(float h, float s, float v);
145 ImU32 getUsernameColor(const std::string& name) const;
146 void renderColoredUsername(const std::string& name) const;
147 void renderPlainMessage(const std::string& text) const;
148
149 std::filesystem::path chatFilePathFor(uint64_t tableId, const std::string& name) const;
150 void ensureGeneral();
151 void markGroupRead(uint64_t groupId);
152
153 // ---- emit frames ----
154 void emitGroupCreate(const ChatGroupModel& g);
155 void emitGroupUpdate(const ChatGroupModel& g);
156 void emitGroupDelete(uint64_t groupId);
157 void emitChatMessageFrame(uint64_t groupId, const std::string& username, const std::string& text, uint64_t ts);
158 void emitGroupLeave(uint64_t groupId);
159 // ---- UI ----
160
162 void renderLeftPanel(float width);
163 void renderRightPanel(float leftPanelWidth);
166 void renderDicePopup();
167
168 // ---- storage ----
169 bool saveLog(std::vector<uint8_t>& buf) const;
170 bool loadLog(const std::vector<uint8_t>& buf);
171
172 // ---- id utils ----
173 uint64_t makeGroupIdFromName(const std::string& name) const; // name is unique; use its hash
174
175 //STATIC METHODS
176 // HSV->RGB helper for ImGui colors
177};
void renderRightPanel(float leftPanelWidth)
uint64_t makeGroupIdFromName(const std::string &name) const
void renderPlainMessage(const std::string &text) const
void pushMessageLocal(uint64_t groupId, const std::string &fromPeer, const std::string &username, const std::string &text, double ts, bool incoming)
bool saveCurrent()
std::array< char, 512 > input_
ChatGroupModel * getGroup(uint64_t id)
void renderEditGroupPopup()
bool loadCurrent()
static double nowSec()
std::set< std::string > resolvePeerIdsForParticipants(const std::set< std::string > &participantUids) const
void renderCreateGroupPopup()
float leftWidth_
std::array< char, 128 > editGroupName_
std::set< std::string > newGroupSel_
static ImVec4 HSVtoRGB(float h, float s, float v)
void emitGroupLeave(uint64_t groupId)
void tryHandleSlashCommand(uint64_t threadId, const std::string &input)
void renderDicePopup()
bool openEditPopup_
void emitChatMessageFrame(uint64_t groupId, const std::string &username, const std::string &text, uint64_t ts)
void renderColoredUsername(const std::string &name) const
ImU32 getUsernameColor(const std::string &name) const
void setActiveGameTable(uint64_t tableId, const std::string &gameTableName)
bool chatWindowFocused_
std::set< std::string > editGroupSel_
void emitGroupUpdate(const ChatGroupModel &g)
void replaceUsernameForUnique(const std::string &uniqueId, const std::string &newUsername)
std::filesystem::path chatFilePathFor(uint64_t tableId, const std::string &name) const
bool openCreatePopup_
std::unordered_map< std::string, ImU32 > nameColorCache_
std::string currentTableName_
Definition ChatManager.h:91
bool isMeParticipantOf(const ChatGroupModel &g) const
bool followScroll_
uint64_t activeGroupId_
void renderDeleteGroupPopup()
bool jumpToBottom_
bool saveLog(std::vector< uint8_t > &buf) const
void writeGroupsToSnapshotGT(std::vector< unsigned char > &buf) const
static ChatMessageModel::Kind classifyMessage(const std::string &s)
uint64_t editGroupId_
std::unordered_map< uint64_t, ChatGroupModel > groups_
uint64_t currentTableId_
Definition ChatManager.h:90
static constexpr uint64_t generalGroupId_
Definition ChatManager.h:89
void ensureGeneral()
bool openDicePopup_
void emitGroupCreate(const ChatGroupModel &g)
bool loadLog(const std::vector< uint8_t > &buf)
std::array< char, 128 > newGroupName_
void setNetwork(std::weak_ptr< NetworkManager > nm)
ChatManager(std::weak_ptr< NetworkManager > nm, std::shared_ptr< IdentityManager > identity_manager)
std::shared_ptr< IdentityManager > identity_manager
void readGroupsFromSnapshotGT(const std::vector< unsigned char > &buf, size_t &off)
bool hasCurrent() const
Definition ChatManager.h:59
void markGroupRead(uint64_t groupId)
bool diceModPerDie_
bool openDeletePopup_
void renderLeftPanel(float width)
std::weak_ptr< NetworkManager > network_
void applyReady(const msg::ReadyMessage &m)
void emitGroupDelete(uint64_t groupId)
std::string ownerUniqueId
Definition ChatManager.h:43
std::deque< ChatMessageModel > messages
Definition ChatManager.h:44
uint32_t unread
Definition ChatManager.h:45
std::string name
Definition ChatManager.h:41
std::set< std::string > participants
Definition ChatManager.h:42
std::string username
Definition ChatManager.h:33
std::string content
Definition ChatManager.h:34
std::string senderUniqueId
Definition ChatManager.h:32