diff --git a/apps/ai-hero/src/app/(content)/[post]/page.tsx b/apps/ai-hero/src/app/(content)/[post]/page.tsx index 894bf5c02..ae26639c9 100644 --- a/apps/ai-hero/src/app/(content)/[post]/page.tsx +++ b/apps/ai-hero/src/app/(content)/[post]/page.tsx @@ -11,7 +11,10 @@ import Scrollycoding from '@/components/codehike/scrollycoding' import { PlayerContainerSkeleton } from '@/components/player-skeleton' import { PrimaryNewsletterCta } from '@/components/primary-newsletter-cta' import { Share } from '@/components/share' +import Spinner from '@/components/spinner' import { courseBuilderAdapter } from '@/db' +import type { List } from '@/lib/lists' +import { getListForPost } from '@/lib/lists-query' import { type Post } from '@/lib/posts' import { getAllPosts, getPost } from '@/lib/posts-query' import { getPricingProps } from '@/lib/pricing-query' @@ -25,7 +28,9 @@ import { compileMDX, MDXRemote } from 'next-mdx-remote/rsc' import remarkGfm from 'remark-gfm' import { Button } from '@coursebuilder/ui' +import { VideoPlayerOverlayProvider } from '@coursebuilder/ui/hooks/use-video-player-overlay' +import PostNextUpFromListPagination from '../_components/post-next-up-from-list-pagination' import { PostPlayer } from '../posts/_components/post-player' import { PostNewsletterCta } from '../posts/_components/post-video-subscribe-form' @@ -141,6 +146,7 @@ export default async function PostPage(props: { const searchParams = await props.searchParams const params = await props.params const post = await getPost(params.post) + const cookieStore = await cookies() const ckSubscriber = cookieStore.has(CK_SUBSCRIBER_KEY) const { allowPurchase, pricingDataLoader, product, commerceProps } = @@ -156,6 +162,8 @@ export default async function PostPage(props: { notFound() } + const listLoader = getListForPost(post.id) + const squareGridPattern = generateGridPattern( post.fields.title, 1000, @@ -170,11 +178,10 @@ export default async function PostPage(props: { return (
- {hasVideo && } + {hasVideo && }
- {/*
- -
-
*/} +
+ }> + +
Share
- {/* */}
@@ -262,7 +257,13 @@ export default async function PostPage(props: { ) } -async function PlayerContainer({ post }: { post: Post | null }) { +async function PlayerContainer({ + post, + listLoader, +}: { + post: Post | null + listLoader: Promise +}) { const displayOverlay = false if (!post) { @@ -276,21 +277,25 @@ async function PlayerContainer({ post }: { post: Post | null }) { const ckSubscriber = cookieStore.has(CK_SUBSCRIBER_KEY) return videoResource ? ( - - } - > -
+ + } > - - {!ckSubscriber && } -
-
+
+ + {!ckSubscriber && } +
+ + ) : resource ? null : null // spacer //
} diff --git a/apps/ai-hero/src/app/(content)/_components/post-next-up-from-list-pagination.tsx b/apps/ai-hero/src/app/(content)/_components/post-next-up-from-list-pagination.tsx new file mode 100644 index 000000000..8b933f774 --- /dev/null +++ b/apps/ai-hero/src/app/(content)/_components/post-next-up-from-list-pagination.tsx @@ -0,0 +1,39 @@ +import React from 'react' +import Link from 'next/link' +import { type List } from '@/lib/lists' +import { getNextUpResourceFromList } from '@/utils/get-nextup-resource-from-list' +import { ArrowRight, ChevronRight } from 'lucide-react' + +import { Button } from '@coursebuilder/ui' + +export default function PostNextUpFromListPagination({ + listLoader, + postId, +}: { + listLoader: Promise + postId: string +}) { + const list = React.use(listLoader) + const nextUp = list && getNextUpResourceFromList(list, postId) + return nextUp?.resource && nextUp?.resource?.fields?.state === 'published' ? ( + + ) : null +} diff --git a/apps/ai-hero/src/app/(content)/lists/_components/create-list-form.tsx b/apps/ai-hero/src/app/(content)/lists/_components/create-list-form.tsx index 799bd0e73..483ffc6df 100644 --- a/apps/ai-hero/src/app/(content)/lists/_components/create-list-form.tsx +++ b/apps/ai-hero/src/app/(content)/lists/_components/create-list-form.tsx @@ -26,7 +26,11 @@ export function CreateListForm() { } return ( -
+ +

Create New List

@@ -48,7 +52,7 @@ export function CreateListForm() { id="description" value={description} onChange={(e) => setDescription(e.target.value)} - className="block w-full rounded-md border border-gray-300 px-3 py-2" + // className="block w-full rounded-md border border-gray-300 px-3 py-2" rows={3} />
diff --git a/apps/ai-hero/src/app/(content)/lists/_components/lists-table.tsx b/apps/ai-hero/src/app/(content)/lists/_components/lists-table.tsx index e26244680..635721383 100644 --- a/apps/ai-hero/src/app/(content)/lists/_components/lists-table.tsx +++ b/apps/ai-hero/src/app/(content)/lists/_components/lists-table.tsx @@ -29,7 +29,7 @@ export function ListsTable({ return ( <> {error && {error}} - +
Title @@ -49,12 +49,14 @@ export function ListsTable({ ? `/lists/${list.fields.slug}/edit` : `/lists/${list.fields.slug}` } - className="text-primary hover:underline" + className="text-primary text-lg font-medium hover:underline" > {list.fields.title} - {list.fields.description} + + {list.fields.description} + {list.fields.type} {list.resources?.length || 0} diff --git a/apps/ai-hero/src/app/(content)/lists/page.tsx b/apps/ai-hero/src/app/(content)/lists/page.tsx index 6fa5c808d..600792b52 100644 --- a/apps/ai-hero/src/app/(content)/lists/page.tsx +++ b/apps/ai-hero/src/app/(content)/lists/page.tsx @@ -25,9 +25,11 @@ export default async function ListsPage() { const canCreateContent = ability.can('create', 'Content') return (
-

Lists

- - {canCreateContent && } +

Lists

+
+ + {canCreateContent && } +
) } diff --git a/apps/ai-hero/src/app/(content)/posts/_components/edit-post-form-metadata.tsx b/apps/ai-hero/src/app/(content)/posts/_components/edit-post-form-metadata.tsx index 89a3f07cb..563a5e073 100644 --- a/apps/ai-hero/src/app/(content)/posts/_components/edit-post-form-metadata.tsx +++ b/apps/ai-hero/src/app/(content)/posts/_components/edit-post-form-metadata.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import { Suspense, use } from 'react' import Link from 'next/link' import { useRouter } from 'next/navigation' -import { PostPlayer } from '@/app/(content)/posts/_components/post-player' +import { SimplePostPlayer } from '@/app/(content)/posts/_components/post-player' import Spinner from '@/components/spinner' import { env } from '@/env.mjs' import { useTranscript } from '@/hooks/use-transcript' @@ -155,7 +155,7 @@ export const PostMetadataFormFields: React.FC<{ <> {videoResource && videoResource.state === 'ready' ? (
- +
+
+ )} +
+ ) +} + +export function SimplePostPlayer({ + muxPlaybackId, + className, + videoResource, }: { muxPlaybackId?: string videoResource: VideoResource diff --git a/apps/ai-hero/src/app/(content)/posts/_components/post-video-subscribe-form.tsx b/apps/ai-hero/src/app/(content)/posts/_components/post-video-subscribe-form.tsx index 923fde45d..de922f5c6 100644 --- a/apps/ai-hero/src/app/(content)/posts/_components/post-video-subscribe-form.tsx +++ b/apps/ai-hero/src/app/(content)/posts/_components/post-video-subscribe-form.tsx @@ -60,7 +60,7 @@ export const PostNewsletterCta: React.FC< className="via-muted-foreground/20 absolute -bottom-px left-0 z-10 h-px w-full bg-gradient-to-r from-transparent to-transparent" aria-hidden="true" /> -
+
row.resource_id) + .map((row: any) => ({ + resource: { + id: row.resource_id, + type: row.resource_type, + fields: row.resource_fields, + }, + position: row.resource_position, + resourceId: row.resource_id, + resourceOfId: firstRow.list_id, + })) + .sort((a: any, b: any) => a.position - b.position), + } + + return ListSchema.parse(list) +} + export async function addPostToList({ postId, listId, diff --git a/apps/ai-hero/src/utils/get-nextup-resource-from-list.ts b/apps/ai-hero/src/utils/get-nextup-resource-from-list.ts new file mode 100644 index 000000000..67ebeb638 --- /dev/null +++ b/apps/ai-hero/src/utils/get-nextup-resource-from-list.ts @@ -0,0 +1,16 @@ +import { type List } from '@/lib/lists' + +export function getNextUpResourceFromList( + list: List, + currentResourceId: string, +) { + if (list?.fields?.type !== 'nextUp') return null + + const currentResourceIndexFromList = list?.resources.findIndex( + (r) => r.resource.id === currentResourceId, + ) + const nextUpIndex = currentResourceIndexFromList + 1 + const nextUpResource = list?.resources?.[nextUpIndex] + + return nextUpResource || null +}