Skip to content
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

Video playback #4878

Merged
merged 11 commits into from
Oct 4, 2024
Merged

Video playback #4878

merged 11 commits into from
Oct 4, 2024

Conversation

benjaminpkane
Copy link
Contributor

@benjaminpkane benjaminpkane commented Oct 2, 2024

What changes are proposed in this pull request?

Adds video looker support for the modal timeline

Screen.Recording.2024-10-02.at.5.19.37.PM.mov

Animated panel with video

from bson import ObjectId
import fiftyone.core.media as fom
import fiftyone.operators as foo
import fiftyone.operators.types as types
import fiftyone.core.view as fov
from fiftyone import ViewField as F


class MyAnimatedPanel(foo.Panel):
    @property
    def config(self):
        return foo.PanelConfig(
            name="example_animated_plot",
            label="Examples: Animated Plot",
            surfaces="modal",
            reload_on_navigation=True,
        )

    def on_load(self, ctx):
        if not ctx.current_sample:
            return

        counts = []
        for frame in self._iter_frames(ctx):
            frame_detections = frame.detections
            counts.append(len(frame_detections.detections))

        ctx.panel.state.plot = {
            "type": "bar",
            "y": counts,
            "x": list(range(len(counts))),
        }
        ctx.panel.data.frame_data = None

    def on_change_current_sample(self, ctx):
        print("on_change_current_sample", ctx.current_sample)
        self.on_load(ctx)

    def render(self, ctx):
        panel = types.Object()
        # define a hidden view that handles loading frames
        panel.obj(
            "frame_data",
            timeline_id="something",
            hidden=True,
            view=types.FrameLoaderView(
                on_load_range=self.on_load_range, target="plot.selectedpoints"
            ),
        )
        # define a property that will be updated by the FrameLoader
        panel.plot("plot", on_click=self.on_click_plot, height=100, width=100)
        return types.Property(
            panel,
            view=types.GridView(
                align_x="center", align_y="center", orientation="vertical"
            ),
        )

    def on_load_range(self, ctx):
        frame_data = []
        r = ctx.params.get("range", [0, 0])

        print("range", r)

        chunk = {}
        for i in range(r[0], r[1]):
            rendered_frame = self.render_frame(i)
            frame_data.append(rendered_frame)
            chunk[f"frame_data.frames[{i}]"] = rendered_frame

        ctx.panel.set_data(chunk)
        ctx.panel.set_state("frame_data.signature", str(r))

    def on_click_plot(self, ctx):
        ctx.ops.notify(
            f"You clicked on the plot! x = {ctx.params.get('x')}",
            variant="info",
        )

    def render_frame(self, frame):
        return [frame]

    def _iter_frames(self, ctx):
        current_sample = fov.make_optimized_select_view(
            ctx.view, ctx.current_sample
        ).first()
        if ctx.view.media_type == fom.VIDEO:
            for frame in current_sample.frames.values():
                yield frame

            return

        if (
            ctx.view.media_type == fom.GROUP
            and ctx.view._parent_media_type == fom.IMAGE
        ):
            for frame in ctx.view.match(
                F("_sample_id") == ObjectId(ctx.current_sample.sample_id)
            ):
                yield frame

            return

        raise ValueError("unexpected")


def register(p):
    p.register(MyAnimatedPanel)

How is this patch tested? If it is not, please explain why.

Existing e2e

Release Notes

  • Added video sample support to the modal timeline

What areas of FiftyOne does this PR affect?

  • App: FiftyOne application changes
  • Build: Build and test infrastructure changes
  • Core: Core fiftyone Python library changes
  • Documentation: FiftyOne documentation changes
  • Other

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced VideoLookerReact component for enhanced video playback functionality.
    • Added TimelineController for managing video timelines.
    • New hooks for improved state and event management, including useLooker and useKeyEvents.
    • Added BUFFERING_PAUSE_TIMEOUT export for better playback control.
  • Improvements

    • Streamlined component logic for better performance and clarity.
    • Conditional rendering based on media type (video or image).
    • Enhanced configuration options with enableTimeline for more flexible looker instances.
  • Bug Fixes

    • Improved handling of keyboard events and sample states.
  • Chores

    • Updated import structures for better organization and readability.

@benjaminpkane benjaminpkane self-assigned this Oct 2, 2024
Copy link
Contributor

coderabbitai bot commented Oct 2, 2024

Walkthrough

The changes in this pull request involve significant updates to the modal components within the application. Key modifications include restructuring imports, introducing new hooks for event handling, and enhancing the functionality of video playback components. The ImaVidLookerReact component has been streamlined by removing outdated state management logic and replacing it with a new useKeyEvents hook. Additionally, a new VideoLookerReact component has been added, which manages video playback and timeline functionality. Overall, the updates aim to improve code organization and component interactions.

Changes

