-
-
Notifications
You must be signed in to change notification settings - Fork 17
feat: exclusive playback provider #166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| "use client" | ||
|
|
||
| import { Slot } from "@radix-ui/react-slot" | ||
| import * as React from "react" | ||
|
|
||
| export interface ExclusivePlaybackProviderProps extends React.ComponentPropsWithoutRef<"div"> { | ||
| asChild?: boolean | ||
| } | ||
|
|
||
| export const ExclusivePlaybackProvider = React.forwardRef< | ||
| HTMLDivElement, | ||
| ExclusivePlaybackProviderProps | ||
| >(function ExclusivePlaybackProvider( | ||
| { asChild, onEndedCapture, onPauseCapture, onPlayCapture, ...props }, | ||
| forwardedRef | ||
| ) { | ||
| const activeMediaRef = React.useRef<HTMLMediaElement | null>(null) | ||
| const Component = asChild ? Slot : "div" | ||
|
|
||
| const handlePlayCapture = (event: React.SyntheticEvent<HTMLDivElement>) => { | ||
| onPlayCapture?.(event) | ||
| if (event.defaultPrevented) return | ||
|
|
||
| const mediaElement = getMediaElement(event) | ||
| if (!mediaElement || activeMediaRef.current === mediaElement) return | ||
|
|
||
| activeMediaRef.current?.pause() | ||
| activeMediaRef.current = mediaElement | ||
| } | ||
|
|
||
| const releaseMedia = (event: React.SyntheticEvent<HTMLDivElement>) => { | ||
| const mediaElement = getMediaElement(event) | ||
| if (activeMediaRef.current === mediaElement) { | ||
| activeMediaRef.current = null | ||
| } | ||
| } | ||
|
|
||
| const handlePauseCapture = (event: React.SyntheticEvent<HTMLDivElement>) => { | ||
| onPauseCapture?.(event) | ||
| if (!event.defaultPrevented) releaseMedia(event) | ||
| } | ||
|
|
||
| const handleEndedCapture = (event: React.SyntheticEvent<HTMLDivElement>) => { | ||
| onEndedCapture?.(event) | ||
| if (!event.defaultPrevented) releaseMedia(event) | ||
|
Comment on lines
+14
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# Locate the repository event hook and established subscription cleanup patterns.
rg -n -C 5 --glob '*.{ts,tsx}' \
'useMediaEvents\s*\(|events\.on\s*\(|MediaEventEmitter' apps/wwwRepository: WINOFFRG/limeplay Length of output: 6401 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- exclusive-playback-provider.tsx ---'
wc -l apps/www/registry/default/ui/exclusive-playback-provider.tsx
cat -n apps/www/registry/default/ui/exclusive-playback-provider.tsx
printf '%s\n' '--- media-provider event API ---'
rg -n -C 8 --glob '*.tsx' \
'function useMediaEvents|export .*useMediaEvents|const useMediaEvents|events\.on\("play"|events\.on\("pause"|events\.on\("ended"' \
apps/www/registry/default/ui apps/www/registry/default/hooks
printf '%s\n' '--- exclusive provider usages ---'
rg -n -C 5 --glob '*.{ts,tsx}' \
'ExclusivePlaybackProvider|onPlayCapture|onPauseCapture|onEndedCapture' apps/wwwRepository: WINOFFRG/limeplay Length of output: 11118 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- media-provider runtime and event types ---'
cat -n apps/www/registry/default/ui/media-provider.tsx | sed -n '1,230p'
cat -n apps/www/registry/default/ui/media-provider.tsx | sed -n '230,430p'
printf '%s\n' '--- native event emission and event-name definitions ---'
rg -n -C 8 --glob '*.{ts,tsx}' \
'emit\("(play|pause|ended)"|["'\''](play|pause|ended)["'\'']|HTMLMediaElement|addEventListener' \
apps/www/registry/defaultRepository: WINOFFRG/limeplay Length of output: 50375 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- media-provider boundaries and player composition ---'
rg -n -C 6 --glob '*.{ts,tsx}' \
'MediaProvider|createMediaKit|ExclusivePlaybackProvider|VideoPlayerContainer|AudioPlayer' \
apps/www/registry/default apps/www/components apps/www/app
printf '%s\n' '--- playback event types and setup context ---'
cat -n apps/www/registry/default/hooks/use-playback.ts | sed -n '1,90p'
cat -n apps/www/registry/default/hooks/use-playback.ts | sed -n '200,380p'
printf '%s\n' '--- event maps and media element registration ---'
rg -n -C 5 --glob '*.{ts,tsx}' \
'interface .*Events|type .*Events|mediaEventMap|setMediaElement|useMediaEvents' \
apps/www/registry/default/hooks apps/www/registry/default/uiRepository: WINOFFRG/limeplay Length of output: 50374 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- provider occurrence summary ---'
rg -l --glob '*.{ts,tsx}' 'MediaProvider' apps/www/registry/default apps/www/components apps/www/app | sort
printf '%s\n' '--- home component structure ---'
cat -n 'apps/www/app/(home)/page.tsx' | sed -n '19,110p'
cat -n apps/www/registry/default/blocks/video-player/player.tsx | sed -n '65,90p'
printf '%s\n' '--- event payload declarations ---'
rg -n --glob '*.ts' \
'^[[:space:]]*(play|pause|ended):' \
apps/www/registry/default/hooks apps/www/registry/default/blocksRepository: WINOFFRG/limeplay Length of output: 5740 🏁 Script executed: #!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
home = Path("apps/www/app/(home)/page.tsx").read_text()
video = Path("apps/www/registry/default/blocks/video-player/player.tsx").read_text()
provider = Path("apps/www/registry/default/ui/media-provider.tsx").read_text()
playback = Path("apps/www/registry/default/hooks/use-playback.ts").read_text()
checks = {
"exclusive wraps the video player": (
"<ExclusivePlaybackProvider" in home
and "<VideoPlayerContainer" in home
and home.index("<ExclusivePlaybackProvider") < home.index("<VideoPlayerContainer")
and home.index("<VideoPlayerContainer") < home.index("</ExclusivePlaybackProvider>")
),
"video player creates a media provider": (
"<MediaProvider" in video
and "</MediaProvider>" in video
and video.index("<MediaProvider") < video.index("</MediaProvider>")
),
"useMediaEvents reads the current runtime": (
"const { events } = useMediaRuntime()" in provider
),
"play/pause/ended event payloads are void": all(
re.search(rf"^\s*{name}: void$", playback, re.MULTILINE)
for name in ("play", "pause", "ended")
),
}
for label, passed in checks.items():
print(f"{'PASS' if passed else 'FAIL'}: {label}")
if not all(checks.values()):
raise SystemExit(1)
PYRepository: WINOFFRG/limeplay Length of output: 332 Provide a cross-provider event source before removing these handlers. This component violates the no- 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| } | ||
|
|
||
| return ( | ||
| <Component | ||
| {...props} | ||
| onEndedCapture={handleEndedCapture} | ||
| onPauseCapture={handlePauseCapture} | ||
| onPlayCapture={handlePlayCapture} | ||
| ref={forwardedRef} | ||
| /> | ||
| ) | ||
| }) | ||
|
|
||
| ExclusivePlaybackProvider.displayName = "ExclusivePlaybackProvider" | ||
|
|
||
| function getMediaElement( | ||
| event: React.SyntheticEvent<HTMLDivElement> | ||
| ): HTMLMediaElement | null { | ||
| return event.target instanceof HTMLMediaElement ? event.target : null | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: This new component is placed in
registry/default/ui/but is not registered inapps/www/registry/collection/registry-ui.ts, making it the only file in that folder that is missing from the registry. Registry consumers can'tshadcn add exclusive-playback-provider, and the source is inconsistent with all sibling components, which are each declared there. Add a matchingregistry:uientry (with the@radix-ui/react-slotdependency) in registry-ui.ts.Prompt for AI agents