video new
This commit is contained in:
parent
c31325e01a
commit
d9a8d171fb
985
package-lock.json
generated
985
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -20,7 +20,7 @@
|
|||||||
"hls.js": "^1.6.13",
|
"hls.js": "^1.6.13",
|
||||||
"idb": "^8.0.3",
|
"idb": "^8.0.3",
|
||||||
"media-chrome": "^4.15.0",
|
"media-chrome": "^4.15.0",
|
||||||
"next": "15.3.3",
|
"next": "^15.5.6",
|
||||||
"next-intl": "^4.1.0",
|
"next-intl": "^4.1.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-circular-progressbar": "^2.2.0",
|
"react-circular-progressbar": "^2.2.0",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
|
|
||||||
import { VideoGuideView } from "@/components/domains/video-guides";
|
import { VideoGuideView } from "@/components/domains/video-guides";
|
||||||
import { DashboardData, DashboardSchema } from "@/entities/dashboard/types";
|
import { VideoGuideSchema } from "@/entities/dashboard/types";
|
||||||
import { http } from "@/shared/api/httpClient";
|
import { http } from "@/shared/api/httpClient";
|
||||||
import { API_ROUTES } from "@/shared/constants/api-routes";
|
import { API_ROUTES } from "@/shared/constants/api-routes";
|
||||||
|
|
||||||
@ -15,15 +15,17 @@ export default async function VideoGuidePage({
|
|||||||
}) {
|
}) {
|
||||||
const { id } = await params;
|
const { id } = await params;
|
||||||
|
|
||||||
// Get fresh dashboard data without cache
|
// Get specific video guide data from new API
|
||||||
const dashboard = await http.get<DashboardData>(API_ROUTES.dashboard(), {
|
const response = await http.get<{ status: string; data: typeof VideoGuideSchema._type }>(
|
||||||
cache: "no-store",
|
API_ROUTES.videoGuide(id),
|
||||||
schema: DashboardSchema,
|
{
|
||||||
});
|
cache: "no-store",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const videoGuide = dashboard.videoGuides?.find(v => v.id === id);
|
const videoGuide = response.data;
|
||||||
|
|
||||||
if (!videoGuide || !videoGuide.isPurchased || !videoGuide.videoLink) {
|
if (!videoGuide || !videoGuide.isPurchased) {
|
||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +34,8 @@ export default async function VideoGuidePage({
|
|||||||
id={videoGuide.id}
|
id={videoGuide.id}
|
||||||
name={videoGuide.name}
|
name={videoGuide.name}
|
||||||
description={videoGuide.description}
|
description={videoGuide.description}
|
||||||
videoLink={videoGuide.videoLink}
|
videoLinkHLS={videoGuide.videoLinkHLS}
|
||||||
|
videoLinkDASH={videoGuide.videoLinkDASH}
|
||||||
contentUrl={videoGuide.contentUrl}
|
contentUrl={videoGuide.contentUrl}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -71,6 +71,7 @@ export default function VideoGuideCard(props: VideoGuideCardProps) {
|
|||||||
alt={name}
|
alt={name}
|
||||||
width={260}
|
width={260}
|
||||||
height={160}
|
height={160}
|
||||||
|
priority
|
||||||
unoptimized
|
unoptimized
|
||||||
className={styles.imageContent}
|
className={styles.imageContent}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -18,17 +18,16 @@ function VideoGuideCardWrapper({ videoGuide }: { videoGuide: VideoGuide }) {
|
|||||||
const { handlePurchase, isCheckoutLoading, isProcessingPurchase } =
|
const { handlePurchase, isCheckoutLoading, isProcessingPurchase } =
|
||||||
useVideoGuidePurchase({
|
useVideoGuidePurchase({
|
||||||
videoGuideId: videoGuide.id,
|
videoGuideId: videoGuide.id,
|
||||||
productId: videoGuide.id,
|
productId: videoGuide.productId, // Используем productId из payment-service
|
||||||
productKey: videoGuide.key,
|
productKey: videoGuide.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Для купленных видео - ссылка на страницу просмотра
|
// Для купленных видео - ссылка на страницу просмотра
|
||||||
const href =
|
const href = videoGuide.isPurchased
|
||||||
videoGuide.isPurchased && videoGuide.videoLink
|
? `/video-guides/${videoGuide.id}`
|
||||||
? `/video-guides/${videoGuide.id}`
|
: "#";
|
||||||
: "#";
|
|
||||||
|
|
||||||
const isClickable = videoGuide.isPurchased && videoGuide.videoLink;
|
const isClickable = videoGuide.isPurchased;
|
||||||
|
|
||||||
const cardElement = (
|
const cardElement = (
|
||||||
<VideoGuideCard
|
<VideoGuideCard
|
||||||
|
|||||||
@ -14,14 +14,16 @@ interface VideoGuideViewProps {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
videoLink: string;
|
videoLinkHLS: string;
|
||||||
|
videoLinkDASH: string;
|
||||||
contentUrl?: string;
|
contentUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function VideoGuideView({
|
export default function VideoGuideView({
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
videoLink,
|
videoLinkHLS,
|
||||||
|
videoLinkDASH,
|
||||||
contentUrl,
|
contentUrl,
|
||||||
}: VideoGuideViewProps) {
|
}: VideoGuideViewProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -51,13 +53,6 @@ export default function VideoGuideView({
|
|||||||
loadMarkdown();
|
loadMarkdown();
|
||||||
}, [contentUrl]);
|
}, [contentUrl]);
|
||||||
|
|
||||||
// TODO: Remove hardcoded URLs when backend is ready
|
|
||||||
// Temporary hardcoded DASH/HLS URLs - using for ALL videos until backend updated
|
|
||||||
const dashUrl = "https://video.witlab.us/videos/TALK_FEELINGS/cmaf/source.mpd";
|
|
||||||
const hlsUrl = "https://video.witlab.us/videos/TALK_FEELINGS/cmaf/source.m3u8";
|
|
||||||
|
|
||||||
const _originalVideoLink = videoLink; // Keep for reference when backend is ready
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
{/* Header with back button and title */}
|
{/* Header with back button and title */}
|
||||||
@ -79,7 +74,7 @@ export default function VideoGuideView({
|
|||||||
<div className={styles.contentWrapper}>
|
<div className={styles.contentWrapper}>
|
||||||
{/* Video Player */}
|
{/* Video Player */}
|
||||||
<div className={styles.videoContainer}>
|
<div className={styles.videoContainer}>
|
||||||
<VideoPlayer mpd={dashUrl} m3u8={hlsUrl} />
|
<VideoPlayer mpd={videoLinkDASH} m3u8={videoLinkHLS} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Description or Markdown Content */}
|
{/* Description or Markdown Content */}
|
||||||
|
|||||||
@ -64,6 +64,7 @@ export type PartnerPortrait = z.infer<typeof PartnerPortraitSchema>;
|
|||||||
/* ---------- Video Guide ---------- */
|
/* ---------- Video Guide ---------- */
|
||||||
export const VideoGuideSchema = z.object({
|
export const VideoGuideSchema = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
|
productId: z.string(), // ID продукта для покупки
|
||||||
key: z.string(),
|
key: z.string(),
|
||||||
type: z.string(),
|
type: z.string(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
@ -74,7 +75,8 @@ export const VideoGuideSchema = z.object({
|
|||||||
oldPrice: z.number(),
|
oldPrice: z.number(),
|
||||||
discount: z.number(),
|
discount: z.number(),
|
||||||
isPurchased: z.boolean(),
|
isPurchased: z.boolean(),
|
||||||
videoLink: z.string().optional(),
|
videoLinkHLS: z.string(), // HLS format (.m3u8)
|
||||||
|
videoLinkDASH: z.string(), // DASH format (.mpd)
|
||||||
contentUrl: z.string().optional(), // URL to markdown content file
|
contentUrl: z.string().optional(), // URL to markdown content file
|
||||||
});
|
});
|
||||||
export type VideoGuide = z.infer<typeof VideoGuideSchema>;
|
export type VideoGuide = z.infer<typeof VideoGuideSchema>;
|
||||||
|
|||||||
@ -35,8 +35,8 @@ export function useVideoGuidePurchase(options: UseVideoGuidePurchaseOptions) {
|
|||||||
// Включаем лоадер на всей карточке
|
// Включаем лоадер на всей карточке
|
||||||
setIsProcessingPurchase(true);
|
setIsProcessingPurchase(true);
|
||||||
|
|
||||||
// Ждем 4 секунды
|
// Ждем 3 секунды перед обновлением
|
||||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||||
|
|
||||||
// Обновляем данные dashboard в transition
|
// Обновляем данные dashboard в transition
|
||||||
// isPending будет true пока данные загружаются
|
// isPending будет true пока данные загружаются
|
||||||
|
|||||||
@ -13,6 +13,8 @@ const createRoute = (
|
|||||||
|
|
||||||
export const API_ROUTES = {
|
export const API_ROUTES = {
|
||||||
dashboard: () => createRoute(["dashboard"]),
|
dashboard: () => createRoute(["dashboard"]),
|
||||||
|
videoGuides: () => createRoute(["video-guides"], ROOT_ROUTE_V2),
|
||||||
|
videoGuide: (id: string) => createRoute(["video-guides", id], ROOT_ROUTE_V2),
|
||||||
checkVideoGuidePurchase: (productKey: string) =>
|
checkVideoGuidePurchase: (productKey: string) =>
|
||||||
createRoute(["products", "video-guides", productKey, "check-purchase"]),
|
createRoute(["products", "video-guides", productKey, "check-purchase"]),
|
||||||
subscriptions: () => createRoute(["payment", "subscriptions"], ROOT_ROUTE_V3),
|
subscriptions: () => createRoute(["payment", "subscriptions"], ROOT_ROUTE_V3),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user