Skip to content

Discord Modifications #3

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

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Draft
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]

- Make `onViewableItemsChanged` better handle floating-point error.

## [1.6.3] - 2023-11-09

- Changes for RN 0.73 support
Expand All @@ -28,6 +30,8 @@ and adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
- https://github.com/Shopify/flash-list/pull/890
- Add option to clear cached layouts on update
- https://github.com/Shopify/flash-list/pull/910
- Add type definition for `ViewToken["item"]`
- https://github.com/Shopify/flash-list/pull/817

## [1.5.0] - 2023-07-12

Expand Down
26 changes: 24 additions & 2 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,35 @@ buildscript {
}
}

def supportsNamespace() {
def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
def major = parsed[0].toInteger()
def minor = parsed[1].toInteger()

// Namespace support was added in 7.3.0
if (major == 7 && minor >= 3) {
return true
}

return major >= 8
}

android {
if (supportsNamespace()) {
namespace = "com.shopify.reactnative.flash_list"
sourceSets {
main {
manifest.srcFile "src/main/AndroidManifestNew.xml"
}
}
}

compileSdkVersion _compileSdkVersion
buildToolsVersion _buildToolsVersion

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}

sourceSets {
Expand Down
2 changes: 2 additions & 0 deletions android/src/main/AndroidManifestNew.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
10 changes: 5 additions & 5 deletions documentation/docs/fundamentals/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,17 +350,17 @@ This event is raised once the list has drawn items on the screen. It also report
### `onViewableItemsChanged`

```tsx
interface ViewToken {
interface ViewToken<TItem> {
index: number;
isViewable: boolean;
item: string;
item: TItem;
key: string;
timestamp: number;
}

onViewableItemsChanged?: ((info: {
viewableItems: ViewToken[];
changed: ViewToken[];
viewableItems: ViewToken<TItem>[];
changed: ViewToken<TItem>[];
}) => void) | null | undefined
```

Expand Down Expand Up @@ -518,7 +518,7 @@ type ViewabilityConfigCallbackPairs = ViewabilityConfigCallbackPair[];
interface ViewabilityConfigCallbackPair {
viewabilityConfig: ViewabilityConfig;
onViewableItemsChanged:
| ((info: { viewableItems: ViewToken[]; changed: ViewToken[] }) => void)
| ((info: { viewableItems: ViewToken<TItem>[]; changed: ViewToken<TItem>[] }) => void)
| null;
}

Expand Down
1 change: 0 additions & 1 deletion jestSetup.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,5 @@ jest.mock("@shopify/flash-list", () => {
return {
...jest.requireActual("@shopify/flash-list"),
FlashList: MockFlashList,
AnimatedFlashList: MockFlashList,
};
});
17 changes: 8 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
"author": "shopify",
"license": "MIT",
"homepage": "https://shopify.github.io/flash-list/",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"main": "src/index.ts",
"types": "src/index.d.ts",
"scripts": {
"up": "bundle install && yarn fixture-up && yarn e2e-up && yarn build",
"fixture-web-up": "cd fixture/web-app && yarn && cd ../../",
Expand Down Expand Up @@ -61,20 +61,19 @@
"@babel/core": "^7.18.5",
"@babel/runtime": "^7.18.3",
"@quilted/react-testing": "^0.5.14",
"@react-native-community/eslint-config": "^3.0.3",
"@shopify/eslint-plugin": "^41.3.1",
"@react-native-community/eslint-config": "^3.2.0",
"@shopify/eslint-plugin": "^44.0.0",
"@types/jest": "^28.1.3",
"@types/react-native": "0.72.2",
"babel-jest": "^28.1.1",
"enhanced-resolve": "^5.9.3",
"eslint": "8.18.0",
"eslint": "8.57.0",
"gh-pages": "^4.0.0",
"jest": "^28.1.1",
"metro-react-native-babel-preset": "^0.71.1",
"prettier": "^2.7.1",
"react": "17.0.2",
"react-native": "0.68.5",
"typescript": "^4.7.4"
"react": "18.2.0",
"react-native": "0.72.3",
"typescript": "^5.0.3"
},
"files": [
"android",
Expand Down
11 changes: 0 additions & 11 deletions src/AnimatedFlashList.ts

This file was deleted.

65 changes: 53 additions & 12 deletions src/FlashList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,28 @@ import {
updateContentStyle,
} from "./utils/ContentContainerUtils";

function shallowEqual(a: any, b: any) {
if (a === b) {
return true;
}
if (a == null || b == null) {
return a == b;
}

const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) {
return false;
}
for (let i = 0; i < keysA.length; i++) {
const key = keysA[i];
if (a[key] !== b[key]) {
return false;
}
}
return true;
}

interface StickyProps extends StickyContainerProps {
children: any;
}
Expand Down Expand Up @@ -217,7 +239,7 @@ class FlashList<T> extends React.PureComponent<
data: null,
layoutProvider: null!!,
dataProvider: new DataProvider((r1, r2) => {
return r1 !== r2;
return !shallowEqual(r1, r2);
}, getStableId),
numColumns: 0,
};
Expand Down Expand Up @@ -280,6 +302,7 @@ class FlashList<T> extends React.PureComponent<
/>
);
}
return null;
};