File Path Change Summary
app/packages/core/src/components/Modal/ImaVidLooker.tsx - Imports updated for useClearSelectedLabels, useShowOverlays, and useInitializeImaVidSubscriptions.
- Replaced hoveredSample handling with useKeyEvents hook.
- Removed useEffect for looker state updates.
app/packages/core/src/components/Modal/ModalLooker.tsx - Updated imports for ImageLooker and VideoLookerReact.
- Removed useEventHandler and useLookerOptionsUpdate.
- Refactored ModalLookerNoTimeline to use useLooker hook.
- Added conditional rendering for videos.
app/packages/core/src/components/Modal/VideoLooker.tsx - Introduced VideoLookerReact component for video playback.
- Added TimelineController for managing video timeline.
- Implemented hooks for playback management and event handling.
app/packages/core/src/components/Modal/hooks.ts - Added new hook useLookerOptionsUpdate for managing looker options in Recoil state.
app/packages/core/src/components/Modal/use-key-events.ts - New custom hook for managing keyboard event handling based on hovered sample state.
app/packages/core/src/components/Modal/use-looker.ts - Introduced useLooker hook for managing looker lifecycle and interactions.
app/packages/core/src/components/Modal/utils.ts - Added shortcutToHelpItems function for transforming shortcut collections.
app/packages/looker/src/elements/base.ts - Modified boot method to use optional chaining for event listener addition.
app/packages/looker/src/elements/common/looker.ts - Updated import statements and destructuring in getEvents method for state handling.
app/packages/looker/src/index.ts - Updated exports to include getFrameNumber alongside freeVideos.
app/packages/looker/src/lookers/imavid/index.ts - Added export for BUFFERING_PAUSE_TIMEOUT.
app/packages/looker/src/lookers/video.ts - Enhanced VideoLooker class with frame management and new getVideo method.
app/packages/looker/src/state.ts - Updated interfaces to include shouldHandleKeyEvents and enableTimeline properties.
app/packages/state/src/hooks/useCreateLooker.ts - Added optional parameter enableTimeline to the useCreateLooker function.
app/packages/state/src/recoil/modal.ts - Changed modalLooker atom type from `AbstractLooker

Possibly related PRs

  • ImaVid with new timeline API #4816: The changes in this PR involve the ImaVidLookerReact component, which integrates a video looker with a timeline feature, closely related to the modifications made in the main PR to the ImaVidLooker component.
  • timeline fix: loadrange in imavid #4857: This PR enhances the loadRange method in the ImaVidLooker component, which is directly relevant to the changes made in the main PR regarding the ImaVidLooker functionality.
  • fix e2e flakiness because of timeline API #4892: This PR addresses e2e test flakiness related to the timeline API, which is relevant to the changes in the main PR that enhance the functionality of the ImaVidLooker component, particularly regarding playback and buffering states.

Suggested labels

app

Suggested reviewers

  • imanjra
  • ritch

Poem

In the modal realm where videos play,
New hooks and components brighten the day.
With frames and timelines, we dance and cheer,
A clearer code path, our vision is clear!
So hop along, let’s celebrate this change,
For in our code garden, we’ll always arrange! 🐇✨


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?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@benjaminpkane benjaminpkane marked this pull request as ready for review October 4, 2024 15:55
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🧹 Outside diff range and nitpick comments (9)
app/packages/core/src/components/Modal/hooks.ts (1)

22-41: Good implementation, but type safety can be improved

The useLookerOptionsUpdate hook is well-implemented and follows React and Recoil best practices. However, there are a few areas where we can enhance type safety and documentation:

  1. Improve type safety for the update parameter:
- async (update: object, updater?: (updated: {}) => void) => {
+ async (update: Partial<LookerOptions>, updater?: (updated: LookerOptions) => void) => {
  1. Add JSDoc comments for better documentation:
/**
 * Hook to update Looker options in Recoil state
 * @returns A function to update Looker options
 */
export const useLookerOptionsUpdate = () => {
  return useRecoilCallback(
    ({ snapshot, set }) =>
      /**
       * Updates Looker options
       * @param update - Partial Looker options to update
       * @param updater - Optional callback function called with updated options
       */
      async (update: Partial<LookerOptions>, updater?: (updated: LookerOptions) => void) => {
        // ... (rest of the implementation)
      }
  );
};
  1. Consider defining a type for the return value of the hook for improved clarity:
type UpdateLookerOptions = (update: Partial<LookerOptions>, updater?: (updated: LookerOptions) => void) => Promise<void>;

export const useLookerOptionsUpdate = (): UpdateLookerOptions => {
  // ... (rest of the implementation)
};

These changes will improve type safety, make the code more self-documenting, and enhance maintainability.

🧰 Tools
🪛 Biome

[error] 25-25: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

app/packages/looker/src/elements/common/looker.ts (2)

29-33: Improved state structure in keydown event handler

The changes to the state destructuring are good:

  1. Moving shouldHandleKeyEvents under options improves the organization of the state object.
  2. This change makes the code more maintainable by grouping related properties.

For consistency, consider destructuring options separately:

const { SHORTCUTS, error, options } = state;
const { shouldHandleKeyEvents } = options;

This approach would make it easier to add more options in the future without changing the destructuring pattern.


52-52: Consistent state structure in keyup event handler

The change to the state destructuring in the keyup handler is consistent with the keydown handler, which is good for maintaining code uniformity.

For consistency with the previous suggestion, consider restructuring this line as follows:

update(({ SHORTCUTS, error, options }) => {
  const { shouldHandleKeyEvents } = options;
  // ... rest of the function
});

This approach would make the code more readable and easier to maintain, especially if more options are added in the future.

app/packages/state/src/recoil/modal.ts (1)

1-6: LGTM! Consider grouping related imports.

The changes to the import statements look good and align with the updates in the file. The removal of unused imports and the addition of the Lookers type import improve code clarity.

Consider grouping related imports together for better organization. For example:

import { PointInfo, type Sample } from "@fiftyone/looker";
import { mainSample, mainSampleQuery } from "@fiftyone/relay";
import { atom, selector } from "recoil";
import { graphQLSelector } from "recoil-relay";
import { VariablesOf } from "relay-runtime";

import type { Lookers } from "../hooks";
import { ComputeCoordinatesReturnType } from "../hooks/useTooltip";
app/packages/state/src/hooks/useCreateLooker.ts (1)

39-40: LGTM: Added timeline support. Consider default value.

The addition of the enableTimeline parameter aligns well with the PR objective of adding video looker support for the modal timeline. It provides flexibility while maintaining backward compatibility.

Consider providing a default value for enableTimeline to make the intended default behavior explicit:

enableTimeline: boolean = false
app/packages/core/src/components/Modal/ImaVidLooker.tsx (1)

Line range hint 1-359: Consider further modularization for improved maintainability

The ImaVidLookerReact component is well-structured and uses modern React practices such as hooks and Recoil for state management. However, given its complexity in handling various aspects of video playback and timeline management, consider further modularizing the component by extracting some logic into separate custom hooks or sub-components. This could improve readability and make the component easier to maintain and test.

For example, you could create separate hooks for:

  1. Timeline management
  2. Frame rendering and buffering
  3. Event handling

This refactoring would align even more closely with React best practices and the principle of separation of concerns.

app/packages/looker/src/state.ts (3)

178-178: LGTM! Consider adding JSDoc comment for clarity.

The addition of shouldHandleKeyEvents as an optional boolean property is a good improvement for more granular control over key event handling. It follows TypeScript best practices.

Consider adding a JSDoc comment to explain the purpose and default behavior of this property:

/** 
 * Determines whether key events should be handled.
 * @default true
 */
shouldHandleKeyEvents?: boolean;

222-222: LGTM! Consider making the property optional.

The addition of enableTimeline is a good feature for controlling the visibility of the timeline in video configurations. It follows TypeScript naming conventions.

Consider making this property optional and providing a default value:

enableTimeline?: boolean;

This change would allow for backward compatibility and provide more flexibility in usage. You can then set a default value in the implementation if needed.


Line range hint 465-465: LGTM! Consider adding a JSDoc comment for clarity.

The addition of bufferManager of type BufferManager is a good improvement for managing buffered frames in the ImaVid context. It ensures type safety and follows naming conventions.

Consider adding a JSDoc comment to explain the purpose and usage of this property:

/** 
 * Manages the buffering of video frames.
 */
bufferManager: BufferManager;
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 77f664c and 2bafc34.

⛔ Files ignored due to path filters (2)
  • app/packages/looker/package.json is excluded by !**/*.json
  • app/yarn.lock is excluded by !**/yarn.lock, !**/*.lock, !**/*.lock
📒 Files selected for processing (15)
  • app/packages/core/src/components/Modal/ImaVidLooker.tsx (2 hunks)
  • app/packages/core/src/components/Modal/ModalLooker.tsx (3 hunks)
  • app/packages/core/src/components/Modal/VideoLooker.tsx (1 hunks)
  • app/packages/core/src/components/Modal/hooks.ts (1 hunks)
  • app/packages/core/src/components/Modal/use-key-events.ts (1 hunks)
  • app/packages/core/src/components/Modal/use-looker.ts (1 hunks)
  • app/packages/core/src/components/Modal/utils.ts (1 hunks)
  • app/packages/looker/src/elements/base.ts (1 hunks)
  • app/packages/looker/src/elements/common/looker.ts (3 hunks)
  • app/packages/looker/src/index.ts (1 hunks)
  • app/packages/looker/src/lookers/imavid/index.ts (1 hunks)
  • app/packages/looker/src/lookers/video.ts (3 hunks)
  • app/packages/looker/src/state.ts (2 hunks)
  • app/packages/state/src/hooks/useCreateLooker.ts (4 hunks)
  • app/packages/state/src/recoil/modal.ts (3 hunks)
✅ Files skipped from review due to trivial changes (1)
  • app/packages/looker/src/lookers/imavid/index.ts
🧰 Additional context used
📓 Path-based instructions (14)
app/packages/core/src/components/Modal/ImaVidLooker.tsx (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Modal/ModalLooker.tsx (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Modal/VideoLooker.tsx (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Modal/hooks.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Modal/use-key-events.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Modal/use-looker.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Modal/utils.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/elements/base.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/elements/common/looker.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/index.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/lookers/video.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/looker/src/state.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/state/src/hooks/useCreateLooker.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/state/src/recoil/modal.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

🪛 Biome
app/packages/core/src/components/Modal/hooks.ts

[error] 25-25: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

🔇 Additional comments (20)
app/packages/looker/src/index.ts (1)

6-6: LGTM! Verify usage of the new export.

The addition of getFrameNumber export aligns with the PR objective of enhancing video support in the modal timeline. The export statement follows TypeScript best practices.

To ensure the new export is being used correctly, run the following script:

This script will help identify where the newly exported function is being imported and used in the codebase outside of the looker package.

✅ Verification successful

Usage of getFrameNumber is verified.

The getFrameNumber function is imported and used in:

  • app/packages/core/src/components/Modal/VideoLooker.tsx
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of the newly exported 'getFrameNumber' function

# Search for imports of 'getFrameNumber' from the looker package
echo "Searching for imports of 'getFrameNumber':"
rg --type typescript "import.*getFrameNumber.*from.*@fiftyone/looker" -g '!app/packages/looker/**'

# Search for usage of 'getFrameNumber' function
echo "Searching for usage of 'getFrameNumber' function:"
rg --type typescript "getFrameNumber\(" -g '!app/packages/looker/**'

Length of output: 457


Script:

#!/bin/bash
# Description: Verify the usage of the newly exported 'getFrameNumber' function without using unsupported file types

# Search for imports of 'getFrameNumber' from the looker package
echo "Searching for imports of 'getFrameNumber':"
rg "import.*getFrameNumber.*from.*@fiftyone/looker" --glob '!app/packages/looker/**' --glob '*.ts*'

# Search for usage of 'getFrameNumber' function
echo "Searching for usage of 'getFrameNumber' function:"
rg "getFrameNumber\(" --glob '!app/packages/looker/**' --glob '*.ts*'

Length of output: 619

app/packages/looker/src/elements/base.ts (1)

67-67: Excellent use of optional chaining operator

The change from this.element && to this.element?. is a good improvement. It simplifies the code while maintaining the same functionality. This change aligns with TypeScript best practices by using the optional chaining operator, which makes the code more concise and easier to read.

app/packages/looker/src/elements/common/looker.ts (1)

6-9: Improved import statements

The changes to the import statements are beneficial:

  1. Using import type for BaseState, Control, and Events is a TypeScript best practice for type-only imports. This can lead to better performance and smaller bundle sizes in production builds.
  2. Separating the ControlEventKeyType import improves code organization and makes it clear which imports are for types and which are for values.

These changes align well with TypeScript and React best practices.

app/packages/state/src/recoil/modal.ts (2)

72-75: LGTM! Improved null safety.

The change from id.endsWith("-modal") to id?.endsWith("-modal") is a good improvement. It adds null safety by using the optional chaining operator, preventing potential runtime errors if id is undefined or null.

This change aligns with TypeScript best practices and makes the code more robust.


Line range hint 26-30: LGTM! Consider reviewing the need for mutable state.

The update to the modalLooker atom type from AbstractLooker<BaseState> | null to Lookers | null is consistent with the import changes and likely provides a more appropriate type for the modal looker state.

However, the use of dangerouslyAllowMutability: true raises a potential concern. This option allows the atom's value to be mutated directly, which can lead to unexpected behavior in Recoil. Could you verify if this is necessary? If possible, consider removing this option to ensure immutability.

To help verify the usage of modalLooker, you can run the following script:

This will help identify if there are any places where direct mutation might be occurring, justifying the use of dangerouslyAllowMutability.

✅ Verification successful

Remove dangerouslyAllowMutability: true from modalLooker

The search for usages of modalLooker did not reveal any direct mutations. Therefore, the dangerouslyAllowMutability: true option may be unnecessary and can be removed to ensure immutability.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for all usages of modalLooker in the codebase
rg --type typescript --type tsx 'modalLooker'

Length of output: 80


Script:

#!/bin/bash
# Search for all usages of modalLooker in TypeScript and TSX files
rg --type ts --type tsx 'modalLooker'

Length of output: 65


Script:

#!/bin/bash
# Search for all usages of modalLooker in TypeScript and TSX files using glob patterns
rg 'modalLooker' --glob '*.ts' --glob '*.tsx'

Length of output: 835

app/packages/state/src/hooks/useCreateLooker.ts (2)

12-12: LGTM: Improved type import.

The change to a type-only import for BaseState and ImaVidConfig is a good practice in TypeScript. It clearly separates type imports from value imports, potentially improving compilation speed without affecting runtime behavior.


116-116: LGTM: Correctly implemented timeline configuration.

The addition of enableTimeline to the config object correctly implements the new functionality, passing the timeline configuration to the looker instance. This change is consistent with the function signature update and aligns with the PR objectives.

app/packages/core/src/components/Modal/ImaVidLooker.tsx (2)

20-26: LGTM: Import statements reorganized for better modularity

The changes in import statements improve the code organization by separating concerns and potentially reducing circular dependencies. The addition of useKeyEvents suggests a good practice of extracting complex logic into custom hooks.


136-136: LGTM: Improved key event handling with custom hook

The addition of useKeyEvents hook enhances the component's keyboard interaction handling. This change aligns with React best practices by extracting complex logic into a custom hook, improving readability and maintainability.

app/packages/looker/src/state.ts (1)

Line range hint 1-639: Overall, good improvements to state management.

The changes in this file enhance the flexibility and functionality of the state management system, particularly for video handling and key event management. The additions follow TypeScript best practices and naming conventions. Consider implementing the suggested minor improvements for even better clarity and flexibility.

app/packages/core/src/components/Modal/use-key-events.ts (2)

7-12: hoveredSampleId selector is correctly implemented

The selector properly retrieves the _id of the hovered sample using Recoil's selector, which aligns with best practices.


14-18: Function parameters are appropriately typed

The exported default function correctly defines its parameters with appropriate TypeScript types, ensuring type safety and clarity.

app/packages/core/src/components/Modal/ModalLooker.tsx (6)

2-2: Use of import type for ImageLooker

Good use of import type to import ImageLooker as a type-only import, ensuring no unnecessary runtime import.


7-9: Imports added for video support are appropriate

The added imports for VideoLookerReact, useModalContext, and useLooker correctly support the new video playback functionality.


31-31: Confirm the typing in useLooker<ImageLooker>(props)

Using useLooker<ImageLooker>(props) specifies the type as ImageLooker. Ensure that this is appropriate for non-video datasets and doesn't affect video datasets.

Verify that ImageLooker is the correct type here and that video datasets are correctly handled elsewhere.


43-43: Review necessity of type assertion with as fos.Lookers

The type assertion looker as fos.Lookers is used. Confirm whether this assertion is necessary or if the types can be adjusted to avoid it, which can help prevent potential type safety issues.

To verify the type of looker, check the return type of useLooker and see if it can be directly typed as fos.Lookers.


80-89: Ensure correct conditional rendering order in ModalLooker

The component conditionally renders based on shouldRenderImavid and video. Verify that the conditions are correctly ordered and that there are no scenarios where both shouldRenderImavid and video are true but the wrong component renders.

Consider adding unit tests to cover different combinations of shouldRenderImavid and video to ensure the rendering logic functions as intended.


27-27: sample property in LookerProps changed to required

The sample property in LookerProps is now required. Please ensure that all components using this interface provide the sample prop to prevent potential TypeScript errors.

To confirm that all usages of LookerProps have been updated, run the following script:

✅ Verification successful

Verification Successful: All usages of LookerProps provide the required sample prop.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all usages of `LookerProps` to verify that `sample` is provided.

# Search for all instances of `LookerProps` type.
rg --type-add 'tsx' --glob '*.tsx' 'LookerProps' -A 5

Length of output: 111


Script:

#!/bin/bash
# Description: Find all usages of `LookerProps` to verify that `sample` is provided.

# Corrected search for all instances of `LookerProps` type.
rg --type-add 'tsx:*.tsx' 'LookerProps' -A 5

Length of output: 1444

app/packages/core/src/components/Modal/use-looker.ts (1)

13-101: Well-structured implementation of useLooker hook

The useLooker hook is well-implemented, effectively managing the lifecycle and interactions of the looker component. The use of React hooks, Recoil state, and event handlers adheres to best practices in React and TypeScript.

app/packages/looker/src/lookers/video.ts (1)

23-25: Verify the consistency of state management libraries

The code introduces imports from both jotai and Recoil (@fiftyone/playback). Specifically, getDefaultStore is imported from jotai, and setFrameNumberAtom is imported from @fiftyone/playback, which suggests the use of Recoil.

Please verify if mixing jotai and Recoil aligns with our project's state management practices. Using multiple state management libraries can lead to increased complexity and potential inconsistencies.

Run the following script to check the usage of jotai and Recoil in the codebase:

Comment on lines +22 to +39
useEffect(() => {
if (ref.current) {
// initial call should wait for load event
const update = () => {
looker.updateOptions({
shouldHandleKeyEvents: id === hoveredId,
});
ready.current = true;

looker.removeEventListener("load", update);
};
looker.addEventListener("load", update);
} else if (ready.current) {
looker.updateOptions({
shouldHandleKeyEvents: id === hoveredId,
});
}
}, [hoveredId, id, looker, ref]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid including mutable refs in the useEffect dependency array

Including ref (a mutable ref object) in the dependency array of useEffect is unnecessary and can lead to unexpected behavior. Changes to ref.current do not trigger re-renders, and the ref object's reference remains constant. It's recommended to remove ref from the dependency array to prevent potential issues.

Apply this diff to remove ref from the dependency array:

-  }, [hoveredId, id, looker, ref]);
+  }, [hoveredId, id, looker]);
📝 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.

Suggested change
useEffect(() => {
if (ref.current) {
// initial call should wait for load event
const update = () => {
looker.updateOptions({
shouldHandleKeyEvents: id === hoveredId,
});
ready.current = true;
looker.removeEventListener("load", update);
};
looker.addEventListener("load", update);
} else if (ready.current) {
looker.updateOptions({
shouldHandleKeyEvents: id === hoveredId,
});
}
}, [hoveredId, id, looker, ref]);
useEffect(() => {
if (ref.current) {
// initial call should wait for load event
const update = () => {
looker.updateOptions({
shouldHandleKeyEvents: id === hoveredId,
});
ready.current = true;
looker.removeEventListener("load", update);
};
looker.addEventListener("load", update);
} else if (ready.current) {
looker.updateOptions({
shouldHandleKeyEvents: id === hoveredId,
});
}
}, [hoveredId, id, looker]);

⚠️ Potential issue

Add a cleanup function to useEffect to prevent memory leaks

Currently, the useEffect hook adds an event listener to looker, but it only removes the listener inside the update function after the 'load' event fires. If the component unmounts before the 'load' event occurs, the listener will not be removed, potentially causing memory leaks. To ensure proper cleanup, consider adding a cleanup function that removes the event listener when the component unmounts.

Apply this diff to add the cleanup function:

  useEffect(() => {
+   const update = () => {
+     looker.updateOptions({
+       shouldHandleKeyEvents: id === hoveredId,
+     });
+     ready.current = true;
+     looker.removeEventListener("load", update);
+   };
+
    if (ref.current) {
-     // initial call should wait for load event
-     const update = () => {
-       looker.updateOptions({
-         shouldHandleKeyEvents: id === hoveredId,
-       });
-       ready.current = true;
-       looker.removeEventListener("load", update);
-     };
      looker.addEventListener("load", update);
    } else if (ready.current) {
      looker.updateOptions({
        shouldHandleKeyEvents: id === hoveredId,
      });
    }
+   return () => {
+     looker.removeEventListener("load", update);
+   };
- }, [hoveredId, id, looker, ref]);
+ }, [hoveredId, id, looker]);
📝 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.

Suggested change
useEffect(() => {
if (ref.current) {
// initial call should wait for load event
const update = () => {
looker.updateOptions({
shouldHandleKeyEvents: id === hoveredId,
});
ready.current = true;
looker.removeEventListener("load", update);
};
looker.addEventListener("load", update);
} else if (ready.current) {
looker.updateOptions({
shouldHandleKeyEvents: id === hoveredId,
});
}
}, [hoveredId, id, looker, ref]);
useEffect(() => {
const update = () => {
looker.updateOptions({
shouldHandleKeyEvents: id === hoveredId,
});
ready.current = true;
looker.removeEventListener("load", update);
};
if (ref.current) {
looker.addEventListener("load", update);
} else if (ready.current) {
looker.updateOptions({
shouldHandleKeyEvents: id === hoveredId,
});
}
return () => {
looker.removeEventListener("load", update);
};
}, [hoveredId, id, looker]);

app/packages/core/src/components/Modal/use-looker.ts Outdated Show resolved Hide resolved
app/packages/core/src/components/Modal/use-looker.ts Outdated Show resolved Hide resolved
Comment on lines +530 to +535
if (this.state.config.enableTimeline) {
getDefaultStore().set(setFrameNumberAtom, {
name: `timeline-${this.state.config.sampleId}`,
newFrameNumber: this.state.frameNumber,
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure safe usage of Jotai's default store

In the postProcess method, the code sets the frame number using getDefaultStore().set(...). Directly manipulating the default store may bypass the benefits of using state management patterns like hooks or context providers.

Please ensure that this approach aligns with our application's state management conventions and does not introduce unforeseen side effects.

Consider abstracting state updates through a centralized state management layer or utilizing context to maintain consistency and predictability.

Comment on lines +558 to +560
getVideo() {
return this.lookerElement.children[0].element as HTMLVideoElement;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add null checks to prevent potential runtime errors

The getVideo() method assumes that this.lookerElement.children[0] and this.lookerElement.children[0].element exist. To avoid potential runtime errors when these properties are undefined or null, consider adding checks before accessing them.

Apply this diff to add safety checks:

getVideo() {
-  return this.lookerElement.children[0].element as HTMLVideoElement;
+  const child = this.lookerElement.children[0];
+  if (child && child.element) {
+    return child.element as HTMLVideoElement;
+  }
+  return null;
}
📝 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.

Suggested change
getVideo() {
return this.lookerElement.children[0].element as HTMLVideoElement;
}
getVideo() {
const child = this.lookerElement.children[0];
if (child && child.element) {
return child.element as HTMLVideoElement;
}
return null;
}

sashankaryal
sashankaryal previously approved these changes Oct 4, 2024
Copy link
Contributor

@sashankaryal sashankaryal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking good!

@benjaminpkane benjaminpkane requested review from a team as code owners October 4, 2024 21:26
@benjaminpkane benjaminpkane changed the base branch from develop to release/v1.0.1 October 4, 2024 21:27
@benjaminpkane benjaminpkane dismissed sashankaryal’s stale review October 4, 2024 21:27

The base branch was changed.

@benjaminpkane benjaminpkane changed the base branch from release/v1.0.1 to develop October 4, 2024 21:29
Copy link
Contributor

@sashankaryal sashankaryal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (5)
app/packages/core/src/components/Modal/hooks.ts (1)

22-41: Improve type safety and readability in useLookerOptionsUpdate hook

The new hook looks good overall, but there are a few improvements we can make to enhance type safety and readability:

  1. Define a more specific type for the update parameter instead of using object.
  2. Explicitly type the updater function parameter.
  3. Consider creating a custom type for the Looker options.

Here's a suggested refactoring:

type LookerOptions = {
  showJSON: boolean;
  showHelp: boolean;
  // Add other option properties here
};

export const useLookerOptionsUpdate = () => {
  return useRecoilCallback(
    ({ snapshot, set }) =>
      async (update: Partial<LookerOptions>, updater?: (updated: LookerOptions) => void) => {
        const currentOptions = await snapshot.getPromise(
          fos.savedLookerOptions
        ) as LookerOptions;

        const panels = await snapshot.getPromise(fos.lookerPanels);
        const updated: LookerOptions = {
          ...currentOptions,
          ...update,
          showJSON: panels.json.isOpen,
          showHelp: panels.help.isOpen,
        };
        set(fos.savedLookerOptions, updated);
        if (updater) updater(updated);
      }
  );
};

These changes will improve type safety and make the code more self-documenting. The LookerOptions type can be expanded to include all relevant options, providing better type checking throughout the application.

🧰 Tools
🪛 Biome

[error] 25-25: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

app/packages/core/src/components/Modal/ModalLooker.tsx (4)

2-2: LGTM! Consider optimizing imports.

The new imports for ImageLooker and VideoLookerReact align well with the component's enhanced functionality. The addition of useEffect and useMemo to the React import is also appropriate given their usage in the component.

Consider using named imports for React hooks to potentially improve bundle size optimization:

import React from "react";
import { useEffect, useMemo } from "react";

Also applies to: 4-4, 7-7, 8-8, 9-9


Line range hint 11-15: LGTM! Consider enhancing type safety.

The useShowOverlays hook is well-implemented using Recoil's useRecoilCallback. It provides a clean way to update the showOverlays state based on a custom event.

Consider adding a type for the custom event to enhance type safety:

interface ShowOverlaysEvent extends CustomEvent {
  detail: boolean;
}

export const useShowOverlays = () => {
  return useRecoilCallback(({ set }) => async (event: ShowOverlaysEvent) => {
    set(fos.showOverlays, event.detail);
  });
};

30-60: LGTM! Well-refactored component.

The refactoring of ModalLookerNoTimeline is well done. The use of React.memo for memoization and the useLooker hook for managing the looker instance simplifies the component and improves its performance.

Consider extracting the inline styles to a separate constant or using a CSS-in-JS solution for better maintainability:

const containerStyle = {
  width: "100%",
  height: "100%",
  position: "relative",
};

return (
  <div
    ref={ref}
    id={id}
    data-cy="modal-looker-container"
    style={{
      ...containerStyle,
      background: theme.background.level2,
    }}
  />
);

80-80: LGTM! Enhanced support for video datasets.

The addition of video dataset support in the ModalLooker component is a great improvement. The conditional rendering of VideoLookerReact for video datasets enhances the component's flexibility.

Consider using early returns to improve readability:

if (shouldRenderImavid) {
  return <ImaVidLookerReact sample={sample} />;
}

if (video) {
  return <VideoLookerReact sample={sample} />;
}

return <ModalLookerNoTimeline sample={sample} />;

Also applies to: 86-89

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 10bc67d and ecd3c7c.

⛔ Files ignored due to path filters (2)
  • app/packages/looker/package.json is excluded by !**/*.json
  • app/yarn.lock is excluded by !**/yarn.lock, !**/*.lock, !**/*.lock
📒 Files selected for processing (15)
  • app/packages/core/src/components/Modal/ImaVidLooker.tsx (2 hunks)
  • app/packages/core/src/components/Modal/ModalLooker.tsx (3 hunks)
  • app/packages/core/src/components/Modal/VideoLooker.tsx (1 hunks)
  • app/packages/core/src/components/Modal/hooks.ts (1 hunks)
  • app/packages/core/src/components/Modal/use-key-events.ts (1 hunks)
  • app/packages/core/src/components/Modal/use-looker.ts (1 hunks)
  • app/packages/core/src/components/Modal/utils.ts (1 hunks)
  • app/packages/looker/src/elements/base.ts (1 hunks)
  • app/packages/looker/src/elements/common/looker.ts (3 hunks)
  • app/packages/looker/src/index.ts (1 hunks)
  • app/packages/looker/src/lookers/imavid/index.ts (1 hunks)
  • app/packages/looker/src/lookers/video.ts (3 hunks)
  • app/packages/looker/src/state.ts (3 hunks)
  • app/packages/state/src/hooks/useCreateLooker.ts (4 hunks)
  • app/packages/state/src/recoil/modal.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
  • app/packages/core/src/components/Modal/ImaVidLooker.tsx
  • app/packages/core/src/components/Modal/utils.ts
  • app/packages/looker/src/elements/base.ts
  • app/packages/looker/src/elements/common/looker.ts
  • app/packages/looker/src/index.ts
  • app/packages/looker/src/lookers/imavid/index.ts
  • app/packages/looker/src/lookers/video.ts
  • app/packages/looker/src/state.ts
  • app/packages/state/src/hooks/useCreateLooker.ts
  • app/packages/state/src/recoil/modal.ts
🧰 Additional context used
📓 Path-based instructions (5)
app/packages/core/src/components/Modal/ModalLooker.tsx (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Modal/VideoLooker.tsx (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Modal/hooks.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Modal/use-key-events.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/core/src/components/Modal/use-looker.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

🪛 Biome
app/packages/core/src/components/Modal/hooks.ts

[error] 25-25: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

🔇 Additional comments (10)
app/packages/core/src/components/Modal/VideoLooker.tsx (6)

1-11: Import statements look good

The import statements are well-organized and include all necessary dependencies for the component's functionality. They follow best practices for React and TypeScript.


13-15: Interface definition is correct

The VideoLookerReactProps interface is well-defined and provides proper type safety for the component props.


1-80: Overall assessment: Good implementation with room for minor improvements

The VideoLookerReact and TimelineController components are well-structured and implement the required video playback functionality. However, there are a few areas that can be improved:

  1. Remove unnecessary useMemo for frameRate.
  2. Handle potential undefined frameRate.
  3. Ensure handling of already-loaded videos in the useEffect hook.
  4. Simplify the config assignment in useCreateTimeline.

Implementing these suggestions will enhance the reliability and performance of the components.


63-72: 🛠️ Refactor suggestion

Simplify 'config' assignment in 'useCreateTimeline'

As mentioned in a previous review, the conditional check for totalFrames is unnecessary since it's always defined when this component is rendered (as checked in the parent component). You can directly assign the config without the ternary operator.

Apply this diff to simplify the code:

  useCreateTimeline({
    name: timelineName,
-   config: totalFrames
-     ? {
-         totalFrames,
-         loop: true,
-       }
-     : undefined,
+   config: {
+     totalFrames,
+     loop: true,
+   },
    optOutOfAnimation: true,
  });

Likely invalid or redundant comment.


22-23: ⚠️ Potential issue

Handle potential undefined 'frameRate'

The frameRate could potentially be undefined, which may cause issues when calling getFrameNumber. Consider providing a default value or handling the undefined case to prevent potential runtime errors.

You can assign a default frame rate or add a check:

-  const frameRate = sample.frameRate;
+  const frameRate = sample.frameRate ?? DEFAULT_FRAME_RATE;

Or update the usage in getFrameNumber:

  const duration = looker.getVideo().duration;
- setTotalFrames(getFrameNumber(duration, duration, frameRate));
+ if (frameRate) {
+   setTotalFrames(getFrameNumber(duration, duration, frameRate));
+ }

Likely invalid or redundant comment.


25-33: ⚠️ Potential issue

Ensure handling of already-loaded videos in 'useEffect'

The current implementation doesn't handle the case where the video has already loaded before the useEffect runs. This could result in totalFrames remaining undefined if the video loads very quickly.

Modify the effect to handle this scenario:

  useEffect(() => {
    const load = () => {
      const duration = looker.getVideo().duration;
      setTotalFrames(getFrameNumber(duration, duration, frameRate));
      looker.removeEventListener("load", load);
    };
    looker.addEventListener("load", load);
+   // Check if the video is already loaded
+   if (looker.getVideo().readyState >= 3) {
+     load();
+   }
    return () => {
      looker.removeEventListener("load", load);
    };
  }, [frameRate, looker]);

Likely invalid or redundant comment.

app/packages/core/src/components/Modal/ModalLooker.tsx (3)

Line range hint 17-25: LGTM! Well-implemented hook.

The useClearSelectedLabels hook is correctly implemented using Recoil's useRecoilCallback. It provides a clean way to clear the selectedLabels state. The empty dependency array ensures that the callback is only created once, which is an optimization.


Line range hint 1-92: Great job on the refactoring and feature additions!

The changes to this file significantly improve the modal looker system:

  1. Enhanced support for different types of lookers (image, video, ImaVid).
  2. Improved state management using Recoil and custom hooks.
  3. Better component structure with clear separation of concerns.
  4. Consistent use of React best practices (memoization, hooks).

These improvements enhance the flexibility and maintainability of the modal looker system while adding support for video datasets.


27-27: LGTM! Verify impact on existing code.

Making the sample property required in the LookerProps interface is a good change that enforces proper usage of the Looker components.

Please verify that this change doesn't break existing code. Run the following script to check for any usage of LookerProps that might need updating:

✅ Verification successful

Verified! No additional code updates required.

The change to make the sample property required in the LookerProps interface only affects ModalLooker.tsx. There are no other usages of LookerProps in the codebase that need updating.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find usages of LookerProps that might need updating

# Test: Search for LookerProps usage
rg --type typescript "LookerProps" -C 5

Length of output: 74


Script:

#!/bin/bash
# Description: Find usages of LookerProps that might need updating

# Test: Search for LookerProps usage in TypeScript files
rg --type ts --type tsx "LookerProps" -C 5

Length of output: 70


Script:

#!/bin/bash
# Description: Find usages of LookerProps that might need updating

# Test: Search for LookerProps usage in .ts and .tsx files
rg "LookerProps" --glob "*.ts" --glob "*.tsx" -C 5

Length of output: 2109

app/packages/core/src/components/Modal/use-key-events.ts (1)

22-39: Previous review comments are still applicable

The issues raised in previous reviews about including ref in the dependency array of useEffect and adding a cleanup function to prevent memory leaks remain unresolved.

Please address these concerns to ensure proper functionality and prevent potential issues.

Comment on lines +21 to +23
const frameRate = useMemo(() => {
return sample.frameRate;
}, [sample]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove unnecessary useMemo for 'frameRate'

As mentioned in a previous review, the use of useMemo for frameRate is unnecessary. Accessing sample.frameRate is a simple operation that doesn't require memoization.

Apply this diff to simplify the code:

-  const frameRate = useMemo(() => {
-    return sample.frameRate;
-  }, [sample]);
+  const frameRate = sample.frameRate;
📝 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.

Suggested change
const frameRate = useMemo(() => {
return sample.frameRate;
}, [sample]);
const frameRate = sample.frameRate;

Comment on lines +7 to +12
export const hoveredSampleId = selector<string>({
key: "hoveredSampleId",
get: ({ get }) => {
return get(hoveredSample)?._id;
},
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Adjust the selector's return type to account for possible undefined values

The hoveredSampleId selector is currently declared with the type selector<string>, but it may return undefined if hoveredSample is null or undefined. This can lead to type errors when consuming hoveredSampleId.

Consider updating the selector's type to selector<string | undefined> to accurately reflect the possible return values.

Apply this diff to adjust the return type:

-export const hoveredSampleId = selector<string>({
+export const hoveredSampleId = selector<string | undefined>({
📝 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.

Suggested change
export const hoveredSampleId = selector<string>({
key: "hoveredSampleId",
get: ({ get }) => {
return get(hoveredSample)?._id;
},
});
export const hoveredSampleId = selector<string | undefined>({
key: "hoveredSampleId",
get: ({ get }) => {
return get(hoveredSample)?._id;
},
});

}: {
sample: fos.ModalSample;
}) {
const [id] = useState(() => uuid());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Suggestion: Use useRef instead of useState for a stable id

Since the id value is intended to remain constant and does not need to trigger re-renders, using useRef is more appropriate than useState.

Apply this diff to implement the change:

-  const [id] = useState(() => uuid());
+  const id = useRef(uuid()).current;
📝 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.

Suggested change
const [id] = useState(() => uuid());
const id = useRef(uuid()).current;

@benjaminpkane benjaminpkane merged commit 54ce128 into develop Oct 4, 2024
11 checks passed
@benjaminpkane benjaminpkane deleted the video-playback branch October 4, 2024 21:54
benjaminpkane added a commit that referenced this pull request Oct 4, 2024
* organizing

* rm Tmp

* cleanup

* add example back

* cleaning

* useKeyEvents hook

* after load handling

* fix import

* enable timeline

* default to true

* refreshers
sashankaryal pushed a commit that referenced this pull request Oct 4, 2024
* organizing

* rm Tmp

* cleanup

* add example back

* cleaning

* useKeyEvents hook

* after load handling

* fix import

* enable timeline

* default to true

* refreshers
@coderabbitai coderabbitai bot mentioned this pull request Oct 10, 2024
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants