diff --git a/src/components/Video.tsx b/src/components/Video.tsx
index 384a7d8..328c5ef 100644
--- a/src/components/Video.tsx
+++ b/src/components/Video.tsx
@@ -1,122 +1,210 @@
import NextImage from "next/image";
-import Link from "next/link";
+import NextLink from "next/link";
import { useMemo } from "react";
+import {
+ FiCopy as CopyIcon,
+ FiLink as LinkIcon,
+ FiYoutube as YoutubeIcon
+} from "react-icons/fi";
import { Card, CardBody, CardFooter } from "@nextui-org/card";
import { Divider } from "@nextui-org/divider";
import { Image } from "@nextui-org/image";
+import { Link } from "@nextui-org/link";
import { Tooltip } from "@nextui-org/tooltip";
import { Video as VideoProps } from "@/client/typings/video";
import formatBigNumber from "@/utils/formatBigNumber";
import formatDuration from "@/utils/formatDuration";
import formatUploadedTime from "@/utils/formatUploadedTime";
-import { channelUrl, videoUrl } from "@/utils/urls";
+import {
+ channelUrl,
+ videoUrl,
+ youtubeChannelUrl,
+ youtubeVideoUrl
+} from "@/utils/urls";
import { videoSize } from "@/utils/videoSize";
import { ContextMenu } from "./ContextMenu";
import { Component } from "@/typings/component";
-import { ContextMenuItem } from "@/typings/contextMenu";
+import { ContextMenuItem, ContextMenuItemType } from "@/typings/contextMenu";
export const Video: Component<{ data: VideoProps; size?: number }> = ({
data,
size = 40
}) => {
const url = videoUrl(data.id);
+ const channel = data.author.id ? channelUrl(data.author.id) : "#";
const [width, height] = videoSize(size);
const menuItems = useMemo(() => {
+ const hasAuthor = !!data.author.id;
+
const items: ContextMenuItem[] = [
- { title: "Go to video", key: "gotoVideo", href: url },
{
- title: "Copy video id",
- key: "videoId",
- onClick: (): void => {
- navigator.clipboard.writeText(data.id);
- },
- showDivider: true
+ type: ContextMenuItemType.Category,
+ title: "Video",
+ showDivider: true,
+ key: "video",
+ items: [
+ {
+ type: ContextMenuItemType.Action,
+ title: "Go to video",
+ description: "Opens in this tab",
+ icon:
,
+ key: "goToVideo",
+ href: url
+ },
+ {
+ type: ContextMenuItemType.Action,
+ title: "Copy video id",
+ icon:
,
+ key: "videoId",
+ onClick: (): void => {
+ navigator.clipboard.writeText(data.id);
+ }
+ },
+ {
+ type: ContextMenuItemType.Action,
+ title: "Copy YouTube video url",
+ icon:
,
+ key: "youtubeUrl",
+ onClick: (): void => {
+ const url = youtubeVideoUrl(data.id);
+
+ navigator.clipboard.writeText(url.toString());
+ }
+ }
+ ]
},
{
- title: "Open thumbnail",
+ type: ContextMenuItemType.Category,
+ title: "Thumbnail",
+ showDivider: hasAuthor,
key: "thumbnail",
- href: data.thumbnail
- },
- {
- title: "Copy thumnail url",
- key: "thumbnailUrl",
- onClick: (): void => {
- navigator.clipboard.writeText(data.thumbnail);
- },
- showDivider: true
+ items: [
+ {
+ type: ContextMenuItemType.Action,
+ title: "Open thumbnail",
+ description: "Opens in this tab",
+ icon:
,
+ key: "thumbnail",
+ href: data.thumbnail
+ },
+ {
+ type: ContextMenuItemType.Action,
+ title: "Copy thumnail url",
+ icon:
,
+ key: "thumbnailUrl",
+ onClick: (): void => {
+ navigator.clipboard.writeText(data.thumbnail);
+ }
+ }
+ ]
}
];
- if (data.author.id) {
+ if (data.author.id)
items.push({
- title: "Go to channel",
- key: "gotoChannel",
- href: channelUrl(data.author.id)
- });
+ type: ContextMenuItemType.Category,
+ title: "Channel",
+ key: "channel",
+ items: [
+ {
+ type: ContextMenuItemType.Action,
+ title: "Go to channel",
+ description: "Opens in this tab",
+ icon:
,
+ key: "goToChannel",
+ href: channel
+ },
+ {
+ type: ContextMenuItemType.Action,
+ title: "Copy channel id",
+ icon:
,
+ key: "channelId",
+ onClick: (): void => {
+ if (data.author.id) navigator.clipboard.writeText(data.author.id);
+ }
+ },
+ {
+ type: ContextMenuItemType.Action,
+ title: "Copy YouTube channel url",
+ icon:
,
+ key: "youtubeUrl",
+ onClick: (): void => {
+ if (!data.author.id) return;
- items.push({
- title: "Copy channel id",
- key: "channelId",
- onClick: (): void => {
- navigator.clipboard.writeText(data.author.id ?? "");
- }
+ const url = youtubeChannelUrl(data.author.id);
+
+ navigator.clipboard.writeText(url.toString());
+ }
+ }
+ ]
});
- }
return items;
- }, [data, url]);
+ }, [data, channel, url]);
return (
-
-
-
-
+
+
+
+
+
-
- {formatDuration(data.duration)}
-
-
-
-
-
-
+
+ {formatDuration(data.duration)}
+
+
+
+
+
+
+
{data.title}
+
+
+
+
+ {data.author.name}
+
+
+ {data.uploaded && (
+
+
+ {formatUploadedTime(data.uploaded)}
+
+
+ )}
+
+
+ Views: {formatBigNumber(data.views)}
-
-
- {data.author.name}
-
- {data.uploaded && (
-
-
- {formatUploadedTime(data.uploaded)}
-
-
- )}
-
-
- Views: {formatBigNumber(data.views)}
-
-
-
-
-
-
+
+
+
+
);
};
diff --git a/src/constants.ts b/src/constants.ts
index b302473..ff410cf 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -1 +1,3 @@
export const defaultRegion = "US" as const;
+
+export const youtubeUrl = new URL("https://youtube.com");
diff --git a/src/typings/contextMenu.ts b/src/typings/contextMenu.ts
index 65859d1..e87b47a 100644
--- a/src/typings/contextMenu.ts
+++ b/src/typings/contextMenu.ts
@@ -1,7 +1,26 @@
-export interface ContextMenuItem {
- title: string;
+export interface BaseContextMenuItem {
+ type: ContextMenuItemType;
key: string;
+ title: string;
showDivider?: boolean;
+}
+
+export enum ContextMenuItemType {
+ Action,
+ Category
+}
+
+export interface ContextMenuAction extends BaseContextMenuItem {
+ type: ContextMenuItemType.Action;
+ description?: string;
href?: string;
+ icon?: React.JSX.Element;
onClick?: () => unknown;
}
+
+export interface ContextMenuCategory extends BaseContextMenuItem {
+ type: ContextMenuItemType.Category;
+ items: ContextMenuAction[];
+}
+
+export type ContextMenuItem = ContextMenuAction | ContextMenuCategory;
diff --git a/src/utils/urls.ts b/src/utils/urls.ts
index 6c43eb2..78d39fc 100644
--- a/src/utils/urls.ts
+++ b/src/utils/urls.ts
@@ -1,4 +1,20 @@
+import path from "path";
+
+import { youtubeUrl } from "@/constants";
+
export const videoUrl = (videoId: string): string => `/watch?v=${videoId}`;
export const channelUrl = (channelId: string): string =>
`/channel/${channelId}`;
+
+export const youtubeVideoUrl = (videoId: string): URL => {
+ const url = new URL("watch", youtubeUrl);
+
+ url.searchParams.append("v", videoId);
+
+ return url;
+};
+
+export const youtubeChannelUrl = (channelId: string): URL => {
+ return new URL(path.join("channel", channelId), youtubeUrl);
+};