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