Feature: Clips
Relevant source files
The following files were used as context for generating this wiki page:
- convex/model/clips/mutations.ts
- convex/model/clips/queries.ts
- src/app/clips/page.tsx
- src/app/clips/[clipSlug]/page.tsx
- src/features/clips/components/create-clip-sheet.tsx
- src/features/clips/components/edit-clip-sheet.tsx
- src/app/clips/[clipSlug]/_components/clip-content.tsx
- src/app/clips/_components/clips-content.tsx
The Clips feature provides short, impactful Christ-centered video or audio segments extracted from full-length talks. It serves as a discovery layer for the platform, allowing users to consume quick encouragement and navigate to full talks or speaker profiles.
Architecture and Data Model
Clips function as child entities of both Speakers and Talks. The system stores metadata including media URLs, descriptions, and life-cycle statuses to manage content visibility.
Entity Relationships
The following diagram illustrates how Clips relate to other core entities:
This diagram shows that a clip optionally belongs to one speaker and one talk, forming a hierarchical content structure.
Sources: convex/model/clips/mutations.ts:70-138, src/app/clips/[clipSlug]/page.tsx:43-58
Data Schema Properties
| Property | Type | Description |
|---|---|---|
title | string | The display name of the clip. |
slug | string | URL-friendly identifier, unique and generated from the title. |
mediaUrl | string | The direct link to the video or audio source (e.g., YouTube). |
status | enum | Lifecycle state: backlog, published, or archived. |
speakerId | id | Optional reference to a speakers document. |
talkId | id | Optional reference to a talks document. |
description | string | Optional summary of the clip content. |
Sources: convex/model/clips/mutations.ts:70-80, convex/model/clips/queries.ts:50-70
Backend Logic
The backend manages content retrieval through Convex queries and data modification via mutations. It enforces business rules regarding visibility and slug uniqueness.
Content Discovery and Filtering
The listClips query provides the primary discovery mechanism. It implements a multi-stage filtering process to ensure public users only see relevant, accessible content.
This flow represents the logic within listClips which ensures clips are only shown if their associated talk is also published.
Sources: convex/model/clips/queries.ts:85-159
Mutation Workflows
- Creation: The
createClipmutation validates the title, generates a unique slug using thegenerateSlugutility, and assigns apublishedAttimestamp if the status is set to 'published'. - Updating: The
updateClipmutation allows partial updates. If the title changes, the system recalculates the slug to maintain SEO consistency. - Archiving: A soft-delete mechanism that sets the status to
archivedand removes thepublishedAtdate. - Destruction: Permanently removes the clip document and cleans up associated user data, such as
userFavoriteClips.
Sources: convex/model/clips/mutations.ts:13-138
Frontend Components
The feature is exposed through public listing pages, detail pages, and administrative management sheets.
Clip Presentation
- Clips Page: The root
/clipsroute usesgetClipsto fetch a paginated list and renders them viaClipsContentandClipsList. - Clip Detail Page: The
/clips/[clipSlug]route performs a server-side fetch for the clip, its speaker, and the originating talk. It generatesJsonLdmetadata for SEO (VideoObject or AudioObject). - Metadata Sidebar: The
ClipContentcomponent includes a sidebar displaying the description and links to the full talk and speaker profile.
Sources: src/app/clips/page.tsx:28-45, src/app/clips/[clipSlug]/page.tsx:43-75, src/app/clips/[clipSlug]/_components/clip-content.tsx:20-56
Administrative Management
Admins manage clips through side sheets that utilize React Hook Form and Zod for validation.
This sequence shows the interaction between the UI sheets and the backend during content management.
Sources: src/features/clips/components/create-clip-sheet.tsx:45-65, src/features/clips/components/edit-clip-sheet.tsx:55-80
Implementation Details
Slug Generation Logic
When updating a clip, the system only changes the slug if the title is explicitly modified. This prevents accidental URL breakage.
if (updates.title !== undefined) {
if (!updates.title.trim()) {
throwValidationError('Title cannot be empty', 'title');
}
const newSlug = await generateSlug(ctx, 'clips', updates.title, clipId);
if (newSlug !== clip.slug) {
updates.slug = newSlug;
}
}
Sources: convex/model/clips/mutations.ts:109-122
Sitemap and SEO
The listClipSlugsForSitemap query retrieves all published clip slugs and their last update times to facilitate search engine indexing. The detail page dynamically chooses between VideoObject and AudioObject schema types based on the media URL.
Sources: convex/model/clips/queries.ts:32-45, src/app/clips/[clipSlug]/page.tsx:50-52
Clips provide a condensed content format that drives engagement by highlighting specific moments from full talks while maintaining strict relationships with Speakers and parent Talk entities.