Korai Docs
ChatWithVideo

Chat with Video - Store

Zustand store managing video chat state

Overview

The Video Chat Store uses Zustand to manage all state related to the chat with video feature, including video details, transcript data, thread generation state, and UI controls.

Store File

Location: src/features/chat/store/video-chat-store.ts

State Interface

interface VideoChatState {
  // Video state
  videoUrl: string;
  videoId: string;
  transcript: string;
  hasTranscript: boolean;
  isLoadingTranscript: boolean;

  // Thread state
  threads: ThreadData[];
  generatingThreads: boolean;
  threadsModalOpen: boolean;

  // Actions
  setVideoUrl: (url: string) => void;
  setVideoId: (id: string) => void;
  setTranscript: (transcript: string) => void;
  setHasTranscript: (has: boolean) => void;
  setIsLoadingTranscript: (loading: boolean) => void;
  setThreads: (threads: ThreadData[]) => void;
  setGeneratingThreads: (generating: boolean) => void;
  setThreadsModalOpen: (open: boolean) => void;
  resetChat: () => void;
}

ThreadData Interface

interface ThreadData {
  post: number;        // Post number (1, 2, 3...)
  content: string;     // Post content text
  total: number;       // Total number of posts in thread
  thumbnail?: string;  // Video thumbnail (first post only)
}

State Properties

Video State

videoUrl

  • Type: string
  • Default: ''
  • Purpose: Stores the YouTube URL entered by user
  • Usage: Input field value, passed to transcript fetching

videoId

  • Type: string
  • Default: ''
  • Purpose: Extracted YouTube video ID
  • Usage: Used for thumbnail URLs and API calls

transcript

  • Type: string
  • Default: ''
  • Purpose: Full video transcript text
  • Usage: Passed to AI as context in system prompt

hasTranscript

  • Type: boolean
  • Default: false
  • Purpose: Indicates if transcript has been successfully loaded
  • Usage: Controls whether to show chat interface or video input

isLoadingTranscript

  • Type: boolean
  • Default: false
  • Purpose: Loading state during transcript fetch
  • Usage: Disables input and shows loading spinner

Thread State

threads

  • Type: ThreadData[]
  • Default: []
  • Purpose: Stores generated Twitter thread posts
  • Usage: Displayed in thread modal, copied to clipboard

generatingThreads

  • Type: boolean
  • Default: false
  • Purpose: Loading state during thread generation
  • Usage: Shows skeleton loader in thread modal

threadsModalOpen

  • Type: boolean
  • Default: false
  • Purpose: Controls visibility of thread modal
  • Usage: Opens when thread generation starts, closes on dismiss

Actions

setVideoUrl

setVideoUrl: (url: string) => void

Updates the YouTube video URL.

Usage:

const { setVideoUrl } = useVideoChatStore();
setVideoUrl('https://youtube.com/watch?v=...');

setVideoId

setVideoId: (id: string) => void

Updates the extracted video ID.

Usage:

const { setVideoId } = useVideoChatStore();
setVideoId('dQw4w9WgXcQ');

setTranscript

setTranscript: (transcript: string) => void

Updates the video transcript text.

Usage:

const { setTranscript } = useVideoChatStore();
setTranscript(data.transcript.fullTranscript);

setHasTranscript

setHasTranscript: (has: boolean) => void

Updates whether transcript has been loaded.

Usage:

const { setHasTranscript } = useVideoChatStore();
setHasTranscript(true);

setIsLoadingTranscript

setIsLoadingTranscript: (loading: boolean) => void

Updates the transcript loading state.

Usage:

const { setIsLoadingTranscript } = useVideoChatStore();
setIsLoadingTranscript(true);
// ... fetch transcript ...
setIsLoadingTranscript(false);

setThreads

setThreads: (threads: ThreadData[]) => void

Updates the generated thread posts array.

Usage:

const { setThreads } = useVideoChatStore();
setThreads([
  { post: 1, content: '...', total: 5, thumbnail: '...' },
  { post: 2, content: '...', total: 5 },
  // ...
]);

setGeneratingThreads

setGeneratingThreads: (generating: boolean) => void

Updates the thread generation loading state.

Usage:

const { setGeneratingThreads } = useVideoChatStore();
setGeneratingThreads(true);

setThreadsModalOpen

setThreadsModalOpen: (open: boolean) => void

Controls thread modal visibility.

Usage:

const { setThreadsModalOpen } = useVideoChatStore();
setThreadsModalOpen(true); // Open modal

resetChat

resetChat: () => void

Resets all state to initial values.

Usage:

const { resetChat } = useVideoChatStore();
resetChat(); // Clear everything, start fresh

Initial State

const initialState = {
  videoUrl: '',
  videoId: '',
  transcript: '',
  hasTranscript: false,
  isLoadingTranscript: false,
  threads: [],
  generatingThreads: false,
  threadsModalOpen: false
};

Store Implementation

export const useVideoChatStore = create<VideoChatState>((set) => ({
  ...initialState,

  setVideoUrl: (url) => set({ videoUrl: url }),
  setVideoId: (id) => set({ videoId: id }),
  setTranscript: (transcript) => set({ transcript }),
  setHasTranscript: (has) => set({ hasTranscript: has }),
  setIsLoadingTranscript: (loading) => set({ isLoadingTranscript: loading }),
  setThreads: (threads) => set({ threads }),
  setGeneratingThreads: (generating) => set({ generatingThreads: generating }),
  setThreadsModalOpen: (open) => set({ threadsModalOpen: open }),
  resetChat: () => set(initialState)
}));

Usage Example

In a Component

import { useVideoChatStore } from '../store/video-chat-store';

function ChatComponent() {
  const {
    videoUrl,
    transcript,
    hasTranscript,
    isLoadingTranscript,
    setVideoUrl,
    setTranscript
  } = useVideoChatStore();

  // Use state and actions
  return (
    <div>
      <input
        value={videoUrl}
        onChange={(e) => setVideoUrl(e.target.value)}
      />
      {hasTranscript && <ChatInterface transcript={transcript} />}
    </div>
  );
}

In a Hook

import { useVideoChatStore } from '../store/video-chat-store';

export function useVideoTranscript() {
  const {
    videoUrl,
    setTranscript,
    setHasTranscript,
    setIsLoadingTranscript
  } = useVideoChatStore();

  const fetchTranscript = async () => {
    setIsLoadingTranscript(true);
    // Fetch logic...
    setTranscript(data.transcript);
    setHasTranscript(true);
    setIsLoadingTranscript(false);
  };

  return { fetchTranscript };
}

State Flow

Video Loading Flow

User enters URL → setVideoUrl(url)

Validation passes → setIsLoadingTranscript(true)

Extract video ID → setVideoId(id)

Fetch transcript from API

Success → setTranscript(text) → setHasTranscript(true)

setIsLoadingTranscript(false)

Thread Generation Flow

User clicks "Thread" → setGeneratingThreads(true)

Open modal → setThreadsModalOpen(true)

Clear previous threads → setThreads([])

AI generates thread posts

Parse and format → setThreads(parsedThreads)

setGeneratingThreads(false)

Reset Flow

User clicks "New Video" → resetChat()

All state returns to initial values

Chat interface hidden, video input shown