v3: fixed inconsistent page titles
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 41s
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 41s
This commit is contained in:
@@ -24,6 +24,7 @@ import {
|
||||
} from "../model.ts";
|
||||
|
||||
import { useAuth } from "../hooks/useAuth.ts";
|
||||
import { useDocumentTitle } from "../hooks/useDocumentTitle.ts";
|
||||
import { relativeTime } from "../utils/relativeTime.ts";
|
||||
import { useWS } from "../hooks/useWS.ts";
|
||||
import { Avatar } from "../components/Avatar.tsx";
|
||||
@@ -55,6 +56,14 @@ export function Dump() {
|
||||
const [op, setOp] = useState<PublicUser | null>(null);
|
||||
const [playlistModalOpen, setPlaylistModalOpen] = useState(false);
|
||||
|
||||
useDocumentTitle(
|
||||
dumpState.status === "loaded"
|
||||
? dumpState.dump.title
|
||||
: dumpState.status === "error"
|
||||
? null
|
||||
: undefined,
|
||||
);
|
||||
|
||||
const [comments, setComments] = useState<Comment[]>([]);
|
||||
const [relatedDumps, setRelatedDumps] = useState<Dump[]>([]);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import { API_URL, VALIDATION } from "../config/api.ts";
|
||||
import type { Dump, RawDump, UpdateDumpRequest } from "../model.ts";
|
||||
import { deserializeDump, parseAPIResponse } from "../model.ts";
|
||||
import { useRequiredAuth } from "../hooks/useAuth.ts";
|
||||
import { useDocumentTitle } from "../hooks/useDocumentTitle.ts";
|
||||
import { formatBytes } from "../utils/format.ts";
|
||||
import { dumpFileUrl, dumpThumbnailUrl, dumpUrl } from "../utils/urls.ts";
|
||||
import { PageShell } from "../components/PageShell.tsx";
|
||||
@@ -41,6 +42,15 @@ export function DumpEdit() {
|
||||
const { authFetch, token } = useRequiredAuth();
|
||||
|
||||
const [state, setState] = useState<DumpEditState>({ status: "loading" });
|
||||
|
||||
useDocumentTitle(
|
||||
state.status === "loaded"
|
||||
? t`Editing ${state.dump.title}`
|
||||
: state.status === "error"
|
||||
? null
|
||||
: undefined,
|
||||
);
|
||||
|
||||
const [confirmDelete, setConfirmDelete] = useState(false);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [thumbUploading, setThumbUploading] = useState(false);
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
import { friendlyFetchError } from "../utils/apiError.ts";
|
||||
import { useFeedCache } from "../hooks/useFeedCache.ts";
|
||||
import { useScrollSave } from "../hooks/useScrollSave.ts";
|
||||
import { useDocumentTitle } from "../hooks/useDocumentTitle.ts";
|
||||
import { useAuth } from "../hooks/useAuth.ts";
|
||||
import { useWS } from "../hooks/useWS.ts";
|
||||
import { useDumpListSync } from "../hooks/useDumpListSync.ts";
|
||||
@@ -55,6 +56,8 @@ export function Index() {
|
||||
const justDeletedId = (location.state as { deletedDumpId?: string } | null)
|
||||
?.deletedDumpId;
|
||||
|
||||
useDocumentTitle(null);
|
||||
|
||||
const { user, token } = useAuth();
|
||||
const {
|
||||
voteCounts,
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { Link } from "react-router";
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { Trans } from "@lingui/react/macro";
|
||||
import { PageShell } from "../components/PageShell.tsx";
|
||||
import { useDocumentTitle } from "../hooks/useDocumentTitle.ts";
|
||||
|
||||
export function NotFound() {
|
||||
useDocumentTitle(t`Page not found`);
|
||||
return (
|
||||
<PageShell centered>
|
||||
<h1>404</h1>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { relativeTime } from "../utils/relativeTime.ts";
|
||||
|
||||
import { API_URL, NOTIFICATIONS_PAGE_SIZE } from "../config/api.ts";
|
||||
import { useAuth } from "../hooks/useAuth.ts";
|
||||
import { useDocumentTitle } from "../hooks/useDocumentTitle.ts";
|
||||
import { ErrorCard } from "../components/ErrorCard.tsx";
|
||||
import { Tooltip } from "../components/Tooltip.tsx";
|
||||
import { useWS } from "../hooks/useWS.ts";
|
||||
@@ -256,6 +257,7 @@ function groupByDate(
|
||||
}
|
||||
|
||||
export function Notifications() {
|
||||
useDocumentTitle(t`Notifications`);
|
||||
const { authFetch } = useAuth();
|
||||
const { clearUnreadNotifications, lastNotification } = useWS();
|
||||
const [state, setState] = useState<State>({ status: "loading" });
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
import { playlistUrl } from "../utils/urls.ts";
|
||||
import { can } from "../utils/permissions.ts";
|
||||
import { useAuth } from "../hooks/useAuth.ts";
|
||||
import { useDocumentTitle } from "../hooks/useDocumentTitle.ts";
|
||||
import { useWS } from "../hooks/useWS.ts";
|
||||
import { relativeTime } from "../utils/relativeTime.ts";
|
||||
import { DumpCard } from "../components/DumpCard.tsx";
|
||||
@@ -71,6 +72,14 @@ export function PlaylistDetail() {
|
||||
// Stable UUID for WS comparisons — avoids re-running effects on every state change
|
||||
const playlistUUID = state.status === "loaded" ? state.playlist.id : null;
|
||||
|
||||
useDocumentTitle(
|
||||
state.status === "loaded"
|
||||
? state.playlist.title
|
||||
: state.status === "error"
|
||||
? null
|
||||
: undefined,
|
||||
);
|
||||
|
||||
// activeDumpIds: which dumps are currently in the playlist (the canonical set)
|
||||
const [activeDumpIds, setActiveDumpIds] = useState<Set<string>>(new Set());
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { t } from "@lingui/core/macro";
|
||||
import { Trans } from "@lingui/react/macro";
|
||||
|
||||
import { API_URL, VALIDATION } from "../config/api.ts";
|
||||
import { useDocumentTitle } from "../hooks/useDocumentTitle.ts";
|
||||
import { PageShell } from "../components/PageShell.tsx";
|
||||
import {
|
||||
expectOk,
|
||||
@@ -20,6 +21,7 @@ interface Values {
|
||||
}
|
||||
|
||||
export function ResetPassword() {
|
||||
useDocumentTitle(t`Reset password`);
|
||||
const [params] = useSearchParams();
|
||||
const navigate = useNavigate();
|
||||
const token = params.get("token") ?? "";
|
||||
|
||||
@@ -8,6 +8,7 @@ import { DumpCard } from "../components/DumpCard.tsx";
|
||||
import { PlaylistCard } from "../components/PlaylistCard.tsx";
|
||||
import { ErrorCard } from "../components/ErrorCard.tsx";
|
||||
import { useAuth } from "../hooks/useAuth.ts";
|
||||
import { useDocumentTitle } from "../hooks/useDocumentTitle.ts";
|
||||
import { useWS } from "../hooks/useWS.ts";
|
||||
import { useInfiniteScroll } from "../hooks/useInfiniteScroll.ts";
|
||||
import { useTabParam } from "../hooks/useTabParam.ts";
|
||||
@@ -46,6 +47,7 @@ type SearchState =
|
||||
};
|
||||
|
||||
export function Search() {
|
||||
useDocumentTitle(t`Search`);
|
||||
const [searchParams] = useSearchParams();
|
||||
const q = searchParams.get("q") ?? "";
|
||||
const [tab, setTab] = useTabParam<Tab>(SEARCH_TABS, "dumps");
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Trans } from "@lingui/react/macro";
|
||||
import { Link, useParams } from "react-router";
|
||||
|
||||
import { useAuth } from "../hooks/useAuth.ts";
|
||||
import { useDocumentTitle } from "../hooks/useDocumentTitle.ts";
|
||||
import { useWS } from "../hooks/useWS.ts";
|
||||
import { useDumpListSync } from "../hooks/useDumpListSync.ts";
|
||||
import { usePositionAwareSync } from "../hooks/usePositionAwareSync.ts";
|
||||
@@ -19,6 +20,8 @@ export function UserDumps() {
|
||||
const { user: me } = useAuth();
|
||||
const { voteCounts, myVotes, lastDumpEvent, castVote, removeVote } = useWS();
|
||||
|
||||
useDocumentTitle(username ?? null);
|
||||
|
||||
const { state, setItems, sentinelRef } = useUserDumpFeed(
|
||||
username,
|
||||
"dumps",
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
type RawAuthResponse,
|
||||
} from "../model.ts";
|
||||
import { useAuth } from "../hooks/useAuth.ts";
|
||||
import { useDocumentTitle } from "../hooks/useDocumentTitle.ts";
|
||||
import { PageShell } from "../components/PageShell.tsx";
|
||||
import {
|
||||
expectOk,
|
||||
@@ -30,6 +31,7 @@ interface ResetValues {
|
||||
}
|
||||
|
||||
export function UserLogin() {
|
||||
useDocumentTitle(t`Log in`);
|
||||
const navigate = useNavigate();
|
||||
const { login } = useAuth();
|
||||
const [showReset, setShowReset] = useState(false);
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
hydratePlaylist,
|
||||
} from "../model.ts";
|
||||
import { useAuth } from "../hooks/useAuth.ts";
|
||||
import { useDocumentTitle } from "../hooks/useDocumentTitle.ts";
|
||||
import { useWS } from "../hooks/useWS.ts";
|
||||
import { usePlaylistListSync } from "../hooks/usePlaylistListSync.ts";
|
||||
import { usePositionAwareSync } from "../hooks/usePositionAwareSync.ts";
|
||||
@@ -60,6 +61,8 @@ function initialFeed(items: Playlist[], hasMore: boolean): PlaylistFeed {
|
||||
export function UserPlaylists() {
|
||||
const { username } = useParams();
|
||||
const { user: me, authFetch, token } = useAuth();
|
||||
|
||||
useDocumentTitle(username ?? null);
|
||||
const { lastPlaylistEvent } = useWS();
|
||||
|
||||
const { cached: cachedCreated, saveState: saveCreated } = useFeedCache<
|
||||
|
||||
@@ -38,6 +38,7 @@ import { NewPlaylistForm } from "../components/NewPlaylistForm.tsx";
|
||||
import { PageShell } from "../components/PageShell.tsx";
|
||||
import { PageError } from "../components/PageError.tsx";
|
||||
import { useAuth } from "../hooks/useAuth.ts";
|
||||
import { useDocumentTitle } from "../hooks/useDocumentTitle.ts";
|
||||
import { useTheme } from "../hooks/useTheme.ts";
|
||||
import { useDefaultFeedTab } from "../hooks/useDefaultFeedTab.ts";
|
||||
import { useWS } from "../hooks/useWS.ts";
|
||||
@@ -211,6 +212,14 @@ export function UserPublicProfile() {
|
||||
|
||||
const [state, setState] = useState<ProfileState>({ status: "loading" });
|
||||
|
||||
useDocumentTitle(
|
||||
state.status === "loaded"
|
||||
? state.user.username
|
||||
: state.status === "error"
|
||||
? null
|
||||
: undefined,
|
||||
);
|
||||
|
||||
const profileUserId = state.status === "loaded" ? state.user.id : null;
|
||||
const isOwnProfile = me?.id === profileUserId;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
type RegisterRequest,
|
||||
} from "../model.ts";
|
||||
import { useAuth } from "../hooks/useAuth.ts";
|
||||
import { useDocumentTitle } from "../hooks/useDocumentTitle.ts";
|
||||
import { PageShell } from "../components/PageShell.tsx";
|
||||
import { ErrorCard } from "../components/ErrorCard.tsx";
|
||||
import {
|
||||
@@ -33,6 +34,7 @@ interface Values {
|
||||
}
|
||||
|
||||
export function UserRegister() {
|
||||
useDocumentTitle(t`Register`);
|
||||
const navigate = useNavigate();
|
||||
const { login } = useAuth();
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
@@ -7,6 +7,7 @@ import { API_URL } from "../config/api.ts";
|
||||
import type { Dump } from "../model.ts";
|
||||
import { deserializeDump } from "../model.ts";
|
||||
import { useAuth } from "../hooks/useAuth.ts";
|
||||
import { useDocumentTitle } from "../hooks/useDocumentTitle.ts";
|
||||
import { useWS } from "../hooks/useWS.ts";
|
||||
import { useDumpListSync } from "../hooks/useDumpListSync.ts";
|
||||
import { useFading } from "../hooks/useFading.ts";
|
||||
@@ -21,6 +22,8 @@ export function UserUpvoted() {
|
||||
const { user: me } = useAuth();
|
||||
const { voteCounts, myVotes, lastVoteEvent, castVote, removeVote } = useWS();
|
||||
|
||||
useDocumentTitle(username ?? null);
|
||||
|
||||
const [votedIds, setVotedIds] = useState<Set<string>>(new Set());
|
||||
const { fading, startFading, cancelFading, cancelAll } = useFading();
|
||||
const prevMyVotesRef = useRef<Set<string> | null>(null);
|
||||
|
||||
Reference in New Issue
Block a user