-
Notifications
You must be signed in to change notification settings - Fork 591
[WIP] feat(ONYX-2187): fix railViewed/itemViewed tracking gaps on live home rails #13819
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
Open
dariakoko
wants to merge
8
commits into
main
Choose a base branch
from
ONYX-2187/address-impression-tracking-issues
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
7c3a8c8
chore: adjust reail_viewed impressions tracking for wtyl
dariakoko 21d5a1c
feat: track rail_viewed for live sections after pull to refresh
dariakoko ac61585
feat: nwfy impressions tracking on refresh and pull to refresh
dariakoko c3ad85c
fix: avoid unnecessary re-renders when clearing tracked section guard
dariakoko ff2de45
chore: write tests
dariakoko fc8801d
chore: refactor, address edje cases
dariakoko 129227a
fix: pause NWFY/WTYL visibility polling while Home isnt focused
dariakoko aef72b6
fix: apply the threshold symmetrically to the top of grid item on nwfy
dariakoko File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
100 changes: 100 additions & 0 deletions
100
src/app/Components/ArtworkGrids/useGridItemVisibility.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| import { useIsFocused } from "@react-navigation/native" | ||
| import { BOTTOM_TABS_HEIGHT } from "app/Navigation/AuthenticatedRoutes/Tabs" | ||
| import { RefObject, useCallback, useEffect, useRef } from "react" | ||
| import { Dimensions, View } from "react-native" | ||
| import { useSafeAreaInsets } from "react-native-safe-area-context" | ||
|
|
||
| const POLL_INTERVAL = 1000 | ||
|
|
||
| interface UseGridItemVisibilityArgs { | ||
| ref: RefObject<View | null> | ||
| /** The value indicates the minimum percentage of the container that must be visible (vertically or horizontally). A value of 1 means 100%, 0.7 means 70%, and so forth. The default value is 1 (100%). */ | ||
| threshold?: number | ||
| enabled: boolean | ||
| onVisibilityChange: (visible: boolean) => void | ||
| } | ||
|
|
||
| /** | ||
| * Purpose-built alternative to the shared `Sentinel` for grid items that need repeatable | ||
| * visibility detection. `Sentinel`'s own polling effect has an empty dependency array, so its | ||
| * internal comparison value freezes at mount and it can only ever report one "true" transition for | ||
| * its entire lifetime — harmless for its other, fire-once consumers elsewhere in the app, but not | ||
| * for a rail that live-refreshes and needs to re-track items that are already on screen. | ||
| * | ||
| * Reports current visibility on every poll tick, unconditionally, rather than only on a change — | ||
| * so it doesn't need to be told when a live refresh happens at all. The caller's own "already | ||
| * tracked" guard (reset on refresh) is what decides whether a given report actually turns into an | ||
| * event; a redundant "still visible" report while that guard is set is just a harmless no-op. | ||
| * | ||
| * Implemented as a hook (rather than a component wrapping the item in an extra View, as an earlier | ||
| * version did) specifically so it doesn't change the rendered tree shape: the caller attaches | ||
| * `ref` directly to its existing content container. Wrapping the content in an additional View | ||
| * confused FlashList's masonry column-height measurement, pushing later items far off-screen. | ||
| */ | ||
| export const useGridItemVisibility = ({ | ||
| ref, | ||
| threshold = 1, | ||
| enabled, | ||
| onVisibilityChange, | ||
| }: UseGridItemVisibilityArgs) => { | ||
| const insets = useSafeAreaInsets() | ||
| const isFocused = useIsFocused() | ||
|
|
||
| // Read via a ref so the poll below always calls the latest version, regardless of when its | ||
| // closure was created. | ||
| const onVisibilityChangeRef = useRef(onVisibilityChange) | ||
| onVisibilityChangeRef.current = onVisibilityChange | ||
|
|
||
| const measure = useCallback( | ||
| (report: (visible: boolean) => void) => { | ||
| ref.current?.measure( | ||
| (_x: number, _y: number, width: number, height: number, pageX: number, pageY: number) => { | ||
| const window = Dimensions.get("window") | ||
| // The visible content area ends above the bottom tab bar, so an item sitting behind it | ||
| // shouldn't count toward the visible fraction — using the raw window height here let | ||
| // items near the bottom tab bar read as "50%+ visible" when most of that overlap was | ||
| // actually hidden behind the tab bar. | ||
| const viewportHeight = window.height - BOTTOM_TABS_HEIGHT - insets.bottom | ||
|
|
||
| if (height <= 0 || width <= 0) { | ||
| report(false) | ||
| return | ||
| } | ||
|
|
||
| // Overlap between the item's bounds and the viewport on each axis, as a fraction of the | ||
| // item's own size — symmetric at both edges, unlike a pair of one-sided inequalities | ||
| // (e.g. a hard `pageY >= 0` for the top combined with a thresholded check at the bottom | ||
| // only), which would under-report items that are mostly visible but clipped at the top. | ||
| const visibleHeight = Math.max( | ||
| 0, | ||
| Math.min(pageY + height, viewportHeight) - Math.max(pageY, 0) | ||
| ) | ||
| const visibleWidth = Math.max( | ||
| 0, | ||
| Math.min(pageX + width, window.width) - Math.max(pageX, 0) | ||
| ) | ||
|
|
||
| const isVisible = visibleHeight >= height * threshold && visibleWidth >= width * threshold | ||
|
|
||
| report(isVisible) | ||
| } | ||
| ) | ||
| }, | ||
| [threshold, ref, insets.bottom] | ||
| ) | ||
|
|
||
| // Paused while Home isn't focused (e.g. a pushed artwork screen on top of it) — react-native- | ||
| // screens freezes the Home tree in that state without unmounting it, so this interval would | ||
| // otherwise keep firing a native measure() every second for as long as the user is elsewhere. | ||
| useEffect(() => { | ||
| if (!enabled || !isFocused) { | ||
| return | ||
| } | ||
|
|
||
| const interval = setInterval(() => { | ||
| measure((visible) => onVisibilityChangeRef.current(visible)) | ||
| }, POLL_INTERVAL) | ||
|
|
||
| return () => clearInterval(interval) | ||
| }, [enabled, isFocused, measure]) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.