kink-discovery / frontend /board-utils.js
Perplexed7675's picture
Sync from kink_cli (Docker Space)
3bd91cd verified
Raw
History Blame Contribute Delete
3.98 kB
const EMPTY_BOARD = { to_me: [], by_me: [], together: [], hidden: [], no_go: [] };
export function sortedBoardItems(items) {
return [...items].sort(
(left, right) => (right.popularity || 0) - (left.popularity || 0) || left.name.localeCompare(right.name),
);
}
export function optimisticBoard(previousBoard, kinkId, interestState, directions, qc, auth) {
const kid = String(kinkId);
const nextBoard = Object.fromEntries(
Object.entries(previousBoard || EMPTY_BOARD).map(([key, items]) => [
key,
(items || []).filter((item) => String(item.id) !== kid),
]),
);
const sourceItem =
findBoardItemInData(previousBoard, kid) || (qc ? findBoardItemFromQuery(qc, auth, kid) : null);
/** If play-board has not loaded yet (or kink not in recs cache), still place a stub so we never strip a row and return empty buckets. */
const itemBase = sourceItem || { id: kid, name: kid, summary: "", popularity: 0 };
const item = { ...itemBase, interest_state: interestState, directions };
if (interestState === "hard_no") {
nextBoard.no_go.push(item);
} else if (interestState === "not_interested") {
nextBoard.hidden.push(item);
} else {
const targetDirections = directions.length ? directions : ["together"];
for (const direction of targetDirections) {
if (nextBoard[direction]) nextBoard[direction].push({ ...item });
}
}
for (const key of Object.keys(nextBoard)) nextBoard[key] = sortedBoardItems(nextBoard[key]);
return nextBoard;
}
export function findBoardItemInData(board, kinkId) {
const kid = String(kinkId);
for (const items of Object.values(board || EMPTY_BOARD)) {
const match = (items || []).find((item) => String(item.id) === kid);
if (match) return match;
}
return null;
}
function normalizeKinkItem(raw) {
if (!raw?.id || !raw?.name) return null;
return {
id: raw.id,
name: raw.name,
summary: raw.summary || raw.definition || raw.detail_summary || "",
popularity: raw.popularity || 0,
};
}
function findInItemsPayload(payload, kinkId) {
const kid = String(kinkId);
const match = payload?.items?.find((item) => String(item?.kink?.id) === kid);
return normalizeKinkItem(match?.kink || null);
}
function findBoardItemFromQuery(qc, auth, kinkId) {
const detail = qc.getQueryData(["kink", kinkId]);
const fromDetail = normalizeKinkItem(detail);
if (fromDetail) return fromDetail;
const scopedKeys = [
["recs", auth?.userId, ""],
["prompts", auth?.userId, ""],
];
for (const key of scopedKeys) {
const fromScoped = findInItemsPayload(qc.getQueryData(key), kinkId);
if (fromScoped) return fromScoped;
}
const queryCache = qc.getQueryCache();
const itemQueries = queryCache.findAll({ predicate: (query) => {
const key = query.queryKey || [];
return key[0] === "recs" || key[0] === "prompts" || key[0] === "search";
}});
for (const query of itemQueries) {
const fromItems = findInItemsPayload(query.state?.data, kinkId);
if (fromItems) return fromItems;
}
const overlapQueries = queryCache.findAll({ predicate: (query) => (query.queryKey || [])[0] === "group-overlap" });
for (const query of overlapQueries) {
const data = query.state?.data || {};
const direct = (data.direct_matches || []).find((item) => item?.id === kinkId);
if (direct) return normalizeKinkItem(direct);
const similar = (data.similar_matches || []).find((item) => item?.kink?.id === kinkId)?.kink;
const fromSimilar = normalizeKinkItem(similar);
if (fromSimilar) return fromSimilar;
}
return null;
}
export function removeKinkFromItemsPayload(payload, kinkId) {
if (!payload?.items) return payload;
const kid = String(kinkId);
return {
...payload,
items: payload.items.filter((item) => String(item?.kink?.id) !== kid),
};
}
export function playCommitKey(kinkId, interestState, directions = []) {
return `${kinkId}::${interestState}::${[...(directions || [])].sort().join("|")}`;
}