Skip to content

Commit

Permalink
feat: lean into content resource (#97)
Browse files Browse the repository at this point in the history
* feat: lean into content resource

* chore: lockfile

* chore: lockfile

* version bumps
  • Loading branch information
joelhooks committed Mar 6, 2024
1 parent 4c57873 commit 9faae9f
Show file tree
Hide file tree
Showing 29 changed files with 523 additions and 395 deletions.
5 changes: 5 additions & 0 deletions .changeset/great-cobras-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"create-course-app": patch
---

next-auth version bump
7 changes: 4 additions & 3 deletions apps/course-builder-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"@mdx-js/react": "^3.0.0",
"@msgpack/msgpack": "3.0.0-beta2",
"@mux/mux-player-react": "^2.2.0",
"@next/mdx": "canary",
"@next/mdx": "14.1.2-canary.1",
"@planetscale/database": "1.14.0",
"@radix-ui/colors": "^3.0.0",
"@radix-ui/react-alert-dialog": "^1.0.5",
Expand Down Expand Up @@ -103,6 +103,7 @@
"clsx": "^2.0.0",
"cmdk": "^0.2.0",
"codemirror": "^6.0.1",
"crypto": "^1.0.1",
"date-fns": "^2.30.0",
"drizzle-orm": "^0.29.3",
"framer-motion": "^10.16.5",
Expand All @@ -116,8 +117,8 @@
"lucide-react": "^0.288.0",
"memoize-one": "^6.0.0",
"nanoid": "^5.0.2",
"next": "canary",
"next-auth": "^4.24.5",
"next": "14.1.2-canary.1",
"next-auth": "^4.24.6",
"next-axiom": "^1.1.1",
"next-mdx-remote": "^4.4.1",
"next-themes": "^0.2.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import * as React from 'react'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import { CodemirrorEditor } from '@/app/_components/codemirror'
import { updateArticle } from '@/app/articles/_components/article-form-actions'
import { useIsMobile } from '@/hooks/use-is-mobile'
import { useSocket } from '@/hooks/use-socket'
import { Article, ArticleSchema, ArticleVisibilitySchema } from '@/lib/articles'
import { updateArticle } from '@/lib/articles-query'
import { FeedbackMarker } from '@/lib/feedback-marker'
import { cn } from '@/lib/utils'
import { api } from '@/trpc/react'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import * as React from 'react'
import { useRouter } from 'next/navigation'
import { createArticle, NewArticle, NewArticleSchema } from '@/app/articles/_components/article-form-actions'
import { NewArticle, NewArticleSchema } from '@/lib/articles'
import { createArticle } from '@/lib/articles-query'
import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'

Expand Down
4 changes: 0 additions & 4 deletions apps/course-builder-web/src/app/rsc/action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,13 @@ async function submitUserMessage(content: string) {

const { success, limit, reset, remaining } = await ratelimit.limit(`ratelimit_${ip}`)

console.log({ success, limit, reset, remaining, ip })

if (!success) {
return {
id: Date.now(),
display: <div>Rate limit exceeded. Please try again in {reset} seconds.</div>,
}
}

console.log('submitUserMessage', content)

const aiState = getMutableAIState<typeof AI>()
aiState.update([
...aiState.get(),
Expand Down
4 changes: 2 additions & 2 deletions apps/course-builder-web/src/app/tips/[slug]/edit/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import { VIDEO_RESOURCE_CREATED_EVENT } from '@/inngest/events/video-resource'
import { inngest } from '@/inngest/inngest.server'
import { getAbility } from '@/lib/ability'
import { getVideoResource } from '@/lib/video-resource'
import { getVideoResource } from '@/lib/video-resource-query'
import { getServerAuthSession } from '@/server/auth'

export async function reprocessTranscript({ videoResourceId }: { videoResourceId: string | null }) {
export async function reprocessTranscript({ videoResourceId }: { videoResourceId?: string | null }) {
// template for the url to download the mp4 file from mux
// https://stream.mux.com/{PLAYBACK_ID}/{MP4_FILE_NAME}?download={FILE_NAME}
const session = await getServerAuthSession()
Expand Down
2 changes: 1 addition & 1 deletion apps/course-builder-web/src/app/tips/[slug]/edit/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react'
import { notFound } from 'next/navigation'
import { getAbility } from '@/lib/ability'
import { getTip } from '@/lib/tips-query'
import { getTranscriptWithScreenshots, getVideoResource } from '@/lib/video-resource'
import { getTranscriptWithScreenshots, getVideoResource } from '@/lib/video-resource-query'
import { getServerAuthSession } from '@/server/auth'

import { EditTipForm } from '../../_components/edit-tip-form'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { ImageResponse } from 'next/og'
import { getTip } from '@/lib/tips-query'

export const runtime = 'edge'
export const revalidate = 60

export default async function TipOG({ params }: { params: { slug: string } }) {
Expand Down
2 changes: 1 addition & 1 deletion apps/course-builder-web/src/app/tips/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { TipPlayer } from '@/app/tips/_components/tip-player'
import { getAbility } from '@/lib/ability'
import { type Tip } from '@/lib/tips'
import { getTip } from '@/lib/tips-query'
import { getTranscript, getVideoResource } from '@/lib/video-resource'
import { getTranscript, getVideoResource } from '@/lib/video-resource-query'
import { getServerAuthSession } from '@/server/auth'
import { getOGImageUrlForResource } from '@/utils/get-og-image-url-for-resource'
import ReactMarkdown from 'react-markdown'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import Image from 'next/image'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import { CodemirrorEditor } from '@/app/_components/codemirror'
import { requestCodeExtraction, updateTip } from '@/app/tips/_components/tip-form-actions'
import { requestCodeExtraction } from '@/app/tips/_components/tip-form-actions'
import { TipPlayer } from '@/app/tips/_components/tip-player'
import { reprocessTranscript } from '@/app/tips/[slug]/edit/actions'
import { useIsMobile } from '@/hooks/use-is-mobile'
import { useSocket } from '@/hooks/use-socket'
import { FeedbackMarker } from '@/lib/feedback-marker'
import { TipSchema, TipUpdate, type Tip } from '@/lib/tips'
import { updateTip } from '@/lib/tips-query'
import { cn } from '@/lib/utils'
import { VideoResource } from '@/lib/video-resource'
import { api } from '@/trpc/react'
Expand Down Expand Up @@ -106,7 +107,7 @@ const DesktopEditTipForm: React.FC<EditTipFormProps> = ({
const [updateTipStatus, setUpdateTipStatus] = React.useState<'idle' | 'loading' | 'success' | 'error'>('idle')
const [feedbackMarkers, setFeedbackMarkers] = React.useState<FeedbackMarker[]>([])
const [transcript, setTranscript] = React.useState<string | null>(videoResource?.transcript || null)
const [videoResourceId, setVideoResourceId] = React.useState<string | null>(tip.videoResourceId)
const [videoResourceId, setVideoResourceId] = React.useState<string | null | undefined>(tip.videoResourceId)
const router = useRouter()

const { mutateAsync: generateFeedback } = api.writing.generateFeedback.useMutation()
Expand Down Expand Up @@ -405,7 +406,7 @@ const MobileEditTipForm: React.FC<EditTipFormProps> = ({
const [updateTipStatus, setUpdateTipStatus] = React.useState<'idle' | 'loading' | 'success' | 'error'>('idle')
const [feedbackMarkers, setFeedbackMarkers] = React.useState<FeedbackMarker[]>([])
const [transcript, setTranscript] = React.useState<string | null>(videoResource?.transcript || null)
const [videoResourceId, setVideoResourceId] = React.useState<string | null>(tip.videoResourceId)
const [videoResourceId, setVideoResourceId] = React.useState<string | null | undefined>(tip.videoResourceId)
const router = useRouter()

useSocket({
Expand Down
88 changes: 67 additions & 21 deletions apps/course-builder-web/src/app/tips/_components/new-tip-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@

import * as React from 'react'
import { useRouter } from 'next/navigation'
import { createTip } from '@/app/tips/_components/tip-form-actions'
import { TipUploader } from '@/app/tips/_components/tip-uploader'
import { NewTip, NewTipSchema } from '@/lib/tips'
import { api } from '@/trpc/react'
import { createTip } from '@/lib/tips-query'
import { getVideoResource } from '@/lib/video-resource-query'
import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
import { z } from 'zod'

import {
Button,
Expand All @@ -23,7 +22,9 @@ import {
} from '@coursebuilder/ui'

export function NewTipForm() {
const [videoResourceId, setVideoResourceId] = React.useState<string>('')
const [videoResourceId, setVideoResourceId] = React.useState<string | undefined>()
const [videoResourceValid, setVideoResourceValid] = React.useState<boolean>(false)
const [isValidatingVideoResource, setIsValidatingVideoResource] = React.useState<boolean>(false)
const router = useRouter()

const form = useForm<NewTip>({
Expand All @@ -34,17 +35,65 @@ export function NewTipForm() {
},
})

const onSubmit = async (values: NewTip) => {
form.reset()
setVideoResourceId('')
form.setValue('videoResourceId', '')
async function* pollVideoResource(
videoResourceId: string,
maxAttempts = 30,
initialDelay = 250,
delayIncrement = 250,
) {
let delay = initialDelay

for (let attempt = 1; attempt <= maxAttempts; attempt++) {
const videoResource = await getVideoResource(videoResourceId)
if (videoResource) {
yield videoResource
return
}

await new Promise((resolve) => setTimeout(resolve, delay))
delay += delayIncrement
}

throw new Error('Video resource not found after maximum attempts')
}

const tip = await createTip(values)
const onSubmit = async (values: NewTip) => {
try {
for await (const _ of pollVideoResource(values.videoResourceId)) {
const tip = await createTip(values)
if (!tip) {
// handle edge, e.g. toast an error message
return
}
router.push(`/tips/${tip.slug}/edit`)
}
} catch (error) {
console.error('Error polling video resource:', error)
// handle error, e.g. toast an error message
} finally {
form.reset()
setVideoResourceId(undefined)
form.setValue('videoResourceId', '')
}
}

if (!tip) {
// handle edge, e.g. toast an error message
} else {
router.push(`/tips/${tip.slug}/edit`)
async function handleSetVideoResourceId(videoResourceId: string) {
setVideoResourceId(videoResourceId)
setIsValidatingVideoResource(true)
form.setValue('videoResourceId', videoResourceId)
try {
for await (const videoResource of pollVideoResource(videoResourceId)) {
console.log('Video resource found:', videoResource)
setVideoResourceValid(true)
}
setIsValidatingVideoResource(false)
} catch (error) {
console.error('Error validating video resource:', error)
setVideoResourceValid(false)
form.setError('videoResourceId', { message: 'Video resource not found' })
setVideoResourceId('')
form.setValue('videoResourceId', '')
setIsValidatingVideoResource(false)
}
}

Expand Down Expand Up @@ -80,13 +129,8 @@ export function NewTipForm() {
<FormControl>
<Input type="hidden" {...field} />
</FormControl>
{videoResourceId.length === 0 ? (
<TipUploader
setVideoResourceId={(videoResourceId: string) => {
form.setValue('videoResourceId', videoResourceId)
setVideoResourceId(videoResourceId)
}}
/>
{!videoResourceId ? (
<TipUploader setVideoResourceId={handleSetVideoResourceId} />
) : (
<div>
{videoResourceId}{' '}
Expand All @@ -100,12 +144,14 @@ export function NewTipForm() {
</Button>
</div>
)}
{isValidatingVideoResource && !videoResourceValid ? <FormMessage>Processing Upload</FormMessage> : null}

<FormMessage />
</FormItem>
)}
/>

<Button type="submit" className="mt-2" variant="default" disabled={videoResourceId.length === 0}>
<Button type="submit" className="mt-2" variant="default" disabled={!videoResourceValid}>
Create Draft Tip
</Button>
</form>
Expand Down

0 comments on commit 9faae9f

Please sign in to comment.