componentDidMount() {
Expand Down Expand Up @@ -316,9 +339,14 @@ class FlashList<T> extends React.PureComponent<

// RecyclerListView simply ignores if initialScrollIndex is set to 0 because it doesn't understand headers
// Using initialOffset to force RLV to scroll to the right place
const initialOffset =
(this.isInitialScrollIndexInFirstRow() && this.distanceFromWindow) ||
undefined;
let initialOffset: number | undefined = this.props.initialScrollOffset ?? 0;
if (this.isInitialScrollIndexInFirstRow() && this.distanceFromWindow) {
initialOffset += this.distanceFromWindow;
}
if (initialOffset === 0) {
initialOffset = undefined;
}

const finalDrawDistance =
drawDistance === undefined
? PlatformConfig.defaultDrawDistance
Expand Down Expand Up @@ -402,10 +430,10 @@ class FlashList<T> extends React.PureComponent<
this.props.onScrollBeginDrag?.(event);
};

private onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
private onScroll: RecyclerListViewProps["onScroll"] = (event) => {
this.recordInteraction();
this.viewabilityManager.updateViewableItems();
this.props.onScroll?.(event);
this.props.onScroll?.(event as NativeSyntheticEvent<NativeScrollEvent>);
};

private getUpdatedWindowCorrectionConfig() {
Expand Down Expand Up @@ -452,7 +480,13 @@ class FlashList<T> extends React.PureComponent<
}
};

