v3: linter and formatter pass
This commit is contained in:
@@ -107,7 +107,9 @@ export async function createUrlDump(
|
|||||||
broadcastNewDump(dump);
|
broadcastNewDump(dump);
|
||||||
notifyUserFollowersNewDump(userId, dumpId, title);
|
notifyUserFollowersNewDump(userId, dumpId, title);
|
||||||
}
|
}
|
||||||
if (request.comment) notifyMentions(userId, request.comment, "dump", dumpId, title);
|
if (request.comment) {
|
||||||
|
notifyMentions(userId, request.comment, "dump", dumpId, title);
|
||||||
|
}
|
||||||
return dump;
|
return dump;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,7 +337,13 @@ export async function updateDump(
|
|||||||
if (updatedDump.isPrivate && !dump.isPrivate) broadcastDumpDeleted(dumpId);
|
if (updatedDump.isPrivate && !dump.isPrivate) broadcastDumpDeleted(dumpId);
|
||||||
else if (!updatedDump.isPrivate) broadcastDumpUpdated(updatedDump);
|
else if (!updatedDump.isPrivate) broadcastDumpUpdated(updatedDump);
|
||||||
if (updatedDump.comment) {
|
if (updatedDump.comment) {
|
||||||
notifyMentions(dump.userId, updatedDump.comment, "dump", dumpId, updatedDump.title);
|
notifyMentions(
|
||||||
|
dump.userId,
|
||||||
|
updatedDump.comment,
|
||||||
|
"dump",
|
||||||
|
dumpId,
|
||||||
|
updatedDump.title,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return updatedDump;
|
return updatedDump;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,9 +207,11 @@ export function notifyMentions(
|
|||||||
).get(mentionerUserId) as { username: string } | undefined;
|
).get(mentionerUserId) as { username: string } | undefined;
|
||||||
if (!mentionerRow) return;
|
if (!mentionerRow) return;
|
||||||
|
|
||||||
const usernames = [...new Set(
|
const usernames = [
|
||||||
[...body.matchAll(MENTION_RE)].map((m) => m[1].toLowerCase()),
|
...new Set(
|
||||||
)];
|
[...body.matchAll(MENTION_RE)].map((m) => m[1].toLowerCase()),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
for (const username of usernames) {
|
for (const username of usernames) {
|
||||||
const mentionedRow = db.prepare(
|
const mentionedRow = db.prepare(
|
||||||
|
|||||||
@@ -73,7 +73,9 @@ export function createPlaylist(
|
|||||||
isPublic: req.isPublic,
|
isPublic: req.isPublic,
|
||||||
createdAt,
|
createdAt,
|
||||||
};
|
};
|
||||||
if (req.description) notifyMentions(userId, req.description, "playlist", id, req.title);
|
if (req.description) {
|
||||||
|
notifyMentions(userId, req.description, "playlist", id, req.title);
|
||||||
|
}
|
||||||
broadcastPlaylistCreated(playlist);
|
broadcastPlaylistCreated(playlist);
|
||||||
return playlist;
|
return playlist;
|
||||||
}
|
}
|
||||||
@@ -157,7 +159,14 @@ export function updatePlaylist(
|
|||||||
const newSlug = makeSlug(newTitle, playlist.id);
|
const newSlug = makeSlug(newTitle, playlist.id);
|
||||||
db.prepare(
|
db.prepare(
|
||||||
`UPDATE playlists SET title = ?, slug = ?, description = ?, is_public = ?, updated_at = ? WHERE id = ?;`,
|
`UPDATE playlists SET title = ?, slug = ?, description = ?, is_public = ?, updated_at = ? WHERE id = ?;`,
|
||||||
).run(newTitle, newSlug, newDescription, newIsPublic ? 1 : 0, now.toISOString(), playlist.id);
|
).run(
|
||||||
|
newTitle,
|
||||||
|
newSlug,
|
||||||
|
newDescription,
|
||||||
|
newIsPublic ? 1 : 0,
|
||||||
|
now.toISOString(),
|
||||||
|
playlist.id,
|
||||||
|
);
|
||||||
|
|
||||||
const updated: Playlist = {
|
const updated: Playlist = {
|
||||||
...playlist,
|
...playlist,
|
||||||
@@ -167,7 +176,15 @@ export function updatePlaylist(
|
|||||||
isPublic: newIsPublic,
|
isPublic: newIsPublic,
|
||||||
updatedAt: now,
|
updatedAt: now,
|
||||||
};
|
};
|
||||||
if (newDescription) notifyMentions(requestingUserId, newDescription, "playlist", playlist.id, newTitle);
|
if (newDescription) {
|
||||||
|
notifyMentions(
|
||||||
|
requestingUserId,
|
||||||
|
newDescription,
|
||||||
|
"playlist",
|
||||||
|
playlist.id,
|
||||||
|
newTitle,
|
||||||
|
);
|
||||||
|
}
|
||||||
broadcastPlaylistUpdated(updated);
|
broadcastPlaylistUpdated(updated);
|
||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2666,8 +2666,12 @@ body.has-player .fab-new {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes comment-highlight {
|
@keyframes comment-highlight {
|
||||||
0% { background: color-mix(in srgb, var(--color-accent) 18%, transparent); }
|
0% {
|
||||||
100% { background: transparent; }
|
background: color-mix(in srgb, var(--color-accent) 18%, transparent);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.comment-node--highlight {
|
.comment-node--highlight {
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
|
|||||||
@@ -161,7 +161,9 @@ function CommentNode({
|
|||||||
{children.length > 0 && (
|
{children.length > 0 && (
|
||||||
<ul
|
<ul
|
||||||
className="comment-replies"
|
className="comment-replies"
|
||||||
style={depth >= MAX_INDENT_DEPTH ? { paddingLeft: 0, marginLeft: 0, borderLeft: "none" } : undefined}
|
style={depth >= MAX_INDENT_DEPTH
|
||||||
|
? { paddingLeft: 0, marginLeft: 0, borderLeft: "none" }
|
||||||
|
: undefined}
|
||||||
>
|
>
|
||||||
{children.map((child) => (
|
{children.map((child) => (
|
||||||
<CommentNode
|
<CommentNode
|
||||||
@@ -229,7 +231,9 @@ function CommentNode({
|
|||||||
value={editBody}
|
value={editBody}
|
||||||
onChange={setEditBody}
|
onChange={setEditBody}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) handleEditSave(e);
|
if (
|
||||||
|
e.key === "Enter" && (e.ctrlKey || e.metaKey)
|
||||||
|
) handleEditSave(e);
|
||||||
}}
|
}}
|
||||||
autoResize
|
autoResize
|
||||||
rows={1}
|
rows={1}
|
||||||
@@ -299,7 +303,10 @@ function CommentNode({
|
|||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
message="Delete this comment?"
|
message="Delete this comment?"
|
||||||
confirmLabel="Delete"
|
confirmLabel="Delete"
|
||||||
onConfirm={() => { setConfirmDelete(false); handleDelete(); }}
|
onConfirm={() => {
|
||||||
|
setConfirmDelete(false);
|
||||||
|
handleDelete();
|
||||||
|
}}
|
||||||
onCancel={() => setConfirmDelete(false)}
|
onCancel={() => setConfirmDelete(false)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -312,7 +319,9 @@ function CommentNode({
|
|||||||
value={replyBody}
|
value={replyBody}
|
||||||
onChange={setReplyBody}
|
onChange={setReplyBody}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) handleReply(e);
|
if (
|
||||||
|
e.key === "Enter" && (e.ctrlKey || e.metaKey)
|
||||||
|
) handleReply(e);
|
||||||
}}
|
}}
|
||||||
placeholder="Write a reply…"
|
placeholder="Write a reply…"
|
||||||
autoResize
|
autoResize
|
||||||
@@ -348,7 +357,9 @@ function CommentNode({
|
|||||||
{children.length > 0 && (
|
{children.length > 0 && (
|
||||||
<ul
|
<ul
|
||||||
className="comment-replies"
|
className="comment-replies"
|
||||||
style={depth >= MAX_INDENT_DEPTH ? { paddingLeft: 0, marginLeft: 0, borderLeft: "none" } : undefined}
|
style={depth >= MAX_INDENT_DEPTH
|
||||||
|
? { paddingLeft: 0, marginLeft: 0, borderLeft: "none" }
|
||||||
|
: undefined}
|
||||||
>
|
>
|
||||||
{children.map((child) => (
|
{children.map((child) => (
|
||||||
<CommentNode
|
<CommentNode
|
||||||
@@ -440,7 +451,9 @@ export function CommentThread({
|
|||||||
value={topLevelBody}
|
value={topLevelBody}
|
||||||
onChange={setTopLevelBody}
|
onChange={setTopLevelBody}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) handleTopLevelSubmit(e);
|
if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
|
||||||
|
handleTopLevelSubmit(e);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
placeholder="Add a comment…"
|
placeholder="Add a comment…"
|
||||||
autoResize
|
autoResize
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import type {
|
|||||||
import { deserializeDump, deserializePlaylistMembership } from "../model.ts";
|
import { deserializeDump, deserializePlaylistMembership } from "../model.ts";
|
||||||
import { useAuth } from "../hooks/useAuth.ts";
|
import { useAuth } from "../hooks/useAuth.ts";
|
||||||
import { useWS } from "../hooks/useWS.ts";
|
import { useWS } from "../hooks/useWS.ts";
|
||||||
import { formatBytes } from "../utils/format.ts";
|
|
||||||
import { dumpUrl } from "../utils/urls.ts";
|
import { dumpUrl } from "../utils/urls.ts";
|
||||||
import RichContentCard from "./RichContentCard.tsx";
|
import RichContentCard from "./RichContentCard.tsx";
|
||||||
import { MediaPlayer } from "./MediaPlayer.tsx";
|
import { MediaPlayer } from "./MediaPlayer.tsx";
|
||||||
|
|||||||
@@ -71,23 +71,21 @@ export function FileDropZone({
|
|||||||
<div className="fdz-wrapper">
|
<div className="fdz-wrapper">
|
||||||
{label && <span className="fdz-label">{label}</span>}
|
{label && <span className="fdz-label">{label}</span>}
|
||||||
<div
|
<div
|
||||||
className={`fdz${dragging ? " fdz--drag" : ""}${disabled ? " fdz--disabled" : ""}${file ? " fdz--filled" : ""}`}
|
className={`fdz${dragging ? " fdz--drag" : ""}${
|
||||||
|
disabled ? " fdz--disabled" : ""
|
||||||
|
}${file ? " fdz--filled" : ""}`}
|
||||||
onDragOver={handleDragOver}
|
onDragOver={handleDragOver}
|
||||||
onDragLeave={handleDragLeave}
|
onDragLeave={handleDragLeave}
|
||||||
onDrop={handleDrop}
|
onDrop={handleDrop}
|
||||||
onClick={file ? undefined : handleClick}
|
onClick={file ? undefined : handleClick}
|
||||||
role={file ? undefined : "button"}
|
role={file ? undefined : "button"}
|
||||||
tabIndex={file || disabled ? undefined : 0}
|
tabIndex={file || disabled ? undefined : 0}
|
||||||
onKeyDown={
|
onKeyDown={file || disabled ? undefined : (e) => {
|
||||||
file || disabled
|
if (e.key === "Enter" || e.key === " ") {
|
||||||
? undefined
|
e.preventDefault();
|
||||||
: (e) => {
|
handleClick();
|
||||||
if (e.key === "Enter" || e.key === " ") {
|
}
|
||||||
e.preventDefault();
|
}}
|
||||||
handleClick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
import {
|
import { forwardRef, useEffect, useImperativeHandle, useRef } from "react";
|
||||||
forwardRef,
|
|
||||||
useEffect,
|
|
||||||
useImperativeHandle,
|
|
||||||
useRef,
|
|
||||||
} from "react";
|
|
||||||
import { MentionDropdown } from "./MentionDropdown.tsx";
|
import { MentionDropdown } from "./MentionDropdown.tsx";
|
||||||
import { useMentionAutocomplete } from "../hooks/useMentionAutocomplete.ts";
|
import { useMentionAutocomplete } from "../hooks/useMentionAutocomplete.ts";
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ export function useDumpListSync(
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (deletedDumpIds.size === 0) return;
|
if (deletedDumpIds.size === 0) return;
|
||||||
setDumpsRef.current((prev) => prev.filter((d) => !deletedDumpIds.has(d.id)));
|
setDumpsRef.current((prev) =>
|
||||||
|
prev.filter((d) => !deletedDumpIds.has(d.id))
|
||||||
|
);
|
||||||
}, [deletedDumpIds]);
|
}, [deletedDumpIds]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
import { useCallback, useEffect, useRef, useState, type RefObject } from "react";
|
import {
|
||||||
|
type RefObject,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
import { API_URL } from "../config/api.ts";
|
import { API_URL } from "../config/api.ts";
|
||||||
|
|
||||||
export interface UserResult {
|
export interface UserResult {
|
||||||
@@ -70,7 +76,9 @@ export function useMentionAutocomplete(
|
|||||||
debounceRef.current = setTimeout(async () => {
|
debounceRef.current = setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`${API_URL}/api/users/search?q=${encodeURIComponent(mention.query)}`,
|
`${API_URL}/api/users/search?q=${
|
||||||
|
encodeURIComponent(mention.query)
|
||||||
|
}`,
|
||||||
);
|
);
|
||||||
const body = await res.json();
|
const body = await res.json();
|
||||||
if (body.success && body.data.length > 0) {
|
if (body.success && body.data.length > 0) {
|
||||||
|
|||||||
@@ -125,7 +125,10 @@ export function Dump() {
|
|||||||
if (!el) return;
|
if (!el) return;
|
||||||
el.scrollIntoView({ behavior: "smooth", block: "start" });
|
el.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
el.classList.add("comment-node--highlight");
|
el.classList.add("comment-node--highlight");
|
||||||
const t = setTimeout(() => el.classList.remove("comment-node--highlight"), 2000);
|
const t = setTimeout(
|
||||||
|
() => el.classList.remove("comment-node--highlight"),
|
||||||
|
2000,
|
||||||
|
);
|
||||||
return () => clearTimeout(t);
|
return () => clearTimeout(t);
|
||||||
}, [comments, location.hash]);
|
}, [comments, location.hash]);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { Link, useNavigate } from "react-router";
|
|||||||
import { API_URL } from "../config/api.ts";
|
import { API_URL } from "../config/api.ts";
|
||||||
import type { CreateUrlDumpRequest, RichContent } from "../model.ts";
|
import type { CreateUrlDumpRequest, RichContent } from "../model.ts";
|
||||||
import { useRequiredAuth } from "../hooks/useAuth.ts";
|
import { useRequiredAuth } from "../hooks/useAuth.ts";
|
||||||
import { formatBytes } from "../utils/format.ts";
|
|
||||||
import { dumpUrl } from "../utils/urls.ts";
|
import { dumpUrl } from "../utils/urls.ts";
|
||||||
import { PageShell } from "../components/PageShell.tsx";
|
import { PageShell } from "../components/PageShell.tsx";
|
||||||
import RichContentCard from "../components/RichContentCard.tsx";
|
import RichContentCard from "../components/RichContentCard.tsx";
|
||||||
|
|||||||
@@ -97,7 +97,9 @@ function notificationLink(n: Notification): string {
|
|||||||
return `/dumps/${(data as DumpUpvotedData).dumpId}`;
|
return `/dumps/${(data as DumpUpvotedData).dumpId}`;
|
||||||
case "user_mentioned": {
|
case "user_mentioned": {
|
||||||
const d = data as UserMentionedData;
|
const d = data as UserMentionedData;
|
||||||
if (d.contextType === "comment") return `/dumps/${d.dumpId}#comment-${d.contextId}`;
|
if (d.contextType === "comment") {
|
||||||
|
return `/dumps/${d.dumpId}#comment-${d.contextId}`;
|
||||||
|
}
|
||||||
if (d.contextType === "dump") return `/dumps/${d.contextId}`;
|
if (d.contextType === "dump") return `/dumps/${d.contextId}`;
|
||||||
return `/playlists/${d.contextId}`;
|
return `/playlists/${d.contextId}`;
|
||||||
}
|
}
|
||||||
@@ -109,28 +111,64 @@ function notificationContent(n: Notification): React.ReactNode {
|
|||||||
switch (n.type) {
|
switch (n.type) {
|
||||||
case "user_followed": {
|
case "user_followed": {
|
||||||
const d = data as UserFollowedData;
|
const d = data as UserFollowedData;
|
||||||
return <><strong>{d.followerUsername}</strong>{" started following you"}</>;
|
return (
|
||||||
|
<>
|
||||||
|
<strong>{d.followerUsername}</strong>
|
||||||
|
{" started following you"}
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case "playlist_followed": {
|
case "playlist_followed": {
|
||||||
const d = data as PlaylistFollowedData;
|
const d = data as PlaylistFollowedData;
|
||||||
return <><strong>{d.followerUsername}</strong>{" followed your playlist "}<strong>{d.playlistTitle}</strong></>;
|
return (
|
||||||
|
<>
|
||||||
|
<strong>{d.followerUsername}</strong>
|
||||||
|
{" followed your playlist "}
|
||||||
|
<strong>{d.playlistTitle}</strong>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case "user_dump_posted": {
|
case "user_dump_posted": {
|
||||||
const d = data as UserDumpPostedData;
|
const d = data as UserDumpPostedData;
|
||||||
return <><strong>{d.dumperUsername}</strong>{" posted "}<strong>{d.dumpTitle}</strong></>;
|
return (
|
||||||
|
<>
|
||||||
|
<strong>{d.dumperUsername}</strong>
|
||||||
|
{" posted "}
|
||||||
|
<strong>{d.dumpTitle}</strong>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case "playlist_dump_added": {
|
case "playlist_dump_added": {
|
||||||
const d = data as PlaylistDumpAddedData;
|
const d = data as PlaylistDumpAddedData;
|
||||||
return <><strong>{d.dumpTitle}</strong>{" was added to "}<strong>{d.playlistTitle}</strong></>;
|
return (
|
||||||
|
<>
|
||||||
|
<strong>{d.dumpTitle}</strong>
|
||||||
|
{" was added to "}
|
||||||
|
<strong>{d.playlistTitle}</strong>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case "dump_upvoted": {
|
case "dump_upvoted": {
|
||||||
const d = data as DumpUpvotedData;
|
const d = data as DumpUpvotedData;
|
||||||
return <><strong>{d.voterUsername}</strong>{" upvoted "}<strong>{d.dumpTitle}</strong></>;
|
return (
|
||||||
|
<>
|
||||||
|
<strong>{d.voterUsername}</strong>
|
||||||
|
{" upvoted "}
|
||||||
|
<strong>{d.dumpTitle}</strong>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case "user_mentioned": {
|
case "user_mentioned": {
|
||||||
const d = data as UserMentionedData;
|
const d = data as UserMentionedData;
|
||||||
const where = d.contextTitle || (d.contextType === "comment" ? "a comment" : "a post");
|
const where = d.contextTitle ||
|
||||||
return <><strong>{d.mentionerUsername}</strong>{" mentioned you in "}<strong>{where}</strong></>;
|
(d.contextType === "comment" ? "a comment" : "a post");
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<strong>{d.mentionerUsername}</strong>
|
||||||
|
{" mentioned you in "}
|
||||||
|
<strong>{where}</strong>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -341,7 +341,9 @@ export function PlaylistDetail() {
|
|||||||
dumpMap.set(dump.id, dump);
|
dumpMap.set(dump.id, dump);
|
||||||
const reinserted = order.length > 0
|
const reinserted = order.length > 0
|
||||||
? [
|
? [
|
||||||
...order.filter((id) => dumpMap.has(id)).map((id) => dumpMap.get(id)!),
|
...order.filter((id) => dumpMap.has(id)).map((id) =>
|
||||||
|
dumpMap.get(id)!
|
||||||
|
),
|
||||||
...prev.playlist.dumps.filter((d) => !new Set(order).has(d.id)),
|
...prev.playlist.dumps.filter((d) => !new Set(order).has(d.id)),
|
||||||
]
|
]
|
||||||
: [...prev.playlist.dumps, dump];
|
: [...prev.playlist.dumps, dump];
|
||||||
@@ -684,7 +686,9 @@ export function PlaylistDetail() {
|
|||||||
</time>
|
</time>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{playlist.updatedAt && (
|
{playlist.updatedAt && (
|
||||||
<Tooltip text={`Edited ${playlist.updatedAt.toLocaleString()}`}>
|
<Tooltip
|
||||||
|
text={`Edited ${playlist.updatedAt.toLocaleString()}`}
|
||||||
|
>
|
||||||
<span className="playlist-edited-label">
|
<span className="playlist-edited-label">
|
||||||
edited {relativeTime(playlist.updatedAt)}
|
edited {relativeTime(playlist.updatedAt)}
|
||||||
</span>
|
</span>
|
||||||
@@ -705,7 +709,9 @@ export function PlaylistDetail() {
|
|||||||
: (
|
: (
|
||||||
<div
|
<div
|
||||||
className="playlist-dump-list"
|
className="playlist-dump-list"
|
||||||
onDragOver={isOwner ? (e) => e.preventDefault() : undefined}
|
onDragOver={isOwner
|
||||||
|
? (e) => e.preventDefault()
|
||||||
|
: undefined}
|
||||||
>
|
>
|
||||||
{visibleDumps.map((dump) => {
|
{visibleDumps.map((dump) => {
|
||||||
const isActive = activeDumpIds.has(dump.id);
|
const isActive = activeDumpIds.has(dump.id);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import type { SubmitEvent } from "react";
|
import type { SubmitEvent } from "react";
|
||||||
import { Link, useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
|
|
||||||
import { API_URL } from "../config/api.ts";
|
import { API_URL } from "../config/api.ts";
|
||||||
import { deserializeAuthResponse } from "../model.ts";
|
import { deserializeAuthResponse } from "../model.ts";
|
||||||
|
|||||||
@@ -77,7 +77,9 @@ export function UserPlaylists() {
|
|||||||
|
|
||||||
const setCreated = useCallback((fn: (prev: Playlist[]) => Playlist[]) => {
|
const setCreated = useCallback((fn: (prev: Playlist[]) => Playlist[]) => {
|
||||||
setState((s) =>
|
setState((s) =>
|
||||||
s.status !== "loaded" ? s : { ...s, created: { ...s.created, items: fn(s.created.items) } }
|
s.status !== "loaded"
|
||||||
|
? s
|
||||||
|
: { ...s, created: { ...s.created, items: fn(s.created.items) } }
|
||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
usePlaylistListSync(setCreated, {
|
usePlaylistListSync(setCreated, {
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
import React, {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useLayoutEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
import { Link, useNavigate, useParams } from "react-router";
|
import { Link, useNavigate, useParams } from "react-router";
|
||||||
|
|
||||||
import { API_URL } from "../config/api.ts";
|
import { API_URL } from "../config/api.ts";
|
||||||
@@ -151,7 +157,9 @@ export function UserPublicProfile() {
|
|||||||
if (next.length < prev.length) {
|
if (next.length < prev.length) {
|
||||||
const nextIds = new Set(next.map((d) => d.id));
|
const nextIds = new Set(next.map((d) => d.id));
|
||||||
prev.forEach((d, idx) => {
|
prev.forEach((d, idx) => {
|
||||||
if (!nextIds.has(d.id)) removedDumpPositionsRef.current.set(d.id, idx);
|
if (!nextIds.has(d.id)) {
|
||||||
|
removedDumpPositionsRef.current.set(d.id, idx);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return { ...s, dumps: { ...s.dumps, items: next } };
|
return { ...s, dumps: { ...s.dumps, items: next } };
|
||||||
@@ -180,7 +188,9 @@ export function UserPublicProfile() {
|
|||||||
if (next.length < prev.length) {
|
if (next.length < prev.length) {
|
||||||
const nextIds = new Set(next.map((d) => d.id));
|
const nextIds = new Set(next.map((d) => d.id));
|
||||||
prev.forEach((d, idx) => {
|
prev.forEach((d, idx) => {
|
||||||
if (!nextIds.has(d.id)) removedVotePositionsRef.current.set(d.id, idx);
|
if (!nextIds.has(d.id)) {
|
||||||
|
removedVotePositionsRef.current.set(d.id, idx);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return { ...s, votes: { ...s.votes, items: next } };
|
return { ...s, votes: { ...s.votes, items: next } };
|
||||||
@@ -422,7 +432,6 @@ export function UserPublicProfile() {
|
|||||||
}
|
}
|
||||||
}, [lastVoteEvent, me, profileUserId]);
|
}, [lastVoteEvent, me, profileUserId]);
|
||||||
|
|
||||||
|
|
||||||
// Save scroll position + loaded state to sessionStorage on scroll
|
// Save scroll position + loaded state to sessionStorage on scroll
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (state.status !== "loaded") return;
|
if (state.status !== "loaded") return;
|
||||||
|
|||||||
Reference in New Issue
Block a user