kindCategories.js raw
1 /**
2 * Kind categories for curating mode.
3 * These define predefined groups of event kinds that can be enabled/disabled together.
4 * The categories match the server-side definitions in pkg/database/curating-acl.go.
5 */
6
7 export const curationKindCategories = [
8 {
9 id: "social",
10 name: "Social/Notes",
11 description: "User profiles, notes, follows, reposts, reactions, and relay lists",
12 kinds: [0, 1, 3, 6, 7, 10002],
13 },
14 {
15 id: "dm",
16 name: "Direct Messages",
17 description: "Encrypted direct messages (legacy and NIP-17 gift-wrapped)",
18 kinds: [4, 14, 1059],
19 },
20 {
21 id: "longform",
22 name: "Long-form Content",
23 description: "Blog posts and article drafts",
24 kinds: [30023, 30024],
25 },
26 {
27 id: "media",
28 name: "Media",
29 description: "File metadata and media attachments",
30 kinds: [1063, 20, 21, 22],
31 },
32 {
33 id: "marketplace_nip15",
34 name: "Marketplace (NIP-15)",
35 description: "Legacy NIP-15 stalls and products",
36 kinds: [30017, 30018, 30019, 30020],
37 },
38 {
39 id: "marketplace_nip99",
40 name: "Marketplace (NIP-99/Gamma)",
41 description: "NIP-99 classified listings, collections, shipping, reviews (Plebeian Market)",
42 kinds: [30402, 30403, 30405, 30406, 31555],
43 },
44 {
45 id: "order_communication",
46 name: "Order Communication",
47 description: "Gamma Markets order messages and payment receipts (kinds 16, 17)",
48 kinds: [16, 17],
49 },
50 {
51 id: "groups_nip29",
52 name: "Group Messaging (NIP-29)",
53 description: "Simple relay-based group chat messages",
54 kinds: [9, 10, 11, 12],
55 },
56 {
57 id: "groups_nip72",
58 name: "Communities (NIP-72)",
59 description: "Community definitions and threaded discussions",
60 kinds: [34550, 1111, 4550],
61 },
62 {
63 id: "lists",
64 name: "Lists/Bookmarks",
65 description: "Mute lists, pin lists, and parameterized list events",
66 kinds: [10000, 10001, 30000, 30001],
67 },
68 ];
69
70 /**
71 * Get all kinds from selected categories.
72 * @param {string[]} categoryIds - Array of category IDs
73 * @returns {number[]} - Array of unique kind numbers
74 */
75 export function getKindsFromCategories(categoryIds) {
76 const kinds = new Set();
77 for (const id of categoryIds) {
78 const category = curationKindCategories.find((c) => c.id === id);
79 if (category) {
80 category.kinds.forEach((k) => kinds.add(k));
81 }
82 }
83 return Array.from(kinds).sort((a, b) => a - b);
84 }
85
86 /**
87 * Get category IDs that contain a given kind.
88 * @param {number} kind - The kind number to look up
89 * @returns {string[]} - Array of category IDs containing this kind
90 */
91 export function getCategoriesForKind(kind) {
92 return curationKindCategories
93 .filter((c) => c.kinds.includes(kind))
94 .map((c) => c.id);
95 }
96
97 /**
98 * Parse a custom kinds string (e.g., "100, 200-300, 500") into an array of kinds.
99 * @param {string} customKinds - Comma-separated list of kinds and ranges
100 * @returns {number[]} - Array of individual kind numbers
101 */
102 export function parseCustomKinds(customKinds) {
103 if (!customKinds || !customKinds.trim()) return [];
104
105 const kinds = new Set();
106 const parts = customKinds.split(",").map((p) => p.trim());
107
108 for (const part of parts) {
109 if (!part) continue;
110
111 // Check if it's a range (e.g., "100-200")
112 if (part.includes("-")) {
113 const [start, end] = part.split("-").map((n) => parseInt(n.trim(), 10));
114 if (!isNaN(start) && !isNaN(end) && start <= end) {
115 // Don't expand ranges > 1000 to avoid memory issues
116 if (end - start <= 1000) {
117 for (let i = start; i <= end; i++) {
118 kinds.add(i);
119 }
120 }
121 }
122 } else {
123 const num = parseInt(part, 10);
124 if (!isNaN(num)) {
125 kinds.add(num);
126 }
127 }
128 }
129
130 return Array.from(kinds).sort((a, b) => a - b);
131 }
132
133 /**
134 * Format a list of kinds into a compact string with ranges.
135 * @param {number[]} kinds - Array of kind numbers
136 * @returns {string} - Formatted string like "1, 3, 5-10, 15"
137 */
138 export function formatKindsCompact(kinds) {
139 if (!kinds || kinds.length === 0) return "";
140
141 const sorted = [...kinds].sort((a, b) => a - b);
142 const ranges = [];
143 let rangeStart = sorted[0];
144 let rangeEnd = sorted[0];
145
146 for (let i = 1; i < sorted.length; i++) {
147 if (sorted[i] === rangeEnd + 1) {
148 rangeEnd = sorted[i];
149 } else {
150 if (rangeEnd > rangeStart + 1) {
151 ranges.push(`${rangeStart}-${rangeEnd}`);
152 } else if (rangeEnd === rangeStart + 1) {
153 ranges.push(`${rangeStart}, ${rangeEnd}`);
154 } else {
155 ranges.push(`${rangeStart}`);
156 }
157 rangeStart = sorted[i];
158 rangeEnd = sorted[i];
159 }
160 }
161
162 // Push the last range
163 if (rangeEnd > rangeStart + 1) {
164 ranges.push(`${rangeStart}-${rangeEnd}`);
165 } else if (rangeEnd === rangeStart + 1) {
166 ranges.push(`${rangeStart}, ${rangeEnd}`);
167 } else {
168 ranges.push(`${rangeStart}`);
169 }
170
171 return ranges.join(", ");
172 }
173