Skip to content

Commit cb1a745

Browse files
committed
fix: extend null handling to like and subscriber counts
Allow videoLikeCount and subscriberCount to be null (VideoData, ChannelData, YoutubeVideoInfo) since the YouTube API can omit these fields too. Update ChannelInfo, VideoInfo, and VideoStats components to accept nullable counts, and extract a toSafeNumber helper shared by formatCount and formatExactCount.
1 parent 6e2365b commit cb1a745

8 files changed

Lines changed: 30 additions & 23 deletions

File tree

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
/** Interface for video information pulled from Youtube Data API v3 */
22
export interface VideoData {
33
videoDescription: string;
4-
videoLikeCount: number;
4+
videoLikeCount: number | null;
55
videoPublishedAt: number | null;
66
videoTitle: string;
77
videoThumbnail: string;
88
videoViewCount: number | null;
99
channelIcon: string;
1010
channelTitle: string;
1111
channelUrl: string;
12-
subscriberCount: number;
12+
subscriberCount: number | null;
1313
};
1414

1515

@@ -18,5 +18,5 @@ export interface ChannelData {
1818
channelIcon: string,
1919
channelTitle: string,
2020
channelUrl: string,
21-
subscriberCount: number
21+
subscriberCount: number | null
2222
};

backend/src/routes/videoApiRoutes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const getChannelData = async (channelId: string): Promise<ChannelData> => {
3232
channelIcon: item.snippet.thumbnails.default.url,
3333
channelTitle: item.snippet.title,
3434
channelUrl: `https://www.youtube.com/channel/${channelId}`,
35-
subscriberCount: Number.parseInt(item.statistics.subscriberCount)
35+
subscriberCount: item.statistics.subscriberCount != null ? Number.parseInt(item.statistics.subscriberCount) : null
3636
};
3737

