-
Notifications
You must be signed in to change notification settings - Fork 1
Sync back to main #108
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
Sync back to main #108
Conversation
…pport) (#28) * Initial plan for issue * Fix SetClockAction and CompleteTimerAction to properly start and stop timers Co-authored-by: SergeiGolos <660992+SergeiGolos@users.noreply.github.com> * Implement timer state management and UI integration Co-authored-by: SergeiGolos <660992+SergeiGolos@users.noreply.github.com> * Add tests for timer functionality Co-authored-by: SergeiGolos <660992+SergeiGolos@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: SergeiGolos <660992+SergeiGolos@users.noreply.github.com>
* Initial plan for issue * Convert Jest tests to Vitest in ActionOverhaul.test.ts Co-authored-by: SergeiGolos <660992+SergeiGolos@users.noreply.github.com> * Add vitest.config.ts file for test configuration Co-authored-by: SergeiGolos <660992+SergeiGolos@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: SergeiGolos <660992+SergeiGolos@users.noreply.github.com>
* Initial plan for issue * Add fade-in animation for new effort rows Co-authored-by: SergeiGolos <660992+SergeiGolos@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: SergeiGolos <660992+SergeiGolos@users.noreply.github.com>
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.
Hello @SergeiGolos, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
Summary of Changes
Hello team, gemini-code-assist here with a summary of this pull request. Based on the code changes, as the PR title and description are minimal, the primary intent of this pull request is to introduce a comprehensive timer state management system. This system defines distinct states like RUNNING_COUNTDOWN
, RUNNING_COUNTUP
, PAUSED
, and STOPPED
to provide more granular control over how timers behave and are displayed in the UI. The changes involve updating the core runtime logic to dispatch these state changes, modifying UI components and context to consume the state, and enhancing the clock registry hook to track both timer durations and states. Additionally, this PR includes a migration of the testing framework from Jest to Vitest and a minor UI enhancement for event list items.
Highlights
- Timer State Management: Introduced a new
TimerState
enum andSetTimerStateAction
to explicitly manage the state of UI timers (countdown, countup, paused, stopped). - Runtime Block Integration: Updated
EffortBlock
andTimedGroupBlock
lifecycle methods (onEnter
,onLeave
,onBlockStart
,onNext
,onBlockStop
) to dispatchSetTimerStateAction
based on block logic and duration availability. - UI Component & Context Updates: Modified the
WodTimer
component andClockContext
to track and expose the timer state, allowing child components to react to whether the timer is running or in countdown mode. TheuseClockRegistry
hook was updated to manage both durations and states. - Testing Framework Migration: Initiated a migration from Jest to Vitest, updating test files and configuration accordingly.
- Timer Completion Logic: Ensured that
CompleteTimerAction
explicitly stops the timer before popping the block, guaranteeing a final timespan is recorded.
Changelog
Click here to see the changelog
- docs/TimerStateManagement.md
- Added new documentation detailing the timer state system, including the
TimerState
enum,SetTimerStateAction
, and integration points in runtime blocks and UI components.
- Added new documentation detailing the timer state system, including the
- src/components/clock/WodTimer.tsx
- Imported
TimerState
,getClockDuration
, andgetClockState
. - Updated
ClockContext.Provider
to includeisRunning
andisCountdown
derived from the primary timer's state (lines 66-69). - Adjusted the
useEffect
hook to initialize durations using the new registry structure (lines 36-54).
- Imported
- src/components/clock/tests/WodTimer.test.tsx
- Added new test file with tests for the
WodTimer
component, specifically verifying its behavior and context values (isRunning
,isCountdown
) based on differentSET_TIMER_STATE
events (lines 54-164). - Mocked
ClockContext
anduseClockRegistry
to facilitate testing.
- Added new test file with tests for the
- src/components/metrics/EventsView.tsx
- Added the
fade-in
CSS class to table rows for unknown events (line 61) and displayable metrics (lines 95, 134).
- Added the
- src/contexts/ClockContext.tsx
- Updated
ClockContextType
to include separate maps fordurations
andstates
, plusisRunning
andisCountdown
booleans (lines 7-13). - Updated the default context value to match the new structure (lines 17-23).
- Renamed
getClockByName
togetClockDuration
and updated its signature (lines 31-36). - Added a new helper function
getClockStateName
to retrieve timer state by name (lines 39-44).
- Updated
- src/core/runtime/tests/TimerRuntime.spec.ts
- Replaced
jest
imports and calls withvitest
equivalents (describe
,it
,expect
,vi
,beforeEach
,afterEach
,vi.mock
,vi.fn
,vi.Mocked
) (lines 1, 14-17, 19, 24, 39-45, 53-55, 63-65, 68-69, 117-118, 135-136, 156-157, 165-166, 194-195, 220-223, 232-235).
- Replaced
- src/core/runtime/actions/CompleteTimerAction.ts
- Modified the
applyBlock
method to explicitly create and apply aStopTimerAction
before popping the block, ensuring the timer is stopped correctly (lines 20-28).
- Modified the
- src/core/runtime/actions/tests/ActionOverhaul.test.ts
- Replaced
jest
imports and calls withvitest
equivalents (describe
,it
,expect
,vi
,vi.fn
) (lines 1, 12-19, 36, 39, 46, 53, 56).
- Replaced
- src/core/runtime/actions/tests/SetTimerStateAction.test.ts
- Added new test file with tests for the
SetTimerStateAction
, verifying event creation with correct state and target (lines 5-35).
- Added new test file with tests for the
- src/core/runtime/blocks/EffortBlock.ts
- Imported
SetTimerStateAction
,TimerState
, andgetDuration
(lines 15-16). - In
onEnter
, added logic to determine initialTimerState
(countdown or countup) based on duration and dispatched aSetTimerStateAction
(lines 33-43). - Changed the target of
SetClockAction
from "runtime" to "primary" (line 42). - In
onLeave
, added aSetTimerStateAction
to set the timer state toSTOPPED
(line 55).
- Imported
- src/core/runtime/blocks/TimedGroupBlock.ts
- Imported
SetTimerStateAction
andTimerState
(line 19). - In
onBlockStart
, added aSetTimerStateAction
to set the timer state toRUNNING_COUNTDOWN
if a duration exists (lines 112-116). - In
onNext
, added aSetTimerStateAction
to reset the timer state toRUNNING_COUNTDOWN
if a duration exists (lines 193-196). - In
onBlockStop
, added aSetTimerStateAction
to set the timer state toSTOPPED
(line 244).
- Imported
- src/core/runtime/blocks/tests/RuntimeBlock.spec.ts
- Replaced
jest
imports and calls withvitest
equivalents (describe
,it
,expect
,vi
,beforeEach
,vi.fn
) (lines 1, 15, 20, 24, 30-31, 227-228, 260, 273).
- Replaced
- src/core/runtime/outputs/SetClockAction.ts
- Added a null check for the current block in
write
(lines 16-18). - Updated
write
to retrievetimeSpans
from the latestRuntimeSpan
and include them in theTimeSpanDuration
object (lines 22-24, 29). - Changed the target of
SetClockAction
from "runtime" to "primary" (line 30).
- Added a null check for the current block in
- src/core/runtime/outputs/SetTimerStateAction.ts
- Added new file defining the
TimerState
enum and theSetTimerStateAction
class, which emitsSET_TIMER_STATE
output events (lines 1-35).
- Added new file defining the
- src/hooks/useClockRegistry.ts
- Defined
ClockRegistryState
interface to hold both durations and states (lines 10-13). - Updated state to
clockRegistryState
holding both maps (lines 24-27). - Modified
useEffect
to processSET_TIMER_STATE
events and update the states map (lines 56-64). - Updated the interval logic to create new maps for both durations and states to force re-render (lines 80-83).
- Changed the return value to an object containing both
durations
andstates
maps (lines 90-93). - Added
getClockState
helper function (lines 107-116). - Updated
getClockDuration
signature to use the new registry structure (lines 103-104).
- Defined
- src/index.css
- Added a
@keyframes fadeIn
animation and a.fade-in
CSS class (lines 19-27).
- Added a
- vitest.config.ts
- Added new file configuring Vitest for the project, including plugins, test environment, and path aliases (lines 1-21).
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command>
or @gemini-code-assist <command>
. Below is a summary of the supported commands.
Feature | Command | Description |
---|---|---|
Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/
folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
A timer ticks, state shifts,
From countup high to countdown gifts.
Running, paused, or stopped,
UI logic's hopped,
On states the runtime lifts.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
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.
Code Review
This pull request introduces timer state management to the wod.wiki project, enhancing the control and display of UI timers. The changes include a new TimerState
enum, a SetTimerStateAction
class, and integration with the WodTimer
component. Additionally, the pull request includes updates to the EffortBlock
and TimedGroupBlock
to manage timer states during their lifecycles. Overall, the changes seem well-structured and address the intended functionality. However, there are a few areas that could benefit from further review and refinement.
Summary of Findings
- Potential Null/Undefined Errors: Several instances exist where object properties are accessed without prior checks for null or undefined values. This could lead to runtime errors if the objects are not properly initialized or if data is missing.
- Timer State Determination Logic: The logic for determining the timer state (countdown vs. countup) is repeated in multiple blocks. Extracting this logic into a separate function would improve code reuse and maintainability.
- Mocking Library Consistency: The test files use a mix of
jest.fn
andvi.fn
for creating mocks. Using a consistent mocking library (preferablyvi.fn
as it seems to be the more prevalent choice) would improve code clarity and maintainability.
Merge Readiness
The pull request introduces valuable timer state management features. However, addressing the potential null/undefined errors and improving code consistency would enhance the overall quality and robustness of the code. I recommend addressing these points before merging. I am unable to approve this pull request, and recommend that others review and approve this code before merging.
useEffect(() => { | ||
if (!clockRegistry || !primary || !total) return; | ||
if (!clockRegistry) return; |
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.
Consider adding a check for primary
and total
before accessing clockRegistry.durations
to avoid potential errors if clockRegistry
is not yet initialized or if these values are undefined. This could prevent unexpected behavior or crashes.
if (!clockRegistry || !primary || !total) return;
const newDurations = new Map(clockRegistry.durations);
// First, stop the timer properly before completing | ||
const stopEvent = new StopEvent(new Date()); | ||
const stopAction = new StopTimerAction(stopEvent); | ||
|
||
// Apply the stop timer action to close the current timespan | ||
stopAction.apply(runtime, block); |
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.
It's good that you're stopping the timer before popping the block. However, consider whether the StopEvent
should be created with the current time or the time when the timer is actually completed. Using the completion time might provide more accurate data for analysis.
// First, stop the timer properly before completing | |
const stopEvent = new StopEvent(new Date()); | |
const stopAction = new StopTimerAction(stopEvent); | |
// Apply the stop timer action to close the current timespan | |
stopAction.apply(runtime, block); | |
// First, stop the timer properly before completing | |
const stopEvent = new StopEvent(new Date()); // Consider using completion time here | |
const stopAction = new StopTimerAction(stopEvent); | |
// Apply the stop timer action to close the current timespan | |
stopAction.apply(runtime, block); |
const duration = this.selectMany(getDuration)[0]; | ||
const timerState = duration?.original | ||
? TimerState.RUNNING_COUNTDOWN | ||
: TimerState.RUNNING_COUNTUP; |
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.
Consider extracting the timer state determination logic into a separate, well-named function to improve readability and maintainability. This would encapsulate the conditional logic and make the onEnter
method easier to understand at a glance.
// Determine if this effort block has a specific duration
const duration = this.selectMany(getDuration)[0];
const timerState = duration?.original
? TimerState.RUNNING_COUNTDOWN
: TimerState.RUNNING_COUNTUP;
// Extract this logic into a separate function, e.g., determineTimerState(duration)
// Add the SetTimerStateAction to set the timer to RUNNING_COUNTDOWN | ||
const duration = this.selectMany(getDuration)[0]; | ||
if (duration?.original) { | ||
actions.push(new SetTimerStateAction(TimerState.RUNNING_COUNTDOWN, "primary")); | ||
} |
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.
Similar to the EffortBlock
, consider extracting the timer state determination logic into a separate function to improve readability and maintainability. This would encapsulate the conditional logic and make the onBlockStart
method easier to understand at a glance.
// Add the SetTimerStateAction to set the timer to RUNNING_COUNTDOWN | |
const duration = this.selectMany(getDuration)[0]; | |
if (duration?.original) { | |
actions.push(new SetTimerStateAction(TimerState.RUNNING_COUNTDOWN, "primary")); | |
} | |
// Add the SetTimerStateAction to set the timer to RUNNING_COUNTDOWN | |
const duration = this.selectMany(getDuration)[0]; | |
if (duration?.original) { | |
actions.push(new SetTimerStateAction(TimerState.RUNNING_COUNTDOWN, "primary")); | |
} | |
// Extract this logic into a separate function, e.g., determineTimerState(duration) |
// Get the current RuntimeSpan's timeSpans to ensure the timer shows as running | ||
const latestRuntimeSpan = block.spans().length > 0 ? block.spans()[block.spans().length - 1] : undefined; | ||
const timeSpans = latestRuntimeSpan?.timeSpans ?? []; |
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.
Consider adding a check to ensure latestRuntimeSpan
is defined before accessing its timeSpans
property. This would prevent potential errors if there are no spans available.
// Get the current RuntimeSpan's timeSpans to ensure the timer shows as running
const latestRuntimeSpan = block.spans().length > 0 ? block.spans()[block.spans().length - 1] : undefined;
const timeSpans = latestRuntimeSpan?.timeSpans ?? [];
// Add check for latestRuntimeSpan before accessing timeSpans
const timeSpans = latestRuntimeSpan ? latestRuntimeSpan.timeSpans ?? [] : [];
event.bag?.duration) { | ||
|
||
// Update the registry with the new duration | ||
newRegistry.set(event.bag.target, event.bag.duration); | ||
newDurations.set(event.bag.target, event.bag.duration); | ||
hasChanges = 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.
Consider adding a check to ensure that event.bag
is defined before accessing its properties to prevent potential errors if the event is malformed or incomplete.
event.bag?.duration) { | |
// Update the registry with the new duration | |
newRegistry.set(event.bag.target, e 9E4A vent.bag.duration); | |
newDurations.set(event.bag.target, event.bag.duration); | |
hasChanges = true; | |
event.bag?.duration) { | |
// Update the registry with the new duration | |
newDurations.set(event.bag.target, event.bag.duration); | |
// Add check for event.bag before accessing its properties | |
if (event.bag && event.bag.target && event.bag.duration) { | |
newDurations.set(event.bag.target, event.bag.duration); |
No description provided.