Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 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
7 changes: 7 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules
example
website
lib
scripts
babel.config.js
*.test.tsx
234 changes: 234 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
{
"root": true,
"plugins": [
"babel",
"jest-dom",
"jest",
"markdown",
"prettier",
"react-hooks",
"sort-destructure-keys"
],
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:prettier/recommended",
"plugin:jest/all",
"plugin:jest-dom/recommended"
],
"rules": {
"array-callback-return": 2,
"arrow-body-style": 2,
"comma-dangle": 0,
"babel/no-invalid-this": 2,
"default-case": 2,
"eqeqeq": [2, "smart"],
"jest/expect-expect": 0,
"jest/no-conditional-expect": 0,
"jsx-quotes": ["error", "prefer-single"],
"linebreak-style": [2, "unix"],
"no-console": 0,
"no-mixed-spaces-and-tabs": 1,
"no-self-compare": 2,
"no-underscore-dangle": [2, { "allowAfterThis": true }],
"no-unused-vars": [1, { "ignoreRestSiblings": true }],
"no-use-before-define": 0, // can throw incorrect errors due to mismatch of @typescript-eslint versions in react-scripts and local package.json
"@typescript-eslint/no-use-before-define": 0,
"no-useless-concat": 2,
"no-var": 2,
"object-shorthand": 1,
"prefer-const": 1,
"react/jsx-sort-props": [
"error",
{
"callbacksLast": false,
"ignoreCase": true,
"noSortAlphabetically": false,
"reservedFirst": false,
"shorthandFirst": false,
"shorthandLast": false
}
],
"react/prop-types": 0,
"require-await": 2,
"semi": [1, "always"],
"sort-destructure-keys/sort-destructure-keys": [
2,
{ "caseSensitive": false }
],
"sort-imports": [
"error",
{
"allowSeparatedGroups": true,
"ignoreCase": true,
"ignoreDeclarationSort": true,
"ignoreMemberSort": false,
"memberSyntaxSortOrder": ["none", "all", "multiple", "single"]
}
],
"sort-keys": [
"error",
"asc",
{ "caseSensitive": false, "minKeys": 2, "natural": false }
],
"valid-typeof": 2,
"import/prefer-default-export": 0,
"import/extensions": [0],
"max-classes-per-file": 0,
"camelcase": 0,
"react-hooks/rules-of-hooks": 1,
"react-hooks/exhaustive-deps": 0,
"jest/prefer-inline-snapshots": 0,
"jest/lowercase-name": 0,
"jest/prefer-expect-assertions": 0,
"jest/no-hooks": 0,
"no-unused-expressions": "off",
"babel/no-unused-expressions": "error",
"jest/no-if": "off"
},
"env": {
"es6": true,
"browser": true
},
"parser": "babel-eslint",
"parserOptions": {
// "allowImportExportEverywhere": true,
"sourceType": "module",
"ecmaVersion": 2018,
"ecmaFeatures": {
"modules": true
}
},
"settings": {
"react": {
"pragma": "React",
"version": "detect"
},
"import/resolver": {
"alias": {
"map": [["mock-builders", "./src/mock-builders"]],
"extensions": [".js", ".jsx", ".ts", ".tsx"]
},
"eslint-import-resolver-babel-module": {},
"node": {
"extensions": [".js", ".jsx", ".ts", ".tsx"],
"paths": ["src"]
}
}
},
"overrides": [
{
"files": ["*.md"],
"rules": {
"react/jsx-no-undef": 0,
"react/react-in-jsx-scope": 0,
"semi": 0,
"no-undef": 0
}
},
{
"env": {
"es6": true,
"browser": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:jest/recommended",
"plugin:prettier/recommended",
"plugin:react/recommended",
"prettier/@typescript-eslint"
],
"files": ["**/*.ts", "**/*.tsx"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"modules": true,
"jsx": true
},
"ecmaVersion": 2018,
"sourceType": "module"
},
"plugins": [
"@typescript-eslint",
"babel",
"markdown",
"prettier",
"react",
"typescript-sort-keys",
"sort-destructure-keys"
],
"rules": {
"@typescript-eslint/explicit-module-boundary-types": 0,
"@typescript-eslint/no-empty-interface": 0,
"@typescript-eslint/ban-ts-comment": 0,
"@typescript-eslint/no-unused-vars": 1,
"@typescript-eslint/no-var-requires": 0,
"react-hooks/exhaustive-deps": 0,
"react-native/no-inline-styles": 0,
"array-callback-return": 2,
"arrow-body-style": 2,
"comma-dangle": 0,
"babel/no-invalid-this": 2,
"default-case": 2,
"eqeqeq": [2, "smart"],
"linebreak-style": [2, "unix"],
"jsx-quotes": ["error", "prefer-single"],
"no-console": 0,
"no-mixed-spaces-and-tabs": 1,
"no-self-compare": 2,
"no-shadow": 0,
"no-underscore-dangle": [2, { "allowAfterThis": true }],
"no-unused-vars": [1, { "ignoreRestSiblings": true }],
"no-useless-concat": 2,
"no-var": 2,
"object-shorthand": 1,
"prefer-const": 1,
"react/jsx-sort-props": [
"error",
{
"callbacksLast": false,
"ignoreCase": true,
"noSortAlphabetically": false,
"reservedFirst": false,
"shorthandFirst": false,
"shorthandLast": false
}
],
"react/prop-types": 0,
"require-await": 2,
"semi": [1, "always"],
"sort-destructure-keys/sort-destructure-keys": [
2,
{ "caseSensitive": false }
],
"sort-imports": [
"error",
{
"allowSeparatedGroups": true,
"ignoreCase": true,
"ignoreDeclarationSort": true,
"ignoreMemberSort": false,
"memberSyntaxSortOrder": ["none", "all", "multiple", "single"]
}
],
"sort-keys": [
"error",
"asc",
{ "caseSensitive": false, "minKeys": 2, "natural": false }
],
"typescript-sort-keys/interface": [
"error",
"asc",
{ "caseSensitive": false, "natural": true, "requiredFirst": true }
],
"typescript-sort-keys/string-enum": [
"error",
"asc",
{ "caseSensitive": false, "natural": true }
],
"valid-typeof": 2
}
}
]
}
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
website/
example/
7 changes: 7 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"arrowParens": "always",
"jsxSingleQuote": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all"
}
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@

