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
9 changes: 6 additions & 3 deletions apps/web/app/(org)/dashboard/caps/Caps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,10 @@ export const Caps = ({

const anyCapSelected = selectedCaps.length > 0;

const { data: analyticsData } = useQuery({
queryKey: ["analytics", data.map((video) => video.id)],
const videoIds = data.map((video) => video.id).sort();

const { data: analyticsData, isLoading: isLoadingAnalytics } = useQuery({
queryKey: ["analytics", videoIds],
queryFn: async () => {
if (!dubApiKeyEnabled || data.length === 0) {
return {};
Expand Down Expand Up @@ -122,8 +124,8 @@ export const Caps = ({

return analyticsData;
},
staleTime: 30000, // 30 seconds
refetchOnWindowFocus: false,
refetchOnMount: true,
});

const analytics = analyticsData || {};
Expand Down Expand Up @@ -331,6 +333,7 @@ export const Caps = ({
}}
userId={user?.id}
customDomain={customDomain}
isLoadingAnalytics={isLoadingAnalytics}
domainVerified={domainVerified}
isSelected={selectedCaps.includes(cap.id)}
anyCapSelected={anyCapSelected}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export interface CapCardProps extends PropsWithChildren {
hasPassword?: boolean;
};
analytics: number;
isLoadingAnalytics: boolean;
onDelete?: () => Promise<any>;
userId?: string;
sharedCapCard?: boolean;
Expand All @@ -81,6 +82,7 @@ export const CapCard = ({
children,
onDelete,
userId,
isLoadingAnalytics,
sharedCapCard = false,
hideSharedStatus = false,
customDomain,
Expand Down Expand Up @@ -461,6 +463,7 @@ export const CapCard = ({
<CapCardAnalytics
capId={cap.id}
displayCount={analytics}
isLoadingAnalytics={isLoadingAnalytics}
totalComments={cap.totalComments}
totalReactions={cap.totalReactions}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ interface CapCardAnalyticsProps {
capId: string;
displayCount: number;
totalComments: number;
isLoadingAnalytics: boolean;
totalReactions: number;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,25 @@ import { useDashboardContext } from "@/app/(org)/dashboard/Contexts";
import { useEffectMutation } from "@/lib/EffectRuntime";
import { Rpc } from "@/lib/Rpcs";
import type { VideoData } from "../../../caps/Caps";
import { CapCard } from "../../../caps/components/CapCard/CapCard";
import { SelectedCapsBar } from "../../../caps/components/SelectedCapsBar";
import { UploadPlaceholderCard } from "../../../caps/components/UploadPlaceholderCard";
import { useUploadingContext } from "../../../caps/UploadingContext";
import { SharedCapCard } from "../../../spaces/[spaceId]/components/SharedCapCard";
import { ClientCapCard } from "./index";

interface FolderVideosSectionProps {
initialVideos: VideoData;
dubApiKeyEnabled: boolean;
cardType?: "shared" | "default";
userId?: string;
}

export default function FolderVideosSection({
initialVideos,
dubApiKeyEnabled,
cardType = "default",
userId,
}: FolderVideosSectionProps) {
const router = useRouter();
const { isUploading } = useUploadingContext();
const { activeOrganization } = useDashboardContext();
const { activeOrganization, user } = useDashboardContext();

const [selectedCaps, setSelectedCaps] = useState<Video.VideoId[]>([]);
const previousCountRef = useRef<number>(0);
Expand Down Expand Up @@ -145,8 +142,8 @@ export default function FolderVideosSection({
});
return analyticsData;
},
staleTime: 30000, // 30 seconds
refetchOnWindowFocus: false,
refetchOnMount: true,
});

const analytics = analyticsData || {};
Expand All @@ -167,34 +164,20 @@ export default function FolderVideosSection({
{isUploading && (
<UploadPlaceholderCard key={"upload-placeholder"} />
)}

{cardType === "shared"
? initialVideos.map((video) => (
<SharedCapCard
key={video.id}
cap={video}
hideSharedStatus
analytics={analytics[video.id] || 0}
organizationName={
activeOrganization?.organization.name || ""
}
userId={userId}
/>
))
: initialVideos.map((video) => (
<ClientCapCard
key={video.id}
videoId={video.id}
cap={video}
analytics={analytics[video.id] || 0}
isLoadingAnalytics={isLoadingAnalytics}
isSelected={selectedCaps.includes(video.id)}
anyCapSelected={selectedCaps.length > 0}
isDeleting={deleteCaps.isPending}
onSelectToggle={() => handleCapSelection(video.id)}
onDelete={() => deleteCaps.mutateAsync(selectedCaps)}
/>
))}
{initialVideos.map((video) => (
<CapCard
key={video.id}
cap={video}
analytics={analytics[video.id] || 0}
userId={user?.id}
isLoadingAnalytics={isLoadingAnalytics}
isSelected={selectedCaps.includes(video.id)}
anyCapSelected={selectedCaps.length > 0}
isDeleting={deleteCaps.isPending}
onSelectToggle={() => handleCapSelection(video.id)}
onDelete={() => deleteCaps.mutateAsync(selectedCaps)}
/>
))}
</>
)}
</div>
Expand Down
3 changes: 2 additions & 1 deletion apps/web/app/(org)/dashboard/spaces/[spaceId]/SharedCaps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export const SharedCaps = ({

const organizationMemberCount = organizationMembers?.length || 0;

const { data: analyticsData } = useQuery({
const { data: analyticsData, isLoading: isLoadingAnalytics } = useQuery({
queryKey: ["analytics", data.map((video) => video.id)],
queryFn: async () => {
if (!dubApiKeyEnabled || data.length === 0) {
Expand Down Expand Up @@ -289,6 +289,7 @@ export const SharedCaps = ({
key={cap.id}
cap={cap}
hideSharedStatus
isLoadingAnalytics={isLoadingAnalytics}
analytics={analytics[cap.id] || 0}
organizationName={activeOrganization?.organization.name || ""}
spaceName={spaceData?.name || ""}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface SharedCapCardProps {
metadata?: VideoMetadata;
};
analytics: number;
isLoadingAnalytics: boolean;
organizationName: string;
userId?: string;
hideSharedStatus?: boolean;
Expand Down Expand Up @@ -44,6 +45,7 @@ export const SharedCapCard: React.FC<SharedCapCardProps> = ({
<div onDragStart={onDragStart} onDragEnd={onDragEnd}>
<CapCard
hideSharedStatus={hideSharedStatus}
isLoadingAnalytics={isLoadingAnalytics}
cap={cap}
analytics={displayCount}
sharedCapCard
Expand Down
4 changes: 2 additions & 2 deletions apps/web/components/VideoThumbnail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface VideoThumbnailProps {
imageClass?: string;
objectFit?: string;
containerClass?: string;
videoDuration?: string;
videoDuration?: string | number;
}

const formatDuration = (duration: string) => {
Expand Down Expand Up @@ -110,7 +110,7 @@ export const VideoThumbnail: React.FC<VideoThumbnailProps> = memo(
</div>
{videoDuration && Number(videoDuration) > 0 && (
<p className="text-white leading-0 px-2 left-3 rounded-full backdrop-blur-sm absolute z-10 bottom-3 bg-black/50 text-[11px]">
{formatDuration(videoDuration)}
{formatDuration(videoDuration.toString())}
</p>
)}
{imageUrl.data && (
Expand Down
26 changes: 20 additions & 6 deletions apps/web/lib/folder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
users,
videos,
} from "@cap/database/schema";
import type { Video } from "@cap/web-domain";
import { Folder } from "@cap/web-domain";
import { and, desc, eq } from "drizzle-orm";
import { sql } from "drizzle-orm/sql";
Expand Down Expand Up @@ -112,7 +113,10 @@ async function getSharedSpacesForVideos(videoIds: string[]) {

// Add space-level sharing
spaceSharing.forEach((space) => {
(sharedSpacesMap[space.videoId] ??= []).push({
if (!sharedSpacesMap[space.videoId]) {
sharedSpacesMap[space.videoId] = [];
}
sharedSpacesMap[space.videoId].push({
id: space.id,
name: space.name,
organizationId: space.organizationId,
Expand All @@ -123,7 +127,10 @@ async function getSharedSpacesForVideos(videoIds: string[]) {

// Add organization-level sharing
orgSharing.forEach((org) => {
(sharedSpacesMap[org.videoId] ??= []).push({
if (!sharedSpacesMap[org.videoId]) {
sharedSpacesMap[org.videoId] = [];
}
sharedSpacesMap[org.videoId].push({
id: org.id,
name: org.name,
organizationId: org.organizationId,
Expand All @@ -144,6 +151,7 @@ export async function getVideosByFolderId(folderId: string) {
ownerId: videos.ownerId,
name: videos.name,
createdAt: videos.createdAt,
public: videos.public,
metadata: videos.metadata,
totalComments: sql<number>`COUNT(DISTINCT CASE WHEN ${comments.type} = 'text' THEN ${comments.id} END)`,
totalReactions: sql<number>`COUNT(DISTINCT CASE WHEN ${comments.type} = 'emoji' THEN ${comments.id} END)`,
Expand Down Expand Up @@ -180,6 +188,7 @@ export async function getVideosByFolderId(folderId: string) {
videos.ownerId,
videos.name,
videos.createdAt,
videos.public,
videos.metadata,
users.name,
)
Expand All @@ -196,10 +205,14 @@ export async function getVideosByFolderId(folderId: string) {

// Process the video data to match the expected format
const processedVideoData = videoData.map((video) => {
const { effectiveDate, ...videoWithoutEffectiveDate } = video;

return {
...videoWithoutEffectiveDate,
id: video.id as Video.VideoId, // Cast to Video.VideoId branded type
ownerId: video.ownerId,
name: video.name,
createdAt: video.createdAt,
public: video.public,
totalComments: video.totalComments,
totalReactions: video.totalReactions,
sharedOrganizations: Array.isArray(video.sharedOrganizations)
? video.sharedOrganizations.filter(
(organization) => organization.id !== null,
Expand All @@ -212,10 +225,11 @@ export async function getVideosByFolderId(folderId: string) {
metadata: video.metadata as
| {
customCreatedAt?: string;
[key: string]: any;
[key: string]: unknown;
}
| undefined,
hasPassword: video.hasPassword === 1,
foldersData: [], // Empty array since videos in a folder don't need folder data
};
});

Expand Down
Loading