tangled
alpha
login
or
join now
maxine.puppykitty.racing
/
kinklist
0
fork
atom
this repo has no description
0
fork
atom
overview
issues
pulls
pipelines
Improve signal logic, fix bad typings
maxine.puppykitty.racing
3 weeks ago
89f9ca37
e0c2b0a8
+4
-102
1 changed file
expand all
collapse all
unified
split
public
main.tsx
+4
-102
public/main.tsx
···
25
26
export interface KinkCategory {
27
name: string;
28
-
description: string;
29
kinks: Kink[];
30
participants: string[];
31
}
32
33
-
/**
34
-
* @param {string} kinkStr
35
-
*/
36
function parseKinks(kinkStr: string) {
37
const kinkCode = kinkStr
38
.split("\n")
39
.map((e) => e.trim())
40
.filter((e) => e);
41
42
-
/** @type {KinkCategory[]} */
43
const kinkCategories: KinkCategory[] = [];
44
45
-
/** @type {Kink[]} */
46
const kinksById: Kink[] = [];
47
48
-
let curKinkCategory: Partial<KinkCategory> | undefined;
49
let curKinkId = 0;
50
for (const line of kinkCode) {
51
if (line.startsWith("#")) {
···
57
kinks: [],
58
participants: ["Unknown"],
59
};
60
-
// @ts-ignore
61
kinkCategories.push(curKinkCategory);
62
} else if (line.startsWith("(") && line.endsWith(")")) {
63
if (curKinkCategory === undefined) {
···
90
*/
91
const kinkSelections: Map<Kink, Map<string, Signal<Choice>>> = new Map();
92
93
-
/**
94
-
* Maps kink -> participant -> choice option -> button element
95
-
* Entries may be undefined!
96
-
*/
97
-
const kinkButtons: Map<Kink, Map<string, Map<string, (action: "select" | "deselect") => void>>> = new Map();
98
-
99
-
/**
100
-
* @param {Kink} kink
101
-
* @returns {KinkCategory | undefined}
102
-
*/
103
function findKinkCategory(kink: Kink): KinkCategory | undefined {
104
for (const category of kinkData.value.kinkCategories) {
105
const index = category.kinks.indexOf(kink);
···
115
typeDescription: string;
116
}
117
118
-
/**
119
-
* @param {LegendChoiceProps} props
120
-
* @returns
121
-
*/
122
function LegendChoice({ type, typeDescription }: LegendChoiceProps) {
123
return (
124
<div class="level-item is-justify-content-flex-start">
···
138
);
139
}
140
141
-
/**
142
-
* @param {string} str
143
-
* @param {string} char
144
-
* @returns {[string, string?]}
145
-
*/
146
function sliceOnce(str: string, char: string): [string, string?] {
147
const index = str.indexOf(char);
148
···
155
return [str.trim(), undefined];
156
}
157
158
-
/**
159
-
* @param {string} str
160
-
* @param {string} symbolStart
161
-
* @param {string?} symbolEnd
162
-
* @returns {string}
163
-
*/
164
function removeSymbols(str: string, symbolStart: string, symbolEnd: string | null = null): string {
165
if (str.startsWith(symbolStart)) {
166
str = str.slice(1);
···
175
176
const base64Alphabet = [..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"]; // base64 alphabet
177
178
-
/**
179
-
* @param {number} n
180
-
* @returns {string}
181
-
*/
182
const toBinary = (n: number): string => n.toString(2).padStart(8, "0"); // convert num to 8-bit binary string
183
184
/**
185
* https://stackoverflow.com/a/62362724
186
-
* @param {number[] | Uint8Array} arr
187
-
* @returns {string}
188
*/
189
function bytesArrToBase64(arr: number[] | Uint8Array): string {
190
// @ts-expect-error
···
209
}
210
/**
211
* https://stackoverflow.com/a/62364519
212
-
* @param {string} str
213
-
* @returns {Uint8Array}
214
*/
215
function base64ToBytesArr(str: string): Uint8Array {
216
if (Uint8Array.fromBase64) {
···
229
return new Uint8Array(result);
230
}
231
232
-
/**
233
-
* @param {Kink} kink
234
-
* @param {string} participant
235
-
* @returns {Signal<Choice>}
236
-
*/
237
function getSelectedKinkOrDefault(kink: Kink, participant: string): Signal<Choice> {
238
let theKink = kinkSelections.get(kink);
239
if (!theKink) kinkSelections.set(kink, theKink = new Map());
···
242
return theSignal;
243
}
244
245
-
/**
246
-
* @param {Kink} kink
247
-
* @param {string} participant
248
-
* @param {string} toChoiceId
249
-
*/
250
function setKinkSelection(kink: Kink, participant: string, toChoiceId: Choice, updateHash = true) {
251
getSelectedKinkOrDefault(kink, participant).value = toChoiceId;
252
253
-
for (const [thatChoiceId, thatButton] of getKinkButtonStates(kink, participant).entries()) {
254
-
if (thatChoiceId === toChoiceId) {
255
-
thatButton("select");
256
-
} else {
257
-
thatButton("deselect");
258
-
}
259
-
}
260
-
261
if (updateHash) {
262
window.location.hash = serializeChoices();
263
}
···
345
choiceDescription: string;
346
}
347
348
-
/**
349
-
* @param {KinkChoiceButtonProps} props
350
-
* @returns
351
-
*/
352
function KinkChoiceButton({ kink, participant, choiceId, choiceDescription }: KinkChoiceButtonProps) {
353
-
/**
354
-
* @param {boolean} state
355
-
* @param {'select' | 'deselect'} action
356
-
* @returns {boolean}
357
-
*/
358
-
function reducer(state: boolean, action: "select" | "deselect"): boolean {
359
-
if (action === "select") {
360
-
return true;
361
-
} else if (action === "deselect") {
362
-
return false;
363
-
}
364
-
return state;
365
-
}
366
-
367
-
const [selected, update] = useReducer(reducer, false);
368
-
369
-
let participants = kinkButtons.get(kink);
370
-
if (participants === undefined) {
371
-
kinkButtons.set(kink, (participants = new Map()));
372
-
}
373
-
374
-
let choices = participants.get(participant);
375
-
if (choices === undefined) {
376
-
participants.set(participant, (choices = new Map()));
377
-
}
378
-
379
-
choices.set(choiceId, update);
380
381
return (
382
<div class="column">
383
<button
384
-
class={`choice ${choiceId}${selected ? " selected" : ""}`}
385
title={choiceDescription}
386
onClick={() => {
387
setKinkSelection(kink, participant, choiceId);
···
389
/>
390
</div>
391
);
392
-
}
393
-
394
-
/**
395
-
* Gets the mappings from choice->button element for a kink+participant combo.
396
-
* @param {Kink} kink
397
-
* @param {string} participant
398
-
* @returns {Map<string, (action: "select" | "deselect") => void>}
399
-
*/
400
-
function getKinkButtonStates(kink: Kink, participant: string): Map<string, (action: "select" | "deselect") => void> {
401
-
// console.log(kinkButtons, kink, participant);
402
-
return kinkButtons.get(kink)!.get(participant)!;
403
}
404
405
function TheKink({ kinkCategory, kink }: { kinkCategory: KinkCategory; kink: Kink }) {
···
25
26
export interface KinkCategory {
27
name: string;
28
+
description: string | undefined;
29
kinks: Kink[];
30
participants: string[];
31
}
32
0
0
0
33
function parseKinks(kinkStr: string) {
34
const kinkCode = kinkStr
35
.split("\n")
36
.map((e) => e.trim())
37
.filter((e) => e);
38
0
39
const kinkCategories: KinkCategory[] = [];
40
0
41
const kinksById: Kink[] = [];
42
43
+
let curKinkCategory: KinkCategory | undefined;
44
let curKinkId = 0;
45
for (const line of kinkCode) {
46
if (line.startsWith("#")) {
···
52
kinks: [],
53
participants: ["Unknown"],
54
};
0
55
kinkCategories.push(curKinkCategory);
56
} else if (line.startsWith("(") && line.endsWith(")")) {
57
if (curKinkCategory === undefined) {
···
84
*/
85
const kinkSelections: Map<Kink, Map<string, Signal<Choice>>> = new Map();
86
0
0
0
0
0
0
0
0
0
0
87
function findKinkCategory(kink: Kink): KinkCategory | undefined {
88
for (const category of kinkData.value.kinkCategories) {
89
const index = category.kinks.indexOf(kink);
···
99
typeDescription: string;
100
}
101
0
0
0
0
102
function LegendChoice({ type, typeDescription }: LegendChoiceProps) {
103
return (
104
<div class="level-item is-justify-content-flex-start">
···
118
);
119
}
120
0
0
0
0
0
121
function sliceOnce(str: string, char: string): [string, string?] {
122
const index = str.indexOf(char);
123
···
130
return [str.trim(), undefined];
131
}
132
0
0
0
0
0
0
133
function removeSymbols(str: string, symbolStart: string, symbolEnd: string | null = null): string {
134
if (str.startsWith(symbolStart)) {
135
str = str.slice(1);
···
144
145
const base64Alphabet = [..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"]; // base64 alphabet
146
0
0
0
0
147
const toBinary = (n: number): string => n.toString(2).padStart(8, "0"); // convert num to 8-bit binary string
148
149
/**
150
* https://stackoverflow.com/a/62362724
0
0
151
*/
152
function bytesArrToBase64(arr: number[] | Uint8Array): string {
153
// @ts-expect-error
···
172
}
173
/**
174
* https://stackoverflow.com/a/62364519
0
0
175
*/
176
function base64ToBytesArr(str: string): Uint8Array {
177
if (Uint8Array.fromBase64) {
···
190
return new Uint8Array(result);
191
}
192
0
0
0
0
0
193
function getSelectedKinkOrDefault(kink: Kink, participant: string): Signal<Choice> {
194
let theKink = kinkSelections.get(kink);
195
if (!theKink) kinkSelections.set(kink, theKink = new Map());
···
198
return theSignal;
199
}
200
0
0
0
0
0
201
function setKinkSelection(kink: Kink, participant: string, toChoiceId: Choice, updateHash = true) {
202
getSelectedKinkOrDefault(kink, participant).value = toChoiceId;
203
0
0
0
0
0
0
0
0
204
if (updateHash) {
205
window.location.hash = serializeChoices();
206
}
···
288
choiceDescription: string;
289
}
290
0
0
0
0
291
function KinkChoiceButton({ kink, participant, choiceId, choiceDescription }: KinkChoiceButtonProps) {
292
+
const selected = computed(() => getSelectedKinkOrDefault(kink, participant).value === choiceId);
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
293
294
return (
295
<div class="column">
296
<button
297
+
class={`choice ${choiceId}${selected.value ? " selected" : ""}`}
298
title={choiceDescription}
299
onClick={() => {
300
setKinkSelection(kink, participant, choiceId);
···
302
/>
303
</div>
304
);
0
0
0
0
0
0
0
0
0
0
0
305
}
306
307
function TheKink({ kinkCategory, kink }: { kinkCategory: KinkCategory; kink: Kink }) {