Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/web/actions/videos/get-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { db } from "@cap/database";
import { users, videos } from "@cap/database/schema";
import type { VideoMetadata } from "@cap/database/types";
import { serverEnv } from "@cap/env";
import { provideOptionalAuth, VideosPolicy } from "@cap/web-backend";
import { Policy, type Video } from "@cap/web-domain";
import { eq } from "drizzle-orm";
Expand Down Expand Up @@ -45,7 +46,7 @@ export async function getVideoStatus(

const metadata: VideoMetadata = (video.metadata as VideoMetadata) || {};

if (!video.transcriptionStatus) {
if (!video.transcriptionStatus && serverEnv().DEEPGRAM_API_KEY) {
console.log(
`[Get Status] Transcription not started for video ${videoId}, triggering transcription`,
);
Expand Down
11 changes: 2 additions & 9 deletions apps/web/app/s/[videoId]/Share.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { ShareVideo } from "./_components/ShareVideo";
import { Sidebar } from "./_components/Sidebar";
import SummaryChapters from "./_components/SummaryChapters";
import { Toolbar } from "./_components/Toolbar";
import type { VideoData } from "./types";

type CommentWithAuthor = typeof commentsSchema.$inferSelect & {
authorName: string | null;
Expand All @@ -34,16 +35,8 @@ export type CommentType = typeof commentsSchema.$inferSelect & {
sending?: boolean;
};

type VideoWithOrganizationInfo = typeof videos.$inferSelect & {
organizationMembers?: string[];
organizationId?: string;
sharedOrganizations?: { id: string; name: string }[];
hasPassword?: boolean;
orgSettings?: OrganizationSettings | null;
};

interface ShareProps {
data: VideoWithOrganizationInfo;
data: VideoData;
comments: MaybePromise<CommentWithAuthor[]>;
views: MaybePromise<number>;
customDomain: string | null;
Expand Down
112 changes: 0 additions & 112 deletions apps/web/app/s/[videoId]/_components/ImageViewer.tsx

This file was deleted.

29 changes: 12 additions & 17 deletions apps/web/app/s/[videoId]/_components/ShareHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
"use client";

import type { userSelectProps } from "@cap/database/auth/session";
import type { videos } from "@cap/database/schema";
import { buildEnv, NODE_ENV } from "@cap/env";
import { Button } from "@cap/ui";
import { userIsPro } from "@cap/utils";
import type { ImageUpload } from "@cap/web-domain";
import { type ImageUpload, User } from "@cap/web-domain";
import { faChevronDown, faLock } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Check, Copy, Globe2 } from "lucide-react";
Expand All @@ -21,6 +19,7 @@ import { useCurrentUser } from "@/app/Layout/AuthContext";
import { SignedImageUrl } from "@/components/SignedImageUrl";
import { UpgradeModal } from "@/components/UpgradeModal";
import { usePublicEnv } from "@/utils/public-env";
import type { VideoData } from "../types";

export const ShareHeader = ({
data,
Expand All @@ -30,11 +29,7 @@ export const ShareHeader = ({
sharedSpaces = [],
spacesData = null,
}: {
data: typeof videos.$inferSelect & {
ownerName?: string | null;
ownerImage?: ImageUpload.ImageUrl | null;
ownerIsPro?: boolean;
};
data: VideoData;
customDomain?: string | null;
domainVerified?: boolean;
sharedOrganizations?: { id: string; name: string }[];
Expand Down Expand Up @@ -65,7 +60,7 @@ export const ShareHeader = ({
const contextSharedSpaces = contextData?.sharedSpaces || null;
const effectiveSharedSpaces = contextSharedSpaces || sharedSpaces;

const isOwner = user && user.id === data.ownerId;
const isOwner = user && user.id === data.owner.id;

const { webUrl } = usePublicEnv();

Expand Down Expand Up @@ -132,8 +127,6 @@ export const ShareHeader = ({
}
};

const isVideoOwnerPro: boolean = data.ownerIsPro ?? false;

const handleSharingUpdated = () => {
refresh();
};
Expand Down Expand Up @@ -182,9 +175,11 @@ export const ShareHeader = ({
}
};

const userIsOwnerAndNotPro = user?.id === data.owner.id && !data.owner.isPro;

return (
<>
{isOwner && !isVideoOwnerPro && (
{userIsOwnerAndNotPro && (
<div className="flex sticky flex-col sm:flex-row inset-x-0 top-0 z-10 gap-4 justify-center items-center px-3 py-2 mx-auto w-[calc(100%-20px)] max-w-fit rounded-b-xl border bg-gray-4 border-gray-6">
<p className="text-center text-gray-12">
Shareable links are limited to 5 mins on the free plan.
Expand Down Expand Up @@ -238,16 +233,16 @@ export const ShareHeader = ({
</div>
<div className="flex gap-7 items-center">
<div className="flex gap-2 items-center">
{data.ownerName && (
{data.name && (
<SignedImageUrl
name={data.ownerName}
image={data.ownerImage}
name={data.name}
image={data.owner.image}
className="size-8"
letterClass="text-base"
/>
)}
<div className="flex flex-col text-left">
<p className="text-sm text-gray-12">{data.ownerName}</p>
<p className="text-sm text-gray-12">{data.name}</p>
<p className="text-xs text-gray-10">
Comment on lines 235 to 246
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix owner display: using video title instead of owner name

SignedImageUrl and the text below use data.name (video title). They should use the owner’s name and image.

- {data.name && (
+ {data.owner?.name && (
    <SignedImageUrl
-     name={data.name}
-     image={data.owner.image}
+     name={data.owner.name}
+     image={data.owner.image}
      className="size-8"
      letterClass="text-base"
    />
  )}
...
- <p className="text-sm text-gray-12">{data.name}</p>
+ <p className="text-sm text-gray-12">{data.owner?.name ?? "Unknown user"}</p>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div className="flex gap-2 items-center">
{data.ownerName && (
{data.name && (
<SignedImageUrl
name={data.ownerName}
image={data.ownerImage}
name={data.name}
image={data.owner.image}
className="size-8"
letterClass="text-base"
/>
)}
<div className="flex flex-col text-left">
<p className="text-sm text-gray-12">{data.ownerName}</p>
<p className="text-sm text-gray-12">{data.name}</p>
<p className="text-xs text-gray-10">
<div className="flex gap-2 items-center">
{data.owner?.name && (
<SignedImageUrl
name={data.owner.name}
image={data.owner.image}
className="size-8"
letterClass="text-base"
/>
)}
<div className="flex flex-col text-left">
<p className="text-sm text-gray-12">{data.owner?.name ?? "Unknown user"}</p>
<p className="text-xs text-gray-10">
🤖 Prompt for AI Agents
In apps/web/app/s/[videoId]/_components/ShareHeader.tsx around lines 235 to 246,
the owner UI is incorrectly using data.name (the video title); change the
SignedImageUrl component prop and the text elements to use the owner's name
(data.owner.name) instead, and add a safe guard (e.g. data.owner?.name and
data.owner?.image) so the image and name render only when owner exists.

{moment(data.createdAt).fromNow()}
</p>
Expand Down Expand Up @@ -285,7 +280,7 @@ export const ShareHeader = ({
)}
</Button>
</div>
{!isVideoOwnerPro && (
{userIsOwnerAndNotPro && (
<button
type="button"
className="flex items-center mt-2 mb-3 text-sm text-gray-400 duration-200 cursor-pointer hover:text-blue-500"
Expand Down
15 changes: 7 additions & 8 deletions apps/web/app/s/[videoId]/_components/ShareVideo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from "react";
import type { OrganizationSettings } from "@/app/(org)/dashboard/dashboard-data";
import { UpgradeModal } from "@/components/UpgradeModal";
import type { VideoData } from "../types";
import { CapVideoPlayer } from "./CapVideoPlayer";
import { HLSVideoPlayer } from "./HLSVideoPlayer";
import {
Expand All @@ -36,10 +37,8 @@ type CommentWithAuthor = typeof commentsSchema.$inferSelect & {
export const ShareVideo = forwardRef<
HTMLVideoElement,
{
data: typeof videos.$inferSelect & {
ownerIsPro?: boolean;
data: VideoData & {
hasActiveUpload?: boolean;
orgSettings?: OrganizationSettings | null;
};
comments: MaybePromise<CommentWithAuthor[]>;
chapters?: { title: string; start: number }[];
Expand Down Expand Up @@ -166,19 +165,19 @@ export const ShareVideo = forwardRef<
let enableCrossOrigin = false;

if (data.source.type === "desktopMP4") {
videoSrc = `/api/playlist?userId=${data.ownerId}&videoId=${data.id}&videoType=mp4`;
videoSrc = `/api/playlist?userId=${data.owner.id}&videoId=${data.id}&videoType=mp4`;
// Start with CORS enabled for desktopMP4, but CapVideoPlayer will dynamically disable if needed
enableCrossOrigin = true;
} else if (
NODE_ENV === "development" ||
((data.skipProcessing === true || data.jobStatus !== "COMPLETE") &&
data.source.type === "MediaConvert")
) {
videoSrc = `/api/playlist?userId=${data.ownerId}&videoId=${data.id}&videoType=master`;
videoSrc = `/api/playlist?userId=${data.owner.id}&videoId=${data.id}&videoType=master`;
} else if (data.source.type === "MediaConvert") {
videoSrc = `/api/playlist?userId=${data.ownerId}&videoId=${data.id}&videoType=video`;
videoSrc = `/api/playlist?userId=${data.owner.id}&videoId=${data.id}&videoType=video`;
} else {
videoSrc = `/api/playlist?userId=${data.ownerId}&videoId=${data.id}&videoType=video`;
videoSrc = `/api/playlist?userId=${data.owner.id}&videoId=${data.id}&videoType=video`;
}

return (
Expand Down Expand Up @@ -221,7 +220,7 @@ export const ShareVideo = forwardRef<
)}
</div>

{!data.ownerIsPro && (
{!data.owner.isPro && (
<div className="absolute top-4 left-4 z-30">
<div
className="block cursor-pointer"
Expand Down
18 changes: 6 additions & 12 deletions apps/web/app/s/[videoId]/_components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AnimatePresence, motion } from "framer-motion";
import { forwardRef, Suspense, useState } from "react";
import type { OrganizationSettings } from "@/app/(org)/dashboard/dashboard-data";
import { useCurrentUser } from "@/app/Layout/AuthContext";
import type { VideoData } from "../types";
import { Activity } from "./tabs/Activity";
import { Settings } from "./tabs/Settings";
import { Summary } from "./tabs/Summary";
Expand All @@ -19,14 +20,8 @@ type CommentType = typeof commentsSchema.$inferSelect & {
authorImage?: ImageUpload.ImageUrl | null;
};

type VideoWithOrganizationInfo = typeof videos.$inferSelect & {
organizationMembers?: string[];
organizationId?: string;
orgSettings?: OrganizationSettings | null;
};

interface SidebarProps {
data: VideoWithOrganizationInfo;
data: VideoData;
commentsData: CommentType[];
optimisticComments: CommentType[];
handleCommentSuccess: (comment: CommentType) => void;
Expand Down Expand Up @@ -91,10 +86,9 @@ export const Sidebar = forwardRef<{ scrollToBottom: () => void }, SidebarProps>(
) => {
const user = useCurrentUser();

const isOwnerOrMember: boolean = Boolean(
user?.id === data.ownerId ||
(data.organizationId &&
data.organizationMembers?.includes(user?.id ?? "")),
const isOwnerOrMember = Boolean(
user?.id === data.owner.id ||
(user && data.organizationMembers?.includes(user.id)),
);

const defaultTab = !(
Expand Down Expand Up @@ -182,7 +176,7 @@ export const Sidebar = forwardRef<{ scrollToBottom: () => void }, SidebarProps>(
/>
);
case "transcript":
return <Transcript data={data} onSeek={onSeek} user={user} />;
return <Transcript data={data} onSeek={onSeek} />;
case "settings":
return <Settings />;
default:
Expand Down
10 changes: 3 additions & 7 deletions apps/web/app/s/[videoId]/_components/Toolbar.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
import type { userSelectProps } from "@cap/database/auth/session";
import type { videos } from "@cap/database/schema";
import { Button } from "@cap/ui";
import { Comment, User } from "@cap/web-domain";
import { Comment } from "@cap/web-domain";
import { AnimatePresence, motion } from "motion/react";
import { startTransition, useEffect, useState } from "react";
import { newComment } from "@/actions/videos/new-comment";
import type { OrganizationSettings } from "@/app/(org)/dashboard/dashboard-data";
import { useCurrentUser } from "@/app/Layout/AuthContext";
import type { CommentType } from "../Share";
import type { VideoData } from "../types";
import { AuthOverlay } from "./AuthOverlay";

const MotionButton = motion.create(Button);

// million-ignore
interface ToolbarProps {
data: typeof videos.$inferSelect & {
orgSettings?: OrganizationSettings | null;
};
data: VideoData;
onOptimisticComment?: (comment: CommentType) => void;
onCommentSuccess?: (comment: CommentType) => void;
disableReactions?: boolean;
Expand Down
Loading
Loading