v3: fixed authorization bug with stale tokens, fixed private dump access bug, various small fixes across the app
Some checks failed
Build and Publish Docker Image / build-and-push (push) Failing after 20s

This commit is contained in:
khannurien
2026-06-27 10:40:07 +00:00
parent 73e0114bf7
commit 76de798faf
24 changed files with 236 additions and 194 deletions

View File

@@ -1,9 +1,9 @@
import { Link, useNavigate } from "react-router";
import { Plural, Trans } from "@lingui/react/macro";
import type { Dump } from "../model.ts";
import { API_URL } from "../config/api.ts";
import { relativeTime } from "../utils/relativeTime.ts";
import { dumpUrl } from "../utils/urls.ts";
import { dumpThumbnailUrl, dumpUrl } from "../utils/urls.ts";
import { useAuth } from "../hooks/useAuth.ts";
import { isDumpVisited, isRecent, markDumpVisited } from "../utils/visited.ts";
import FilePreview from "./FilePreview.tsx";
import RichContentCard from "./RichContentCard.tsx";
@@ -27,6 +27,7 @@ export function DumpCard(
DumpCardProps,
) {
const navigate = useNavigate();
const { token } = useAuth();
const unread = !isOwner && isRecent(dump.createdAt) &&
!isDumpVisited(dump.id);
@@ -54,7 +55,7 @@ export function DumpCard(
richContent={dump.richContent}
compact
thumbnailOverrideUrl={dump.thumbnailMime
? `${API_URL}/api/thumbnails/${dump.id}`
? dumpThumbnailUrl(dump, token)
: undefined}
/>
)

View File

@@ -1,7 +1,8 @@
import { useContext, useEffect, useState } from "react";
import { API_URL } from "../config/api.ts";
import type { Dump } from "../model.ts";
import { formatBytes } from "../utils/format.ts";
import { dumpFileUrl, dumpThumbnailUrl } from "../utils/urls.ts";
import { useAuth } from "../hooks/useAuth.ts";
import { IconPause, IconPlay, MediaPlayer } from "./MediaPlayer.tsx";
import { PlayerContext } from "../contexts/PlayerContext.ts";
import {
@@ -145,11 +146,12 @@ export default function FilePreview(
{ dump, compact = false, global: useGlobal = false }: FilePreviewProps,
) {
const { current, playing, play, togglePlay } = useContext(PlayerContext);
const fileUrl = `${API_URL}/api/files/${dump.id}?v=${dump.fileSize ?? 0}`;
const { token } = useAuth();
const fileUrl = dumpFileUrl(dump, token);
const mime = dump.fileMime ?? "";
const isPlaying = current?.kind === "file" && current.fileUrl === fileUrl;
const thumbOverride = dump.thumbnailMime
? `${API_URL}/api/thumbnails/${dump.id}`
? dumpThumbnailUrl(dump, token)
: null;
if (compact) {
@@ -166,7 +168,7 @@ export default function FilePreview(
);
}
if (mime.startsWith("video/")) {
const thumbUrl = `${API_URL}/api/thumbnails/${dump.id}`;
const thumbUrl = dumpThumbnailUrl(dump, token);
return (
<button
type="button"

View File

@@ -4,7 +4,8 @@ import { Plural, Trans } from "@lingui/react/macro";
import type { Dump } from "../model.ts";
import { API_URL } from "../config/api.ts";
import { relativeTime } from "../utils/relativeTime.ts";
import { dumpUrl } from "../utils/urls.ts";
import { dumpFileUrl, dumpUrl } from "../utils/urls.ts";
import { useAuth } from "../hooks/useAuth.ts";
import { isDumpVisited, isRecent, markDumpVisited } from "../utils/visited.ts";
import { VoteButton } from "./VoteButton.tsx";
import { Markdown } from "./Markdown.tsx";
@@ -29,6 +30,7 @@ export function JournalCard(
JournalCardProps,
) {
const navigate = useNavigate();
const { token } = useAuth();
const { play } = useContext(PlayerContext);
const unread = !isOwner && isRecent(dump.createdAt) &&
!isDumpVisited(dump.id);
@@ -40,7 +42,7 @@ export function JournalCard(
const rawThumbnail =
dump.kind === "file" && dump.fileMime?.startsWith("image/")
? `${API_URL}/api/files/${dump.id}?v=${dump.fileSize ?? 0}`
? dumpFileUrl(dump, token)
: (dump.richContent?.thumbnailUrl ?? null);
// Route external HTTP thumbnails through the server proxy to avoid

View File

@@ -1,4 +1,4 @@
import { useId } from "react";
import { type ReactNode, useId } from "react";
import {
type FieldValues,
type Path,
@@ -12,6 +12,8 @@ import { charCountClass } from "../../utils/charCount.ts";
interface TextFieldProps<T extends FieldValues> {
name: Path<T>;
label?: string;
/** Optional control rendered to the right of the label (e.g. a reset button). */
labelAction?: ReactNode;
type?: "text" | "email" | "password" | "url" | "search";
placeholder?: string;
autoComplete?: string;
@@ -45,6 +47,7 @@ function CharCounter<T extends FieldValues>(
export function TextField<T extends FieldValues>({
name,
label,
labelAction,
type = "text",
placeholder,
autoComplete,
@@ -61,10 +64,17 @@ export function TextField<T extends FieldValues>({
return (
<div className="form-field">
{label && (
<label className="form-label" htmlFor={id}>
{label}
</label>
{(label || labelAction) && (
<div className="form-label-row">
{label
? (
<label className="form-label" htmlFor={id}>
{label}
</label>
)
: <span />}
{labelAction}
</div>
)}
<div className={counted ? "input-with-count" : undefined}>
<input