v3: linter and formatter pass

This commit is contained in:
khannurien
2026-03-22 16:08:12 +00:00
parent 34e908d1bc
commit a104113e05
17 changed files with 159 additions and 56 deletions

View File

@@ -107,7 +107,9 @@ export async function createUrlDump(
broadcastNewDump(dump);
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;
}
@@ -335,7 +337,13 @@ export async function updateDump(
if (updatedDump.isPrivate && !dump.isPrivate) broadcastDumpDeleted(dumpId);
else if (!updatedDump.isPrivate) broadcastDumpUpdated(updatedDump);
if (updatedDump.comment) {
notifyMentions(dump.userId, updatedDump.comment, "dump", dumpId, updatedDump.title);
notifyMentions(
dump.userId,
updatedDump.comment,
"dump",
dumpId,
updatedDump.title,
);
}
return updatedDump;
}

View File

@@ -207,9 +207,11 @@ export function notifyMentions(
).get(mentionerUserId) as { username: string } | undefined;
if (!mentionerRow) return;
const usernames = [...new Set(
[...body.matchAll(MENTION_RE)].map((m) => m[1].toLowerCase()),
)];
const usernames = [
...new Set(
[...body.matchAll(MENTION_RE)].map((m) => m[1].toLowerCase()),
),
];
for (const username of usernames) {
const mentionedRow = db.prepare(

View File

@@ -73,7 +73,9 @@ export function createPlaylist(
isPublic: req.isPublic,
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);
return playlist;
}
@@ -157,7 +159,14 @@ export function updatePlaylist(
const newSlug = makeSlug(newTitle, playlist.id);
db.prepare(
`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 = {
...playlist,
@@ -167,7 +176,15 @@ export function updatePlaylist(
isPublic: newIsPublic,
updatedAt: now,
};
if (newDescription) notifyMentions(requestingUserId, newDescription, "playlist", playlist.id, newTitle);
if (newDescription) {
notifyMentions(
requestingUserId,
newDescription,
"playlist",
playlist.id,
newTitle,
);
}
broadcastPlaylistUpdated(updated);
return updated;
}

View File

@@ -2666,8 +2666,12 @@ body.has-player .fab-new {
}
@keyframes comment-highlight {
0% { background: color-mix(in srgb, var(--color-accent) 18%, transparent); }
100% { background: transparent; }
0% {
background: color-mix(in srgb, var(--color-accent) 18%, transparent);
}
100% {
background: transparent;
}
}
.comment-node--highlight {
border-radius: 6px;

View File

@@ -161,7 +161,9 @@ function CommentNode({
{children.length > 0 && (
<ul
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) => (
<CommentNode
@@ -229,7 +231,9 @@ function CommentNode({
value={editBody}
onChange={setEditBody}
onKeyDown={(e) => {
if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) handleEditSave(e);
if (
e.key === "Enter" && (e.ctrlKey || e.metaKey)
) handleEditSave(e);
}}
autoResize
rows={1}
@@ -299,7 +303,10 @@ function CommentNode({
<ConfirmModal
message="Delete this comment?"
confirmLabel="Delete"
onConfirm={() => { setConfirmDelete(false); handleDelete(); }}
onConfirm={() => {
setConfirmDelete(false);
handleDelete();
}}
onCancel={() => setConfirmDelete(false)}
/>
)}
@@ -312,7 +319,9 @@ function CommentNode({
value={replyBody}
onChange={setReplyBody}
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…"
autoResize
@@ -348,7 +357,9 @@ function CommentNode({
{children.length > 0 && (
<ul
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) => (
<CommentNode
@@ -440,7 +451,9 @@ export function CommentThread({
value={topLevelBody}
onChange={setTopLevelBody}
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…"
autoResize

View File

@@ -13,7 +13,6 @@ import type {
import { deserializeDump, deserializePlaylistMembership } from "../model.ts";
import { useAuth } from "../hooks/useAuth.ts";
import { useWS } from "../hooks/useWS.ts";
import { formatBytes } from "../utils/format.ts";
import { dumpUrl } from "../utils/urls.ts";
import RichContentCard from "./RichContentCard.tsx";
import { MediaPlayer } from "./MediaPlayer.tsx";

View File

@@ -71,23 +71,21 @@ export function FileDropZone({
<div className="fdz-wrapper">
{label && <span className="fdz-label">{label}</span>}
<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}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
onClick={file ? undefined : handleClick}
role={file ? undefined : "button"}
tabIndex={file || disabled ? undefined : 0}
onKeyDown={
file || disabled
? undefined
: (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
handleClick();
}
}
}
onKeyDown={file || disabled ? undefined : (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
handleClick();
}
}}
>
<input
ref={inputRef}

View File

@@ -1,9 +1,4 @@
import {
forwardRef,
useEffect,
useImperativeHandle,
useRef,
} from "react";
import { forwardRef, useEffect, useImperativeHandle, useRef } from "react";
import { MentionDropdown } from "./MentionDropdown.tsx";
import { useMentionAutocomplete } from "../hooks/useMentionAutocomplete.ts";

View File

@@ -28,7 +28,9 @@ export function useDumpListSync(
useEffect(() => {
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]);
useEffect(() => {

View File

@@ -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";
export interface UserResult {
@@ -70,7 +76,9 @@ export function useMentionAutocomplete(
debounceRef.current = setTimeout(async () => {
try {
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();
if (body.success && body.data.length > 0) {

View File

@@ -125,7 +125,10 @@ export function Dump() {
if (!el) return;
el.scrollIntoView({ behavior: "smooth", block: "start" });
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);
}, [comments, location.hash]);

View File

@@ -5,7 +5,6 @@ import { Link, useNavigate } from "react-router";
import { API_URL } from "../config/api.ts";
import type { CreateUrlDumpRequest, RichContent } from "../model.ts";
import { useRequiredAuth } from "../hooks/useAuth.ts";
import { formatBytes } from "../utils/format.ts";
import { dumpUrl } from "../utils/urls.ts";
import { PageShell } from "../components/PageShell.tsx";
import RichContentCard from "../components/RichContentCard.tsx";

View File

@@ -97,7 +97,9 @@ function notificationLink(n: Notification): string {
return `/dumps/${(data as DumpUpvotedData).dumpId}`;
case "user_mentioned": {
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}`;
return `/playlists/${d.contextId}`;
}
@@ -109,28 +111,64 @@ function notificationContent(n: Notification): React.ReactNode {
switch (n.type) {
case "user_followed": {
const d = data as UserFollowedData;
return <><strong>{d.followerUsername}</strong>{" started following you"}</>;
return (
<>
<strong>{d.followerUsername}</strong>
{" started following you"}
</>
);
}
case "playlist_followed": {
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": {
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": {
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": {
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": {
const d = data as UserMentionedData;
const where = d.contextTitle || (d.contextType === "comment" ? "a comment" : "a post");
return <><strong>{d.mentionerUsername}</strong>{" mentioned you in "}<strong>{where}</strong></>;
const where = d.contextTitle ||
(d.contextType === "comment" ? "a comment" : "a post");
return (
<>
<strong>{d.mentionerUsername}</strong>
{" mentioned you in "}
<strong>{where}</strong>
</>
);
}
}
}

View File

@@ -341,7 +341,9 @@ export function PlaylistDetail() {
dumpMap.set(dump.id, dump);
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, dump];
@@ -684,7 +686,9 @@ export function PlaylistDetail() {
</time>
</Tooltip>
{playlist.updatedAt && (
<Tooltip text={`Edited ${playlist.updatedAt.toLocaleString()}`}>
<Tooltip
text={`Edited ${playlist.updatedAt.toLocaleString()}`}
>
<span className="playlist-edited-label">
edited {relativeTime(playlist.updatedAt)}
</span>
@@ -705,7 +709,9 @@ export function PlaylistDetail() {
: (
<div
className="playlist-dump-list"
onDragOver={isOwner ? (e) => e.preventDefault() : undefined}
onDragOver={isOwner
? (e) => e.preventDefault()
: undefined}
>
{visibleDumps.map((dump) => {
const isActive = activeDumpIds.has(dump.id);

View File

@@ -1,6 +1,6 @@
import { useState } 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 { deserializeAuthResponse } from "../model.ts";

View File

@@ -77,7 +77,9 @@ export function UserPlaylists() {
const setCreated = useCallback((fn: (prev: Playlist[]) => Playlist[]) => {
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, {

View File

@@ -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 { API_URL } from "../config/api.ts";
@@ -151,7 +157,9 @@ export function UserPublicProfile() {
if (next.length < prev.length) {
const nextIds = new Set(next.map((d) => d.id));
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 } };
@@ -180,7 +188,9 @@ export function UserPublicProfile() {
if (next.length < prev.length) {
const nextIds = new Set(next.map((d) => d.id));
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 } };
@@ -422,7 +432,6 @@ export function UserPublicProfile() {
}
}, [lastVoteEvent, me, profileUserId]);
// Save scroll position + loaded state to sessionStorage on scroll
useEffect(() => {
if (state.status !== "loaded") return;