Skip to content

Commit 74113a4

Browse files
authored
improve: duration formatting (#912)
* improve duration formatting * Update auto-imports.d.ts
1 parent 9bcc095 commit 74113a4

File tree

9 files changed

+35
-31
lines changed

9 files changed

+35
-31
lines changed

apps/web/app/(org)/dashboard/_components/Navbar/SpacesList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ const SpacesList = ({ toggleMobileNav }: { toggleMobileNav?: () => void }) => {
342342
? `Are you sure you want to delete the space "${pendingDeleteSpace.name}"? This action cannot be undone.`
343343
: ""
344344
}
345-
confirmLabel="Delete"
345+
confirmLabel={removing ? "Deleting..." : "Delete"}
346346
cancelLabel="Cancel"
347347
loading={removing}
348348
onConfirm={confirmRemoveSpace}

apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import {
1919
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
2020
import { useMutation } from "@tanstack/react-query";
2121
import clsx from "clsx";
22-
import moment from "moment";
2322
import Link from "next/link";
2423
import { useRouter } from "next/navigation";
2524
import { type PropsWithChildren, useState } from "react";
@@ -105,23 +104,6 @@ export const CapCard = ({
105104

106105
const router = useRouter();
107106

108-
const formatDuration = (duration: string) => {
109-
if (!duration) return "0 secs";
110-
111-
const momentDuration = moment.duration(duration, "milliseconds");
112-
const totalMinutes = Math.floor(momentDuration.asMinutes());
113-
const totalHours = Math.floor(momentDuration.asHours());
114-
const seconds = momentDuration.seconds();
115-
116-
if (totalHours > 0) {
117-
return "1 hr";
118-
} else if (totalMinutes > 0) {
119-
return `${totalMinutes} mins`;
120-
} else {
121-
return `${seconds} secs`;
122-
}
123-
};
124-
125107
const downloadMutation = useMutation({
126108
mutationFn: async () => {
127109
const response = await downloadVideo(cap.id);
@@ -399,19 +381,14 @@ export const CapCard = ({
399381
icon={<FontAwesomeIcon icon={faVideo} />}
400382
title="Delete Cap"
401383
description={`Are you sure you want to delete the cap "${cap.name}"? This action cannot be undone.`}
402-
confirmLabel="Delete"
384+
confirmLabel={deleteMutation.isPending ? "Deleting..." : "Delete"}
403385
cancelLabel="Cancel"
404386
loading={deleteMutation.isPending}
405387
onConfirm={() => deleteMutation.mutate()}
406388
onCancel={() => setConfirmOpen(false)}
407389
/>
408390
</div>
409391
)}
410-
{cap.metadata?.duration && (
411-
<p className="text-white leading-0 px-2 py-px rounded-full backdrop-blur-sm absolute z-10 left-3 top-[112px] bg-black/50 text-[10px]">
412-
{formatDuration(cap.metadata.duration as string)}
413-
</p>
414-
)}
415392
{!sharedCapCard && onSelectToggle && (
416393
<div
417394
className={clsx(
@@ -452,6 +429,7 @@ export const CapCard = ({
452429
href={`/s/${cap.id}`}
453430
>
454431
<VideoThumbnail
432+
videoDuration={cap.metadata?.duration}
455433
imageClass={clsx(
456434
anyCapSelected
457435
? "opacity-50"

apps/web/app/(org)/dashboard/caps/components/Folder.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ import type { Folder } from "@cap/web-domain";
33
import { faTrash } from "@fortawesome/free-solid-svg-icons";
44
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
55
import { Fit, Layout, useRive } from "@rive-app/react-canvas";
6-
import { useMutation } from "@tanstack/react-query";
76
import clsx from "clsx";
8-
import { Effect } from "effect";
97
import Link from "next/link";
108
import { useRouter } from "next/navigation";
119
import { useEffect, useRef, useState } from "react";
@@ -382,6 +380,7 @@ const FolderCard = ({
382380
icon={<FontAwesomeIcon icon={faTrash} />}
383381
onConfirm={() => deleteFolder.mutate(id)}
384382
onCancel={() => setConfirmDeleteFolderOpen(false)}
383+
confirmLabel={deleteFolder.isPending ? "Deleting..." : "Delete"}
385384
title="Delete Folder"
386385
description={`Are you sure you want to delete the folder "${name}"? This action cannot be undone.`}
387386
/>

apps/web/app/(org)/dashboard/caps/components/SelectedCapsBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export const SelectedCapsBar = ({
8484
} cap${
8585
selectedCaps.length === 1 ? "" : "s"
8686
}? This action cannot be undone.`}
87-
confirmLabel="Delete"
87+
confirmLabel={isDeleting ? "Deleting..." : "Delete"}
8888
cancelLabel="Cancel"
8989
confirmVariant="dark"
9090
loading={isDeleting}

apps/web/app/(org)/dashboard/settings/organization/components/CustomDomain.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export function CustomDomain() {
7272
icon={<FontAwesomeIcon icon={faGlobe} />}
7373
description={`Are you sure you want to remove this custom domain: ${orgCustomDomain}?`}
7474
onConfirm={handleRemoveDomain}
75-
confirmLabel="Remove"
75+
confirmLabel={removeDomainMutation.isPending ? "Removing..." : "Remove"}
7676
cancelLabel="Cancel"
7777
loading={removeDomainMutation.isPending}
7878
onCancel={() => setConfirmOpen(false)}

apps/web/app/(org)/dashboard/settings/organization/components/MembersCard.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ export const MembersCard = ({
130130
from your organization? this action cannot be undone.`
131131
: ""
132132
}
133+
confirmLabel={removing ? "Removing..." : "Remove"}
133134
cancelLabel="Cancel"
134135
loading={removing}
135136
onConfirm={confirmRemoveMember}

apps/web/app/(org)/dashboard/spaces/browse/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ export default function BrowseSpacesPage() {
231231
? `Are you sure you want to delete the space "${pendingDeleteSpace?.name || "selected"}"? This action cannot be undone.`
232232
: "Are you sure you want to delete this space? This action cannot be undone."
233233
}
234-
confirmLabel="Delete"
234+
confirmLabel={removing ? "Deleting..." : "Delete"}
235235
cancelLabel="Cancel"
236236
loading={removing}
237237
onConfirm={confirmRemoveSpace}

apps/web/components/VideoThumbnail.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { LogoSpinner } from "@cap/ui";
22
import { useQuery } from "@tanstack/react-query";
33
import clsx from "clsx";
4+
import moment from "moment";
45
import Image from "next/image";
56
import { memo, useEffect, useRef, useState } from "react";
67
import { useUploadingContext } from "@/app/(org)/dashboard/caps/UploadingContext";
@@ -12,8 +13,28 @@ interface VideoThumbnailProps {
1213
imageClass?: string;
1314
objectFit?: string;
1415
containerClass?: string;
16+
videoDuration?: string;
1517
}
1618

19+
const formatDuration = (duration: string) => {
20+
const durationMs = parseFloat(duration);
21+
const momentDuration = moment.duration(durationMs, "milliseconds");
22+
23+
const totalHours = Math.floor(momentDuration.asHours());
24+
const totalMinutes = Math.floor(momentDuration.asMinutes());
25+
const remainingSeconds = Math.ceil(momentDuration.asSeconds() % 60); // Use ceil to avoid 0 secs
26+
27+
if (totalHours > 0) {
28+
return `${totalHours} hr${totalHours > 1 ? "s" : ""}`;
29+
} else if (totalMinutes > 0) {
30+
return `${totalMinutes} min${totalMinutes > 1 ? "s" : ""}`;
31+
} else if (remainingSeconds > 0) {
32+
return `${remainingSeconds} sec${remainingSeconds !== 1 ? "s" : ""}`;
33+
} else {
34+
return "< 1 sec"; // For very short durations
35+
}
36+
};
37+
1738
function generateRandomGrayScaleColor() {
1839
const minGrayScaleValue = 190;
1940
const maxGrayScaleValue = 235;
@@ -31,6 +52,7 @@ export const VideoThumbnail: React.FC<VideoThumbnailProps> = memo(
3152
imageClass,
3253
objectFit = "cover",
3354
containerClass,
55+
videoDuration,
3456
}) => {
3557
const imageUrl = useQuery({
3658
queryKey: ["thumbnail", userId, videoId],
@@ -86,6 +108,11 @@ export const VideoThumbnail: React.FC<VideoThumbnailProps> = memo(
86108
)
87109
)}
88110
</div>
111+
{videoDuration && Number(videoDuration) > 0 && (
112+
<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]">
113+
{formatDuration(videoDuration)}
114+
</p>
115+
)}
89116
{imageUrl.data && (
90117
<Image
91118
ref={imageRef}

packages/ui-solid/src/auto-imports.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// @ts-nocheck
44
// noinspection JSUnusedGlobalSymbols
55
// Generated by unplugin-auto-import
6-
// biome-ignore lint: disable
76
export {};
87
declare global {
98
const IconCapArrows: typeof import("~icons/cap/arrows.jsx")["default"];

0 commit comments

Comments
 (0)