[FlatList](https://reactnative.dev/docs/flatlist) by react-native only allows infinite scroll in one direction (using `onEndReached`). This package adds capability on top of FlatList to allow infinite scroll from both directions, and also maintains **smooth scroll** UX.

- Accepts prop `onStartReached` & `onEndReached`, which you can use to load more results.
- Accepts prop `onStartReached` & `onEndReached`, which you can use to load more results.
- Calls to onEndReached and onStartReached have been optimized.
- Inline loading Indicators, which can be customized as well.
- Uses [flat-list-mvcp](https://github.com/GetStream/flat-list-mvcp#maintainvisiblecontentposition-prop-support-for-android-react-native) to maintain scroll position or smooth scroll UX.


<table>
<tr>
<td align='center' width="33%"><img src='https://user-images.githubusercontent.com/11586388/109138261-77774800-775a-11eb-806b-2add75755af7.gif' height="600" /></td>
Expand Down
2 changes: 1 addition & 1 deletion example/app.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"name": "BidirectionalInfiniteScrollExample",
"displayName": "BidirectionalInfiniteScroll Example"
"displayName": "BidirectionalInfiniteScrollExample"
}
9 changes: 7 additions & 2 deletions example/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { AppRegistry } from 'react-native';
import { AppRegistry, Platform } from 'react-native';
import App from './src/App';
import { name as appName } from './app.json';

console.log('>>>> ', appName)
AppRegistry.registerComponent(appName, () => App);

if (Platform.OS === 'web') {
const rootTag = document.getElementById('root');
AppRegistry.runApplication(appName, { rootTag });
}
10 changes: 9 additions & 1 deletion example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,21 @@
},
"dependencies": {
"@stream-io/flat-list-mvcp": "0.10.0",
"expo": "^40.0.1",
"react": "16.13.1",
"react-dom": "^17.0.2",
"react-native": "0.63.4",
"react-native-bidirectional-infinite-scroll": "link:../"
"react-native-bidirectional-infinite-scroll": "link:../",
"react-native-web": "^0.15.6",
"react-virtuoso": "^1.6.0"
},
"devDependencies": {
"@babel/core": "^7.12.10",
"@babel/preset-typescript": "^7.13.0",
"@babel/runtime": "^7.12.5",
"@types/react": "^17.0.3",
"@types/react-dom": "^17.0.3",
"@types/react-native": "^0.64.2",
"babel-plugin-module-resolver": "^4.0.0",
"metro-react-native-babel-preset": "^0.64.0"
}
Expand Down
33 changes: 23 additions & 10 deletions example/src/MessageBubble.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,51 @@ type Props = {
item: Message;
};

export const MessageBubble: React.FC<Props> = ({ item }) => {
export const MessageBubble: React.FC<Props> = ({ item, index }) => {
if (item.isMyMessage) {
return (
<View
key={`${item.id}`}
style={[styles.messageBubble, styles.myMessageBubble]}
>
<Text style={styles.myMessageText}>{item.text}</Text>
<View style={styles.container} key={`item-${item.id}`}>
<View
style={[styles.messageBubble, styles.myMessageBubble]}
>
<Text style={styles.myMessageText}>{item.text}</Text>
<Text style={[styles.myMessageText, {
marginTop: 10,
fontWeight: 'bold'
}]}>ID: {item.id} Index: {index}</Text>
</View>
</View>
);
}

return (
<View key={`${item.id}`} style={styles.messageBubble}>
<Text style={styles.messageText}>{item.text}</Text>
<View style={styles.container} key={`item-${item.id}`}>
<View style={styles.messageBubble}>
<Text style={styles.messageText}>{item.text}</Text>
<Text style={[styles.messageText, {
marginTop: 10,
fontWeight: 'bold'
}]}>ID: {item.id} Index: {index}</Text>
</View>
</View>
);
};

const styles = StyleSheet.create({
container: {
width: '100%'
},
messageBubble: {
maxWidth: 300,
padding: 10,
borderRadius: 10,
marginVertical: 5,
marginHorizontal: 5,
backgroundColor: '#F1F0F0',
height: 100
},
myMessageBubble: {
alignSelf: 'flex-end',
// borderColor: '#989898',
// borderWidth: 1,
backgroundColor: '#3784FF',
},
messageText: {
Expand Down
Loading