Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions components/elements/BackToTop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,25 @@ export default function BackToTop({ target }: Readonly<{ target: string }>) {
const [hasScrolled, setHasScrolled] = useState(false);

useEffect(() => {
const state = { rafId: 0, isTicking: false };

const onScroll = () => {
setHasScrolled(window.scrollY > 100);
if (!state.isTicking) {
state.rafId = window.requestAnimationFrame(() => {
setHasScrolled(window.scrollY > 100);
state.isTicking = false;
});
state.isTicking = true;
}
};

window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
window.addEventListener("scroll", onScroll, { passive: true });
return () => {
window.removeEventListener("scroll", onScroll);
if (state.rafId) {
window.cancelAnimationFrame(state.rafId);
}
Comment on lines 10 to +25
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Initialize hasScrolled once after subscribing.

If the page mounts with scrollY > 100 already set, this stays hidden until the next scroll event. Run the handler once during setup so the initial visibility matches the current position.

Suggested fix
     window.addEventListener("scroll", onScroll, { passive: true });
+    onScroll();
     return () => {
       window.removeEventListener("scroll", onScroll);
       if (state.rafId) {
         window.cancelAnimationFrame(state.rafId);
       }
📝 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 onScroll = () => {
setHasScrolled(window.scrollY > 100);
if (!state.isTicking) {
state.rafId = window.requestAnimationFrame(() => {
setHasScrolled(window.scrollY > 100);
state.isTicking = false;
});
state.isTicking = true;
}
};
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
window.addEventListener("scroll", onScroll, { passive: true });
return () => {
window.removeEventListener("scroll", onScroll);
if (state.rafId) {
window.cancelAnimationFrame(state.rafId);
}
const onScroll = () => {
if (!state.isTicking) {
state.rafId = window.requestAnimationFrame(() => {
setHasScrolled(window.scrollY > 100);
state.isTicking = false;
});
state.isTicking = true;
}
};
window.addEventListener("scroll", onScroll, { passive: true });
onScroll();
return () => {
window.removeEventListener("scroll", onScroll);
if (state.rafId) {
window.cancelAnimationFrame(state.rafId);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/elements/BackToTop.tsx` around lines 10 - 25, The component
currently only updates visibility when a scroll event occurs; after attaching
the listener call the existing onScroll handler (or directly run
setHasScrolled(window.scrollY > 100)) once to initialize state so hasScrolled
reflects the current position on mount; update the setup around
window.addEventListener("scroll", onScroll, { passive: true }) to invoke
onScroll() immediately after registering the listener and keep the existing
cleanup that removes the listener and cancels state.rafId, referencing the
onScroll function, state, and setHasScrolled.

};
}, []);

const handleClick = () => {
Expand Down
20 changes: 16 additions & 4 deletions components/layout/DynamicHeaderWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,27 @@ export default function DynamicHeaderWrapper({ navigation }: Readonly<DynamicHea
const [scroll, setScroll] = useState<boolean>(false);

React.useEffect(() => {
const state = { rafId: 0, isTicking: false };

const handleScroll = (): void => {
const scrollCheck: boolean = window.scrollY > 100;
if (scrollCheck !== scroll) {
setScroll(scrollCheck);
if (!state.isTicking) {
state.rafId = window.requestAnimationFrame(() => {
const scrollCheck: boolean = window.scrollY > 100;
if (scrollCheck !== scroll) {
setScroll(scrollCheck);
}
state.isTicking = false;
});
state.isTicking = true;
}
};
document.addEventListener("scroll", handleScroll);

document.addEventListener("scroll", handleScroll, { passive: true });
return () => {
document.removeEventListener("scroll", handleScroll);
if (state.rafId) {
window.cancelAnimationFrame(state.rafId);
}
};
}, [scroll]);
Comment on lines 18 to 41
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The useEffect hook has scroll in its dependency array. This causes the effect to re-run every time the scroll state changes, removing and re-adding the scroll event listener. This is inefficient and undermines the performance optimization.

The dependency on scroll can be removed by simplifying the scroll handler. React's state setter already prevents re-renders if the new value is the same as the current one, so the check if (scrollCheck !== scroll) is not necessary when the dependency is removed.

  React.useEffect(() => {
    const state = { rafId: 0, isTicking: false };

    const handleScroll = (): void => {
      if (!state.isTicking) {
        state.rafId = window.requestAnimationFrame(() => {
          setScroll(window.scrollY > 100);
          state.isTicking = false;
        });
        state.isTicking = true;
      }
    };

    document.addEventListener("scroll", handleScroll, { passive: true });
    return () => {
      document.removeEventListener("scroll", handleScroll);
      if (state.rafId) {
        window.cancelAnimationFrame(state.rafId);
      }
    };
  }, []);


Expand Down
20 changes: 16 additions & 4 deletions components/layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,29 @@ export default function Layout({ headerStyle, footerStyle, breadcrumbTitle: _bre

useEffect(() => {
AOS.init();

const state = { rafId: 0, isTicking: false };

const handleScroll = (): void => {
const scrollCheck: boolean = window.scrollY > 100;
if (scrollCheck !== scroll) {
setScroll(scrollCheck);
if (!state.isTicking) {
state.rafId = window.requestAnimationFrame(() => {
const scrollCheck: boolean = window.scrollY > 100;
if (scrollCheck !== scroll) {
setScroll(scrollCheck);
}
state.isTicking = false;
});
state.isTicking = true;
}
};

document.addEventListener("scroll", handleScroll);
document.addEventListener("scroll", handleScroll, { passive: true });

return () => {
document.removeEventListener("scroll", handleScroll);
if (state.rafId) {
window.cancelAnimationFrame(state.rafId);
}
};
}, [scroll]);
Comment on lines 80 to 106
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The useEffect hook has scroll in its dependency array. This causes the effect to re-run every time the scroll state changes, removing and re-adding the scroll event listener. This is inefficient and undermines the performance optimization.

The dependency on scroll can be removed by simplifying the scroll handler, similar to the implementation in BackToTop.tsx. React's state setter already prevents re-renders if the new value is the same as the current one.

  useEffect(() => {
    AOS.init();

    const state = { rafId: 0, isTicking: false };

    const handleScroll = (): void => {
      if (!state.isTicking) {
        state.rafId = window.requestAnimationFrame(() => {
          setScroll(window.scrollY > 100);
          state.isTicking = false;
        });
        state.isTicking = true;
      }
    };

    document.addEventListener("scroll", handleScroll, { passive: true });

    return () => {
      document.removeEventListener("scroll", handleScroll);
      if (state.rafId) {
        window.cancelAnimationFrame(state.rafId);
      }
    };
  }, []);


Expand Down
1 change: 1 addition & 0 deletions components/layout/SpeakersFilterBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export default function SpeakersFilterBar({ placeholder = "Search by name, tagli
/>
<input
type="text"
name="q"
placeholder={placeholder}
value={query}
onChange={(e) => setQuery(e.target.value)}
Expand Down
1 change: 1 addition & 0 deletions components/layout/TalksFilterBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export default function TalksFilterBar({ tracks, year: _year }: TalksFilterBarPr
/>
<input
type="text"
name="q"
placeholder="Search talks..."
value={searchQuery}
onChange={handleSearchChange}
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/home/home-editions.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe("Home Pages (2023-2026)", () => {
cy.visit(edition.path, { timeout: 120000 });

cy.get(".hero8-header", { timeout: 30000 }).within(() => {
cy.get("h5").should("have.length.at.least", 2);
cy.get(".hero8-header__event-line").should("have.length.at.least", 2);
cy.contains(edition.venue, { matchCase: false }).should("be.visible");
cy.contains(edition.date, { matchCase: false }).should("be.visible");
});
Expand Down
10 changes: 4 additions & 6 deletions cypress/e2e/talks-and-speakers-layout.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@ describe("Talks and Speakers Layout", () => {
it("Talks page has filters at top and 2-column grid", () => {
cy.visit("/2024/talks");

cy.get(".row.mb-5").should("exist");
cy.get(".row.mb-5").contains("Filter by Track");
cy.get(".row.mb-5 .blog-details-section").should("have.length", 2);
cy.get(".talks-filter-bar").should("exist");
cy.get(".talks-filter-bar").contains("All Tracks");

cy.get(".talks-grouped .track-section .row .col-lg-6").should("have.length.at.least", 2);
cy.get(".talks-grouped").should("exist");
});

it("Speakers page has search at top and 4-column grid", () => {
cy.visit("/2024/speakers");

cy.get(".row.mb-5").should("exist");
cy.get(".row.mb-5").contains("Filter Speakers");
cy.get(".talks-filter-bar").should("exist");

cy.get(".team-sperkers-section-area .row .col-lg-3").should("have.length.at.least", 4);
});
Expand Down
Loading
Loading