private container = (props: object, children: React.ReactNode[]) => {
private container: RecyclerListViewProps["renderContentContainer"] = (
props,
child
) => {
const children =
// eslint-disable-next-line no-negated-condition,no-nested-ternary
child != null ? (child instanceof Array ? child : [child]) : [];
this.clearPostLoadTimeout();
return (
<>
Expand Down Expand Up @@ -619,11 +653,13 @@ class FlashList<T> extends React.PureComponent<
private getValidComponent(
component: React.ComponentType | React.ReactElement | null | undefined
) {
if (component == null) return null;
const PassedComponent = component;
return (
(React.isValidElement(PassedComponent) && PassedComponent) ||
(PassedComponent && <PassedComponent />) ||
null
return React.isValidElement(PassedComponent) ? (
PassedComponent
) : (
// @ts-expect-error not sure how to type this properly
<PassedComponent />
);
}

Expand All @@ -641,8 +677,9 @@ class FlashList<T> extends React.PureComponent<
};

private rowRendererWithIndex = (index: number, target: RenderTarget) => {
if (this.props.renderItem == null) return null;
// known issue: expected to pass separators which isn't available in RLV
return this.props.renderItem?.({
return this.props.renderItem({
item: this.props.data![index],
index,
target,
Expand Down Expand Up @@ -769,6 +806,10 @@ class FlashList<T> extends React.PureComponent<
}
}

public updateViewableItems() {
this.viewabilityManager.updateViewableItems();
}

public scrollToEnd(params?: { animated?: boolean | null | undefined }) {
this.rlvRef?.scrollToEnd(Boolean(params?.animated));
}
Expand Down
21 changes: 17 additions & 4 deletions src/FlashListProps.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import type React from "react";
import {
import type {
StyleProp,
ScrollViewProps,
ViewabilityConfig,
ViewabilityConfigCallbackPairs,
ViewStyle,
} from "react-native";

import { BlankAreaEventHandler } from "./native/auto-layout/AutoLayoutView";
import ViewToken from "./viewability/ViewToken";
import type { BlankAreaEventHandler } from "./native/auto-layout/AutoLayoutView";
import type ViewToken from "./viewability/ViewToken";

export interface ListRenderItemInfo<TItem> {
item: TItem;
Expand Down Expand Up @@ -247,7 +247,10 @@ export interface FlashListProps<TItem> extends ScrollViewProps {
* they might be deferred until JS thread is less busy.
*/
onViewableItemsChanged?:
| ((info: { viewableItems: ViewToken[]; changed: ViewToken[] }) => void)
| ((info: {
viewableItems: ViewToken<TItem>[];
changed: ViewToken<TItem>[];
}) => void)
| null
| undefined;

Expand Down Expand Up @@ -332,4 +335,14 @@ export interface FlashListProps<TItem> extends ScrollViewProps {
* `false` again.
*/
disableAutoLayout?: boolean;

initialScrollOffset?: number;

/**
* If the FlashList is in a bottom sheet, some rendered items can be off screen.
* The value in this ref represents the height of the off-screen area, so onViewableItemsChanged
* can consider the visible area of the bottom sheet in its calculations.
*/
bottomViewabilityInsetRef?: React.MutableRefObject<number>;
topViewabilityInsetRef?: React.MutableRefObject<number>;
}
2 changes: 1 addition & 1 deletion src/GridLayoutProviderWithProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
LayoutManager,
} from "recyclerlistview";

import { FlashListProps } from "./FlashListProps";
import type { FlashListProps } from "./FlashListProps";
import { AverageWindow } from "./utils/AverageWindow";
import { applyContentContainerInsetForLayoutManager } from "./utils/ContentContainerUtils";

Expand Down
17 changes: 14 additions & 3 deletions src/MasonryFlashList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import {
import CustomError from "./errors/CustomError";
import ExceptionList from "./errors/ExceptionList";
import FlashList from "./FlashList";
import { FlashListProps, ListRenderItemInfo } from "./FlashListProps";
import type { FlashListProps, ListRenderItemInfo } from "./FlashListProps";
import { applyContentContainerInsetForLayoutManager } from "./utils/ContentContainerUtils";
import ViewToken from "./viewability/ViewToken";
import type ViewToken from "./viewability/ViewToken";

export interface MasonryListRenderItemInfo<TItem>
extends ListRenderItemInfo<TItem> {
Expand All @@ -34,6 +34,7 @@ export interface MasonryFlashListProps<T>
| "onBlankArea"
| "renderItem"
| "viewabilityConfigCallbackPairs"
| "onViewableItemsChanged"
> {
/**
* Allows you to change the column widths of the list. This is helpful if you want some columns to be wider than the others.
Expand All @@ -59,6 +60,14 @@ export interface MasonryFlashListProps<T>
* longer be linearly distributed across the columns; instead they are allocated to the column with the least estimated height.
*/
renderItem: MasonryListRenderItem<T> | null | undefined;

onViewableItemsChanged?:
| ((info: {
viewableItems: ViewToken<MasonryListItem<T>>[];
changed: ViewToken<MasonryListItem<T>>[];
}) => void)
| null
| undefined;
}

type OnScrollCallback = ScrollViewProps["onScroll"];
Expand Down Expand Up @@ -434,7 +443,9 @@ const getFlashListScrollView = (
FlashListScrollView.displayName = "FlashListScrollView";
return FlashListScrollView;
};
const updateViewTokens = (tokens: ViewToken[]) => {
const updateViewTokens = <T extends MasonryListItem<any>>(
tokens: ViewToken<T | undefined>[]
) => {
const length = tokens.length;
for (let i = 0; i < length; i++) {
const token = tokens[i];
Expand Down
2 changes: 1 addition & 1 deletion src/PureComponentWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";

export interface PureComponentWrapperProps {
renderer: (arg: unknown) => JSX.Element | null;
renderer: (arg: any) => JSX.Element | null;

/** Renderer is called with this argument.
* Don't change this value everytime or else component will always rerender. Prefer primitives. */
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/GridLayoutProviderWithProps.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ScrollView } from "react-native";
import { ProgressiveListView } from "recyclerlistview";

import FlashList from "../FlashList";
import type FlashList from "../FlashList";

import { mountFlashList } from "./helpers/mountFlashList";

Expand Down
Loading