v3: added localization, use global player for uploaded audio/video files

This commit is contained in:
khannurien
2026-04-03 15:29:33 +00:00
parent 378b3ffa46
commit 0ce80398a4
64 changed files with 4248 additions and 941 deletions

View File

@@ -6,6 +6,8 @@ import {
useState,
} from "react";
import { Link, useParams } from "react-router";
import { t } from "@lingui/core/macro"
import { Trans } from "@lingui/react/macro";
import { API_URL, DEFAULT_PAGE_SIZE } from "../config/api.ts";
import { friendlyFetchError } from "../utils/apiError.ts";
@@ -74,6 +76,11 @@ export function UserPlaylists() {
);
const [state, setState] = useState<State>({ status: "loading" });
const [prevUsername, setPrevUsername] = useState(username);
if (prevUsername !== username) {
setPrevUsername(username);
setState({ status: "loading" });
}
const [confirmDeleteId, setConfirmDeleteId] = useState<string | null>(null);
const profileUserId = state.status === "loaded" ? state.profileUser.id : null;
@@ -114,7 +121,6 @@ export function UserPlaylists() {
useEffect(() => {
if (!username) return;
setState({ status: "loading" });
const controller = new AbortController();
const authHeaders: HeadersInit = token
@@ -190,7 +196,7 @@ export function UserPlaylists() {
setState({ status: "error", error: friendlyFetchError(err) });
});
return () => controller.abort();
}, [username]);
}, [username, cachedCreated, cachedFollowed, token]);
const loadMoreCreated = useCallback(() => {
if (
@@ -332,7 +338,7 @@ export function UserPlaylists() {
if (state.status === "loading") {
return (
<PageShell>
<p className="page-loading">Loading</p>
<p className="page-loading"><Trans>Loading</Trans></p>
</PageShell>
);
}
@@ -343,7 +349,7 @@ export function UserPlaylists() {
message={state.error}
actions={
<Link to={`/users/${username}`} className="btn-border">
Back to profile
<Trans> Back to profile</Trans>
</Link>
}
/>
@@ -357,7 +363,7 @@ export function UserPlaylists() {
<ProfileSubpageHeader
username={username!}
profileUser={profileUser}
title="Playlists"
title={t`Playlists`}
actions={isOwnProfile && (
<NewPlaylistForm
toggleClassName="btn-primary"
@@ -377,12 +383,13 @@ export function UserPlaylists() {
<section className="profile-section">
<div className="profile-section-header">
<h2 className="profile-section-title">
Created ({created.items.length}
{created.hasMore ? "+" : ""})
<Trans>
Created ({created.items.length}{created.hasMore ? "+" : ""})
</Trans>
</h2>
</div>
{created.items.length === 0
? <p className="empty-state">No playlists yet.</p>
? <p className="empty-state"><Trans>No playlists yet.</Trans></p>
: (
<ul className="dump-feed">
{created.items.map((p) => (
@@ -399,19 +406,24 @@ export function UserPlaylists() {
)}
<div ref={createdSentinelRef} />
{created.loadingMore && (
<p className="feed-loading-more">Loading more</p>
<p className="feed-loading-more"><Trans>Loading more</Trans></p>
)}
</section>
<section className="profile-section">
<div className="profile-section-header">
<h2 className="profile-section-title">
Followed ({followed.items.length}
{followed.hasMore ? "+" : ""})
<Trans>
Followed ({followed.items.length}{followed.hasMore ? "+" : ""})
</Trans>
</h2>
</div>
{followed.items.length === 0
? <p className="empty-state">No followed playlists yet.</p>
? (
<p className="empty-state">
<Trans>No followed playlists yet.</Trans>
</p>
)
: (
<ul className="dump-feed">
{followed.items.map((p) => (
@@ -421,14 +433,14 @@ export function UserPlaylists() {
)}
<div ref={followedSentinelRef} />
{followed.loadingMore && (
<p className="feed-loading-more">Loading more</p>
<p className="feed-loading-more"><Trans>Loading more</Trans></p>
)}
</section>
{confirmDeleteId && (
<ConfirmModal
message="Delete this playlist? This cannot be undone."
confirmLabel="Delete playlist"
message={t`Delete this playlist? This cannot be undone.`}
confirmLabel={t`Delete playlist`}
onConfirm={() => {
handleDelete(confirmDeleteId);
setConfirmDeleteId(null);