3838
return channelData;
@@ -51,7 +51,7 @@ const getVideoData = async (videoId: string): Promise<VideoData> => {
5151

5252
const videoData: VideoData = {
5353
videoDescription: item.snippet.description,
54-
videoLikeCount: Number.parseInt(item.statistics.likeCount),
54+
videoLikeCount: item.statistics.likeCount != null ? Number.parseInt(item.statistics.likeCount) : null,
5555
videoPublishedAt: item.snippet.publishedAt ? Date.parse(item.snippet.publishedAt) : null,
5656
videoTitle: item.snippet.title,
5757
videoThumbnail: item.snippet.thumbnails.high.url,

frontend/src/components/youtube/ChannelInfo.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { formatCount } from "../../utils/formatVideoInfo";
77
*/
88
interface ChannelTitleProps {
99
channelTitle: string;
10-
subscriberCount: number;
10+
subscriberCount: number | null;
1111
}
1212

1313

@@ -18,15 +18,15 @@ interface ChannelInfoProps {
1818
channelIcon: string;
1919
channelTitle: string;
2020
channelUrl: string;
21-
subscriberCount: number;
21+
subscriberCount: number | null;
2222
}
2323

2424

2525
/**
2626
* Renders the channel's title and subscriber count.
2727
*/
2828
function ChannelTitle({ channelTitle, subscriberCount }: ChannelTitleProps) {
29-
const subscriberText = subscriberCount === 1 ? "subscriber" : "subscribers";
29+
const subscriberText = (subscriberCount ?? 0) === 1 ? "subscriber" : "subscribers";
3030

3131
return (
3232
<div className="flex flex-col ml-2">
@@ -44,7 +44,7 @@ export default function ChannelInfo({ channelIcon, channelTitle, channelUrl, sub
4444
return (
4545
<a className="flex items-center h-12" href={channelUrl}>
4646
<UserIcon src={channelIcon} />
47-
<ChannelTitle channelTitle={channelTitle!} subscriberCount={subscriberCount!} />
47+
<ChannelTitle channelTitle={channelTitle} subscriberCount={subscriberCount} />
4848
</a>
4949
);
5050
}

frontend/src/components/youtube/VideoDescription.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,14 @@ function DescriptionStats({ videoPublishedAt, videoViewCount, descriptionExpande
3838
const viewsText = (videoViewCount ?? 0) === 1 ? "view" : "views";
3939

4040
const expandedDateString: string = formatExactDate(videoPublishedAt);
41-
const relativeDateString: string = formatYoutubeDate(videoPublishedAt);
41+
const relativeDateString: string = descriptionExpanded ? "" : formatYoutubeDate(videoPublishedAt);
4242
const expandedViewCount: string = `${formatExactCount(videoViewCount)} ${viewsText}`;
4343

44-
const tooltipParts = descriptionExpanded ? [] : [expandedViewCount, expandedDateString].filter(Boolean);
45-
const tooltipText = tooltipParts.length > 0 ? tooltipParts.join(" • ") : undefined;
44+
const tooltipText = descriptionExpanded
45+
? undefined
46+
: expandedDateString
47+
? `${expandedViewCount}${expandedDateString}`
48+
: expandedViewCount;
4649

4750
return (
4851
<div className="flex" title={tooltipText}>

frontend/src/components/youtube/VideoInfo.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ interface VideoTitleProps {
1414
*/
1515
interface VideoInfoProps {
1616
videoTitle: string;
17-
videoLikeCount: number;
17+
videoLikeCount: number | null;
1818
channelIcon: string;
1919
channelTitle: string;
2020
channelUrl: string;
21-
subscriberCount: number;
21+
subscriberCount: number | null;
2222
}
2323

2424

frontend/src/components/youtube/VideoStats.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,19 @@ import { formatCount } from "../../utils/formatVideoInfo";
66
* Props for VideoLikes component.
77
*/
88
interface VideoLikesProps {
9-
videoLikeCount: number;
9+
videoLikeCount: number | null;
1010
}
1111

1212

1313
/**
1414
* Props for VideoStats component.
1515
*/
1616
interface VideoStatsProps {
17-
videoLikeCount: number;
17+
videoLikeCount: number | null;
1818
channelIcon: string;
1919
channelTitle: string;
2020
channelUrl: string;
21-
subscriberCount: number;
21+
subscriberCount: number | null;
2222
}
2323

2424

frontend/src/interfaces/YoutubeVideoInfo.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import type BaseVideoInfo from "./BaseVideoInfo";
66
*/
77
export default interface YoutubeVideoInfo extends BaseVideoInfo {
88
videoDescription: string;
9-
videoLikeCount: number;
9+
videoLikeCount: number | null;
1010
videoPublishedAt: number | null;
1111
videoTitle: string;
1212
videoThumbnail: string;
1313
videoViewCount: number | null;
1414
channelIcon: string;
1515
channelTitle: string;
1616
channelUrl: string;
17-
subscriberCount: number;
17+
subscriberCount: number | null;
1818
}

frontend/src/utils/formatVideoInfo.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
export function formatCount(n: number | null | undefined, precision: number): string {
1+
function toSafeNumber(n: number | null | undefined): number {
22
const safeN = n ?? 0;
3-
if (!Number.isFinite(safeN)) return "0";
3+
return Number.isFinite(safeN) ? safeN : 0;
4+
}
5+
6+
7+
export function formatCount(n: number | null | undefined, precision: number): string {
8+
const safeN = toSafeNumber(n);
49

510
if (safeN >= 1_000_000_000) return `${(safeN / 1_000_000_000).toFixed(precision).replace(/\.0$/, "")}B`;
611
if (safeN >= 1_000_000) return `${(safeN / 1_000_000).toFixed(precision).replace(/\.0$/, "")}M`;
@@ -10,8 +15,7 @@ export function formatCount(n: number | null | undefined, precision: number): st
1015

1116

1217
export function formatExactCount(n: number | null | undefined): string {
13-
const safeN = n ?? 0;
14-
return Number.isFinite(safeN) ? safeN.toLocaleString() : "0";
18+
return toSafeNumber(n).toLocaleString();
1519
}
1620

1721

0 commit comments

Comments
 (0)