Conversation
Agent-Logs-Url: https://github.com/Open-Source-Bazaar/Open-Source-Bazaar.github.io/sessions/c3d283c3-cef4-454d-bce8-b3bdd8c61cf8 Co-authored-by: TechQuery <19969570+TechQuery@users.noreply.github.com>
…Hero and [id].tsx Agent-Logs-Url: https://github.com/Open-Source-Bazaar/Open-Source-Bazaar.github.io/sessions/7cd3ee7e-073a-494f-b0a1-a5eca022d3e2 Co-authored-by: TechQuery <19969570+TechQuery@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Open-Source-Bazaar/Open-Source-Bazaar.github.io/sessions/d946deb1-3c20-4f85-afa5-f4569b09dd6a Co-authored-by: TechQuery <19969570+TechQuery@users.noreply.github.com>
…elper Agent-Logs-Url: https://github.com/Open-Source-Bazaar/Open-Source-Bazaar.github.io/sessions/4c1cea16-bd51-429b-9ddf-9fea961418e1 Co-authored-by: TechQuery <19969570+TechQuery@users.noreply.github.com>
…, move cell styles to CSS Agent-Logs-Url: https://github.com/Open-Source-Bazaar/Open-Source-Bazaar.github.io/sessions/28a4703b-d5c6-4dc4-8076-10f2873c9ddf Co-authored-by: TechQuery <19969570+TechQuery@users.noreply.github.com>
Co-authored-by: Copilot <copilot@github.com>
There was a problem hiding this comment.
Pull request overview
Refactors the Hackathon countdown by extracting a reusable MobX-based Countdown component and moving agenda-driven countdown state resolution into a dedicated AgendaCountdown wrapper.
Changes:
- Added
components/Base/Countdown.tsxMobX class component that renders an<ol>countdown for arbitrary time units. - Replaced
Hero’s hook-based countdown rendering withAgendaCountdown, and removeduseLiveCountdownState. - Moved countdown-specific styling out of
Hero.module.lessintoAgendaCountdown.module.less.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| pages/hackathon/[id].tsx | Updates Hackathon page to pass agenda + time window + TimeUnit[] into the hero countdown flow. |
| components/Base/Countdown.tsx | Introduces the extracted MobX countdown component and time-unit section calculation. |
| components/Activity/Hackathon/useLiveCountdownState.ts | Removes the now-replaced countdown hook implementation. |
| components/Activity/Hackathon/constant.ts | Removes buildCountdownUnitLabels helper no longer used by the hero countdown. |
| components/Activity/Hackathon/Hero.tsx | Switches hero countdown rendering from local hooks to AgendaCountdown. |
| components/Activity/Hackathon/Hero.module.less | Removes countdown styles from hero and keeps only button styling updates. |
| components/Activity/Hackathon/AgendaCountdown.tsx | Adds agenda-aware countdown wrapper calling resolveCountdownState and re-arming on countdown end. |
| components/Activity/Hackathon/AgendaCountdown.module.less | Adds relocated countdown layout + cell styling and responsive behavior. |
| render() { | ||
| const { className = '', ...props } = this.props; | ||
| const { timeSections } = this; | ||
|
|
||
| return ( | ||
| <ol className={`list-unstyled m-0 ${className}`} {...props}> | ||
| {timeSections.map(({ value, label }) => ( |
There was a problem hiding this comment.
render() spreads ...props from this.props onto the <ol>, but props still contains non-DOM fields like units, endTime, and onEnd. React will forward these as invalid attributes (and units will stringify as "[object Object]"). Destructure and omit the component-only props before spreading onto the DOM element.
| private timer = 0; | ||
|
|
||
| get endTimestamp() { | ||
| return +new Date(this.props.endTime || Date.now()); |
There was a problem hiding this comment.
endTimestamp uses this.props.endTime || Date.now(). If endTime is a valid numeric timestamp of 0 (or another falsy-but-valid value), this will incorrectly fall back to Date.now(). Prefer nullish coalescing (??) or avoid the fallback since endTime is required by the props type.
| return +new Date(this.props.endTime || Date.now()); | |
| return +new Date(this.props.endTime); |
| .map(({ scale }) => scale) | ||
| .reduce((sum, scale) => sum * scale, 1); | ||
|
|
||
| const value = ~~(rest / scale); |
There was a problem hiding this comment.
~~(rest / scale) relies on 32-bit bitwise conversion and can overflow/wrap for large values. Use Math.floor(rest / scale) (or an equivalent safe integer conversion) to avoid incorrect countdown sections for longer durations or custom unit configurations.
| const value = ~~(rest / scale); | |
| const value = Math.floor(rest / scale); |
| {timeSections.map(({ value, label }) => ( | ||
| <li key={label} className="d-flex flex-column justify-content-center align-items-center"> | ||
| <strong>{(value + '').padStart(2, '0')}</strong> | ||
| <span>{label}</span> |
There was a problem hiding this comment.
timeSections.map uses key={label}. label is user-provided (and may not be unique across units or locales), which can cause duplicate keys and unstable rendering. Use a guaranteed-unique key (e.g., include the index or use a stable id field in TimeUnit).
useLiveCountdownStateanduseCountdown(inHero.tsx) used chains ofuseState/useEffect/useMemo/useCallbackfor timer management. These are replaced following the pattern from BootCell's CountDown component: an independent MobX decorator class component for the visual countdown, and a plain function call for agenda-state resolution.New:
Countdown.tsx— independent MobX class componentThe countdown logic is extracted into a self-contained
@observerclass component extendingObservedComponentfrommobx-react-helper, decoupled fromHero. A dedicatedinitTimer()method carries the@reactiondecorator watchingobservedProps.endTime, andcomponentDidMountstarts it explicitly:Props:
endTime?: string | Date | number— the target timestamp (replacescountdownTo)units: TimeUnit[]— arbitrary time units each with{ scale: number, label: string }, porting BootCell's reduction algorithmclassName?: string— passed to the<ol>so callers (e.g. Hero) control grid layout via their own CSS moduleonEnd?: () => void— fired when the countdown reaches zero, allowing callers to decide whether to start a new phaseThe component renders only a pure
<ol>— no label or surrounding layout — keeping it fully customisable by callers.render()usesthis.propsdirectly; no internal CSS module classes are applied.Hero.tsx—HackathonHeroremains a simple functional componentThe
useCountdownhook is removed. The Hero owns the countdown wrapper, optional label, and grid styling, passing them to<Countdown>:Hero.module.less— cell styles moved to.countdownGrid liThe separate
.countdownCellclass is removed. Its styles (border, background, min-height, typography) are now nested under.countdownGrid li, keeping all countdown-related styling in one place and removing the dependency on an internal class name fromCountdown.useLiveCountdownState.ts→ removedReplaced by a direct call to the already-existing
resolveCountdownState()pure function inpages/hackathon/[id].tsx— no reactive state or lifecycle re-renders needed: