Skip to content

Commit 7c38240

Browse files
committed
formatting + safari fix
1 parent 2317c64 commit 7c38240

File tree

5 files changed

+104
-82
lines changed

5 files changed

+104
-82
lines changed

apps/web/app/globals.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,3 +442,11 @@ footer a {
442442
.animate-slideUp {
443443
animation: slideUp 0.3s ease-out forwards;
444444
}
445+
446+
/* Safari-specific styles using CSS hacks */
447+
@media screen and (-webkit-min-device-pixel-ratio:0) {
448+
_::-webkit-full-page-media, _:future, :root #video-container {
449+
height: calc(100% - 1.70rem) !important;
450+
}
451+
}
452+

apps/web/app/s/[videoId]/Share.tsx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
"use client";
22

3-
import { ShareHeader } from "./_components/ShareHeader";
4-
import { ShareVideo } from "./_components/ShareVideo";
5-
import { comments as commentsSchema, videos } from "@cap/database/schema";
63
import { userSelectProps } from "@cap/database/auth/session";
7-
import { Toolbar } from "./_components/Toolbar";
4+
import { comments as commentsSchema, videos } from "@cap/database/schema";
5+
import { clientEnv } from "@cap/env";
86
import { Logo } from "@cap/ui";
7+
import { useEffect, useRef, useState } from "react";
8+
import { ShareHeader } from "./_components/ShareHeader";
9+
import { ShareVideo } from "./_components/ShareVideo";
910
import { Sidebar } from "./_components/Sidebar";
10-
import { useEffect, useState, useRef } from "react";
11-
import { clientEnv } from "@cap/env";
11+
import { Toolbar } from "./_components/Toolbar";
1212

