1
+ import { apiClient } from "@/utils/web-api" ;
2
+ import { userSelectProps } from "@cap/database/auth/session" ;
1
3
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" ;
12
7
import {
13
- Play ,
14
- Pause ,
15
8
Maximize ,
16
- VolumeX ,
17
- Volume2 ,
18
9
MessageSquare ,
10
+ Pause ,
11
+ Play ,
12
+ Volume2 ,
13
+ VolumeX ,
19
14
} 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" ;
23
22
import toast from "react-hot-toast" ;
24
23
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 " ;
28
27
29
28
declare global {
30
29
interface Window {
@@ -984,14 +983,31 @@ export const ShareVideo = forwardRef<
984
983
}
985
984
} , [ previewCanvasRef , previewWidth , previewHeight , isLargeScreen ] ) ;
986
985
986
+ useEffect ( ( ) => {
987
+ // Safari detection for applying specific styles
988
+ const detectSafari = ( ) => {
989
+ const isSafari =
990
+ / ^ ( (? ! c h r o m e | a n d r o i d ) .) * s a f a r i / 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
+
987
1003
if ( data . jobStatus === "ERROR" ) {
988
1004
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" >
990
1006
< div
991
1007
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 "
993
1009
>
994
- < p className = "text-white text-xl " >
1010
+ < p className = "text-xl text-white " >
995
1011
There was an error when processing the video. Please contact
996
1012
support.
997
1013
</ p >
@@ -1020,14 +1036,14 @@ export const ShareVideo = forwardRef<
1020
1036
return (
1021
1037
< div
1022
1038
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"
1024
1040
>
1025
1041
< div
1026
1042
className = { `absolute inset-0 flex items-center justify-center z-10 bg-black transition-opacity duration-300 ${
1027
1043
isLoading ? "opacity-100" : "opacity-0 pointer-events-none"
1028
1044
} `}
1029
1045
>
1030
- < LogoSpinner className = "w-8 sm:w-10 h-auto animate-spin" />
1046
+ < LogoSpinner className = "w-8 h-auto animate-spin sm:w-10 " />
1031
1047
</ div >
1032
1048
< div className = "relative w-full h-full" >
1033
1049
< div className = "absolute inset-0 bg-black" >
@@ -1047,7 +1063,7 @@ export const ShareVideo = forwardRef<
1047
1063
>
1048
1064
< button
1049
1065
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 "
1051
1067
onClick = { ( ) => {
1052
1068
if ( ! videoReadyToPlay ) {
1053
1069
// If video is not ready, set a visual indicator but don't try to play yet
@@ -1065,16 +1081,16 @@ export const ShareVideo = forwardRef<
1065
1081
} }
1066
1082
>
1067
1083
{ 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" />
1069
1085
) : (
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" />
1071
1087
) }
1072
1088
</ button >
1073
1089
</ div >
1074
1090
) }
1075
1091
{ 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 " >
1078
1094
{ currentSubtitle . text
1079
1095
. replace ( "- " , "" )
1080
1096
. replace ( "." , "" )
@@ -1087,7 +1103,7 @@ export const ShareVideo = forwardRef<
1087
1103
{ /* Thumbnail preview - MP4 only, visible only on screens larger than lg */ }
1088
1104
{ showPreview && ! isLoading && isMP4Source && isLargeScreen && (
1089
1105
< 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"
1091
1107
style = { {
1092
1108
left : `${ previewPosition } px` ,
1093
1109
bottom : "70px" ,
@@ -1100,7 +1116,7 @@ export const ShareVideo = forwardRef<
1100
1116
< img
1101
1117
src = { thumbnailUrl }
1102
1118
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"
1104
1120
onError = { ( e ) => {
1105
1121
console . log (
1106
1122
"Thumbnail failed to load, using canvas preview instead"
@@ -1111,11 +1127,11 @@ export const ShareVideo = forwardRef<
1111
1127
) : (
1112
1128
< canvas
1113
1129
ref = { previewCanvasRef }
1114
- className = "w-full h-full object-contain bg-black"
1130
+ className = "object-contain w-full h-full bg-black"
1115
1131
/>
1116
1132
) }
1117
1133
{ ! 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 " >
1119
1135
{ formatTime ( previewTime ) }
1120
1136
</ div >
1121
1137
) }
@@ -1146,7 +1162,7 @@ export const ShareVideo = forwardRef<
1146
1162
>
1147
1163
< div
1148
1164
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"
1150
1166
onMouseDown = { handleSeekMouseDown }
1151
1167
onMouseMove = { ( e ) => {
1152
1168
if ( seeking ) {
@@ -1171,7 +1187,7 @@ export const ShareVideo = forwardRef<
1171
1187
onTouchEnd = { handleSeekMouseUp }
1172
1188
>
1173
1189
{ ! isLoading && comments !== null && (
1174
- < div className = "w-full - mt-7 md:-mt-6" >
1190
+ < div className = "- mt-7 w-full md:-mt-6" >
1175
1191
{ comments . map ( ( comment ) => {
1176
1192
const commentPosition =
1177
1193
comment . timestamp === null
@@ -1194,7 +1210,7 @@ export const ShareVideo = forwardRef<
1194
1210
return (
1195
1211
< div
1196
1212
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"
1198
1214
style = { {
1199
1215
left : `${ commentPosition } %` ,
1200
1216
} }
@@ -1228,31 +1244,29 @@ export const ShareVideo = forwardRef<
1228
1244
style = { { left : `${ watchedPercentage } %` } }
1229
1245
/>
1230
1246
</ 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" >
1232
1248
< 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 >
1250
1264
< div className = "text-xs sm:text-sm text-white font-medium select-none tabular text-clip overflow-hidden whitespace-nowrap space-x-0.5" >
1251
1265
{ formatTime ( currentTime ) } - { formatTime ( longestDuration ) }
1252
1266
</ div >
1253
1267
</ div >
1254
1268
< 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" >
1256
1270
< span className = "inline-flex" >
1257
1271
< button
1258
1272
aria-label = { `Change video speed to ${ videoSpeed } x` }
@@ -1268,7 +1282,7 @@ export const ShareVideo = forwardRef<
1268
1282
< span className = "inline-flex" >
1269
1283
< button
1270
1284
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"
1272
1286
tabIndex = { 0 }
1273
1287
type = "button"
1274
1288
onClick = { ( ) => {
@@ -1308,7 +1322,7 @@ export const ShareVideo = forwardRef<
1308
1322
aria-label = {
1309
1323
subtitlesVisible ? "Hide subtitles" : "Show subtitles"
1310
1324
}
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"
1312
1326
tabIndex = { 0 }
1313
1327
type = "button"
1314
1328
onClick = { ( ) => setSubtitlesVisible ( ! subtitlesVisible ) }
@@ -1354,7 +1368,7 @@ export const ShareVideo = forwardRef<
1354
1368
< span className = "inline-flex" >
1355
1369
< button
1356
1370
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"
1358
1372
tabIndex = { 0 }
1359
1373
type = "button"
1360
1374
onClick = { ( ) => handleMuteClick ( ) }
@@ -1369,7 +1383,7 @@ export const ShareVideo = forwardRef<
1369
1383
< span className = "inline-flex" >
1370
1384
< button
1371
1385
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"
1373
1387
tabIndex = { 0 }
1374
1388
type = "button"
1375
1389
onClick = { handleFullscreenClick }
0 commit comments