-
-
Notifications
You must be signed in to change notification settings - Fork 51
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
pop-out-clock proof of concept #1247
Conversation
WalkthroughThe changes in this pull request introduce a new Changes
Possibly related issues
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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.
Actionable comments posted: 8
🧹 Outside diff range and nitpick comments (9)
apps/client/src/features/viewers/pop-out-clock/PopOutTimer.scss (4)
1-15
: LGTM! Consider using viewport units for width.The import and main container styles are well-structured and follow best practices. The use of CSS variables and Grid layout is commendable.
For improved responsiveness, consider using
width: 100vw;
instead ofwidth: 100%;
to ensure the timer spans the full viewport width, including any potential scrollbar space.
16-20
: Specify transition properties for better performance.The finished state styles are well-implemented, especially the use of
clamp()
for responsive sizing.To optimize performance, specify which properties should transition:
- transition: $viewer-transition-time; + transition: outline $viewer-transition-time, outline-offset $viewer-transition-time;This change ensures only the relevant properties are animated, potentially improving rendering performance.
22-41
: LGTM! Consider adding a minimum font size for better readability.The timer styles are well-implemented, with good use of CSS variables and responsive units.
To ensure readability on very small screens, consider adding a minimum font size:
- font-size: 20vw; + font-size: clamp(24px, 20vw, 200px);This change ensures the timer text doesn't become too small on narrow viewports while maintaining its responsive nature.
43-53
: LGTM! Consider using a CSS variable for consistency.The end message styles are well-implemented, providing a clear and prominent display when the timer ends.
For consistency with the timer styles and easier customization, consider using a CSS variable for the color:
- color: $timer-finished-color; + color: var(--timer-finished-color-override, $timer-finished-color);This change allows for easy overriding of the end message color while maintaining the default from the SCSS variable.
apps/client/src/features/viewers/pop-out-clock/PopOutTimer.options.ts (2)
5-21
: Consider clarifying the "Hide Overtime" description.The "Timer Options" and "Element visibility" sections are well-organized. However, the description for the "Hide Overtime" option could be more precise.
Consider updating the description to clarify that it affects both visual styles:
- description: 'Whether to suppress overtime styles (red borders and red text)', + description: 'Whether to suppress overtime styles (hides both red borders and red text)',This change makes it explicit that both visual elements are affected by this option.
54-91
: LGTM: Comprehensive positioning options with a minor suggestion.The alignment and offset options provide excellent control over the timer's appearance and position. The use of predefined values for alignment enhances usability.
Consider updating the vertical alignment description for accuracy:
- description: 'Moves the vertically in page to start = left | center | end = right', + description: 'Moves vertically in page to start = top | center | end = bottom',This change corrects the directional terms for vertical alignment, improving clarity for users.
apps/client/src/features/viewers/pop-out-clock/PopOutTimer.tsx (3)
129-132
: Remove unused variablei
The variable
i
is initialized and incremented inside the interval but is not used elsewhere in the code. This is unnecessary and can be safely removed.Apply this diff to remove the unused variable:
- let i = 0; const interval = setInterval(() => { changeVideo("green", display, context!, canvas, videoRef.current!); - i++; }, 1000);
10-12
: PropisMirrored
is not used; consider removing itThe
isMirrored
prop is defined inMinimalTimerProps
and extracted in the component but is not used anywhere. If mirroring functionality is intended, please implement it. Otherwise, consider removing this prop to simplify the component.Apply this diff to remove the unused prop:
- interface MinimalTimerProps { - isMirrored: boolean; time: ViewExtendedTimer; viewSettings: ViewSettings; } ... - const { isMirrored, time, viewSettings } = props; + const { time, viewSettings } = props;
50-68
: Variableclicked
is not used; consider removing itThe
clicked
variable is set totrue
whenopenPip
is called but isn't used elsewhere in the component. If it's intended for future use, please implement the functionality. Otherwise, consider removing it to clean up the code.Apply this diff to remove the unused variable:
- let clicked = false; ... const openPip = async () => { if (!videoRef.current) return; - clicked = true; await videoRef.current.play();
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (4)
- apps/client/src/AppRouter.tsx (2 hunks)
- apps/client/src/features/viewers/pop-out-clock/PopOutTimer.options.ts (1 hunks)
- apps/client/src/features/viewers/pop-out-clock/PopOutTimer.scss (1 hunks)
- apps/client/src/features/viewers/pop-out-clock/PopOutTimer.tsx (1 hunks)
🧰 Additional context used
🔇 Additional comments (8)
apps/client/src/features/viewers/pop-out-clock/PopOutTimer.scss (1)
1-53
: Overall, excellent implementation with room for minor enhancements.The
PopOutTimer.scss
file demonstrates a well-structured, responsive, and customizable approach to styling the pop-out clock component. It effectively uses SCSS features, CSS variables, and modern layout techniques like CSS Grid. The suggested improvements are minor and focus on enhancing consistency, accessibility, and performance.Great job on creating a flexible and visually appealing style for the timer component!
apps/client/src/features/viewers/pop-out-clock/PopOutTimer.options.ts (2)
1-4
: LGTM: Imports and constant declaration are appropriate.The imports and the constant declaration are well-structured and relevant to the file's purpose. The naming is clear and the typing is explicit, which enhances code readability and maintainability.
1-91
: Well-structured configuration options for the pop-out clock feature.This file effectively defines a comprehensive set of configuration options for the pop-out clock feature, aligning well with the PR's objective of implementing a proof of concept for a Video Picture-in-Picture (PiP) feature. The options cover various aspects of the timer's appearance and behavior, providing flexibility for future customization.
Key strengths:
- Logical organization of options into sections.
- Clear and descriptive naming of options.
- Comprehensive coverage of styling and positioning options.
The minor suggestions provided in the review will further enhance the clarity and consistency of the options. Overall, this file forms a solid foundation for the pop-out clock functionality.
apps/client/src/AppRouter.tsx (3)
19-19
: LGTM: New import for timer-related types.The addition of this import is appropriate and necessary for the new
PopOutTimer
component. It's good to see that shared types are being used, which helps maintain consistency across the application.
27-27
: LGTM: Lazy loading of PopOutTimer component.The lazy loading of the
PopOutTimer
component is implemented correctly and consistently with other components in this file. This approach is beneficial for code splitting and can improve the application's initial load time.
Line range hint
1-156
: Summary: PopOutTimer implementation is a good start for the proof of concept.The changes introduce the
PopOutTimer
component to the application router, which aligns with the PR's objective of creating a proof of concept for a pop-out clock feature. The implementation follows the existing patterns in the file, such as lazy loading and proper placement within the React.Suspense wrapper.Next steps to consider:
- Refactor the hardcoded values in the
PopOutTimer
props to use dynamic data from the application's state.- Implement proper error handling and loading states for the lazy-loaded component.
- Add unit tests for the new component and its integration with the router.
- Update the application's documentation to include information about this new feature.
Overall, this is a solid foundation for the pop-out clock feature. Great job on getting this proof of concept implemented!
apps/client/src/features/viewers/pop-out-clock/PopOutTimer.tsx (2)
112-122
: Review the necessity of playing the video after setting the sourceAfter setting
videoElement.srcObject
orvideoElement.src
, the call tovideoElement.play()
should be reviewed. Multiple calls toplay()
might result in warnings or errors in some browsers, especially if the video is already playing.
70-81
:⚠️ Potential issueParameter
text
indrawFrame
should reflect the timer displayIn the
drawFrame
function, thetext
parameter receivestitle
, which is always an empty string. To display the timer correctly, ensure thattext
receives thedisplay
value.Apply this diff to fix the parameter:
- let title = ""; ... - changeVideo(color, title, context, canvas, videoElement); + changeVideo(color, display, context, canvas, videoElement);Likely invalid or redundant comment.
{ section: 'View style override' }, | ||
{ | ||
id: 'key', | ||
title: 'Key Colour', | ||
description: 'Background colour in hexadecimal', | ||
prefix: '#', | ||
type: 'string', | ||
placeholder: '00000000 (default)', | ||
}, | ||
{ | ||
id: 'text', | ||
title: 'Text Colour', | ||
description: 'Text colour in hexadecimal', | ||
prefix: '#', | ||
type: 'string', | ||
placeholder: 'fffff (default)', | ||
}, | ||
{ | ||
id: 'textbg', | ||
title: 'Text Background', | ||
description: 'Colour of text background in hexadecimal', | ||
prefix: '#', | ||
type: 'string', | ||
placeholder: '00000000 (default)', | ||
}, | ||
{ | ||
id: 'font', | ||
title: 'Font', | ||
description: 'Font family, will use the fonts available in the system', | ||
type: 'string', | ||
placeholder: 'Arial Black (default)', | ||
}, |
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.
Fix inconsistency in text color placeholder.
The "View style override" section is well-structured, providing comprehensive customization options. However, there's an inconsistency in the text color placeholder.
Apply this change to fix the inconsistency:
- placeholder: 'fffff (default)',
+ placeholder: 'ffffff (default)',
This change ensures that the placeholder represents a valid 6-digit hexadecimal color code, consistent with the other color options.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
{ section: 'View style override' }, | |
{ | |
id: 'key', | |
title: 'Key Colour', | |
description: 'Background colour in hexadecimal', | |
prefix: '#', | |
type: 'string', | |
placeholder: '00000000 (default)', | |
}, | |
{ | |
id: 'text', | |
title: 'Text Colour', | |
description: 'Text colour in hexadecimal', | |
prefix: '#', | |
type: 'string', | |
placeholder: 'fffff (default)', | |
}, | |
{ | |
id: 'textbg', | |
title: 'Text Background', | |
description: 'Colour of text background in hexadecimal', | |
prefix: '#', | |
type: 'string', | |
placeholder: '00000000 (default)', | |
}, | |
{ | |
id: 'font', | |
title: 'Font', | |
description: 'Font family, will use the fonts available in the system', | |
type: 'string', | |
placeholder: 'Arial Black (default)', | |
}, | |
{ section: 'View style override' }, | |
{ | |
id: 'key', | |
title: 'Key Colour', | |
description: 'Background colour in hexadecimal', | |
prefix: '#', | |
type: 'string', | |
placeholder: '00000000 (default)', | |
}, | |
{ | |
id: 'text', | |
title: 'Text Colour', | |
description: 'Text colour in hexadecimal', | |
prefix: '#', | |
type: 'string', | |
placeholder: 'ffffff (default)', | |
}, | |
{ | |
id: 'textbg', | |
title: 'Text Background', | |
description: 'Colour of text background in hexadecimal', | |
prefix: '#', | |
type: 'string', | |
placeholder: '00000000 (default)', | |
}, | |
{ | |
id: 'font', | |
title: 'Font', | |
description: 'Font family, will use the fonts available in the system', | |
type: 'string', | |
placeholder: 'Arial Black (default)', | |
}, |
<PopOutTimer | ||
isMirrored={false} | ||
time={{ | ||
addedTime: 0, | ||
current: null, | ||
duration: null, | ||
elapsed: null, | ||
expectedFinish: null, | ||
finishedAt: null, | ||
phase: TimerPhase.Default, | ||
playback: Playback.Play, | ||
secondaryTimer: null, | ||
startedAt: null, | ||
clock: 0, | ||
timerType: TimerType.CountDown | ||
}} | ||
viewSettings={{ | ||
dangerColor: '', | ||
endMessage: '', | ||
freezeEnd: false, | ||
normalColor: '', | ||
overrideStyles: false, | ||
warningColor: '' | ||
}} /> |
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.
🛠️ Refactor suggestion
Consider refactoring hardcoded values in PopOutTimer props.
While the placement of the PopOutTimer
component is correct, there are a few points to consider:
- The
time
prop contains many hardcoded values. This could lead to maintenance issues and might not reflect the actual state of the application. - The
viewSettings
prop has empty strings for colors and messages.
Given that this is a proof of concept, these issues are understandable. However, for the next iteration, consider:
- Connecting the
time
prop to your application's state management system (e.g., Redux, Context API) to reflect real-time values. - Implementing a configuration file or environment variables for the
viewSettings
to make them easily customizable. - Adding PropTypes or TypeScript interfaces to define the expected prop types and structures.
Would you like assistance in setting up a more dynamic prop structure for the PopOutTimer
component?
useEffect(() => { | ||
const canvas = canvasRef.current; | ||
const videoElement = videoRef.current; | ||
if (canvas && videoElement) { | ||
const context = canvas.getContext('2d'); | ||
if (context) { | ||
changeVideo(color, title, context, canvas, videoElement); | ||
} | ||
setReady(true); | ||
} | ||
}, []); |
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.
Variables color
and title
are not updated; consider removing or updating them
In the useEffect
hook, changeVideo
is called with color
and title
, but these variables are not updated anywhere in the component. As a result, the video might not display the intended content.
If color
and title
are meant to change based on certain conditions or props, ensure they are updated accordingly. Otherwise, consider removing them if they are not needed.
useEffect(() => { | ||
if (ready && canvasRef.current && videoRef.current) { | ||
const canvas = canvasRef.current; | ||
const context = canvas.getContext('2d'); | ||
let i = 0; | ||
const interval = setInterval(() => { | ||
changeVideo("green", display, context!, canvas, videoRef.current!); | ||
i++; | ||
}, 1000); | ||
return () => clearInterval(interval); // Clean up the interval on component unmount | ||
} | ||
}, [ready]); |
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.
🛠️ Refactor suggestion
Consider updating the useEffect
to handle changes in time
The display
value is derived from time
, and if time
changes, the interval may not reflect the updated timer. Consider updating the useEffect
to handle changes in time
to ensure the timer stays accurate.
Update the dependency array and adjust the effect:
- }, [ready, display]);
+ }, [ready, time]);
Committable suggestion was skipped due to low confidence.
Include dependencies in the useEffect
hook to prevent stale values
The useEffect
hook depends on display
, which is derived from time
and may change over time. Currently, the dependency array only includes [ready]
, which could lead to the interval using stale values of display
.
Consider adding display
or its dependencies to the dependency array to ensure the interval reflects the latest timer values.
Apply this diff to fix the dependency array:
- }, [ready]);
+ }, [ready, display]);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
useEffect(() => { | |
if (ready && canvasRef.current && videoRef.current) { | |
const canvas = canvasRef.current; | |
const context = canvas.getContext('2d'); | |
let i = 0; | |
const interval = setInterval(() => { | |
changeVideo("green", display, context!, canvas, videoRef.current!); | |
i++; | |
}, 1000); | |
return () => clearInterval(interval); // Clean up the interval on component unmount | |
} | |
}, [ready]); | |
useEffect(() => { | |
if (ready && canvasRef.current && videoRef.current) { | |
const canvas = canvasRef.current; | |
const context = canvas.getContext('2d'); | |
let i = 0; | |
const interval = setInterval(() => { | |
changeVideo("green", display, context!, canvas, videoRef.current!); | |
i++; | |
}, 1000); | |
return () => clearInterval(interval); // Clean up the interval on component unmount | |
} | |
}, [ready, display]); |
const createVideoBlob = (canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, callback: (url: string) => void) => { | ||
const stream = canvas.captureStream(30); | ||
const mediaRecorder = new MediaRecorder(stream, { mimeType: 'video/webm' }); | ||
const chunks: BlobPart[] = []; | ||
|
||
mediaRecorder.ondataavailable = (event) => { | ||
if (event.data.size > 0) { | ||
chunks.push(event.data); | ||
} | ||
}; | ||
|
||
mediaRecorder.onstop = () => { | ||
const blob = new Blob(chunks, { type: 'video/webm' }); | ||
callback(URL.createObjectURL(blob)); | ||
}; | ||
|
||
mediaRecorder.start(); | ||
setTimeout(() => { | ||
mediaRecorder.stop(); | ||
}, 100); | ||
}; |
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.
🛠️ Refactor suggestion
Optimize video generation by using a continuous stream instead of creating blobs repeatedly
Currently, createVideoBlob
is called every second, creating a new video blob each time. This approach is inefficient and may cause performance issues.
Consider using the canvas stream directly as the source for the video element without creating new blobs. This can be achieved by assigning the canvas stream to the video element's srcObject
.
Apply this diff to refactor the video handling:
- createVideoBlob(canvas, context, (newVideoSource) => {
- if (videoSource) {
- URL.revokeObjectURL(videoSource);
- }
- setVideoSource(newVideoSource);
- videoElement.src = newVideoSource;
- videoElement.play().catch((error) => {
- console.error("Error playing video:", error);
- });
- });
+ const stream = canvas.captureStream(30);
+ videoElement.srcObject = stream;
+ videoElement.play().catch((error) => {
+ console.error("Error playing video:", error);
+ });
Committable suggestion was skipped due to low confidence.
💡 Codebase verification
Proper Cleanup of Media Resources is Not Fully Implemented
- In
apps/client/src/features/viewers/pop-out-clock/PopOutTimer.tsx
, theURL.createObjectURL
is used to create a blob URL but is never revoked usingURL.revokeObjectURL
, which can lead to memory leaks. - Additionally, ensure that the media stream obtained from
canvas.captureStream
is explicitly stopped after usage to release all associated resources.
🔗 Analysis chain
Ensure proper cleanup of media resources to prevent memory leaks
When creating media streams and blobs, it's crucial to properly clean up resources to avoid memory leaks. Ensure that any streams, blobs, or object URLs are correctly released when no longer needed.
Run the following script to check for proper cleanup of media resources:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Search for any unreleased media resources in the codebase.
# Expectation: All created media streams and object URLs should be properly revoked or released.
rg --type js 'URL\.createObjectURL|captureStream' -A 5
Length of output: 56
Script:
#!/bin/bash
# Description: Search for any unreleased media resources in JavaScript and TypeScript files.
# Expectation: All created media streams and object URLs should be properly revoked or released.
rg --type js --type ts --type jsx --type tsx 'URL\.createObjectURL|captureStream' -A 5
Length of output: 116
Script:
#!/bin/bash
# Description: Search for any unreleased media resources in JavaScript and TypeScript files, including JSX and TSX.
# Define custom types for jsx and tsx
rg --type-add 'jsx:*.jsx' --type-add 'tsx:*.tsx' 'URL\.createObjectURL|captureStream' -t js -t ts -t jsx -t tsx -A 5
Length of output: 1906
let color = "#000000"; | ||
let title = ""; | ||
let clicked = false; |
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.
Avoid using let
for variables that persist across renders; use useState
or useRef
instead
The variables color
, title
, and clicked
are declared with let
and are re-initialized on every render. This means they won't retain any changes between renders, which may lead to unexpected behavior.
To maintain their values across renders, consider using useState
or useRef
. Additionally, if title
and clicked
are not being updated or used elsewhere in the component, they might be unnecessary.
Apply this diff to fix the issue:
- let color = "#000000";
- let title = "";
- let clicked = false;
+ const colorRef = useRef("#000000");
+ const titleRef = useRef("");
+ const clickedRef = useRef(false);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
let color = "#000000"; | |
let title = ""; | |
let clicked = false; | |
const colorRef = useRef("#000000"); | |
const titleRef = useRef(""); | |
const clickedRef = useRef(false); | |
``` | |
This suggestion replaces the original `let` declarations with `useRef` hooks, which will allow the values to persist across renders. The suggestion is complete and can be directly applied to replace the original code snippet. | |
Note: To use `useRef`, make sure to import it from 'react' at the top of the file if it's not already imported: | |
```typescript | |
import { useRef } from 'react'; |
Thank you @Haavard15 , we will plug in the data from here |
Very rough proof of concept for Video PiP that can pop out on top of presentation notes etc.