1313
type CommentWithAuthor = typeof commentsSchema.$inferSelect & {
1414
authorName: string | null;
@@ -93,7 +93,7 @@ export const Share: React.FC<ShareProps> = ({
9393

9494
return (
9595
<div className="min-h-screen flex flex-col bg-[#F7F8FA]">
96-
<div className="flex-1 container mx-auto px-4 py-4">
96+
<div className="container flex-1 px-4 py-4 mx-auto">
9797
<ShareHeader
9898
data={data}
9999
user={user}
@@ -103,9 +103,9 @@ export const Share: React.FC<ShareProps> = ({
103103
/>
104104

105105
<div className="mt-4">
106-
<div className="flex flex-col lg:flex-row gap-4">
106+
<div className="flex flex-col gap-4 lg:flex-row">
107107
<div className="flex-1">
108-
<div className="relative aspect-video new-card-style p-3 overflow-hidden">
108+
<div className="overflow-hidden relative p-3 aspect-video new-card-style">
109109
<ShareVideo
110110
data={data}
111111
user={user}
@@ -118,7 +118,7 @@ export const Share: React.FC<ShareProps> = ({
118118
</div>
119119
</div>
120120

121-
<div className="lg:w-80 flex flex-col">
121+
<div className="flex flex-col lg:w-80">
122122
<Sidebar
123123
data={data}
124124
user={user}
@@ -130,17 +130,17 @@ export const Share: React.FC<ShareProps> = ({
130130
</div>
131131
</div>
132132

133-
<div className="hidden lg:block mt-4">
133+
<div className="hidden mt-4 lg:block">
134134
<Toolbar data={data} user={user} />
135135
</div>
136136
</div>
137137
</div>
138138

139-
<div className="mt-auto py-4">
139+
<div className="py-4 mt-auto">
140140
<a
141141
target="_blank"
142142
href={`${clientEnv.NEXT_PUBLIC_WEB_URL}?ref=video_${data.id}`}
143-
className="flex items-center justify-center space-x-2 py-2 px-4 bg-gray-100 new-card-style rounded-full mx-auto w-fit"
143+
className="flex justify-center items-center px-4 py-2 mx-auto space-x-2 bg-gray-100 rounded-full new-card-style w-fit"
144144
>
145145
<span className="text-sm">Recorded with</span>
146146
<Logo className="w-14 h-auto" />

apps/web/app/s/[videoId]/_components/MP4VideoPlayer.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import {
2-
memo,
32
forwardRef,
4-
useRef,
5-
useImperativeHandle,
3+
memo,
4+
useCallback,
65
useEffect,
6+
useImperativeHandle,
7+
useRef,
78
useState,
8-
useCallback,
99
} from "react";
1010

1111
interface MP4VideoPlayerProps {
@@ -224,7 +224,7 @@ export const MP4VideoPlayer = memo(
224224
<video
225225
id="video-player"
226226
ref={videoRef}
227-
className="w-full h-full object-contain"
227+
className="object-contain w-full h-full"
228228
preload="auto"
229229
playsInline
230230
controls={false}

apps/web/app/s/[videoId]/_components/ShareVideo.tsx

Lines changed: 74 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,29 @@
1+
import { apiClient } from "@/utils/web-api";
2+
import { userSelectProps } from "@cap/database/auth/session";
13
import { comments as commentsSchema, videos } from "@cap/database/schema";
2-
import { VideoPlayer } from "./VideoPlayer";
3-
import { MP4VideoPlayer } from "./MP4VideoPlayer";
4-
import {
5-
useState,
6-
useEffect,
7-
useRef,
8-
forwardRef,
9-
useCallback,
10-
useImperativeHandle,
11-
} from "react";
4+
import { clientEnv, NODE_ENV } from "@cap/env";
5+
import { LogoSpinner } from "@cap/ui";
6+
import { S3_BUCKET_URL } from "@cap/utils";
127
import {
13-
Play,
14-
Pause,
158
Maximize,
16-
VolumeX,
17-
Volume2,
189
MessageSquare,
10+
Pause,
11+
Play,
12+
Volume2,
13+
VolumeX,
1914
} from "lucide-react";
20-
import { LogoSpinner } from "@cap/ui";
21-
import { userSelectProps } from "@cap/database/auth/session";
22-
import { fromVtt, Subtitle } from "subtitles-parser-vtt";
15+
import {
16+
forwardRef,
17+
useEffect,
18+
useImperativeHandle,
19+
useRef,
20+
useState,
21+
} from "react";
2322
import toast from "react-hot-toast";
2423
import { Tooltip } from "react-tooltip";
25-
import { apiClient } from "@/utils/web-api";
26-
import { S3_BUCKET_URL } from "@cap/utils";
27-
import { clientEnv, NODE_ENV } from "@cap/env";
24+
import { fromVtt, Subtitle } from "subtitles-parser-vtt";
25+
import { MP4VideoPlayer } from "./MP4VideoPlayer";
26+
import { VideoPlayer } from "./VideoPlayer";
2827

2928
declare global {
3029
interface Window {
@@ -984,14 +983,31 @@ export const ShareVideo = forwardRef<
984983
}
985984
}, [previewCanvasRef, previewWidth, previewHeight, isLargeScreen]);
986985

986+
useEffect(() => {
987+
// Safari detection for applying specific styles
988+
const detectSafari = () => {
989+
const isSafari =
990+
/^((?!chrome|android).)*safari/i.test(navigator.userAgent) ||
991+
(navigator.userAgent.includes("AppleWebKit") &&
992+
!navigator.userAgent.includes("Chrome"));
993+
994+
const videoContainer = document.getElementById("video-container");
995+
if (videoContainer && isSafari) {
996+
videoContainer.style.height = "calc(100% - 1.75rem)";
997+
}
998+
};
999+
1000+
detectSafari();
1001+
}, []);
1002+
9871003
if (data.jobStatus === "ERROR") {
9881004
return (
989-
<div className="flex items-center justify-center w-full h-full rounded-lg overflow-hidden">
1005+
<div className="flex overflow-hidden justify-center items-center w-full h-full rounded-lg">
9901006
<div
9911007
style={{ paddingBottom: "min(806px, 56.25%)" }}
992-
className="relative w-full h-full rounded-lg bg-black flex items-center justify-center p-8"
1008+
className="flex relative justify-center items-center p-8 w-full h-full bg-black rounded-lg"
9931009
>
994-
<p className="text-white text-xl">
1010+
<p className="text-xl text-white">
9951011
There was an error when processing the video. Please contact
9961012
support.
9971013
</p>
@@ -1020,14 +1036,14 @@ export const ShareVideo = forwardRef<
10201036
return (
10211037
<div
10221038
id="video-container"
1023-
className="relative w-full h-full overflow-hidden shadow-lg rounded-lg group"
1039+
className="overflow-hidden relative w-full h-full rounded-lg shadow-lg group"
10241040
>
10251041
<div
10261042
className={`absolute inset-0 flex items-center justify-center z-10 bg-black transition-opacity duration-300 ${
10271043
isLoading ? "opacity-100" : "opacity-0 pointer-events-none"
10281044
}`}
10291045
>
1030-
<LogoSpinner className="w-8 sm:w-10 h-auto animate-spin" />
1046+
<LogoSpinner className="w-8 h-auto animate-spin sm:w-10" />
10311047
</div>
10321048
<div className="relative w-full h-full">
10331049
<div className="absolute inset-0 bg-black">
@@ -1047,7 +1063,7 @@ export const ShareVideo = forwardRef<
10471063
>
10481064
<button
10491065
aria-label={isPlaying ? "Pause video" : "Play video"}
1050-
className="w-full h-full flex items-center justify-center"
1066+
className="flex justify-center items-center w-full h-full"
10511067
onClick={() => {
10521068
if (!videoReadyToPlay) {
10531069
// If video is not ready, set a visual indicator but don't try to play yet
@@ -1065,16 +1081,16 @@ export const ShareVideo = forwardRef<
10651081
}}
10661082
>
10671083
{isPlaying ? (
1068-
<Pause className="w-auto h-10 sm:h-12 md:h-14 text-white hover:opacity-50" />
1084+
<Pause className="w-auto h-10 text-white sm:h-12 md:h-14 hover:opacity-50" />
10691085
) : (
1070-
<Play className="w-auto h-10 sm:h-12 md:h-14 text-white hover:opacity-50" />
1086+
<Play className="w-auto h-10 text-white sm:h-12 md:h-14 hover:opacity-50" />
10711087
)}
10721088
</button>
10731089
</div>
10741090
)}
10751091
{currentSubtitle && currentSubtitle.text && subtitlesVisible && (
1076-
<div className="absolute bottom-12 sm:bottom-16 w-full text-center p-2 z-10">
1077-
<div className="inline px-2 py-1 text-sm sm:text-lg md:text-2xl text-white bg-black bg-opacity-75 rounded-xl">
1092+
<div className="absolute bottom-12 z-10 p-2 w-full text-center sm:bottom-16">
1093+
<div className="inline px-2 py-1 text-sm text-white bg-black bg-opacity-75 rounded-xl sm:text-lg md:text-2xl">
10781094
{currentSubtitle.text
10791095
.replace("- ", "")
10801096
.replace(".", "")
@@ -1087,7 +1103,7 @@ export const ShareVideo = forwardRef<
10871103
{/* Thumbnail preview - MP4 only, visible only on screens larger than lg */}
10881104
{showPreview && !isLoading && isMP4Source && isLargeScreen && (
10891105
<div
1090-
className="absolute z-30 bg-black border border-gray-700 shadow-lg rounded-md overflow-hidden transition-opacity duration-150 hidden xl:block"
1106+
className="hidden overflow-hidden absolute z-30 bg-black rounded-md border border-gray-700 shadow-lg transition-opacity duration-150 xl:block"
10911107
style={{
10921108
left: `${previewPosition}px`,
10931109
bottom: "70px",
@@ -1100,7 +1116,7 @@ export const ShareVideo = forwardRef<
11001116
<img
11011117
src={thumbnailUrl}
11021118
alt={`Preview at ${formatTime(previewTime)}`}
1103-
className="w-full h-full object-contain bg-black"
1119+
className="object-contain w-full h-full bg-black"
11041120
onError={(e) => {
11051121
console.log(
11061122
"Thumbnail failed to load, using canvas preview instead"
@@ -1111,11 +1127,11 @@ export const ShareVideo = forwardRef<
11111127
) : (
11121128
<canvas
11131129
ref={previewCanvasRef}
1114-
className="w-full h-full object-contain bg-black"
1130+
className="object-contain w-full h-full bg-black"
11151131
/>
11161132
)}
11171133
{!thumbnailUrl && (
1118-
<div className="absolute bottom-0 left-0 right-0 bg-black bg-opacity-70 py-1 px-2 text-xs text-white text-center">
1134+
<div className="absolute right-0 bottom-0 left-0 px-2 py-1 text-xs text-center text-white bg-black bg-opacity-70">
11191135
{formatTime(previewTime)}
11201136
</div>
11211137
)}
@@ -1146,7 +1162,7 @@ export const ShareVideo = forwardRef<
11461162
>
11471163
<div
11481164
id="seek"
1149-
className="absolute left-0 right-0 -top-2 h-6 mx-2 sm:mx-4 cursor-pointer"
1165+
className="absolute right-0 left-0 -top-2 mx-2 h-6 cursor-pointer sm:mx-4"
11501166
onMouseDown={handleSeekMouseDown}
11511167
onMouseMove={(e) => {
11521168
if (seeking) {
@@ -1171,7 +1187,7 @@ export const ShareVideo = forwardRef<
11711187
onTouchEnd={handleSeekMouseUp}
11721188
>
11731189
{!isLoading && comments !== null && (
1174-
<div className="w-full -mt-7 md:-mt-6">
1190+
<div className="-mt-7 w-full md:-mt-6">
11751191
{comments.map((comment) => {
11761192
const commentPosition =
11771193
comment.timestamp === null
@@ -1194,7 +1210,7 @@ export const ShareVideo = forwardRef<
11941210
return (
11951211
<div
11961212
key={comment.id}
1197-
className="absolute z-10 text-sm hover:scale-125 transition-all"
1213+
className="absolute z-10 text-sm transition-all hover:scale-125"
11981214
style={{
11991215
left: `${commentPosition}%`,
12001216
}}
@@ -1228,31 +1244,29 @@ export const ShareVideo = forwardRef<
12281244
style={{ left: `${watchedPercentage}%` }}
12291245
/>
12301246
</div>
1231-
<div className="flex items-center justify-between px-4 py-2">
1247+
<div className="flex justify-between items-center px-4 py-2">
12321248
<div className="flex items-center space-x-2 sm:space-x-3">
1233-
<div>
1234-
<span className="inline-flex">
1235-
<button
1236-
aria-label="Play video"
1237-
className="inline-flex items-center text-sm font-medium transition ease-in-out duration-150 focus:outline-none border text-slate-100 border-transparent hover:text-white focus:border-white hover:bg-slate-100 hover:bg-opacity-10 active:bg-slate-100 active:bg-opacity-10 px-1 sm:px-2 py-1 sm:py-2 justify-center rounded-lg"
1238-
tabIndex={0}
1239-
type="button"
1240-
onClick={() => handlePlayPauseClick()}
1241-
>
1242-
{isPlaying ? (
1243-
<Pause className="w-auto h-5 sm:h-6" />
1244-
) : (
1245-
<Play className="w-auto h-5 sm:h-6" />
1246-
)}
1247-
</button>
1248-
</span>
1249-
</div>
1249+
<span className="inline-flex">
1250+
<button
1251+
aria-label="Play video"
1252+
className="inline-flex justify-center items-center px-1 py-1 text-sm font-medium rounded-lg border border-transparent transition duration-150 ease-in-out focus:outline-none text-slate-100 hover:text-white focus:border-white hover:bg-slate-100 hover:bg-opacity-10 active:bg-slate-100 active:bg-opacity-10 sm:px-2 sm:py-2"
1253+
tabIndex={0}
1254+
type="button"
1255+
onClick={() => handlePlayPauseClick()}
1256+
>
1257+
{isPlaying ? (
1258+
<Pause className="w-auto h-5 sm:h-6" />
1259+
) : (
1260+
<Play className="w-auto h-5 sm:h-6" />
1261+
)}
1262+
</button>
1263+
</span>
12501264
<div className="text-xs sm:text-sm text-white font-medium select-none tabular text-clip overflow-hidden whitespace-nowrap space-x-0.5">
12511265
{formatTime(currentTime)} - {formatTime(longestDuration)}
12521266
</div>
12531267
</div>
12541268
<div className="flex justify-end space-x-1 sm:space-x-2">
1255-
<div className="flex items-center justify-end space-x-1 sm:space-x-2">
1269+
<div className="flex justify-end items-center space-x-1 sm:space-x-2">
12561270
<span className="inline-flex">
12571271
<button
12581272
aria-label={`Change video speed to ${videoSpeed}x`}
@@ -1268,7 +1282,7 @@ export const ShareVideo = forwardRef<
12681282
<span className="inline-flex">
12691283
<button
12701284
aria-label={isPlaying ? "Pause video" : "Play video"}
1271-
className="inline-flex items-center text-sm font-medium transition ease-in-out duration-150 focus:outline-none border text-slate-100 border-transparent hover:text-white focus:border-white hover:bg-slate-100 hover:bg-opacity-10 active:bg-slate-100 active:bg-opacity-10 px-1 sm:px-2 py-1 sm:py-2 justify-center rounded-lg"
1285+
className="inline-flex justify-center items-center px-1 py-1 text-sm font-medium rounded-lg border border-transparent transition duration-150 ease-in-out focus:outline-none text-slate-100 hover:text-white focus:border-white hover:bg-slate-100 hover:bg-opacity-10 active:bg-slate-100 active:bg-opacity-10 sm:px-2 sm:py-2"
12721286
tabIndex={0}
12731287
type="button"
12741288
onClick={() => {
@@ -1308,7 +1322,7 @@ export const ShareVideo = forwardRef<
13081322
aria-label={
13091323
subtitlesVisible ? "Hide subtitles" : "Show subtitles"
13101324
}
1311-
className="inline-flex items-center text-sm font-medium transition ease-in-out duration-150 focus:outline-none border text-slate-100 border-transparent hover:text-white focus:border-white hover:bg-slate-100 hover:bg-opacity-10 active:bg-slate-100 active:bg-opacity-10 px-1 sm:px-2 py-1 sm:py-2 justify-center rounded-lg"
1325+
className="inline-flex justify-center items-center px-1 py-1 text-sm font-medium rounded-lg border border-transparent transition duration-150 ease-in-out focus:outline-none text-slate-100 hover:text-white focus:border-white hover:bg-slate-100 hover:bg-opacity-10 active:bg-slate-100 active:bg-opacity-10 sm:px-2 sm:py-2"
13121326
tabIndex={0}
13131327
type="button"
13141328
onClick={() => setSubtitlesVisible(!subtitlesVisible)}
@@ -1354,7 +1368,7 @@ export const ShareVideo = forwardRef<
13541368
<span className="inline-flex">
13551369
<button
13561370
aria-label={videoRef?.current?.muted ? "Unmute" : "Mute"}
1357-
className="inline-flex items-center text-sm font-medium transition ease-in-out duration-150 focus:outline-none border text-slate-100 border-transparent hover:text-white focus:border-white hover:bg-slate-100 hover:bg-opacity-10 active:bg-slate-100 active:bg-opacity-10 px-1 sm:px-2 py-1 sm:py-2 justify-center rounded-lg"
1371+
className="inline-flex justify-center items-center px-1 py-1 text-sm font-medium rounded-lg border border-transparent transition duration-150 ease-in-out focus:outline-none text-slate-100 hover:text-white focus:border-white hover:bg-slate-100 hover:bg-opacity-10 active:bg-slate-100 active:bg-opacity-10 sm:px-2 sm:py-2"
13581372
tabIndex={0}
13591373
type="button"
13601374
onClick={() => handleMuteClick()}
@@ -1369,7 +1383,7 @@ export const ShareVideo = forwardRef<
13691383
<span className="inline-flex">
13701384
<button
13711385
aria-label="Go fullscreen"
1372-
className="inline-flex items-center text-sm font-medium transition ease-in-out duration-150 focus:outline-none border text-slate-100 border-transparent hover:text-white focus:border-white hover:bg-slate-100 hover:bg-opacity-10 active:bg-slate-100 active:bg-opacity-10 px-1 sm:px-2 py-1 sm:py-2 justify-center rounded-lg"
1386+
className="inline-flex justify-center items-center px-1 py-1 text-sm font-medium rounded-lg border border-transparent transition duration-150 ease-in-out focus:outline-none text-slate-100 hover:text-white focus:border-white hover:bg-slate-100 hover:bg-opacity-10 active:bg-slate-100 active:bg-opacity-10 sm:px-2 sm:py-2"
13731387
tabIndex={0}
13741388
type="button"
13751389
onClick={handleFullscreenClick}

0 commit comments

Comments
 (0)