RunicVTT Open Source Virtual Tabletop for TTRPG using P2P
Loading...
Searching...
No Matches
NotesManager.h
Go to the documentation of this file.
1#pragma once
2#include <string>
3#include <string_view>
4#include <unordered_map>
5#include <vector>
6#include <optional>
7#include <filesystem>
8#include <chrono>
9#include <cstdint>
10#include <mutex>
11#include <memory>
12#include "ImGuiToaster.h"
13
14// Plain Note model (no ECS)
15struct Note
16{
17 // Identity & placement
18 std::string uuid; // stable identifier (globally unique)
19 std::optional<std::string> table_name; // optional GameTable name
20 std::filesystem::path file_path; // where it's saved (empty if never saved)
21
22 // Metadata
23 std::string title;
24 std::string author;
25 std::chrono::system_clock::time_point creation_ts{};
26 std::chrono::system_clock::time_point last_update_ts{};
27
28 // Content
29 std::string markdown_text;
30
31 // Runtime flags
32 bool saved_locally = false;
33 bool dirty = false;
34 bool shared = false;
35 std::optional<std::string> shared_from;
36 bool inbox = false;
37
38 // UI hint
39 bool open_editor = true;
40};
41
42// Where notes live on disk
44{
45 std::filesystem::path globalNotesDir; // e.g., <root>/Notes
46 std::filesystem::path gameTablesRootDir; // e.g., <root>/GameTables
47};
48
50{
51public:
52 NotesManager(NotesManagerConfig cfg, std::shared_ptr<ImGuiToaster> toaster);
53
54 // Loaders
55 void loadAllFromDisk();
56 void loadFromGlobal();
57 void loadFromTable(const std::string& tableName);
58
59 // CRUD
60 std::string createNote(std::string title,
61 std::string author,
62 std::optional<std::string> tableName = std::nullopt);
63 bool saveNote(const std::string& uuid);
64 bool saveNoteAs(const std::string& uuid, const std::filesystem::path& absolutePath);
65 bool deleteNote(const std::string& uuid, bool deleteFromDisk);
66
67 // Access (shared_ptr only)
68 std::shared_ptr<Note> getNote(const std::string& uuid);
69 std::shared_ptr<const Note> getNote(const std::string& uuid) const;
70
71 std::vector<std::shared_ptr<Note>> listAll();
72 std::vector<std::shared_ptr<Note>> listMyNotes(); // !inbox
73 std::vector<std::shared_ptr<Note>> listInbox(); // inbox only
74
75 // Mutations
76 void setTitle(const std::string& uuid, std::string title);
77 void setContent(const std::string& uuid, std::string md);
78 void setAuthor(const std::string& uuid, std::string author);
79
80 // Shared Inbox (future network will call these)
81 std::string upsertSharedIncoming(std::string uuid,
82 std::string title,
83 std::string author,
84 std::string markdown,
85 std::optional<std::string> fromPeerName);
86
87 bool saveInboxToLocal(const std::string& uuid,
88 std::optional<std::string> tableName = std::nullopt);
89
90 // Utilities
91 static std::string generateUUID();
92 static std::string slugify(const std::string& title);
93 static int64_t toEpochMillis(std::chrono::system_clock::time_point tp);
94 static std::chrono::system_clock::time_point fromEpochMillis(int64_t ms);
95
96 static bool parseMarkdownFile(const std::filesystem::path& path, Note& outNote);
97 static bool writeMarkdownFile(const std::filesystem::path& path, const Note& note);
98
99 std::filesystem::path defaultSavePath(const Note& note) const;
100
101 // lookups
102 std::shared_ptr<Note> getByUuid(const std::string& uuid) const;
103
104 // resolve "ref" (uuid, title, short-id) -> UUID string (or empty if not found)
105 std::string resolveRef(const std::string& ref) const;
106
107 // (Called when notes are loaded/created/renamed)
108 void indexNote(const std::shared_ptr<Note>& n);
109 void removeFromIndex(const std::string& uuid);
110
111private:
112 // canonical store
113 std::unordered_map<std::string, std::shared_ptr<Note>> notesByUuid_;
114
115 // title → uuid (case-insensitive; exact match only)
116 std::unordered_map<std::string, std::string> titleToUuid_;
117
118 // helpers
119 static bool looksLikeUuid_(const std::string& s);
120 static bool looksLikeShortHex_(const std::string& s);
121 static std::string toLower_(const std::string& s);
122
124 std::shared_ptr<ImGuiToaster> toaster_;
125
126 mutable std::mutex mtx_;
127 std::unordered_map<std::string, std::shared_ptr<Note>> notesById_;
128
129 void scanFolderForMd_(const std::filesystem::path& dir,
130 std::optional<std::string> tableName);
131
132 // Toaster wrappers
133 void toastInfo(const std::string& msg) const;
134 void toastGood(const std::string& msg) const;
135 void toastWarn(const std::string& msg) const;
136 void toastError(const std::string& msg) const;
137};
static bool parseMarkdownFile(const std::filesystem::path &path, Note &outNote)
bool saveInboxToLocal(const std::string &uuid, std::optional< std::string > tableName=std::nullopt)
static bool looksLikeShortHex_(const std::string &s)
static std::string toLower_(const std::string &s)
void toastGood(const std::string &msg) const
void setTitle(const std::string &uuid, std::string title)
std::string createNote(std::string title, std::string author, std::optional< std::string > tableName=std::nullopt)
NotesManagerConfig cfg_
static std::string slugify(const std::string &title)
std::vector< std::shared_ptr< Note > > listInbox()
std::unordered_map< std::string, std::string > titleToUuid_
NotesManager(NotesManagerConfig cfg, std::shared_ptr< ImGuiToaster > toaster)
std::string resolveRef(const std::string &ref) const
std::unordered_map< std::string, std::shared_ptr< Note > > notesByUuid_
std::shared_ptr< Note > getByUuid(const std::string &uuid) const
void removeFromIndex(const std::string &uuid)
void toastWarn(const std::string &msg) const
bool deleteNote(const std::string &uuid, bool deleteFromDisk)
void indexNote(const std::shared_ptr< Note > &n)
std::vector< std::shared_ptr< Note > > listMyNotes()
bool saveNote(const std::string &uuid)
void setContent(const std::string &uuid, std::string md)
bool saveNoteAs(const std::string &uuid, const std::filesystem::path &absolutePath)
void setAuthor(const std::string &uuid, std::string author)
void loadFromGlobal()
void toastError(const std::string &msg) const
static std::string generateUUID()
std::mutex mtx_
std::string upsertSharedIncoming(std::string uuid, std::string title, std::string author, std::string markdown, std::optional< std::string > fromPeerName)
void scanFolderForMd_(const std::filesystem::path &dir, std::optional< std::string > tableName)
static bool looksLikeUuid_(const std::string &s)
std::vector< std::shared_ptr< Note > > listAll()
void toastInfo(const std::string &msg) const
static std::chrono::system_clock::time_point fromEpochMillis(int64_t ms)
void loadAllFromDisk()
std::shared_ptr< ImGuiToaster > toaster_
std::shared_ptr< Note > getNote(const std::string &uuid)
static int64_t toEpochMillis(std::chrono::system_clock::time_point tp)
void loadFromTable(const std::string &tableName)
std::unordered_map< std::string, std::shared_ptr< Note > > notesById_
std::filesystem::path defaultSavePath(const Note &note) const
static bool writeMarkdownFile(const std::filesystem::path &path, const Note &note)
Definition Message.h:28
std::optional< std::string > shared_from
bool open_editor
std::optional< std::string > table_name
bool shared
std::string uuid
std::string title
bool saved_locally
std::string author
bool dirty
bool inbox
std::chrono::system_clock::time_point last_update_ts
std::chrono::system_clock::time_point creation_ts
std::string markdown_text
std::filesystem::path file_path
std::filesystem::path gameTablesRootDir
std::filesystem::path globalNotesDir