├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ └── build.yml ├── .gitignore ├── .ignore ├── .prettierignore ├── .prettierrc ├── .watchmanconfig ├── CHANGELOG.md ├── Makefile ├── README.md ├── SECURITY.md ├── babel.i18next-extract.json ├── bin ├── postpack.sh ├── prepack.sh ├── release.sh └── validate-translations.js ├── docs ├── 0103555406e67ed07e22ac1178eceb1f.png ├── 03562343670f52e40e265604a3d2c1c1.png ├── 13bba9ab6f1d80de5e5252b0fd97cd10.png ├── 18d7cbe55260f9dd4964aa11e935def0.png ├── 34a97b444a701b830f7e789250785643.png ├── 37c3db8e1f4b9a9266fc9af91b04d626.png ├── 631dd199329d34f7b3a092e293c4d9c3.png ├── 6b6c4d8056606e37cc778eaef87fd912.png ├── 7f5d1e6897e29d888aac8dba80a08a1b.png ├── 950a595906dbb8f8982b72c000ce3eee.png ├── Streami18n.md ├── b265294f1379c2f676ec42b16e10d77a.png ├── build │ ├── bundle.24c6c987.js │ ├── bundle.24c6c987.js.LICENSE.txt │ └── images │ │ ├── 0103555406e67ed07e22ac1178eceb1f.png │ │ ├── 03562343670f52e40e265604a3d2c1c1.png │ │ ├── 13bba9ab6f1d80de5e5252b0fd97cd10.png │ │ ├── 18d7cbe55260f9dd4964aa11e935def0.png │ │ ├── 34a97b444a701b830f7e789250785643.png │ │ ├── 37c3db8e1f4b9a9266fc9af91b04d626.png │ │ ├── 631dd199329d34f7b3a092e293c4d9c3.png │ │ ├── 6b6c4d8056606e37cc778eaef87fd912.png │ │ ├── 7f5d1e6897e29d888aac8dba80a08a1b.png │ │ ├── 950a595906dbb8f8982b72c000ce3eee.png │ │ ├── b265294f1379c2f676ec42b16e10d77a.png │ │ ├── c841eabac3b84071d9315f6bcf7028f5.png │ │ ├── dce58fb332b90c4f559d30fcb21b063a.png │ │ ├── e846cb2cac91e8d8c41231f49d4c85a4.png │ │ ├── fc3744c5bf90ccdc7b81d9d99dca4acd.png │ │ └── feb4549aecc16b9160e64f12dfa79a31.png ├── c841eabac3b84071d9315f6bcf7028f5.png ├── cookbook.md ├── dce58fb332b90c4f559d30fcb21b063a.png ├── e846cb2cac91e8d8c41231f49d4c85a4.png ├── ef6dd31ac5d7ede0dd323bfc2df59db3.png ├── fc3744c5bf90ccdc7b81d9d99dca4acd.png ├── feb4549aecc16b9160e64f12dfa79a31.png ├── index.html ├── other-components.md ├── setup.md ├── styles.md └── top-level-components.md ├── dotgit ├── hooks-wrapper ├── hooks │ ├── pre-commit-format.sh │ └── pre-commit-reject-binaries.py └── setup-hooks.sh ├── examples ├── expo │ ├── .env.example │ ├── .expo-shared │ │ └── assets.json │ ├── .gitignore │ ├── App.js │ ├── README.md │ ├── app.json │ ├── assets │ │ ├── adaptive-icon.png │ │ ├── favicon.png │ │ ├── icon.png │ │ └── splash.png │ ├── babel.config.js │ ├── metro.config.js │ ├── package.json │ ├── scripts │ │ ├── initData.js │ │ └── test_notification.js │ └── yarn.lock └── native │ ├── .buckconfig │ ├── .env.example │ ├── .flowconfig │ ├── .gitattributes │ ├── .gitignore │ ├── .prettierrc.js │ ├── .watchmanconfig │ ├── App.js │ ├── README.md │ ├── __tests__ │ └── App-test.js │ ├── android │ ├── app │ │ ├── BUCK │ │ ├── build.gradle │ │ ├── build_defs.bzl │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── native │ │ │ │ └── ReactNativeFlipper.java │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── native │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── res │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle │ ├── app.json │ ├── babel.config.js │ ├── index.js │ ├── ios │ ├── Podfile │ ├── Podfile.lock │ ├── native-tvOS │ │ └── Info.plist │ ├── native-tvOSTests │ │ └── Info.plist │ ├── native.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── native-tvOS.xcscheme │ │ │ └── native.xcscheme │ ├── native.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── native │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── LaunchScreen.storyboard │ │ └── main.m │ └── nativeTests │ │ ├── Info.plist │ │ └── nativeTests.m │ ├── metro.config.js │ ├── package.json │ ├── scripts │ ├── initData.js │ └── test_notification.js │ └── yarn.lock ├── expo-package ├── .eslintignore ├── .gitignore ├── .prettierignore ├── package.json ├── src │ └── index.js └── yarn.lock ├── native-package ├── .eslintignore ├── .gitignore ├── .prettierignore ├── package.json ├── src │ └── index.js └── yarn.lock ├── package.json ├── src ├── Context │ ├── Feed.js │ ├── StreamApp.js │ └── index.js ├── Streami18n.js ├── components │ ├── Activity.js │ ├── Avatar.js │ ├── BackButton.js │ ├── Card.js │ ├── CommentBox.js │ ├── CommentItem.js │ ├── CommentList.js │ ├── CommentsContainer.js │ ├── FlatFeed.js │ ├── FollowButton.js │ ├── IconBadge.js │ ├── LikeButton.js │ ├── LikeList.js │ ├── LoadMoreButton.js │ ├── NewActivitiesNotification.js │ ├── NotificationFeed.js │ ├── ReactionIcon.js │ ├── ReactionIconBar.js │ ├── ReactionList.js │ ├── ReactionToggleIcon.js │ ├── SectionHeader.js │ ├── SinglePost.js │ ├── StatusUpdateForm.js │ ├── UploadImage.js │ ├── UrlPreview.js │ ├── UserBar.js │ ├── UserCard.js │ └── examples │ │ ├── Activity.md │ │ ├── Avatar.md │ │ ├── BackButton.md │ │ ├── Card.md │ │ ├── CommentItem.md │ │ ├── CommentList.md │ │ ├── FollowButton.md │ │ ├── IconBadge.md │ │ ├── LikeButton.md │ │ ├── LikeList.md │ │ ├── NewActivitiesNotification.md │ │ ├── SectionHeader.md │ │ ├── SinglePost.md │ │ ├── UrlPreview.md │ │ ├── UserBar.md │ │ └── resources │ │ ├── heart-outline.png │ │ ├── heart.png │ │ ├── notifications.png │ │ ├── reply.png │ │ └── repost.png ├── errors.js ├── i18n │ ├── en.json │ ├── fr.json │ ├── hi.json │ ├── index.js │ ├── it.json │ ├── nl.json │ ├── ru.json │ └── tr.json ├── images │ ├── githubhero.png │ ├── icons │ │ ├── backarrow-blue.png │ │ ├── backarrow-blue@2x.png │ │ ├── backarrow-blue@3x.png │ │ ├── backarrow.png │ │ ├── backarrow@2x.png │ │ ├── backarrow@3x.png │ │ ├── close-black.png │ │ ├── close-black@2x.png │ │ ├── close-black@3x.png │ │ ├── close-white.png │ │ ├── close-white@2x.png │ │ ├── close-white@3x.png │ │ ├── gallery.png │ │ ├── gallery@2x.png │ │ ├── gallery@3x.png │ │ ├── heart-outline.png │ │ ├── heart-outline@2x.png │ │ ├── heart-outline@3x.png │ │ ├── heart.png │ │ ├── heart@2x.png │ │ ├── heart@3x.png │ │ ├── pickphoto.png │ │ ├── pickphoto@2x.png │ │ ├── pickphoto@3x.png │ │ ├── send-disabled.png │ │ ├── send-disabled@2x.png │ │ ├── send-disabled@3x.png │ │ ├── send.png │ │ ├── send@2x.png │ │ └── send@3x.png │ ├── placeholder.png │ └── stream_logo.png ├── index.js ├── native.js ├── styleguideComponents │ └── PathlineRenderer.js ├── styles.js └── utils.js ├── styleguide.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", "@babel/preset-react"], 3 | "plugins": [ 4 | "@babel/plugin-proposal-class-properties", 5 | "@babel/plugin-transform-runtime", 6 | "@babel/plugin-proposal-object-rest-spread" 7 | ], 8 | "env": { 9 | "production": { 10 | "presets": [ 11 | [ 12 | "@babel/env", 13 | { 14 | "modules": false 15 | } 16 | ] 17 | ] 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | charset = utf-8 11 | 12 | # Indentation override for all JS under lib directory 13 | [*.js] 14 | indent_style = space 15 | indent_size = 2 16 | 17 | # Matches the exact files either package.json or .travis.yml 18 | [{package.json,.travis.yml,.babelrc}] 19 | indent_style = space 20 | indent_size = 2 21 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*{.,-}min.js 2 | node_modules/ 3 | /native-package/lib/ 4 | /expo-package/lib/ 5 | build 6 | dist 7 | /docs/ 8 | /.git/ 9 | .expo/ 10 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:react/recommended", 9 | "plugin:jest/recommended" 10 | ], 11 | "parser": "babel-eslint", 12 | "parserOptions": { 13 | "ecmaFeatures": { 14 | "modules": true 15 | }, 16 | "ecmaVersion": 2018, 17 | "sourceType": "module" 18 | }, 19 | "plugins": ["babel", "markdown"], 20 | "rules": { 21 | "array-callback-return": 2, 22 | "arrow-body-style": 2, 23 | "comma-dangle": 0, 24 | "babel/no-invalid-this": 2, 25 | "default-case": 2, 26 | "eqeqeq": [2, "smart"], 27 | "jest/expect-expect": 0, 28 | "jest/no-conditional-expect": 0, 29 | "jsx-quotes": ["error", "prefer-single"], 30 | "linebreak-style": [2, "unix"], 31 | "no-console": 0, 32 | "no-mixed-spaces-and-tabs": 1, 33 | "no-self-compare": 2, 34 | "no-underscore-dangle": [2, { "allowAfterThis": true }], 35 | "no-unused-vars": [1, { "ignoreRestSiblings": true }], 36 | "no-useless-concat": 2, 37 | "no-var": 2, 38 | "object-shorthand": 1, 39 | "prefer-const": 1, 40 | "react/prop-types": 0, 41 | "require-await": 2, 42 | "semi": [1, "always"], 43 | "valid-typeof": 2 44 | }, 45 | "settings": { 46 | "import/resolver": { 47 | "babel-module": {}, 48 | "node": { 49 | "extensions": [".js", ".jsx", ".ts", ".tsx"], 50 | "paths": ["src"] 51 | } 52 | }, 53 | "react": { 54 | "pragma": "React", 55 | "version": "detect" 56 | } 57 | }, 58 | "overrides": [ 59 | { 60 | "files": ["*.md"], 61 | "rules": { 62 | "no-undef": 0, 63 | "react/jsx-no-undef": 0, 64 | "react/react-in-jsx-scope": 0, 65 | "semi": 0 66 | } 67 | }, 68 | { 69 | "env": { 70 | "es6": true, 71 | "browser": true 72 | }, 73 | "files": ["**/*.ts", "**/*.tsx"], 74 | "parserOptions": { 75 | "ecmaFeatures": { 76 | "modules": true, 77 | "jsx": true 78 | }, 79 | "ecmaVersion": 2018, 80 | "sourceType": "module" 81 | }, 82 | "plugins": ["babel", "markdown", "prettier", "react"], 83 | "rules": { 84 | "react-hooks/exhaustive-deps": 0, 85 | "react-native/no-inline-styles": 0, 86 | "array-callback-return": 2, 87 | "arrow-body-style": 2, 88 | "comma-dangle": 0, 89 | "babel/no-invalid-this": 2, 90 | "default-case": 2, 91 | "eqeqeq": [2, "smart"], 92 | "linebreak-style": [2, "unix"], 93 | "jsx-quotes": ["error", "prefer-single"], 94 | "no-console": 0, 95 | "no-mixed-spaces-and-tabs": 1, 96 | "no-self-compare": 2, 97 | "no-shadow": 0, 98 | "no-underscore-dangle": [2, { "allowAfterThis": true }], 99 | "no-unused-vars": [1, { "ignoreRestSiblings": true }], 100 | "no-useless-concat": 2, 101 | "no-var": 2, 102 | "object-shorthand": 1, 103 | "prefer-const": 1, 104 | "react/prop-types": 0, 105 | "require-await": 2, 106 | "semi": [1, "always"], 107 | "valid-typeof": 2 108 | } 109 | } 110 | ] 111 | } 112 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report (choose this one if not sure) 3 | about: Report a problem with the library 4 | --- 5 | 6 | **Setup (always fill this in):** 7 | 8 | - iOS or Android? 9 | - Expo or regular React Native? 10 | - React Native Version: 11 | - `react-native-activity-feed` or `expo-activity-feed` version: `npm list --pattern `/`yarn list --pattern ` 12 | - `getstream` version: `npm list --pattern getstream`/`yarn list --pattern getstream` 13 | 14 | **Describe the bug** 15 | A clear and concise description of what the bug is. 16 | 17 | **To Reproduce** 18 | Steps to reproduce the behavior: 19 | 20 | 1. Go to '...' 21 | 2. Click on '....' 22 | 3. Scroll down to '....' 23 | 4. See error 24 | 25 | **Expected behavior** 26 | A clear and concise description of what you expected to happen. 27 | 28 | **Screenshots** 29 | If applicable, add screenshots to help explain your problem. 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: 6 | - vishal/fixing-example-apps 7 | - master 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | node: [10, 12, 14] 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: actions/setup-node@v2-beta 17 | with: 18 | node-version: ${{ matrix.node }} 19 | 20 | - name: Get yarn cache directory path 21 | id: yarn-cache-path 22 | run: echo "::set-output name=dir::$(yarn cache dir)" 23 | 24 | - uses: actions/cache@master 25 | id: yarn-cache 26 | with: 27 | path: ${{ steps.yarn-cache-path.outputs.dir }} 28 | key: yarn-${{ hashFiles('**/yarn.lock') }} 29 | restore-keys: yarn- 30 | 31 | - name: Install dependencies 32 | run: yarn install 33 | 34 | - name: Lint and Test with ${{ matrix.node }} 35 | env: 36 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 37 | run: | 38 | yarn lint 39 | yarn docs 40 | yarn validate-translations 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IMPORTANT: If you add/remove a rule here, be sure to do the same in .npmignore 2 | # See https://help.github.com/ignore-files/ for more about ignoring files. 3 | 4 | **/.DS_Store 5 | 6 | # expo 7 | .expo/ 8 | 9 | # dependencies 10 | /node_modules 11 | 12 | # misc 13 | .env 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | secrets*.*sh 19 | /change-permissions.js 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | #ide 26 | .idea 27 | **/.tern-port 28 | package-lock.json 29 | 30 | 31 | 32 | # Ignore lib directory (this is not in npmignore because it should be published) 33 | /lib/ 34 | -------------------------------------------------------------------------------- /.ignore: -------------------------------------------------------------------------------- 1 | !secrets*.*sh 2 | /docs/build/ 3 | !.env 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .eslintignore -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "jsxSingleQuote": true, 4 | "singleQuote": true, 5 | "tabWidth": 2, 6 | "trailingComma": "all" 7 | } 8 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: netlify 2 | .PHONY: example-build example-deps 3 | 4 | EXAMPLES_PATH = examples 5 | EXAMPLES = expo native 6 | EXAMPLES_APPS = $(addprefix $(EXAMPLES_PATH)/,$(EXAMPLES)) 7 | EXAMPLES_APPS_DEPS = $(addsuffix /node_modules/installed_dependencies,$(EXAMPLES_APPS)) 8 | 9 | WRAPPER_PACKAGES = native-package expo-package 10 | WRAPPER_PACKAGES_DEPS = $(addsuffix /node_modules/installed_dependencies,$(WRAPPER_PACKAGES)) 11 | 12 | SOURCES = $(filter(%$(EXAMPLES_PATH)/, $(wildcard *.js) $(wildcard */*.js) $(wildcard */*.scss) $(wildcard */*.png) $(wildcard */*.html) $(wildcard ../client/*/*.js) $(wildcard ../client/*.js)) 13 | LIB_SOURCES = $(wildcard *.js) $(wildcard */*.js) $(wildcard */*.scss) $(wildcard */*.png) $(wildcard */*.html) $(wildcard ../client/*/*.js) $(wildcard ../client/*.js) 14 | 15 | example-deps: $(EXAMPLES_APPS_DEPS) 16 | 17 | $(EXAMPLES_APPS_DEPS): %/node_modules/installed_dependencies: %/yarn.lock %/package.json $(SOURCES) $(WRAPPER_PACKAGES_DEPS) 18 | cd $* && yarn install && npx pod-install 19 | touch $@ 20 | 21 | $(WRAPPER_PACKAGES_DEPS): %/node_modules/installed_dependencies: %/yarn.lock %/package.json $(SOURCES) node_modules/installed_dependencies 22 | cd $* && yarn install 23 | touch $@ 24 | 25 | node_modules/installed_dependencies: yarn.lock package.json 26 | yarn install 27 | touch $@ 28 | 29 | dist/built: $(LIB_SOURCES) node_modules/installed_dependencies 30 | yarn build 31 | touch $@ q 32 | 33 | clean: 34 | rm -rf $(addsuffix /node_modules,$(EXAMPLES_APPS)) || true 35 | rm -rf $(addsuffix /node_modules,$(WRAPPER_PACKAGES)) || true 36 | rm -rf dist || true 37 | rm -rf node_modules || true 38 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting a Vulnerability 2 | At Stream we are committed to the security of our Software. We appreciate your efforts in disclosing vulnerabilities responsibly and we will make every effort to acknowledge your contributions. 3 | 4 | Report security vulnerabilities at the following email address: 5 | ``` 6 | [security@getstream.io](mailto:security@getstream.io) 7 | ``` 8 | Alternatively it is also possible to open a new issue in the affected repository, tagging it with the `security` tag. 9 | 10 | A team member will acknowledge the vulnerability and will follow-up with more detailed information. A representative of the security team will be in touch if more information is needed. 11 | 12 | # Information to include in a report 13 | While we appreciate any information that you are willing to provide, please make sure to include the following: 14 | * Which repository is affected 15 | * Which branch, if relevant 16 | * Be as descriptive as possible, the team will replicate the vulnerability before working on a fix. 17 | -------------------------------------------------------------------------------- /babel.i18next-extract.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", "@babel/preset-react"], 3 | "plugins": [ 4 | [ 5 | "i18next-extract", 6 | { 7 | "contextSeparator": "__", 8 | "defaultContexts": [""], 9 | "defaultNS": "en", 10 | "locales": ["nl", "en", "it", "tr", "fr", "hi", "ru"], 11 | "jsonSpace": 4, 12 | "keySeparator": null, 13 | "nsSeparator": null, 14 | "keyAsDefaultValue": ["en"], 15 | "keyAsDefaultValueForDerivedKeys": false, 16 | "outputPath": "src/i18n/{{locale}}.json", 17 | "discardOldKeys": true 18 | } 19 | ], 20 | "@babel/proposal-class-properties", 21 | "@babel/transform-runtime" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /bin/postpack.sh: -------------------------------------------------------------------------------- 1 | set -exu -------------------------------------------------------------------------------- /bin/prepack.sh: -------------------------------------------------------------------------------- 1 | set -exu 2 | yarn lint -------------------------------------------------------------------------------- /bin/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # shellcheck disable=SC2103 3 | 4 | set -eux 5 | 6 | if ! git diff --exit-code || ! git diff --cached --exit-code; then 7 | echo "ERROR: UNCOMMITTED CHANGES" 8 | exit 1 9 | fi 10 | 11 | echo "Mention the tag. Default - latest" 12 | read tag 13 | 14 | if [ -z "$tag" ] 15 | then 16 | tag="latest" 17 | fi 18 | 19 | cd native-package 20 | npm version --no-git-tag-version "$1" 21 | sed -e 's|"react-native-activity-feed-core": "[^"]*"|"react-native-activity-feed-core": "'"$1"'"|g' -i.bak package.json 22 | rm package.json.bak 23 | 24 | cd ../expo-package 25 | npm version --no-git-tag-version "$1" 26 | sed -e 's|"react-native-activity-feed-core": "[^"]*"|"react-native-activity-feed-core": "'"$1"'"|g' -i.bak package.json 27 | rm package.json.bak 28 | cd .. 29 | git add {expo,native}-package/package.json 30 | #yarn docs 31 | #git add docs 32 | git add expo-package/yarn.lock 33 | git add native-package/yarn.lock 34 | 35 | npm version "$1" --force 36 | 37 | npm publish --tag="$tag" 38 | 39 | cd native-package 40 | npm publish --tag="$tag" 41 | 42 | 43 | cd ../expo-package 44 | npm publish --tag="$tag" 45 | 46 | git push --follow-tags -------------------------------------------------------------------------------- /bin/validate-translations.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const i18nDirectoryRelativePath = '../src/i18n/'; 6 | const directoryPath = path.join(__dirname, i18nDirectoryRelativePath); 7 | let countMissingTranslations = 0; 8 | 9 | fs.readdir(directoryPath, function(err, files) { 10 | if (err) { 11 | return console.log('Unable to scan directory: ' + err); 12 | } 13 | 14 | files.forEach(function(file) { 15 | if (file === 'index.js') return; 16 | // Do whatever you want to do with the file 17 | const data = require(i18nDirectoryRelativePath + file); 18 | const keys = Object.keys(data); 19 | keys.forEach((key) => { 20 | if (!data[key] || data[key] === '') { 21 | countMissingTranslations = countMissingTranslations + 1; 22 | console.error( 23 | '\\033[91m', 24 | 'Missing translation for key "' + key + '" in "' + file + '"', 25 | ); 26 | } 27 | }); 28 | }); 29 | 30 | if (countMissingTranslations > 0) { 31 | process.exitCode = 2; 32 | process.exit(); 33 | } else { 34 | process.exit(0); 35 | } 36 | }); 37 | -------------------------------------------------------------------------------- /docs/0103555406e67ed07e22ac1178eceb1f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/0103555406e67ed07e22ac1178eceb1f.png -------------------------------------------------------------------------------- /docs/03562343670f52e40e265604a3d2c1c1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/03562343670f52e40e265604a3d2c1c1.png -------------------------------------------------------------------------------- /docs/13bba9ab6f1d80de5e5252b0fd97cd10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/13bba9ab6f1d80de5e5252b0fd97cd10.png -------------------------------------------------------------------------------- /docs/18d7cbe55260f9dd4964aa11e935def0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/18d7cbe55260f9dd4964aa11e935def0.png -------------------------------------------------------------------------------- /docs/34a97b444a701b830f7e789250785643.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/34a97b444a701b830f7e789250785643.png -------------------------------------------------------------------------------- /docs/37c3db8e1f4b9a9266fc9af91b04d626.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/37c3db8e1f4b9a9266fc9af91b04d626.png -------------------------------------------------------------------------------- /docs/631dd199329d34f7b3a092e293c4d9c3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/631dd199329d34f7b3a092e293c4d9c3.png -------------------------------------------------------------------------------- /docs/6b6c4d8056606e37cc778eaef87fd912.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/6b6c4d8056606e37cc778eaef87fd912.png -------------------------------------------------------------------------------- /docs/7f5d1e6897e29d888aac8dba80a08a1b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/7f5d1e6897e29d888aac8dba80a08a1b.png -------------------------------------------------------------------------------- /docs/950a595906dbb8f8982b72c000ce3eee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/950a595906dbb8f8982b72c000ce3eee.png -------------------------------------------------------------------------------- /docs/b265294f1379c2f676ec42b16e10d77a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/b265294f1379c2f676ec42b16e10d77a.png -------------------------------------------------------------------------------- /docs/build/bundle.24c6c987.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /* 2 | object-assign 3 | (c) Sindre Sorhus 4 | @license MIT 5 | */ 6 | 7 | /*! 8 | * @overview es6-promise - a tiny implementation of Promises/A+. 9 | * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) 10 | * @license Licensed under MIT license 11 | * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE 12 | * @version v4.2.8+1e68dce6 13 | */ 14 | 15 | /*! 16 | * The buffer module from node.js, for the browser. 17 | * 18 | * @author Feross Aboukhadijeh 19 | * @license MIT 20 | */ 21 | 22 | /*! 23 | * The buffer module from node.js, for the browser. 24 | * 25 | * @author Feross Aboukhadijeh 26 | * @license MIT 27 | */ 28 | 29 | /*! 30 | * mime-db 31 | * Copyright(c) 2014 Jonathan Ong 32 | * MIT Licensed 33 | */ 34 | 35 | /*! 36 | * mime-types 37 | * Copyright(c) 2014 Jonathan Ong 38 | * Copyright(c) 2015 Douglas Christopher Wilson 39 | * MIT Licensed 40 | */ 41 | 42 | /*! 43 | * regjsgen 0.5.2 44 | * Copyright 2014-2020 Benjamin Tan 45 | * Available under the MIT license 46 | */ 47 | 48 | /*! 49 | * validate.js 0.12.0 50 | * 51 | * (c) 2013-2017 Nicklas Ansman, 2013 Wrapp 52 | * Validate.js may be freely distributed under the MIT license. 53 | * For all details and documentation: 54 | * http://validatejs.org/ 55 | */ 56 | 57 | /*! clipboard-copy. MIT License. Feross Aboukhadijeh */ 58 | 59 | /*! https://mths.be/regenerate v1.4.2 by @mathias | MIT license */ 60 | 61 | /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */ 62 | 63 | /** 64 | * @license 65 | * Lodash 66 | * Copyright OpenJS Foundation and other contributors 67 | * Released under MIT license 68 | * Based on Underscore.js 1.8.3 69 | * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 70 | */ 71 | 72 | /** 73 | * A better abstraction over CSS. 74 | * 75 | * @copyright Oleg Isonen (Slobodskoi) / Isonen 2014-present 76 | * @website https://github.com/cssinjs/jss 77 | * @license MIT 78 | */ 79 | 80 | /** 81 | * Prism: Lightweight, robust, elegant syntax highlighting 82 | * 83 | * @license MIT 84 | * @author Lea Verou 85 | * @namespace 86 | * @public 87 | */ 88 | 89 | /** @license React v0.19.1 90 | * scheduler.production.min.js 91 | * 92 | * Copyright (c) Facebook, Inc. and its affiliates. 93 | * 94 | * This source code is licensed under the MIT license found in the 95 | * LICENSE file in the root directory of this source tree. 96 | */ 97 | 98 | /** @license React v16.13.1 99 | * react-dom-unstable-native-dependencies.production.min.js 100 | * 101 | * Copyright (c) Facebook, Inc. and its affiliates. 102 | * 103 | * This source code is licensed under the MIT license found in the 104 | * LICENSE file in the root directory of this source tree. 105 | */ 106 | 107 | /** @license React v16.14.0 108 | * react-dom.production.min.js 109 | * 110 | * Copyright (c) Facebook, Inc. and its affiliates. 111 | * 112 | * This source code is licensed under the MIT license found in the 113 | * LICENSE file in the root directory of this source tree. 114 | */ 115 | 116 | /** @license React v16.14.0 117 | * react.production.min.js 118 | * 119 | * Copyright (c) Facebook, Inc. and its affiliates. 120 | * 121 | * This source code is licensed under the MIT license found in the 122 | * LICENSE file in the root directory of this source tree. 123 | */ 124 | -------------------------------------------------------------------------------- /docs/build/images/0103555406e67ed07e22ac1178eceb1f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/build/images/0103555406e67ed07e22ac1178eceb1f.png -------------------------------------------------------------------------------- /docs/build/images/03562343670f52e40e265604a3d2c1c1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/build/images/03562343670f52e40e265604a3d2c1c1.png -------------------------------------------------------------------------------- /docs/build/images/13bba9ab6f1d80de5e5252b0fd97cd10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/build/images/13bba9ab6f1d80de5e5252b0fd97cd10.png -------------------------------------------------------------------------------- /docs/build/images/18d7cbe55260f9dd4964aa11e935def0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/build/images/18d7cbe55260f9dd4964aa11e935def0.png -------------------------------------------------------------------------------- /docs/build/images/34a97b444a701b830f7e789250785643.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/build/images/34a97b444a701b830f7e789250785643.png -------------------------------------------------------------------------------- /docs/build/images/37c3db8e1f4b9a9266fc9af91b04d626.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/build/images/37c3db8e1f4b9a9266fc9af91b04d626.png -------------------------------------------------------------------------------- /docs/build/images/631dd199329d34f7b3a092e293c4d9c3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/build/images/631dd199329d34f7b3a092e293c4d9c3.png -------------------------------------------------------------------------------- /docs/build/images/6b6c4d8056606e37cc778eaef87fd912.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/build/images/6b6c4d8056606e37cc778eaef87fd912.png -------------------------------------------------------------------------------- /docs/build/images/7f5d1e6897e29d888aac8dba80a08a1b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/build/images/7f5d1e6897e29d888aac8dba80a08a1b.png -------------------------------------------------------------------------------- /docs/build/images/950a595906dbb8f8982b72c000ce3eee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/build/images/950a595906dbb8f8982b72c000ce3eee.png -------------------------------------------------------------------------------- /docs/build/images/b265294f1379c2f676ec42b16e10d77a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/build/images/b265294f1379c2f676ec42b16e10d77a.png -------------------------------------------------------------------------------- /docs/build/images/c841eabac3b84071d9315f6bcf7028f5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/build/images/c841eabac3b84071d9315f6bcf7028f5.png -------------------------------------------------------------------------------- /docs/build/images/dce58fb332b90c4f559d30fcb21b063a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/build/images/dce58fb332b90c4f559d30fcb21b063a.png -------------------------------------------------------------------------------- /docs/build/images/e846cb2cac91e8d8c41231f49d4c85a4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/build/images/e846cb2cac91e8d8c41231f49d4c85a4.png -------------------------------------------------------------------------------- /docs/build/images/fc3744c5bf90ccdc7b81d9d99dca4acd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/build/images/fc3744c5bf90ccdc7b81d9d99dca4acd.png -------------------------------------------------------------------------------- /docs/build/images/feb4549aecc16b9160e64f12dfa79a31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/build/images/feb4549aecc16b9160e64f12dfa79a31.png -------------------------------------------------------------------------------- /docs/c841eabac3b84071d9315f6bcf7028f5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/c841eabac3b84071d9315f6bcf7028f5.png -------------------------------------------------------------------------------- /docs/cookbook.md: -------------------------------------------------------------------------------- 1 | 2 | ### Make a custom component with access to API client 3 | 4 | Sometimes the functionality offered by the library is not enough and you need to use the API client directly. 5 | 6 | #### Optional but suggested, make sure that you have Flow enabled to get type validation and hits 7 | 8 | The entire library is covered with Flow, this really helps reducing the risk of bugs and simplifies coding a lot. 9 | 10 | #### Create a component as StreamApp consumer 11 | 12 | The API client is exposed by the `` provider component, your component can access the already initialized API client 13 | by subscribing to it. More information on this pattern is available on React's official [docs](https://reactjs.org/docs/context.html) 14 | 15 | ```jsx static 16 | export default class ShareButton extends React.Component { 17 | 18 | render() { 19 | return ( 20 | 21 | {(appCtx) => { 22 | return ; 23 | }} 24 | 25 | ); 26 | } 27 | 28 | } 29 | ``` 30 | 31 | #### Create an internal component that handles all logic 32 | 33 | In order to keep code clean, we suggest splitting the code in two separate components; one subscribing to `StreamApp.Consumer` and another 34 | receiving its props and do all the heavy lifting. 35 | 36 | ```jsx static 37 | class ShareButtonInner extends React.Component { 38 | 39 | async onPress() { 40 | await this.props.client.reactions.add('share', this.props.activity); 41 | } 42 | 43 | render() { 44 | 45 | Share 46 | 47 | } 48 | 49 | } 50 | ``` 51 | 52 | #### Render your component as child 53 | 54 | ```jsx static 55 | 56 | 59 | {/* ... */} 60 | 61 | 64 | 65 | {/* ... */} 66 | 67 | ``` 68 | 69 | 70 | Note: reading the code of the top-level components is highly suggested and probably the best way to find your way around. 71 | -------------------------------------------------------------------------------- /docs/dce58fb332b90c4f559d30fcb21b063a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/dce58fb332b90c4f559d30fcb21b063a.png -------------------------------------------------------------------------------- /docs/e846cb2cac91e8d8c41231f49d4c85a4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/e846cb2cac91e8d8c41231f49d4c85a4.png -------------------------------------------------------------------------------- /docs/ef6dd31ac5d7ede0dd323bfc2df59db3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/ef6dd31ac5d7ede0dd323bfc2df59db3.png -------------------------------------------------------------------------------- /docs/fc3744c5bf90ccdc7b81d9d99dca4acd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/fc3744c5bf90ccdc7b81d9d99dca4acd.png -------------------------------------------------------------------------------- /docs/feb4549aecc16b9160e64f12dfa79a31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/docs/feb4549aecc16b9160e64f12dfa79a31.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React native activity feeds - Docs 8 | 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/other-components.md: -------------------------------------------------------------------------------- 1 | These components are either used inside of the top level components or expose simple UI around feeds and activities. In most cases you will extend this components or place them as props or children of a top level component. 2 | 3 | Common examples are: changing the layout of activities, adding likes and comments to activities and hooking up press handlers with your app's navigation. 4 | -------------------------------------------------------------------------------- /docs/setup.md: -------------------------------------------------------------------------------- 1 | This library provides components that intereact with Stream's APIs. In order to use the components you need to have an account and to nest the UI interacting with feeds inside the element which you must configure with the correct API keys and token. 2 | 3 | ```js static 4 | 9 | 10 | 11 | ``` 12 | 13 | ### Current user and defaults 14 | 15 | Your token contains the user id of the user that uses your application. Because of this, all components will automatically use that as default for rendering feeds, adding activities and reactions such as comments and likes. 16 | 17 | **Note:** most top level components like the flat feed, come with common default prop values (eg. userId, feed group, ...). 18 | -------------------------------------------------------------------------------- /docs/styles.md: -------------------------------------------------------------------------------- 1 | Components from this library come with default styling which you can overwrite via the `styles` props. 2 | 3 | You can also change specific styles globally using the `updateStyle` function from the `styles` module. 4 | 5 | ```js static 6 | 7 | import { updateStyle } from 'expo-activity-feed'; 8 | 9 | updateStyle('avatar', { 10 | container: { 11 | shadowColor: '#fff', 12 | } 13 | }); 14 | 15 | ``` 16 | 17 | Here is the list of all styles organized by component name: 18 | 19 | ```jsx noeditor 20 | var PureComponent = require('react').PureComponent; 21 | const styles = require('../src/styles.js').styles; 22 | const StyleSheet = require('react-native').StyleSheet; 23 | 24 | class KeyValue extends PureComponent { 25 | render() { 26 | let {tuples} = this.props; 27 | return ( 28 |
    29 | {tuples.map( tuple => ( 30 |
  • 31 | {tuple[0]}: {JSON.stringify(tuple[1])} 32 |
  • 33 | ))} 34 |
35 | ); 36 | }; 37 | }; 38 | 39 | class StyleTable extends PureComponent { 40 | render() { 41 | return (<> 42 | {Object.keys(styles).map(s => { 43 | return ( 44 | <> 45 |

{s}

46 | {Object.keys(styles[s]).map(ss => { 47 | const flatSS = StyleSheet.flatten(styles[s][ss]); 48 | return ( 49 |
    50 |
  • {ss}:
  • 51 |
  • 52 | ([k, flatSS[k]]))} 54 | /> 55 |
  • 56 |
57 | ) 58 | })} 59 | 60 | ); 61 | })} 62 | ); 63 | } 64 | }; 65 | 66 | 67 | ``` 68 | -------------------------------------------------------------------------------- /docs/top-level-components.md: -------------------------------------------------------------------------------- 1 | The most important kind of components provide most of the functionality and integrate with Stream APIs. In almost all cases, your application will start with one of these components and customize them to fit your needs (eg. change style, swap internal components, change layout, ...). 2 | 3 | **Note:** for this components to work with the APIs, they have to be places inside of the element, which it is used to perform API calls. -------------------------------------------------------------------------------- /dotgit/hooks-wrapper: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Runs all executable pre-commit-* hooks and exits after, 4 | # if any of them was not successful. 5 | # 6 | # Based on 7 | # https://github.com/ELLIOTTCABLE/Paws.js/blob/Master/Scripts/git-hooks/chain-hooks.sh 8 | # http://osdir.com/ml/git/2009-01/msg00308.html 9 | # 10 | # assumes your scripts are located at /bin/git/hooks 11 | exitcodes=() 12 | hookname=`basename $0` 13 | # our special hooks folder 14 | CUSTOM_HOOKS_DIR=$(git rev-parse --show-toplevel)/dotgit/hooks 15 | # find gits native hooks folder 16 | NATIVE_HOOKS_DIR=$(git rev-parse --show-toplevel)/.git/hooks 17 | 18 | # Run each hook, passing through STDIN and storing the exit code. 19 | # We don't want to bail at the first failure, as the user might 20 | # then bypass the hooks without knowing about additional issues. 21 | 22 | for hook in $CUSTOM_HOOKS_DIR/$(basename $0)-*; do 23 | test -x "$hook" || continue 24 | $hook "$@" 25 | exitcodes+=($?) 26 | done 27 | 28 | # check if there was a local hook that was moved previously 29 | if [ -f "$NATIVE_HOOKS_DIR/$hookname.local" ]; then 30 | out=`$NATIVE_HOOKS_DIR/$hookname.local "$@"` 31 | exitcodes+=($?) 32 | echo "$out" 33 | fi 34 | 35 | # If any exit code isn't 0, bail. 36 | for i in "${exitcodes[@]}"; do 37 | [ "$i" == 0 ] || exit $i 38 | done -------------------------------------------------------------------------------- /dotgit/hooks/pre-commit-format.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | if ! yarn run lint; then 6 | yarn run lint-fix 7 | echo "some files were not formatted correctly (prettier/eslint) commit aborted!" 8 | echo "your changes are still staged, you can accept formatting changes with git add or ignore them by adding --no-verify to git commit" 9 | exit 1 10 | fi -------------------------------------------------------------------------------- /dotgit/hooks/pre-commit-reject-binaries.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import subprocess 4 | import sys 5 | 6 | # from https://gist.github.com/LukasKnuth/1839424 7 | """ 8 | This is a git commit-hook which can be used to check if huge files 9 | where accidentally added to the staging area and are about to be 10 | committed. 11 | If there is a file which is bigger then the given "max_file_size"- 12 | variable, the script will exit non-zero and abort the commit. 13 | This script is meant to be added as a "pre-commit"-hook. See this 14 | page for further information: 15 | http://progit.org/book/ch7-3.html#installing_a_hook 16 | In order to make the script work probably, you'll need to set the 17 | above path to the python interpreter (first line of the file) 18 | according to your system (under *NIX do "which python" to find out). 19 | Also, the "git_binary_path"-variable should contain the absolute 20 | path to your "git"-executable (you can use "which" here, too). 21 | See the included README-file for further information. 22 | The script was developed and has been confirmed to work under 23 | python 3.2.2 and git 1.7.7.1 (might also work with earlier versions!) 24 | """ 25 | 26 | # The maximum file-size for a file to be committed: 27 | max_file_size = 64 * 1024 # in KB (= 1024 byte) 28 | # The path to the git-binary: 29 | git_binary_path = "/usr/bin/git" 30 | 31 | 32 | def sizeof_fmt(num): 33 | for x in ["bytes", "KB", "MB", "GB", "TB"]: 34 | if num < 1024.0: 35 | return "%3.1f %s" % (num, x) 36 | num /= 1024.0 37 | 38 | 39 | # Now, do the checking: 40 | try: 41 | print("Checking for files bigger then " + sizeof_fmt(max_file_size * 1024)) 42 | # Check all files in the staging-area: 43 | text = subprocess.check_output( 44 | [git_binary_path, "status", "--porcelain", "-uno"], stderr=subprocess.STDOUT 45 | ).decode("utf-8") 46 | file_list = text.splitlines() 47 | 48 | # Check all files: 49 | for file_s in file_list: 50 | if os.path.isfile(file_s[3:]): 51 | stat = os.stat(file_s[3:]) 52 | if stat.st_size > (max_file_size * 1024): 53 | # File is to big, abort the commit: 54 | print( 55 | "'" + file_s[3:] + "' is too huge to be commited!", 56 | "(" + sizeof_fmt(stat.st_size) + ")", 57 | ) 58 | sys.exit(1) 59 | 60 | # Everything seams to be okay: 61 | print("No huge files found.") 62 | sys.exit(0) 63 | except Exception: 64 | # There was a problem 65 | e = sys.exc_info()[1] 66 | print(e.args) 67 | print("Oops...") 68 | sys.exit(12) 69 | -------------------------------------------------------------------------------- /dotgit/setup-hooks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # based on http://stackoverflow.com/a/3464399/1383268 3 | # assumes that the hooks-wrapper script is located at /bin/git/hooks-wrapper 4 | 5 | HOOK_NAMES="applypatch-msg pre-applypatch post-applypatch pre-commit prepare-commit-msg commit-msg post-commit pre-rebase post-checkout post-merge pre-receive update post-receive post-update pre-auto-gc pre-push" 6 | # find gits native hooks folder 7 | HOOKS_DIR=$(git rev-parse --show-toplevel)/.git/hooks 8 | 9 | for hook in $HOOK_NAMES; do 10 | # If the hook already exists, is a file, and is not a symlink 11 | if [ ! -h $HOOKS_DIR/$hook ] && [ -f $HOOKS_DIR/$hook ]; then 12 | mv $HOOKS_DIR/$hook $HOOKS_DIR/$hook.local 13 | fi 14 | # create the symlink, overwriting the file if it exists 15 | # probably the only way this would happen is if you're using an old version of git 16 | # -- back when the sample hooks were not executable, instead of being named ____.sample 17 | ln -s -f ../../dotgit/hooks-wrapper $HOOKS_DIR/$hook 18 | done -------------------------------------------------------------------------------- /examples/expo/.env.example: -------------------------------------------------------------------------------- 1 | STREAM_API_KEY= 2 | STREAM_API_SECRET= 3 | STREAM_APP_ID= 4 | -------------------------------------------------------------------------------- /examples/expo/.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, 3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true 4 | } 5 | -------------------------------------------------------------------------------- /examples/expo/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | npm-debug.* 4 | *.jks 5 | *.p8 6 | *.p12 7 | *.key 8 | *.mobileprovision 9 | *.orig.* 10 | web-build/ 11 | 12 | # macOS 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /examples/expo/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SafeAreaView } from 'react-native'; 3 | 4 | import { STREAM_API_KEY, STREAM_API_TOKEN, STREAM_APP_ID } from '@env'; 5 | 6 | import { 7 | StreamApp, 8 | FlatFeed, 9 | Activity, 10 | StatusUpdateForm, 11 | LikeButton, 12 | } from 'expo-activity-feed'; 13 | 14 | const App = () => { 15 | const apiKey = STREAM_API_KEY; 16 | const appId = STREAM_APP_ID; 17 | const token = STREAM_API_TOKEN; 18 | 19 | if (!apiKey) { 20 | console.error('STREAM_API_KEY should be set'); 21 | return null; 22 | } 23 | 24 | if (!appId) { 25 | console.error('STREAM_APP_ID should be set'); 26 | return null; 27 | } 28 | 29 | if (!token) { 30 | console.error('STREAM_TOKEN should be set'); 31 | return null; 32 | } 33 | 34 | const renderActivity = (props) => ( 35 | } /> 36 | ); 37 | 38 | return ( 39 | 40 | 41 | 42 | 43 | 44 | 45 | ); 46 | }; 47 | 48 | export default App; 49 | -------------------------------------------------------------------------------- /examples/expo/README.md: -------------------------------------------------------------------------------- 1 | # Expo example 2 | 3 | Make sure node version is >= v10.13.0 4 | 5 | ## 1. Setup the example app 6 | 7 | ```sh 8 | yarn global add expo-cli 9 | 10 | # Clone the repository 11 | git clone https://github.com/GetStream/react-native-activity-feed.git 12 | 13 | # Go to expo example directory 14 | cd react-native-activity-feed/examples/expo 15 | 16 | # Install the dependencies 17 | yarn 18 | ``` 19 | 20 | ## 2. Setup up Stream credentials 21 | 22 | Get your Stream API credentials from the [user dashboard](https://getstream.io/dashboard/) and make sure your application has these feed groups: 23 | 24 | - user (type Flat) 25 | - timeline (type Flat) 26 | - notification (type Notification) 27 | 28 | _If you followed the [React Native tutorial](https://getstream.io/react-native-activity-feed/tutorial/), you already have a pre-configured app on your account that you can use for this project._ 29 | 30 | ```sh 31 | mv .env.example .env 32 | ``` 33 | 34 | Open the `.env` file in your favorite editor. And fill in the credentials. 35 | 36 | ## 3. Get your userToken and populate some app data 37 | 38 | ```sh 39 | yarn run init-data 40 | ``` 41 | 42 | Copy the line this script outputs and put it in your `.env` file. 43 | 44 | ## 4. Start your app 45 | 46 | ```sh 47 | yarn start 48 | ``` 49 | -------------------------------------------------------------------------------- /examples/expo/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "expo", 4 | "slug": "expo", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "splash": { 9 | "image": "./assets/splash.png", 10 | "resizeMode": "contain", 11 | "backgroundColor": "#ffffff" 12 | }, 13 | "updates": { 14 | "fallbackToCacheTimeout": 0 15 | }, 16 | "assetBundlePatterns": ["**/*"], 17 | "ios": { 18 | "supportsTablet": true 19 | }, 20 | "android": { 21 | "adaptiveIcon": { 22 | "foregroundImage": "./assets/adaptive-icon.png", 23 | "backgroundColor": "#FFFFFF" 24 | } 25 | }, 26 | "web": { 27 | "favicon": "./assets/favicon.png" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/expo/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/examples/expo/assets/adaptive-icon.png -------------------------------------------------------------------------------- /examples/expo/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/examples/expo/assets/favicon.png -------------------------------------------------------------------------------- /examples/expo/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/examples/expo/assets/icon.png -------------------------------------------------------------------------------- /examples/expo/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/examples/expo/assets/splash.png -------------------------------------------------------------------------------- /examples/expo/babel.config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-undef 2 | module.exports = function(api) { 3 | api.cache(true); 4 | return { 5 | presets: ['babel-preset-expo', 'module:metro-react-native-babel-preset'], 6 | env: { 7 | development: { 8 | plugins: ['module:react-native-dotenv'], 9 | }, 10 | }, 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /examples/expo/metro.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | function resolvePath(...parts) { 3 | const thisPath = PATH.resolve.apply(PATH, parts); 4 | if (!FS.existsSync(thisPath)) return; 5 | 6 | return FS.realpathSync(thisPath); 7 | } 8 | 9 | function isExternalModule(modulePath) { 10 | return modulePath.substring(0, __dirname.length) !== __dirname; 11 | } 12 | 13 | function listDirectories(rootPath, cb) { 14 | FS.readdirSync(rootPath).forEach((fileName) => { 15 | if (fileName.charAt(0) === '.') return; 16 | 17 | let fullFileName = PATH.join(rootPath, fileName), 18 | stats = FS.lstatSync(fullFileName), 19 | symbolic = false; 20 | 21 | if (stats.isSymbolicLink()) { 22 | fullFileName = resolvePath(fullFileName); 23 | if (!fullFileName) return; 24 | 25 | stats = FS.lstatSync(fullFileName); 26 | 27 | symbolic = true; 28 | } 29 | 30 | if (!stats.isDirectory()) return; 31 | 32 | const external = isExternalModule(fullFileName); 33 | cb({ rootPath, symbolic, external, fullFileName, fileName }); 34 | }); 35 | } 36 | 37 | function buildFullModuleMap( 38 | moduleRoot, 39 | mainModuleMap, 40 | externalModuleMap, 41 | _alreadyVisited, 42 | _prefix, 43 | ) { 44 | if (!moduleRoot) return; 45 | 46 | const alreadyVisited = _alreadyVisited || {}, 47 | prefix = _prefix; 48 | 49 | if (alreadyVisited && alreadyVisited.hasOwnProperty(moduleRoot)) return; 50 | 51 | alreadyVisited[moduleRoot] = true; 52 | 53 | listDirectories( 54 | moduleRoot, 55 | ({ fileName, fullFileName, symbolic, external }) => { 56 | if (symbolic) 57 | return buildFullModuleMap( 58 | resolvePath(fullFileName, 'node_modules'), 59 | mainModuleMap, 60 | externalModuleMap, 61 | alreadyVisited, 62 | ); 63 | 64 | const moduleMap = external ? externalModuleMap : mainModuleMap, 65 | moduleName = prefix ? PATH.join(prefix, fileName) : fileName; 66 | 67 | if (fileName.charAt(0) !== '@') moduleMap[moduleName] = fullFileName; 68 | else 69 | return buildFullModuleMap( 70 | fullFileName, 71 | mainModuleMap, 72 | externalModuleMap, 73 | alreadyVisited, 74 | fileName, 75 | ); 76 | }, 77 | ); 78 | } 79 | 80 | function buildModuleResolutionMap() { 81 | const moduleMap = {}, 82 | externalModuleMap = {}; 83 | 84 | buildFullModuleMap(baseModulePath, moduleMap, externalModuleMap); 85 | 86 | // Root project modules take precedence over external modules 87 | return Object.assign({}, externalModuleMap, moduleMap); 88 | } 89 | 90 | function findAlternateRoots( 91 | moduleRoot = baseModulePath, 92 | alternateRoots = [], 93 | _alreadyVisited, 94 | ) { 95 | const alreadyVisited = _alreadyVisited || {}; 96 | if (alreadyVisited && alreadyVisited.hasOwnProperty(moduleRoot)) return; 97 | 98 | alreadyVisited[moduleRoot] = true; 99 | 100 | listDirectories(moduleRoot, ({ fullFileName, fileName, external }) => { 101 | if (fileName.charAt(0) !== '@') { 102 | if (external) alternateRoots.push(fullFileName); 103 | } else { 104 | findAlternateRoots(fullFileName, alternateRoots, alreadyVisited); 105 | } 106 | }); 107 | 108 | return alternateRoots; 109 | } 110 | 111 | function getPolyfillHelper() { 112 | let getPolyfills; 113 | 114 | // Get default react-native polyfills 115 | try { 116 | getPolyfills = require('react-native/rn-get-polyfills'); 117 | } catch (e) { 118 | getPolyfills = () => []; 119 | } 120 | 121 | // See if project has custom polyfills, if so, include the PATH to them 122 | try { 123 | const customPolyfills = require.resolve('./polyfills.js'); 124 | getPolyfills = (function(originalGetPolyfills) { 125 | return () => originalGetPolyfills().concat(customPolyfills); 126 | })(getPolyfills); 127 | } catch (e) { 128 | //ignore 129 | } 130 | 131 | return getPolyfills; 132 | } 133 | 134 | const PATH = require('path'); 135 | const FS = require('fs'), 136 | blacklist = require('metro-config/src/defaults/blacklist'); 137 | 138 | const repoDir = PATH.dirname(PATH.dirname(__dirname)); 139 | const moduleBlacklist = [ 140 | new RegExp(repoDir + '/native-package/.*'), 141 | new RegExp(repoDir + '/expo-package/node_modules/.*'), 142 | new RegExp(repoDir + '/node_modules/.*'), 143 | new RegExp(repoDir + '/examples/native/.*'), 144 | ], 145 | baseModulePath = resolvePath(__dirname, 'node_modules'), 146 | // watch alternate roots (outside of project root) 147 | alternateRoots = findAlternateRoots(), 148 | // build full module map for proper 149 | // resolution of modules in external roots 150 | extraNodeModules = buildModuleResolutionMap(); 151 | 152 | if (alternateRoots && alternateRoots.length) 153 | console.log('Found alternate project roots: ', alternateRoots); 154 | 155 | module.exports = { 156 | resolver: { 157 | blacklistRE: blacklist(moduleBlacklist), 158 | extraNodeModules, 159 | useWatchman: false, 160 | }, 161 | watchFolders: [PATH.resolve(__dirname)].concat(alternateRoots), 162 | // transformer: { 163 | // babelTransformerPath: require.resolve('./compiler/transformer'), 164 | // }, 165 | serializer: { 166 | getPolyfills: getPolyfillHelper(), 167 | }, 168 | }; 169 | -------------------------------------------------------------------------------- /examples/expo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject", 9 | "init-data": "node scripts/initData.js" 10 | }, 11 | "dependencies": { 12 | "@react-native-community/eslint-config": "^2.0.0", 13 | "@react-native-community/masked-view": "0.1.10", 14 | "@react-navigation/bottom-tabs": "^5.11.2", 15 | "@react-navigation/native": "^5.8.10", 16 | "@react-navigation/stack": "^5.12.8", 17 | "expo": "~40.0.0", 18 | "expo-activity-feed": "link:../../expo-package", 19 | "expo-image-picker": "~9.2.0", 20 | "expo-linear-gradient": "~8.4.0", 21 | "expo-permissions": "~10.0.0", 22 | "expo-status-bar": "~1.0.3", 23 | "faker": "^4.1.0", 24 | "numeral": "^2.0.6", 25 | "react": "16.13.1", 26 | "react-dom": "16.13.1", 27 | "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz", 28 | "react-native-activity-feed-core": "link:../..", 29 | "react-native-dotenv": "^2.4.3", 30 | "react-native-gesture-handler": "~1.8.0", 31 | "react-native-reanimated": "~1.13.0", 32 | "react-native-safe-area-context": "3.1.9", 33 | "react-native-safe-area-view": "^1.1.1", 34 | "react-native-screens": "~2.15.0", 35 | "react-native-web": "~0.13.12" 36 | }, 37 | "devDependencies": { 38 | "@babel/core": "~7.9.0", 39 | "@babel/node": "^7.12.10", 40 | "babel-cli": "^6.26.0", 41 | "babel-eslint": "^8.2.5", 42 | "babel-plugin-root-import": "^6.1.0", 43 | "babel-plugin-transform-inline-environment-variables": "^0.4.3", 44 | "babel-preset-env": "^1.7.0", 45 | "babel-preset-expo": "^8.3.0", 46 | "dotenv": "^6.0.0", 47 | "eslint": "^5.1.0", 48 | "eslint-plugin-jest": "^21.17.0", 49 | "eslint-plugin-react": "^7.10.0", 50 | "jest-expo": "~27.0.0", 51 | "prettier": "^1.13.7", 52 | "react-native-scripts": "1.14.0", 53 | "react-test-renderer": "16.3.1" 54 | }, 55 | "private": true 56 | } 57 | -------------------------------------------------------------------------------- /examples/expo/scripts/test_notification.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | // @flow 3 | const { connect } = require('getstream'); 4 | const dotenv = require('dotenv'); 5 | 6 | dotenv.config(); 7 | 8 | async function main() { 9 | const apiKey = process.env.STREAM_API_KEY; 10 | const apiSecret = process.env.STREAM_API_SECRET; 11 | const appId = process.env.STREAM_APP_ID; 12 | if (!apiKey) { 13 | console.error('STREAM_API_KEY should be set'); 14 | return; 15 | } 16 | 17 | if (!appId) { 18 | console.error('STREAM_APP_ID should be set'); 19 | return; 20 | } 21 | 22 | if (!apiSecret) { 23 | console.error('STREAM_SECRET should be set'); 24 | return; 25 | } 26 | 27 | console.log(apiKey, apiSecret); 28 | const serverClient = connect(apiKey, apiSecret, appId); 29 | 30 | function createUserClient(userId) { 31 | return connect(apiKey, serverClient.createUserToken(userId), appId); 32 | } 33 | 34 | const batman = createUserClient('batman'); 35 | const content = 'test2'; 36 | console.log(await batman.feed('notification').get({ limit: 1 })); 37 | await batman.feed('notification').addActivity({ 38 | actor: batman.currentUser, 39 | verb: 'post', 40 | object: content, 41 | 42 | content, 43 | }); 44 | console.log(await batman.feed('notification').get({ limit: 1 })); 45 | } 46 | main(); 47 | -------------------------------------------------------------------------------- /examples/native/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /examples/native/.env.example: -------------------------------------------------------------------------------- 1 | STREAM_API_KEY= 2 | STREAM_API_SECRET= 3 | STREAM_APP_ID= 4 | -------------------------------------------------------------------------------- /examples/native/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore polyfills 9 | node_modules/react-native/Libraries/polyfills/.* 10 | 11 | ; These should not be required directly 12 | ; require from fbjs/lib instead: require('fbjs/lib/warning') 13 | node_modules/warning/.* 14 | 15 | ; Flow doesn't support platforms 16 | .*/Libraries/Utilities/LoadingView.js 17 | 18 | [untyped] 19 | .*/node_modules/@react-native-community/cli/.*/.* 20 | 21 | [include] 22 | 23 | [libs] 24 | node_modules/react-native/interface.js 25 | node_modules/react-native/flow/ 26 | 27 | [options] 28 | emoji=true 29 | 30 | esproposal.optional_chaining=enable 31 | esproposal.nullish_coalescing=enable 32 | 33 | module.file_ext=.js 34 | module.file_ext=.json 35 | module.file_ext=.ios.js 36 | 37 | munge_underscores=true 38 | 39 | module.name_mapper='^react-native/\(.*\)$' -> '/node_modules/react-native/\1' 40 | module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '/node_modules/react-native/Libraries/Image/RelativeImageStub' 41 | 42 | suppress_type=$FlowIssue 43 | suppress_type=$FlowFixMe 44 | suppress_type=$FlowFixMeProps 45 | suppress_type=$FlowFixMeState 46 | 47 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\) 48 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+ 49 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 50 | 51 | [lints] 52 | sketchy-null-number=warn 53 | sketchy-null-mixed=warn 54 | sketchy-number=warn 55 | untyped-type-import=warn 56 | nonstrict-import=warn 57 | deprecated-type=warn 58 | unsafe-getters-setters=warn 59 | unnecessary-invariant=warn 60 | signature-verification-failure=warn 61 | deprecated-utility=error 62 | 63 | [strict] 64 | deprecated-type 65 | nonstrict-import 66 | sketchy-null 67 | unclear-type 68 | unsafe-getters-setters 69 | untyped-import 70 | untyped-type-import 71 | 72 | [version] 73 | ^0.122.0 74 | -------------------------------------------------------------------------------- /examples/native/.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /examples/native/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | 24 | # Android/IntelliJ 25 | # 26 | build/ 27 | .idea 28 | .gradle 29 | local.properties 30 | *.iml 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | yarn-error.log 37 | 38 | # BUCK 39 | buck-out/ 40 | \.buckd/ 41 | *.keystore 42 | !debug.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # CocoaPods 59 | /ios/Pods/ 60 | -------------------------------------------------------------------------------- /examples/native/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSpacing: false, 3 | jsxBracketSameLine: true, 4 | singleQuote: true, 5 | trailingComma: 'all', 6 | }; 7 | -------------------------------------------------------------------------------- /examples/native/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /examples/native/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {SafeAreaView} from 'react-native'; 3 | 4 | import {STREAM_API_KEY, STREAM_API_TOKEN, STREAM_APP_ID} from '@env'; 5 | 6 | import { 7 | StreamApp, 8 | FlatFeed, 9 | Activity, 10 | StatusUpdateForm, 11 | LikeButton, 12 | } from 'react-native-activity-feed'; 13 | 14 | const App = () => { 15 | const apiKey = STREAM_API_KEY; 16 | const appId = STREAM_APP_ID; 17 | const token = STREAM_API_TOKEN; 18 | 19 | if (!apiKey) { 20 | console.error('STREAM_API_KEY should be set'); 21 | return null; 22 | } 23 | 24 | if (!appId) { 25 | console.error('STREAM_APP_ID should be set'); 26 | return null; 27 | } 28 | 29 | if (!token) { 30 | console.error('STREAM_TOKEN should be set'); 31 | return null; 32 | } 33 | 34 | const renderActivity = props => ( 35 | } /> 36 | ); 37 | 38 | return ( 39 | 40 | 41 | 42 | 43 | 44 | 45 | ); 46 | }; 47 | 48 | export default App; 49 | -------------------------------------------------------------------------------- /examples/native/README.md: -------------------------------------------------------------------------------- 1 | # Native example 2 | 3 | Make sure node version is >= v10.13.0 4 | 5 | ## 1. Setup the example app 6 | 7 | ```sh 8 | # Install expo cli, skip if already installed. 9 | yarn global add expo-cli 10 | 11 | # Clone the repository 12 | git clone https://github.com/GetStream/react-native-activity-feed.git 13 | 14 | # Go to native example directory 15 | cd react-native-activity-feed/examples/native 16 | 17 | # Install the npm and pod dependencies 18 | yarn && npx pod-install 19 | ``` 20 | 21 | ## 2. Setup up Stream credentials 22 | 23 | Get your Stream API credentials from the [user dashboard](https://getstream.io/dashboard/) and make sure your application has these feed groups: 24 | 25 | - user (type Flat) 26 | - timeline (type Flat) 27 | - notification (type Notification) 28 | 29 | _If you followed the [React Native tutorial](https://getstream.io/react-native-activity-feed/tutorial/), you already have a pre-configured app on your account that you can use for this project._ 30 | 31 | ```sh 32 | mv .env.example .env 33 | ``` 34 | 35 | Open the `.env` file in your favorite editor. And fill in the credentials. 36 | 37 | ## 3. Get your userToken and populate some app data 38 | 39 | ```sh 40 | yarn run init-data 41 | ``` 42 | 43 | Copy the line this script outputs and put it in your `.env` file. 44 | 45 | ## 4. Start your app 46 | 47 | ```sh 48 | yarn start 49 | ``` 50 | -------------------------------------------------------------------------------- /examples/native/__tests__/App-test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import 'react-native'; 6 | import React from 'react'; 7 | import App from '../App'; 8 | 9 | // Note: test renderer must be required after react-native. 10 | import renderer from 'react-test-renderer'; 11 | 12 | it('renders correctly', () => { 13 | renderer.create(); 14 | }); 15 | -------------------------------------------------------------------------------- /examples/native/android/app/BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") 12 | 13 | lib_deps = [] 14 | 15 | create_aar_targets(glob(["libs/*.aar"])) 16 | 17 | create_jar_targets(glob(["libs/*.jar"])) 18 | 19 | android_library( 20 | name = "all-libs", 21 | exported_deps = lib_deps, 22 | ) 23 | 24 | android_library( 25 | name = "app-code", 26 | srcs = glob([ 27 | "src/main/java/**/*.java", 28 | ]), 29 | deps = [ 30 | ":all-libs", 31 | ":build_config", 32 | ":res", 33 | ], 34 | ) 35 | 36 | android_build_config( 37 | name = "build_config", 38 | package = "com.nativeexample", 39 | ) 40 | 41 | android_resource( 42 | name = "res", 43 | package = "com.nativeexample", 44 | res = "src/main/res", 45 | ) 46 | 47 | android_binary( 48 | name = "app", 49 | keystore = "//android/keystores:debug", 50 | manifest = "src/main/AndroidManifest.xml", 51 | package_type = "debug", 52 | deps = [ 53 | ":app-code", 54 | ], 55 | ) 56 | -------------------------------------------------------------------------------- /examples/native/android/app/build_defs.bzl: -------------------------------------------------------------------------------- 1 | """Helper definitions to glob .aar and .jar targets""" 2 | 3 | def create_aar_targets(aarfiles): 4 | for aarfile in aarfiles: 5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] 6 | lib_deps.append(":" + name) 7 | android_prebuilt_aar( 8 | name = name, 9 | aar = aarfile, 10 | ) 11 | 12 | def create_jar_targets(jarfiles): 13 | for jarfile in jarfiles: 14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] 15 | lib_deps.append(":" + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | -------------------------------------------------------------------------------- /examples/native/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/examples/native/android/app/debug.keystore -------------------------------------------------------------------------------- /examples/native/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /examples/native/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/native/android/app/src/debug/java/com/native/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.nativeexample; 8 | 9 | import android.content.Context; 10 | import com.facebook.flipper.android.AndroidFlipperClient; 11 | import com.facebook.flipper.android.utils.FlipperUtils; 12 | import com.facebook.flipper.core.FlipperClient; 13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; 14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; 15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; 16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping; 17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; 18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; 19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; 20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin; 21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; 22 | import com.facebook.react.ReactInstanceManager; 23 | import com.facebook.react.bridge.ReactContext; 24 | import com.facebook.react.modules.network.NetworkingModule; 25 | import okhttp3.OkHttpClient; 26 | 27 | public class ReactNativeFlipper { 28 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 29 | if (FlipperUtils.shouldEnableFlipper(context)) { 30 | final FlipperClient client = AndroidFlipperClient.getInstance(context); 31 | 32 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); 33 | client.addPlugin(new ReactFlipperPlugin()); 34 | client.addPlugin(new DatabasesFlipperPlugin(context)); 35 | client.addPlugin(new SharedPreferencesFlipperPlugin(context)); 36 | client.addPlugin(CrashReporterPlugin.getInstance()); 37 | 38 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); 39 | NetworkingModule.setCustomClientBuilder( 40 | new NetworkingModule.CustomClientBuilder() { 41 | @Override 42 | public void apply(OkHttpClient.Builder builder) { 43 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); 44 | } 45 | }); 46 | client.addPlugin(networkFlipperPlugin); 47 | client.start(); 48 | 49 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized 50 | // Hence we run if after all native modules have been initialized 51 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); 52 | if (reactContext == null) { 53 | reactInstanceManager.addReactInstanceEventListener( 54 | new ReactInstanceManager.ReactInstanceEventListener() { 55 | @Override 56 | public void onReactContextInitialized(ReactContext reactContext) { 57 | reactInstanceManager.removeReactInstanceEventListener(this); 58 | reactContext.runOnNativeModulesQueueThread( 59 | new Runnable() { 60 | @Override 61 | public void run() { 62 | client.addPlugin(new FrescoFlipperPlugin()); 63 | } 64 | }); 65 | } 66 | }); 67 | } else { 68 | client.addPlugin(new FrescoFlipperPlugin()); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /examples/native/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 13 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /examples/native/android/app/src/main/java/com/native/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.nativeexample; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. This is used to schedule 9 | * rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "native"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/native/android/app/src/main/java/com/native/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.nativeexample; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import com.facebook.react.PackageList; 6 | import com.facebook.react.ReactApplication; 7 | import com.facebook.react.ReactInstanceManager; 8 | import com.facebook.react.ReactNativeHost; 9 | import com.facebook.react.ReactPackage; 10 | import com.facebook.soloader.SoLoader; 11 | import java.lang.reflect.InvocationTargetException; 12 | import java.util.List; 13 | 14 | public class MainApplication extends Application implements ReactApplication { 15 | 16 | private final ReactNativeHost mReactNativeHost = 17 | new ReactNativeHost(this) { 18 | @Override 19 | public boolean getUseDeveloperSupport() { 20 | return BuildConfig.DEBUG; 21 | } 22 | 23 | @Override 24 | protected List getPackages() { 25 | @SuppressWarnings("UnnecessaryLocalVariable") 26 | List packages = new PackageList(this).getPackages(); 27 | // Packages that cannot be autolinked yet can be added manually here, for example: 28 | // packages.add(new MyReactNativePackage()); 29 | return packages; 30 | } 31 | 32 | @Override 33 | protected String getJSMainModuleName() { 34 | return "index"; 35 | } 36 | }; 37 | 38 | @Override 39 | public ReactNativeHost getReactNativeHost() { 40 | return mReactNativeHost; 41 | } 42 | 43 | @Override 44 | public void onCreate() { 45 | super.onCreate(); 46 | SoLoader.init(this, /* native exopackage */ false); 47 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 48 | } 49 | 50 | /** 51 | * Loads Flipper in React Native templates. Call this in the onCreate method with something like 52 | * initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 53 | * 54 | * @param context 55 | * @param reactInstanceManager 56 | */ 57 | private static void initializeFlipper( 58 | Context context, ReactInstanceManager reactInstanceManager) { 59 | if (BuildConfig.DEBUG) { 60 | try { 61 | /* 62 | We use reflection here to pick up the class that initializes Flipper, 63 | since Flipper library is not available in release mode 64 | */ 65 | Class aClass = Class.forName("com.nativeexample.ReactNativeFlipper"); 66 | aClass 67 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class) 68 | .invoke(null, context, reactInstanceManager); 69 | } catch (ClassNotFoundException e) { 70 | e.printStackTrace(); 71 | } catch (NoSuchMethodException e) { 72 | e.printStackTrace(); 73 | } catch (IllegalAccessException e) { 74 | e.printStackTrace(); 75 | } catch (InvocationTargetException e) { 76 | e.printStackTrace(); 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /examples/native/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/examples/native/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/native/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/examples/native/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/native/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/examples/native/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/native/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/examples/native/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/native/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/examples/native/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/native/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/examples/native/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/native/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/examples/native/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/native/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/examples/native/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/native/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/examples/native/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/native/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/examples/native/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/native/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | native 3 | 4 | -------------------------------------------------------------------------------- /examples/native/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/native/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "29.0.2" 6 | minSdkVersion = 16 7 | compileSdkVersion = 29 8 | targetSdkVersion = 29 9 | } 10 | repositories { 11 | google() 12 | jcenter() 13 | } 14 | dependencies { 15 | classpath("com.android.tools.build:gradle:3.5.4") 16 | // NOTE: Do not place your application dependencies here; they belong 17 | // in the individual module build.gradle files 18 | } 19 | } 20 | 21 | allprojects { 22 | repositories { 23 | mavenLocal() 24 | maven { 25 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 26 | url("$rootDir/../node_modules/react-native/android") 27 | } 28 | // ADD THIS 29 | maven { url 'https://maven.google.com' } 30 | 31 | // ADD THIS 32 | maven { url "https://www.jitpack.io" } 33 | maven { 34 | // Android JSC is installed from npm 35 | url("$rootDir/../node_modules/jsc-android/dist") 36 | } 37 | 38 | google() 39 | jcenter() 40 | maven { url 'https://www.jitpack.io' } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/native/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | # Automatically convert third-party libraries to use AndroidX 25 | android.enableJetifier=true 26 | 27 | # Version of flipper SDK to use with React Native 28 | FLIPPER_VERSION=0.54.0 29 | -------------------------------------------------------------------------------- /examples/native/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/examples/native/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /examples/native/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /examples/native/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | @rem Execute Gradle 88 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 89 | 90 | :end 91 | @rem End local scope for the variables with windows NT shell 92 | if "%ERRORLEVEL%"=="0" goto mainEnd 93 | 94 | :fail 95 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 96 | rem the _cmd.exe /c_ return code! 97 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 98 | exit /b 1 99 | 100 | :mainEnd 101 | if "%OS%"=="Windows_NT" endlocal 102 | 103 | :omega 104 | -------------------------------------------------------------------------------- /examples/native/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'native' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | -------------------------------------------------------------------------------- /examples/native/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "native", 3 | "displayName": "native" 4 | } 5 | -------------------------------------------------------------------------------- /examples/native/babel.config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-undef 2 | module.exports = { 3 | presets: ['module:metro-react-native-babel-preset'], 4 | env: { 5 | development: { 6 | plugins: ['module:react-native-dotenv'], 7 | }, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /examples/native/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import {AppRegistry} from 'react-native'; 6 | import App from './App'; 7 | import {name as appName} from './app.json'; 8 | 9 | AppRegistry.registerComponent(appName, () => App); 10 | -------------------------------------------------------------------------------- /examples/native/ios/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | platform :ios, '10.0' 5 | 6 | target 'native' do 7 | config = use_native_modules! 8 | 9 | use_react_native!(:path => config["reactNativePath"]) 10 | 11 | target 'nativeTests' do 12 | inherit! :complete 13 | # Pods for testing 14 | end 15 | 16 | # Enables Flipper. 17 | # 18 | # Note that if you have use_frameworks! enabled, Flipper will not work and 19 | # you should disable these next few lines. 20 | use_flipper! 21 | post_install do |installer| 22 | flipper_post_install(installer) 23 | end 24 | end 25 | 26 | target 'native-tvOS' do 27 | # Pods for native-tvOS 28 | 29 | target 'native-tvOSTests' do 30 | inherit! :search_paths 31 | # Pods for testing 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /examples/native/ios/native-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSExceptionDomains 28 | 29 | localhost 30 | 31 | NSExceptionAllowsInsecureHTTPLoads 32 | 33 | 34 | 35 | 36 | NSLocationWhenInUseUsageDescription 37 | 38 | UILaunchStoryboardName 39 | LaunchScreen 40 | UIRequiredDeviceCapabilities 41 | 42 | armv7 43 | 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | UIViewControllerBasedStatusBarAppearance 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /examples/native/ios/native-tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/native/ios/native.xcodeproj/xcshareddata/xcschemes/native-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /examples/native/ios/native.xcodeproj/xcshareddata/xcschemes/native.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /examples/native/ios/native.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/native/ios/native.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/native/ios/native/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : UIResponder 5 | 6 | @property (nonatomic, strong) UIWindow *window; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /examples/native/ios/native/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | #import 5 | #import 6 | 7 | #ifdef FB_SONARKIT_ENABLED 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | 15 | static void InitializeFlipper(UIApplication *application) { 16 | FlipperClient *client = [FlipperClient sharedClient]; 17 | SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; 18 | [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; 19 | [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; 20 | [client addPlugin:[FlipperKitReactPlugin new]]; 21 | [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; 22 | [client start]; 23 | } 24 | #endif 25 | 26 | @implementation AppDelegate 27 | 28 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 29 | { 30 | #ifdef FB_SONARKIT_ENABLED 31 | InitializeFlipper(application); 32 | #endif 33 | 34 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 35 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge 36 | moduleName:@"native" 37 | initialProperties:nil]; 38 | 39 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 40 | 41 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 42 | UIViewController *rootViewController = [UIViewController new]; 43 | rootViewController.view = rootView; 44 | self.window.rootViewController = rootViewController; 45 | [self.window makeKeyAndVisible]; 46 | return YES; 47 | } 48 | 49 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 50 | { 51 | #if DEBUG 52 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 53 | #else 54 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 55 | #endif 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /examples/native/ios/native/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "idiom": "iphone", 5 | "scale": "2x", 6 | "size": "20x20" 7 | }, 8 | { 9 | "idiom": "iphone", 10 | "scale": "3x", 11 | "size": "20x20" 12 | }, 13 | { 14 | "idiom": "iphone", 15 | "scale": "2x", 16 | "size": "29x29" 17 | }, 18 | { 19 | "idiom": "iphone", 20 | "scale": "3x", 21 | "size": "29x29" 22 | }, 23 | { 24 | "idiom": "iphone", 25 | "scale": "2x", 26 | "size": "40x40" 27 | }, 28 | { 29 | "idiom": "iphone", 30 | "scale": "3x", 31 | "size": "40x40" 32 | }, 33 | { 34 | "idiom": "iphone", 35 | "scale": "2x", 36 | "size": "60x60" 37 | }, 38 | { 39 | "idiom": "iphone", 40 | "scale": "3x", 41 | "size": "60x60" 42 | }, 43 | { 44 | "idiom": "ios-marketing", 45 | "scale": "1x", 46 | "size": "1024x1024" 47 | } 48 | ], 49 | "info": { 50 | "author": "xcode", 51 | "version": 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/native/ios/native/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "version": 1, 4 | "author": "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/native/ios/native/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | native 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | NSExceptionDomains 32 | 33 | localhost 34 | 35 | NSExceptionAllowsInsecureHTTPLoads 36 | 37 | 38 | 39 | 40 | NSLocationWhenInUseUsageDescription 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | NSPhotoLibraryUsageDescription 45 | $(PRODUCT_NAME) would like access to your photo gallery 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UIViewControllerBasedStatusBarAppearance 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /examples/native/ios/native/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/native/ios/nativeTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/native/ios/nativeTests/nativeTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | #import 6 | 7 | #define TIMEOUT_SECONDS 600 8 | #define TEXT_TO_LOOK_FOR @"Welcome to React" 9 | 10 | @interface nativeTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation nativeTests 15 | 16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 17 | { 18 | if (test(view)) { 19 | return YES; 20 | } 21 | for (UIView *subview in [view subviews]) { 22 | if ([self findSubviewInView:subview matching:test]) { 23 | return YES; 24 | } 25 | } 26 | return NO; 27 | } 28 | 29 | - (void)testRendersWelcomeScreen 30 | { 31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 33 | BOOL foundElement = NO; 34 | 35 | __block NSString *redboxError = nil; 36 | #ifdef DEBUG 37 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 38 | if (level >= RCTLogLevelError) { 39 | redboxError = message; 40 | } 41 | }); 42 | #endif 43 | 44 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 45 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 46 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 47 | 48 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 49 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 50 | return YES; 51 | } 52 | return NO; 53 | }]; 54 | } 55 | 56 | #ifdef DEBUG 57 | RCTSetLogFunction(RCTDefaultLogFunction); 58 | #endif 59 | 60 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 61 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 62 | } 63 | 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /examples/native/metro.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | function resolvePath(...parts) { 3 | const thisPath = PATH.resolve.apply(PATH, parts); 4 | if (!FS.existsSync(thisPath)) { 5 | return; 6 | } 7 | 8 | return FS.realpathSync(thisPath); 9 | } 10 | 11 | function isExternalModule(modulePath) { 12 | return modulePath.substring(0, __dirname.length) !== __dirname; 13 | } 14 | 15 | function listDirectories(rootPath, cb) { 16 | FS.readdirSync(rootPath).forEach(fileName => { 17 | if (fileName.charAt(0) === '.') { 18 | return; 19 | } 20 | 21 | let fullFileName = PATH.join(rootPath, fileName), 22 | stats = FS.lstatSync(fullFileName), 23 | symbolic = false; 24 | 25 | if (stats.isSymbolicLink()) { 26 | fullFileName = resolvePath(fullFileName); 27 | if (!fullFileName) { 28 | return; 29 | } 30 | 31 | stats = FS.lstatSync(fullFileName); 32 | 33 | symbolic = true; 34 | } 35 | 36 | if (!stats.isDirectory()) { 37 | return; 38 | } 39 | 40 | const external = isExternalModule(fullFileName); 41 | cb({rootPath, symbolic, external, fullFileName, fileName}); 42 | }); 43 | } 44 | 45 | function buildFullModuleMap( 46 | moduleRoot, 47 | mainModuleMap, 48 | externalModuleMap, 49 | _alreadyVisited, 50 | _prefix, 51 | ) { 52 | if (!moduleRoot) { 53 | return; 54 | } 55 | 56 | const alreadyVisited = _alreadyVisited || {}, 57 | prefix = _prefix; 58 | 59 | if (alreadyVisited && alreadyVisited.hasOwnProperty(moduleRoot)) { 60 | return; 61 | } 62 | 63 | alreadyVisited[moduleRoot] = true; 64 | 65 | listDirectories( 66 | moduleRoot, 67 | ({fileName, fullFileName, symbolic, external}) => { 68 | if (symbolic) { 69 | return buildFullModuleMap( 70 | resolvePath(fullFileName, 'node_modules'), 71 | mainModuleMap, 72 | externalModuleMap, 73 | alreadyVisited, 74 | ); 75 | } 76 | 77 | const moduleMap = external ? externalModuleMap : mainModuleMap, 78 | moduleName = prefix ? PATH.join(prefix, fileName) : fileName; 79 | 80 | if (fileName.charAt(0) !== '@') { 81 | moduleMap[moduleName] = fullFileName; 82 | } else { 83 | return buildFullModuleMap( 84 | fullFileName, 85 | mainModuleMap, 86 | externalModuleMap, 87 | alreadyVisited, 88 | fileName, 89 | ); 90 | } 91 | }, 92 | ); 93 | } 94 | 95 | function buildModuleResolutionMap() { 96 | const moduleMap = {}, 97 | externalModuleMap = {}; 98 | 99 | buildFullModuleMap(baseModulePath, moduleMap, externalModuleMap); 100 | 101 | // Root project modules take precedence over external modules 102 | return Object.assign({}, externalModuleMap, moduleMap); 103 | } 104 | 105 | function findAlternateRoots( 106 | moduleRoot = baseModulePath, 107 | alternateRoots = [], 108 | _alreadyVisited, 109 | ) { 110 | const alreadyVisited = _alreadyVisited || {}; 111 | if (alreadyVisited && alreadyVisited.hasOwnProperty(moduleRoot)) { 112 | return; 113 | } 114 | 115 | alreadyVisited[moduleRoot] = true; 116 | 117 | listDirectories(moduleRoot, ({fullFileName, fileName, external}) => { 118 | if (fileName.charAt(0) !== '@') { 119 | if (external) { 120 | alternateRoots.push(fullFileName); 121 | } 122 | } else { 123 | findAlternateRoots(fullFileName, alternateRoots, alreadyVisited); 124 | } 125 | }); 126 | 127 | return alternateRoots; 128 | } 129 | 130 | function getPolyfillHelper() { 131 | let getPolyfills; 132 | 133 | // Get default react-native polyfills 134 | try { 135 | getPolyfills = require('react-native/rn-get-polyfills'); 136 | } catch (e) { 137 | getPolyfills = () => []; 138 | } 139 | 140 | // See if project has custom polyfills, if so, include the PATH to them 141 | try { 142 | const customPolyfills = require.resolve('./polyfills.js'); 143 | getPolyfills = (function(originalGetPolyfills) { 144 | return () => originalGetPolyfills().concat(customPolyfills); 145 | })(getPolyfills); 146 | } catch (e) { 147 | //ignore 148 | } 149 | 150 | return getPolyfills; 151 | } 152 | 153 | const PATH = require('path'); 154 | const FS = require('fs'), 155 | blacklist = require('metro-config/src/defaults/blacklist'); 156 | 157 | const repoDir = PATH.dirname(PATH.dirname(__dirname)); 158 | console.log('>>>>', repoDir); 159 | const moduleBlacklist = [ 160 | new RegExp(repoDir + '/examples/one/.*'), 161 | new RegExp(repoDir + '/examples/expo/.*'), 162 | new RegExp(repoDir + '/native-example/.*'), 163 | new RegExp(repoDir + '/example/.*'), 164 | new RegExp(repoDir + '/native-package/node_modules/.*'), 165 | new RegExp(repoDir + '/expo-package/.*'), 166 | new RegExp(repoDir + '/node_modules/.*'), 167 | ], 168 | baseModulePath = resolvePath(__dirname, 'node_modules'), 169 | // watch alternate roots (outside of project root) 170 | alternateRoots = findAlternateRoots(), 171 | // build full module map for proper 172 | // resolution of modules in external roots 173 | extraNodeModules = buildModuleResolutionMap(); 174 | 175 | if (alternateRoots && alternateRoots.length) { 176 | console.log('Found alternate project roots: ', alternateRoots); 177 | } 178 | 179 | module.exports = { 180 | resolver: { 181 | blacklistRE: blacklist(moduleBlacklist), 182 | extraNodeModules, 183 | useWatchman: false, 184 | }, 185 | watchFolders: [PATH.resolve(__dirname)].concat(alternateRoots), 186 | // transformer: { 187 | // babelTransformerPath: require.resolve('./compiler/transformer'), 188 | // }, 189 | serializer: { 190 | getPolyfills: getPolyfillHelper(), 191 | }, 192 | }; 193 | -------------------------------------------------------------------------------- /examples/native/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "native", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "ios": "react-native run-ios", 8 | "start": "react-native start", 9 | "test": "jest", 10 | "lint": "eslint .", 11 | "init-data": "node scripts/initData.js" 12 | }, 13 | "dependencies": { 14 | "react": "16.13.1", 15 | "react-native": "0.63.4", 16 | "react-native-activity-feed": "link:../../native-package", 17 | "react-native-activity-feed-core": "link:../..", 18 | "react-native-dotenv": "^2.4.3", 19 | "react-native-image-crop-picker": "^0.35.2", 20 | "faker": "^4.1.0" 21 | }, 22 | "devDependencies": { 23 | "@babel/core": "^7.12.10", 24 | "@babel/runtime": "^7.12.5", 25 | "@react-native-community/eslint-config": "^2.0.0", 26 | "babel-jest": "^26.6.3", 27 | "eslint": "^7.17.0", 28 | "jest": "^26.6.3", 29 | "metro-react-native-babel-preset": "^0.64.0", 30 | "react-test-renderer": "16.13.1" 31 | }, 32 | "jest": { 33 | "preset": "react-native" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/native/scripts/test_notification.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | // @flow 3 | const {connect} = require('getstream'); 4 | const dotenv = require('dotenv'); 5 | 6 | dotenv.config(); 7 | 8 | async function main() { 9 | const apiKey = process.env.STREAM_API_KEY; 10 | const apiSecret = process.env.STREAM_API_SECRET; 11 | const appId = process.env.STREAM_APP_ID; 12 | if (!apiKey) { 13 | console.error('STREAM_API_KEY should be set'); 14 | return; 15 | } 16 | 17 | if (!appId) { 18 | console.error('STREAM_APP_ID should be set'); 19 | return; 20 | } 21 | 22 | if (!apiSecret) { 23 | console.error('STREAM_SECRET should be set'); 24 | return; 25 | } 26 | 27 | console.log(apiKey, apiSecret); 28 | const serverClient = connect(apiKey, apiSecret, appId); 29 | 30 | function createUserClient(userId) { 31 | return connect(apiKey, serverClient.createUserToken(userId), appId); 32 | } 33 | 34 | const batman = createUserClient('batman'); 35 | const content = 'test2'; 36 | console.log(await batman.feed('notification').get({limit: 1})); 37 | await batman.feed('notification').addActivity({ 38 | actor: batman.currentUser, 39 | verb: 'post', 40 | object: content, 41 | 42 | content, 43 | }); 44 | console.log(await batman.feed('notification').get({limit: 1})); 45 | } 46 | main(); 47 | -------------------------------------------------------------------------------- /expo-package/.eslintignore: -------------------------------------------------------------------------------- 1 | /lib/ 2 | -------------------------------------------------------------------------------- /expo-package/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /lib/ 3 | -------------------------------------------------------------------------------- /expo-package/.prettierignore: -------------------------------------------------------------------------------- 1 | /lib/ 2 | -------------------------------------------------------------------------------- /expo-package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "expo-activity-feed", 3 | "version": "1.1.1", 4 | "license": "BSD-3-Clause", 5 | "author": { 6 | "company": "Stream.io Inc" 7 | }, 8 | "keywords": [ 9 | "feed", 10 | "newsfeed", 11 | "activity stream", 12 | "react native", 13 | "notification feed" 14 | ], 15 | "devDependencies": { 16 | "@babel/cli": "^7.1.0", 17 | "@babel/core": "^7.0.0", 18 | "@babel/node": "^7.0.0", 19 | "@babel/plugin-external-helpers": "^7.0.0", 20 | "@babel/plugin-proposal-class-properties": "^7.1.0", 21 | "@babel/plugin-transform-runtime": "^7.2.0", 22 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0", 23 | "babel-preset-env": "^1.7.0", 24 | "eslint": "^5.1.0", 25 | "eslint-plugin-jest": "^21.17.0", 26 | "eslint-plugin-react": "^7.10.0", 27 | "expo": "^29.0.0", 28 | "prettier": "^1.13.7", 29 | "react-native": "~0.55.2", 30 | "react-native-scripts": "1.14.0", 31 | "react-test-renderer": "16.3.1" 32 | }, 33 | "main": "./src/index.js", 34 | "scripts": { 35 | "eslint": "eslint '**/*.js' --max-warnings 0", 36 | "prettier": "prettier --list-different '**/*.{js,ts}'", 37 | "lint": "yarn prettier && yarn eslint", 38 | "lint-fix": "prettier --write '**/*.{js,ts}' && eslint --fix '**/*.js'", 39 | "test": "jest", 40 | "preversion": "yarn && yarn lint", 41 | "prepack": " cp ../README.md .", 42 | "postpack": "rm README.md" 43 | }, 44 | "dependencies": { 45 | "react-native-activity-feed-core": "v1.1.1" 46 | }, 47 | "peerDependencies": { 48 | "expo": "^29.0.0 || ^28.0.0 || ^27.0.0", 49 | "expo-image-picker": "^9.2.0", 50 | "expo-media-library": "^10.0.0", 51 | "expo-permissions": "^10.0.0" 52 | }, 53 | "files": [ 54 | "src", 55 | "README.md" 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /expo-package/src/index.js: -------------------------------------------------------------------------------- 1 | import { registerNativeHandlers } from 'react-native-activity-feed-core'; 2 | import * as Permissions from 'expo-permissions'; 3 | import * as ImagePicker from 'expo-image-picker'; 4 | 5 | registerNativeHandlers({ 6 | pickImage: async ({ compressImageQuality = 0.2 }) => { 7 | try { 8 | const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL); 9 | if (status !== 'granted') { 10 | return { 11 | cancelled: true, 12 | }; 13 | } 14 | 15 | const { cancelled, ...rest } = await ImagePicker.launchImageLibraryAsync({ 16 | allowsEditing: false, 17 | aspect: [4, 3], 18 | quality: compressImageQuality, 19 | }); 20 | 21 | if (cancelled) { 22 | return { 23 | cancelled, 24 | }; 25 | } 26 | return { 27 | cancelled: false, 28 | uri: rest.uri, 29 | }; 30 | } catch (err) { 31 | return { 32 | cancelled: true, 33 | }; 34 | } 35 | }, 36 | }); 37 | 38 | export * from 'react-native-activity-feed-core'; 39 | -------------------------------------------------------------------------------- /native-package/.eslintignore: -------------------------------------------------------------------------------- 1 | /lib/ 2 | -------------------------------------------------------------------------------- /native-package/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /lib/ 3 | -------------------------------------------------------------------------------- /native-package/.prettierignore: -------------------------------------------------------------------------------- 1 | /lib/ 2 | -------------------------------------------------------------------------------- /native-package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-activity-feed", 3 | "version": "1.1.1", 4 | "license": "BSD-3-Clause", 5 | "author": { 6 | "company": "Stream.io Inc" 7 | }, 8 | "keywords": [ 9 | "feed", 10 | "newsfeed", 11 | "activity stream", 12 | "react native", 13 | "notification feed" 14 | ], 15 | "devDependencies": { 16 | "@babel/cli": "^7.1.0", 17 | "@babel/core": "^7.0.0", 18 | "@babel/node": "^7.0.0", 19 | "@babel/plugin-external-helpers": "^7.0.0", 20 | "@babel/plugin-proposal-class-properties": "^7.1.0", 21 | "@babel/plugin-transform-runtime": "^7.2.0", 22 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0", 23 | "@babel/preset-env": "^7.0.0", 24 | "@babel/preset-react": "^7.0.0", 25 | "@babel/runtime-corejs2": "^7.2.0", 26 | "babel-eslint": "^8.2.5", 27 | "babel-plugin-root-import": "^6.1.0", 28 | "babel-plugin-transform-inline-environment-variables": "^0.4.3", 29 | "eslint": "^5.1.0", 30 | "eslint-plugin-jest": "^21.17.0", 31 | "eslint-plugin-react": "^7.10.0", 32 | "prettier": "^1.13.7", 33 | "react-native": "~0.55.2", 34 | "react-native-scripts": "1.14.0", 35 | "react-test-renderer": "16.3.1" 36 | }, 37 | "main": "./src/index.js", 38 | "scripts": { 39 | "eslint": "eslint '**/*.js' --max-warnings 0", 40 | "prettier": "prettier --list-different '**/*.{js,ts}'", 41 | "lint": "yarn prettier && yarn eslint", 42 | "lint-fix": "prettier --write '**/*.{js,ts}' && eslint --fix '**/*.js'", 43 | "test": "jest", 44 | "preversion": "yarn && yarn lint", 45 | "prepack": " cp ../README.md .", 46 | "postpack": "rm README.md" 47 | }, 48 | "dependencies": { 49 | "react-native-activity-feed-core": "v1.1.1", 50 | "react-native-image-crop-picker": ">=0.33.2" 51 | }, 52 | "files": [ 53 | "src", 54 | "README.md" 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /native-package/src/index.js: -------------------------------------------------------------------------------- 1 | import { registerNativeHandlers } from 'react-native-activity-feed-core'; 2 | import ImagePicker from 'react-native-image-crop-picker'; 3 | 4 | import { Platform } from 'react-native'; 5 | 6 | registerNativeHandlers({ 7 | pickImage: async ({ compressImageQuality }) => { 8 | try { 9 | const image = await ImagePicker.openPicker({ 10 | compressImageQuality, 11 | forceJpg: true, 12 | includeBase64: Platform.OS === 'ios', 13 | mediaType: 'photo', 14 | writeTempFile: false, 15 | }); 16 | 17 | return { 18 | cancelled: false, 19 | uri: 20 | Platform.OS === 'ios' 21 | ? image.sourceURL || `data:${image.mime};base64,${image.data}` 22 | : image.path, 23 | }; 24 | } catch (err) { 25 | return { 26 | cancelled: true, 27 | }; 28 | } 29 | }, 30 | }); 31 | 32 | export * from 'react-native-activity-feed-core'; 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-activity-feed-core", 3 | "version": "1.1.1", 4 | "license": "BSD-3-Clause", 5 | "author": { 6 | "company": "Stream.io Inc" 7 | }, 8 | "keywords": [ 9 | "feed", 10 | "newsfeed", 11 | "activity stream", 12 | "react native", 13 | "notification feed" 14 | ], 15 | "devDependencies": { 16 | "@babel/cli": "^7.1.0", 17 | "@babel/core": "^7.0.0", 18 | "@babel/node": "^7.0.0", 19 | "@babel/plugin-external-helpers": "^7.0.0", 20 | "@babel/plugin-proposal-class-properties": "^7.1.0", 21 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0", 22 | "@babel/plugin-transform-runtime": "^7.2.0", 23 | "@babel/preset-env": "^7.0.0", 24 | "@babel/preset-react": "^7.0.0", 25 | "@babel/runtime-corejs2": "^7.2.0", 26 | "babel-eslint": "^8.2.5", 27 | "babel-loader": "^8.0.0-beta.4", 28 | "babel-plugin-add-module-exports": "^0.2.1", 29 | "babel-plugin-i18next-extract": "^0.6.1", 30 | "babel-plugin-macros": "^2.5.1", 31 | "babel-plugin-module-resolver": "^4.1.0", 32 | "babel-plugin-react-native-web": "^0.14.10", 33 | "babel-preset-env": "^7.0.0-beta.3", 34 | "babel-register": "^7.0.0-beta.3", 35 | "cross-env": "^5.1.4", 36 | "eslint": "^5.12.1", 37 | "eslint-plugin-jest": "^21.17.0", 38 | "eslint-plugin-react": "^7.10.0", 39 | "faker": "^4.1.0", 40 | "file-loader": "^2.0.0", 41 | "husky": "^1.3.1", 42 | "metro-react-native-babel-preset": "^0.53.1", 43 | "prettier": "^1.13.7", 44 | "react-art": "^16.4.2", 45 | "react-dom": "^16.4.2", 46 | "react-native": "~0.55.2", 47 | "react-native-scripts": "1.14.0", 48 | "react-native-web": "^0.11.4", 49 | "react-styleguidist": "^11.1.5", 50 | "react-test-renderer": "16.3.1", 51 | "webpack": "^4.17.1" 52 | }, 53 | "main": "./src/index.js", 54 | "scripts": { 55 | "build": "yarn build-translations", 56 | "eslint": "eslint '**/*.{js,md}' --max-warnings 0", 57 | "prettier": "prettier --list-different '**/*.{js,ts,md,json}' .eslintrc.json .prettierrc .babelrc", 58 | "prettier-fix": "prettier --write '**/*.{js,ts,md,json}' .eslintrc.json .prettierrc .babelrc", 59 | "build-translations": "yarn run babel --config-file ./babel.i18next-extract.json 'src/components/**/*.{js,jsx,ts,tsx}' --quiet && yarn lint-fix", 60 | "validate-translations": "node bin/validate-translations.js", 61 | "lint": "prettier --list-different '**/*.{js,ts,md,json}' .eslintrc.json .prettierrc .babelrc && eslint '**/*.{js,md}' --max-warnings 0", 62 | "lint-fix": "prettier --write '**/*.{js,ts,md,json}' .eslintrc.json .prettierrc .babelrc && eslint --fix '**/*.{js,md}' --max-warnings 0", 63 | "docs": "styleguidist build", 64 | "docs-server": "styleguidist server", 65 | "test": "jest", 66 | "release": "bin/release.sh", 67 | "preversion": "yarn && yarn lint", 68 | "prepack": "bin/prepack.sh", 69 | "postpack": "bin/postpack.sh" 70 | }, 71 | "husky": { 72 | "hooks": { 73 | "pre-commit": "dotgit/hooks/pre-commit-format.sh && dotgit/hooks/pre-commit-reject-binaries.py" 74 | } 75 | }, 76 | "dependencies": { 77 | "dayjs": "^1.8.23", 78 | "es6-symbol": "^3.1.1", 79 | "eslint-plugin-babel": "^5.3.0", 80 | "eslint-plugin-markdown": "^1.0.0", 81 | "getstream": "~7.2.1", 82 | "i18next": "^19.3.4", 83 | "immutable": "^4.0.0-rc.9", 84 | "mime-types": "^2.1.21", 85 | "moment": "^2.22.2", 86 | "numeral": "^2.0.6", 87 | "path": "^0.12.7", 88 | "react-native-keyboard-spacer": "^0.4.1", 89 | "react-native-safe-area-view": "0.9.0", 90 | "react-native-sticky-keyboard-accessory": "^0.1.3", 91 | "stream-analytics": "^2.7.1", 92 | "url-parse": "^1.4.3" 93 | }, 94 | "peerDependencies": { 95 | "react": "16.3.1", 96 | "react-native": "~0.55.2", 97 | "react-navigation": "^2.3.1" 98 | }, 99 | "files": [ 100 | "src", 101 | "README.md" 102 | ] 103 | } 104 | -------------------------------------------------------------------------------- /src/Context/index.js: -------------------------------------------------------------------------------- 1 | // 2 | export * from './StreamApp'; 3 | export { Feed, FeedContext } from './Feed'; 4 | -------------------------------------------------------------------------------- /src/components/Avatar.js: -------------------------------------------------------------------------------- 1 | // 2 | import React from 'react'; 3 | import { View, Image } from 'react-native'; 4 | import PropTypes from 'prop-types'; 5 | 6 | import UploadImage from './UploadImage'; 7 | import { StreamApp } from '../Context'; 8 | import { buildStylesheet } from '../styles'; 9 | 10 | /** 11 | * A users' profile picture 12 | * @example ./examples/Avatar.md 13 | */ 14 | export default class Avatar extends React.Component { 15 | static propTypes = { 16 | /** The image source or a getter fn */ 17 | source: PropTypes.oneOfType([ 18 | PropTypes.string, 19 | /** 20 | * @param activeUser User object 21 | * @return string 22 | */ 23 | PropTypes.func, 24 | ]), 25 | size: PropTypes.number, 26 | editButton: PropTypes.bool, 27 | noShadow: PropTypes.bool, 28 | notRound: PropTypes.bool, 29 | 30 | onUploadButtonPress: PropTypes.func, 31 | styles: PropTypes.object, 32 | }; 33 | 34 | render() { 35 | const { source, ...props } = this.props; 36 | if (typeof source === 'function') { 37 | const funcSource = source; 38 | return ( 39 | 40 | {(appCtx) => { 41 | let newSource; 42 | if (appCtx.user.full) { 43 | newSource = funcSource(appCtx.user.full); 44 | } 45 | return ; 46 | }} 47 | 48 | ); 49 | } else { 50 | return ; 51 | } 52 | } 53 | } 54 | 55 | const AvatarInner = (props) => { 56 | const { 57 | source, 58 | size = 200, 59 | noShadow, 60 | notRound, 61 | editButton, 62 | onUploadButtonPress, 63 | } = props; 64 | const styles = buildStylesheet('avatar', props.styles || {}); 65 | const borderRadius = notRound ? undefined : size / 2; 66 | return ( 67 | 77 | 89 | {editButton ? ( 90 | 91 | ) : null} 92 | 93 | ); 94 | }; 95 | -------------------------------------------------------------------------------- /src/components/BackButton.js: -------------------------------------------------------------------------------- 1 | // 2 | import React from 'react'; 3 | import { Image, TouchableOpacity } from 'react-native'; 4 | import PropTypes from 'prop-types'; 5 | 6 | import { buildStylesheet } from '../styles'; 7 | 8 | /** 9 | * Back button 10 | * @example ./examples/BackButton.md 11 | */ 12 | export default class BackButton extends React.Component { 13 | static propTypes = { 14 | styles: PropTypes.object, 15 | blue: PropTypes.bool, 16 | pressed: PropTypes.func, 17 | }; 18 | 19 | render() { 20 | const styles = buildStylesheet('backButton', this.props.styles); 21 | const { blue, pressed } = this.props; 22 | 23 | return ( 24 | 25 | 33 | 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/components/Card.js: -------------------------------------------------------------------------------- 1 | // 2 | import React from 'react'; 3 | import { View, Text, Image, TouchableOpacity, Linking } from 'react-native'; 4 | import PropTypes from 'prop-types'; 5 | 6 | import { buildStylesheet } from '../styles'; 7 | 8 | import _ from 'lodash'; 9 | import { sanitizeUrlForLinking } from '../utils'; 10 | 11 | /** 12 | * Card element 13 | * @example ./examples/Card.md 14 | */ 15 | const Card = (props) => { 16 | const { title, description, image, url } = props; 17 | const styles = buildStylesheet('card', props.styles); 18 | 19 | return ( 20 | { 22 | Linking.openURL(sanitizeUrlForLinking(url)); 23 | }} 24 | style={styles.container} 25 | > 26 | 31 | 32 | {_.truncate(title, { length: 60 })} 33 | 34 | {_.truncate(description, { length: 60 })} 35 | 36 | 37 | 38 | ); 39 | }; 40 | 41 | Card.propTypes = { 42 | title: PropTypes.string, 43 | description: PropTypes.string, 44 | image: PropTypes.string, 45 | url: PropTypes.string, 46 | styles: PropTypes.object, 47 | pressed: PropTypes.func, 48 | }; 49 | 50 | export default Card; 51 | -------------------------------------------------------------------------------- /src/components/CommentBox.js: -------------------------------------------------------------------------------- 1 | // 2 | import React from 'react'; 3 | import { View, TextInput } from 'react-native'; 4 | import KeyboardAccessory from 'react-native-sticky-keyboard-accessory'; 5 | import PropTypes from 'prop-types'; 6 | 7 | import Avatar from './Avatar'; 8 | 9 | import { buildStylesheet } from '../styles'; 10 | import { withTranslationContext } from '../Context'; 11 | 12 | /** 13 | * Comment box with keyboard control, avatar and text input 14 | * All props are fulfilled automatically if used as a child element 15 | * of an activity. 16 | */ 17 | class CommentBox extends React.Component { 18 | static defaultProps = { 19 | styles: {}, 20 | height: 80, 21 | verticalOffset: 25, 22 | noKeyboardAccessory: false, 23 | }; 24 | 25 | state = { 26 | text: '', 27 | }; 28 | 29 | postComment(event) { 30 | if (this.props.onSubmit !== undefined) { 31 | this.props.onSubmit(event.nativeEvent.text); 32 | } else { 33 | this.props.onAddReaction('comment', this.props.activity, { 34 | text: event.nativeEvent.text, 35 | }); 36 | } 37 | } 38 | 39 | render() { 40 | const { noKeyboardAccessory, textInputProps, t } = this.props; 41 | 42 | const styles = buildStylesheet('commentBox', this.props.styles); 43 | const input = ( 44 | 45 | {this.props.noAvatar || ( 46 | 51 | )} 52 | this.setState({ text })} 57 | onSubmitEditing={(event) => { 58 | this.setState({ text: '' }); 59 | this.postComment(event); 60 | }} 61 | placeholder={t('Start Typing...')} 62 | returnKeyType='send' 63 | {...textInputProps} 64 | /> 65 | 66 | ); 67 | if (noKeyboardAccessory) { 68 | return input; 69 | } 70 | 71 | return ( 72 | 73 | 74 | 75 | {input} 76 | 77 | 78 | ); 79 | } 80 | } 81 | 82 | CommentBox.propTypes = { 83 | /** Callback function called when the text is submitted, by default it adds a 84 | * comment reaction to the provided activity */ 85 | onSubmit: PropTypes.func, 86 | /** Height in pixels for the whole component */ 87 | height: PropTypes.number, 88 | /** Props used to render the Avatar component */ 89 | avatarProps: PropTypes.object, 90 | /** Skips the Avatar component when provided */ 91 | noAvatar: PropTypes.bool, 92 | /** Style changes to default */ 93 | styles: PropTypes.object, 94 | /** activity */ 95 | // @todo: Fix it 96 | activity: PropTypes.object, 97 | /** 98 | * event callback handler fired when the enter button is pressed 99 | * @param {*} string 100 | * @param {*} ActivityData 101 | * @param {*} any 102 | */ 103 | onAddReaction: PropTypes.func, 104 | /** Removes KeyboardAccessory. When disabling this keep in mind that the 105 | * input won't move with the keyboard anymore. */ 106 | noKeyboardAccessory: PropTypes.bool, 107 | /** Custom verticalOffset for the KeyboardAccessory if for some reason the 108 | * component is positioned wrongly when the keyboard opens. If the item is 109 | * positioned too high this should be a negative number, if it's positioned 110 | * too low it should be positive. One known case where this happens is when 111 | * using react-navigation with `tabBarPosition: 'bottom'`. */ 112 | verticalOffset: PropTypes.number, 113 | /** Any props the React Native TextInput accepts */ 114 | textInputProps: PropTypes.object, 115 | }; 116 | 117 | export default withTranslationContext(CommentBox); 118 | -------------------------------------------------------------------------------- /src/components/CommentItem.js: -------------------------------------------------------------------------------- 1 | // 2 | import React from 'react'; 3 | import { View, Text } from 'react-native'; 4 | import PropTypes from 'prop-types'; 5 | 6 | import { humanizeTimestamp, smartRender } from '../utils'; 7 | import Avatar from './Avatar'; 8 | import { buildStylesheet } from '../styles'; 9 | 10 | import { withTranslationContext } from '../Context'; 11 | 12 | /** 13 | * Renders a comment 14 | * @example ./examples/CommentItem.md 15 | */ 16 | class CommentItem extends React.Component { 17 | componentDidCatch(error, info) { 18 | if (this.props.componentDidCatch) { 19 | this.props.componentDidCatch(error, info, this.props); 20 | } else { 21 | console.error(error); 22 | console.error('The following comment caused the previous error'); 23 | console.error(this.props.comment); 24 | } 25 | } 26 | 27 | render() { 28 | const { comment, tDateTimeParser } = this.props; 29 | const styles = buildStylesheet('commentItem', this.props.styles || {}); 30 | return ( 31 | 32 | 33 | 34 | 35 | {comment.user.data.name} 36 | {comment.data.text} 37 | 38 | {humanizeTimestamp(comment.created_at, tDateTimeParser)} 39 | 40 | 41 | 42 | {smartRender(this.props.Footer)} 43 | 44 | ); 45 | } 46 | } 47 | 48 | CommentItem.propTypes = { 49 | /** The comment that should be displayed */ 50 | comment: PropTypes.shape({ 51 | user: PropTypes.object, 52 | data: PropTypes.shape({ 53 | name: PropTypes.string, 54 | }), 55 | created_at: PropTypes.oneOfType([ 56 | PropTypes.string, 57 | PropTypes.instanceOf(Date), 58 | ]), 59 | }), 60 | /** 61 | * UI component which should be displayed in the Footer of the component, such as a like button */ 62 | Footer: PropTypes.oneOfType([PropTypes.node, PropTypes.elementType]), 63 | /** Styling of the component */ 64 | styles: PropTypes.object, 65 | /** 66 | * Handle errors in the render method in a custom way, by default this component logs the error in the console 67 | * @param {*} error Error object 68 | * @param {*} info object 69 | * @param {*} props object 70 | */ 71 | componentDidCatch: PropTypes.func, 72 | }; 73 | export default withTranslationContext(CommentItem); 74 | -------------------------------------------------------------------------------- /src/components/CommentList.js: -------------------------------------------------------------------------------- 1 | // 2 | import React from 'react'; 3 | import PropTypes from 'prop-types'; 4 | 5 | import SectionHeader from './SectionHeader'; 6 | import CommentItem from './CommentItem'; 7 | import ReactionList from './ReactionList'; 8 | import LoadMoreButton from './LoadMoreButton'; 9 | import { smartRender } from '../utils'; 10 | 11 | /** 12 | * CommentList uses ReactionList under the hood to render a list of comments. 13 | * To use this component in a `FlatFeed` you have to provide the following 14 | * props to `FlatFeed`: 15 | * ``` 16 | * options={{withOwnReactions: true}} 17 | * ``` 18 | * 19 | * @example ./examples/CommentList.md 20 | */ 21 | export default class CommentList extends React.PureComponent { 22 | static defaultProps = { 23 | CommentItem, 24 | LoadMoreButton, 25 | infiniteScroll: false, 26 | oldestToNewest: false, 27 | reverseOrder: false, 28 | }; 29 | 30 | _Reaction = ({ reaction }) => 31 | smartRender(this.props.CommentItem, { comment: reaction }); 32 | render() { 33 | const { 34 | activityId, 35 | activityPath, 36 | infiniteScroll, 37 | oldestToNewest, 38 | reverseOrder, 39 | flatListProps, 40 | LoadMoreButton, 41 | } = this.props; 42 | return ( 43 | 54 | Comments 55 | 56 | ); 57 | } 58 | } 59 | 60 | CommentList.propTypes = { 61 | /** The ID of the activity for which these comments are */ 62 | activityId: PropTypes.string, 63 | /** The component that should render the comment */ 64 | CommentItem: PropTypes.oneOfType([PropTypes.node, PropTypes.elementType]), 65 | /** Only needed for reposted activities where you want to show the comments 66 | * of the original activity, not of the repost */ 67 | activityPath: PropTypes.arrayOf(PropTypes.string), 68 | /** UI The component that should render the reaction */ 69 | LoadMoreButton: PropTypes.oneOfType([PropTypes.node, PropTypes.elementType]), 70 | /** If the CommentList should paginate when scrolling, by default it shows a 71 | * "Load more" button */ 72 | infiniteScroll: PropTypes.bool, 73 | /** Show and load reactions starting with the oldest reaction first, instead 74 | * of the default where reactions are displayed and loaded most recent first. 75 | * */ 76 | /** Any props the react native FlatList accepts */ 77 | flatListProps: PropTypes.object, 78 | /** Show and load reactions starting with the oldest reaction first, instead 79 | * of the default where reactions are displayed and loaded most recent first. 80 | * */ 81 | oldestToNewest: PropTypes.bool, 82 | /** Reverse the order the reactions are displayed in. */ 83 | reverseOrder: PropTypes.bool, 84 | }; 85 | -------------------------------------------------------------------------------- /src/components/CommentsContainer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View } from 'react-native'; 3 | 4 | const CommentsContainer = ({ 5 | data, 6 | renderComment, 7 | renderMoreLink, 8 | maxComments, 9 | // ...props 10 | }) => ( 11 | 12 | {data.slice(0, maxComments).map(renderComment)} 13 | {data.length > 0 && data.length > maxComments ? renderMoreLink() : null} 14 | 15 | ); 16 | 17 | export default CommentsContainer; 18 | -------------------------------------------------------------------------------- /src/components/FollowButton.js: -------------------------------------------------------------------------------- 1 | // 2 | import React from 'react'; 3 | import { TouchableOpacity, Text, View } from 'react-native'; 4 | import PropTypes from 'prop-types'; 5 | 6 | import { buildStylesheet } from '../styles'; 7 | 8 | import { withTranslationContext } from '../Context'; 9 | 10 | /** 11 | * Renders a toggle button to follow another user's feed 12 | * @example ./examples/FollowButton.md 13 | */ 14 | class FollowButton extends React.Component { 15 | constructor(props) { 16 | super(props); 17 | this.state = { followed: this.props.followed || false }; 18 | } 19 | 20 | static defaultProps = { 21 | followed: false, 22 | }; 23 | 24 | render() { 25 | const { clicked, t } = this.props; 26 | const styles = buildStylesheet('followButton', this.props.styles); 27 | 28 | return ( 29 | 30 | 36 | 37 | {this.state.followed ? t('Following') : t('Follow')} 38 | 39 | 40 | 41 | ); 42 | } 43 | } 44 | 45 | FollowButton.propTypes = { 46 | /** callback function used on click */ 47 | clicked: PropTypes.func, 48 | /** initial follow state */ 49 | followed: PropTypes.bool, 50 | styles: PropTypes.object, 51 | }; 52 | 53 | export default withTranslationContext(FollowButton); 54 | -------------------------------------------------------------------------------- /src/components/IconBadge.js: -------------------------------------------------------------------------------- 1 | // 2 | import * as React from 'react'; 3 | import { View, Text } from 'react-native'; 4 | import PropTypes from 'prop-types'; 5 | 6 | import { Feed, FeedContext } from '../Context'; 7 | import { buildStylesheet } from '../styles'; 8 | 9 | /** 10 | * A badge icon that notifies the user if a feed has new activities 11 | * @example ./examples/IconBadge.md 12 | */ 13 | export default class IconBadge extends React.Component { 14 | static defaultProps = { 15 | feedGroup: 'notification', 16 | showNumber: false, 17 | }; 18 | 19 | render() { 20 | return ( 21 | 22 | 23 | {(feedCtx) => } 24 | 25 | 26 | ); 27 | } 28 | } 29 | 30 | IconBadge.propTypes = { 31 | feedGroup: PropTypes.string.isRequired, 32 | userId: PropTypes.string, 33 | styles: PropTypes.object, 34 | showNumber: PropTypes.number, 35 | hidden: PropTypes.bool, 36 | }; 37 | class IconBadgeInner extends React.Component { 38 | async componentDidMount() { 39 | await this.props.refreshUnreadUnseen(); 40 | } 41 | 42 | render() { 43 | const { children, showNumber, hidden } = this.props; 44 | 45 | const styles = buildStylesheet('iconBadge', this.props.styles); 46 | 47 | return ( 48 | 49 | {children} 50 | {!hidden && this.props.unseen > 0 && ( 51 | 52 | 53 | {showNumber && ( 54 | {this.props.unseen} 55 | )} 56 | 57 | 58 | )} 59 | 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/components/LikeButton.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import { buildStylesheet } from '../styles'; 5 | import ReactionToggleIcon from './ReactionToggleIcon'; 6 | 7 | /** 8 | * Like button ready to be embedded as Activity footer 9 | * @example ./examples/LikeButton.md 10 | */ 11 | export default class LikeButton extends React.Component { 12 | static defaultProps = { 13 | reactionKind: 'like', 14 | }; 15 | _onPress = () => { 16 | const { 17 | activity, 18 | reaction, 19 | reactionKind, 20 | onToggleReaction, 21 | onToggleChildReaction, 22 | } = this.props; 23 | 24 | if (reaction && onToggleChildReaction) { 25 | return onToggleChildReaction(reactionKind, reaction, {}, {}); 26 | } 27 | return onToggleReaction(reactionKind, activity, {}, {}); 28 | }; 29 | 30 | render() { 31 | const { activity, reaction, reactionKind } = this.props; 32 | const styles = buildStylesheet('likeButton', this.props.styles); 33 | let counts, own_reactions; 34 | if (reaction && this.props.onToggleChildReaction) { 35 | counts = reaction.children_counts; 36 | own_reactions = reaction.own_children; 37 | } else { 38 | counts = activity.reaction_counts; 39 | own_reactions = activity.own_reactions; 40 | } 41 | 42 | return ( 43 | 52 | ); 53 | } 54 | } 55 | 56 | LikeButton.propTypes = { 57 | /** 58 | * The activity received from Stream that should be liked when pressing the LikeButton. 59 | **/ 60 | activity: PropTypes.object.isRequired, 61 | /** 62 | * The reaction received from Stream that should be liked when pressing the 63 | * LikeButton. Liking a reaction requires to pass both this field and 64 | * the `onToggleChildReaction` as well. 65 | */ 66 | reaction: PropTypes.object, 67 | /** The reactionKind that is used to like, you can for instance set this to 68 | * `heart`. */ 69 | reactionKind: PropTypes.string, 70 | /** The function that toggles reactions on activities. */ 71 | onToggleReaction: PropTypes.func, 72 | /** The function that toggles reactions on reactions. */ 73 | onToggleChildReaction: PropTypes.func, 74 | /** Styling of the button */ 75 | styles: PropTypes.object, 76 | }; 77 | -------------------------------------------------------------------------------- /src/components/LikeList.js: -------------------------------------------------------------------------------- 1 | // 2 | import React from 'react'; 3 | import { TouchableOpacity } from 'react-native'; 4 | import PropTypes from 'prop-types'; 5 | 6 | import Avatar from './Avatar'; 7 | import ReactionList from './ReactionList'; 8 | import SectionHeader from './SectionHeader'; 9 | import { withTranslationContext } from '../Context'; 10 | 11 | /** 12 | * LikeList uses ReactionList under the hood to render a list of likes. 13 | * 14 | * @example ./examples/LikeList.md 15 | */ 16 | class LikeList extends React.PureComponent { 17 | static defaultProps = { 18 | reactionKind: 'like', 19 | }; 20 | render() { 21 | const { activityId, activityPath, t } = this.props; 22 | return ( 23 | ( 32 | 33 | 38 | 39 | )} 40 | noPagination 41 | > 42 | {t('Likes')} 43 | 44 | ); 45 | } 46 | } 47 | 48 | LikeList.propTypes = { 49 | /** The ID of the activity for which these comments are */ 50 | activityId: PropTypes.string.isRequired, 51 | reactionKind: PropTypes.string.isRequired, 52 | /** Only needed for reposted activities where you want to show the likes of the original activity, not of the repost */ 53 | activityPath: PropTypes.arrayOf(PropTypes.string), 54 | }; 55 | 56 | export default withTranslationContext(LikeList); 57 | -------------------------------------------------------------------------------- /src/components/LoadMoreButton.js: -------------------------------------------------------------------------------- 1 | // 2 | import * as React from 'react'; 3 | import { Text, TouchableOpacity } from 'react-native'; 4 | import PropTypes from 'prop-types'; 5 | 6 | import { buildStylesheet } from '../styles'; 7 | import { withTranslationContext } from '../Context'; 8 | 9 | class LoadMoreButton extends React.Component { 10 | static defaultProps = { 11 | children: 'Load more', 12 | }; 13 | 14 | render() { 15 | const { children, refreshing, onPress, t } = this.props; 16 | 17 | const styles = buildStylesheet('loadMoreButton', this.props.styles); 18 | return ( 19 | 24 | {children || t('Load more')} 25 | 26 | ); 27 | } 28 | } 29 | 30 | LoadMoreButton.propTypes = { 31 | onPress: PropTypes.func, 32 | refreshing: PropTypes.bool, 33 | styles: PropTypes.object, 34 | }; 35 | 36 | export default withTranslationContext(LoadMoreButton); 37 | -------------------------------------------------------------------------------- /src/components/NewActivitiesNotification.js: -------------------------------------------------------------------------------- 1 | // 2 | import * as React from 'react'; 3 | import { TouchableOpacity, Text } from 'react-native'; 4 | import PropTypes from 'prop-types'; 5 | 6 | import { buildStylesheet } from '../styles'; 7 | 8 | import { withTranslationContext } from '../Context'; 9 | 10 | /** 11 | * Renders a notification message when new activities are received by a feed 12 | * @example ./examples/NewActivitiesNotification.md 13 | */ 14 | class NewActivitiesNotification extends React.Component { 15 | static defaultProps = { 16 | labelSingular: 'activity', 17 | labelPlural: 'activities', 18 | }; 19 | 20 | _labelFunction = () => { 21 | const { 22 | adds, 23 | deletes, 24 | labelSingular, 25 | labelPlural, 26 | labelFunction, 27 | t, 28 | } = this.props; 29 | const addCount = (adds || []).length; 30 | const deleteCount = (deletes || []).length; 31 | const count = addCount + deleteCount; 32 | if (labelFunction) { 33 | return labelFunction({ 34 | count, 35 | addCount, 36 | deleteCount, 37 | labelSingular, 38 | labelPlural, 39 | }); 40 | } 41 | if (addCount === 0) { 42 | return null; 43 | } 44 | 45 | let singleNotificationText = ''; 46 | let pluralNotificationText = ''; 47 | 48 | if (labelSingular) { 49 | singleNotificationText = `You have 1 new ${labelSingular}`; 50 | } else { 51 | singleNotificationText = t('You have 1 new notification'); 52 | } 53 | 54 | if (labelPlural) { 55 | pluralNotificationText = `You have ${addCount} new ${labelPlural}`; 56 | } else { 57 | pluralNotificationText = t( 58 | 'You have {{ notificationCount }} new notifications', 59 | { 60 | notificationCount: addCount, 61 | }, 62 | ); 63 | } 64 | 65 | return addCount > 1 ? pluralNotificationText : singleNotificationText; 66 | }; 67 | 68 | render() { 69 | const styles = buildStylesheet('pagerBlock', this.props.styles); 70 | const label = this._labelFunction(); 71 | return label != null ? ( 72 | 73 | {label} 74 | 75 | ) : null; 76 | } 77 | } 78 | 79 | NewActivitiesNotification.propTypes = { 80 | adds: PropTypes.arrayOf(PropTypes.object), 81 | deletes: PropTypes.arrayOf(PropTypes.string), 82 | labelSingular: PropTypes.string, 83 | labelPlural: PropTypes.string, 84 | /** 85 | * A function that returns either the string to display or null in case no 86 | * notification should be displayed 87 | * @param {object} param0 e.g., 88 | * { 89 | * count: number, 90 | * deleteCount: number, 91 | * addCount: number, 92 | * labelPlural: string, 93 | * labelSingular: string, 94 | * } 95 | */ 96 | labelFunction: PropTypes.func, 97 | styles: PropTypes.object, 98 | onPress: PropTypes.func, 99 | }; 100 | 101 | export default withTranslationContext(NewActivitiesNotification); 102 | -------------------------------------------------------------------------------- /src/components/ReactionIcon.js: -------------------------------------------------------------------------------- 1 | // 2 | import React from 'react'; 3 | import { Image, Text, TouchableOpacity } from 'react-native'; 4 | import PropTypes from 'prop-types'; 5 | 6 | import { buildStylesheet } from '../styles'; 7 | 8 | import { withTranslationContext } from '../Context'; 9 | 10 | function defaultLabelFunction(count, props) { 11 | const { labelSingle, labelPlural, labelFunction, kind, t } = props; 12 | if (labelFunction) { 13 | return labelFunction({ 14 | count, 15 | labelSingle, 16 | labelPlural, 17 | }); 18 | } 19 | 20 | let label; 21 | 22 | if (labelSingle && labelPlural) { 23 | label = count === 1 ? `1 ${labelSingle}` : `${count} ${labelPlural}`; 24 | } 25 | 26 | if (!labelSingle || !labelPlural) { 27 | switch (kind) { 28 | case 'like': 29 | label = 30 | count === 1 31 | ? t('1 like') 32 | : t('{{ countLikes }} likes', { countLikes: count }); 33 | break; 34 | case 'repost': 35 | label = 36 | count === 1 37 | ? t('1 repost') 38 | : t('{{ countReposts }} reposts', { countReposts: count }); 39 | break; 40 | case 'comment': 41 | label = 42 | count === 1 43 | ? t('1 comment') 44 | : t('{{ countComments }} comments', { countComments: count }); 45 | break; 46 | default: 47 | break; 48 | } 49 | } 50 | 51 | return label; 52 | } 53 | 54 | const ReactionIcon = withTranslationContext((props) => { 55 | let count = null; 56 | if (props.counts && props.kind) { 57 | count = props.counts[props.kind] || 0; 58 | } 59 | const styles = buildStylesheet('reactionIcon', props.styles); 60 | 61 | const dimensions = {}; 62 | if (props.height !== undefined) { 63 | dimensions.height = props.height; 64 | } 65 | if (props.width !== undefined) { 66 | dimensions.width = props.width; 67 | } 68 | 69 | return ( 70 | 71 | 72 | {count != null ? ( 73 | {defaultLabelFunction(count, props)} 74 | ) : null} 75 | 76 | ); 77 | }); 78 | 79 | ReactionIcon.propTypes = { 80 | /** The icon to display */ 81 | icon: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 82 | /** The reaction counts for the activity */ 83 | counts: PropTypes.objectOf(PropTypes.number), 84 | /** The kind of reaction that this displays */ 85 | kind: PropTypes.string, 86 | /** The height of the icon */ 87 | height: PropTypes.number, 88 | /** The width of the icon */ 89 | width: PropTypes.number, 90 | /** 91 | * Function to call when pressed, usually this should call `props.onToggleReaction` 92 | * @param {string} kind 93 | */ 94 | onPress: PropTypes.func, 95 | /** The label to display if the count is one (e.g "like") */ 96 | labelSingle: PropTypes.string, 97 | /** The label to display if the count is more than one (e.g "likes") */ 98 | labelPlural: PropTypes.string, 99 | /** Styling of the icon */ 100 | styles: PropTypes.object, 101 | /** 102 | * A function that returns either the string to display next to the icon or 103 | * null in case no string should be displayed. This can be used for 104 | * internationalization. 105 | * @param {object} param 106 | */ 107 | labelFunction: PropTypes.func, 108 | }; 109 | 110 | export default ReactionIcon; 111 | -------------------------------------------------------------------------------- /src/components/ReactionIconBar.js: -------------------------------------------------------------------------------- 1 | // 2 | import * as React from 'react'; 3 | import { View } from 'react-native'; 4 | import { buildStylesheet } from '../styles'; 5 | 6 | export default function ReactionIconBar(props) { 7 | const styles = buildStylesheet('reactionIconBar', props.styles); 8 | 9 | return {props.children}; 10 | } 11 | -------------------------------------------------------------------------------- /src/components/ReactionToggleIcon.js: -------------------------------------------------------------------------------- 1 | // 2 | import React from 'react'; 3 | import PropTypes from 'prop-types'; 4 | 5 | import ReactionIcon from './ReactionIcon'; 6 | 7 | /** 8 | * A generic component that can be used to toggle a reaction and display it's 9 | * current state. Mostly used for reactions such as like and repost. 10 | * The [source for 11 | * LikeButton](https://github.com/GetStream/react-native-activity-feed/blob/master/src/components/LikeButton.js) 12 | * is a good example of the usage of this component. 13 | */ 14 | export default function ReactionToggleIcon({ 15 | activeIcon, 16 | inactiveIcon, 17 | own_reactions, 18 | kind = 'like', 19 | ...props 20 | }) { 21 | let icon = inactiveIcon; 22 | if (own_reactions && own_reactions[kind] && own_reactions[kind].length) { 23 | icon = activeIcon; 24 | } 25 | return ; 26 | } 27 | 28 | ReactionToggleIcon.propTypes = { 29 | /** The icon to show when the user has done this reaction (e.g. a filled in heart) */ 30 | activeIcon: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 31 | /** The icon to show when the user has not done this reaction yet (e.g. an empty in heart) */ 32 | inactiveIcon: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 33 | /** The kind of reaction that this toggles */ 34 | kind: PropTypes.string, 35 | /** The height of the icon */ 36 | height: PropTypes.number, 37 | /** The width of the icon */ 38 | width: PropTypes.number, 39 | /** The map with own reactions */ 40 | own_reactions: PropTypes.objectOf(PropTypes.array), 41 | /** The reaction counts for the activity */ 42 | counts: PropTypes.objectOf(PropTypes.number), 43 | /** 44 | * Function to call when pressed, usually this should call `props.onToggleReaction` 45 | * @param {string} kind 46 | */ 47 | onPress: PropTypes.func, 48 | /** The label to display if the count is one (e.g "like") */ 49 | labelSingle: PropTypes.string, 50 | /** The label to display if the count is more than one (e.g "likes") */ 51 | labelPlural: PropTypes.string, 52 | /** Styling of the icon */ 53 | styles: PropTypes.object, 54 | /** 55 | * A function that returns either the string to display next to the icon or 56 | * null in case no string should be displayed. This can be used for 57 | * internationalization. 58 | * 59 | * e.g., (count, labelPlural, labelSingle) => "returning label string" 60 | * */ 61 | labelFunction: PropTypes.func, 62 | }; 63 | -------------------------------------------------------------------------------- /src/components/SectionHeader.js: -------------------------------------------------------------------------------- 1 | // 2 | import * as React from 'react'; 3 | import { View, Text } from 'react-native'; 4 | import { buildStylesheet } from '../styles'; 5 | 6 | /** 7 | * Header components for list of reactions (eg. LikesList) 8 | * @example ./examples/SectionHeader.md 9 | */ 10 | export default function SectionHeader(props) { 11 | const styles = buildStylesheet('sectionHeader', props.styles); 12 | return ( 13 | 14 | {props.children} 15 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /src/components/SinglePost.js: -------------------------------------------------------------------------------- 1 | // 2 | import React from 'react'; 3 | import PropTypes from 'prop-types'; 4 | 5 | import FlatFeed from './FlatFeed'; 6 | 7 | /** 8 | * Shows the detail of a single activity 9 | * @example ./examples/SinglePost.md 10 | */ 11 | export default class SinglePost extends React.Component { 12 | render() { 13 | return ( 14 | 15 | 26 | client 27 | .feed(feedGroup, userId) 28 | .getActivityDetail(this.props.activity.id, options) 29 | } 30 | doReactionAddRequest={this.props.doReactionAddRequest} 31 | doReactionDeleteRequest={this.props.doReactionDeleteRequest} 32 | doChildReactionAddRequest={this.props.doChildReactionAddRequest} 33 | doChildReactionDeleteRequest={this.props.doChildReactionDeleteRequest} 34 | doReactionsFilterRequest={this.props.doReactionsFilterRequest} 35 | Footer={this.props.Footer} 36 | setListRef={this.props.setListRef} 37 | noPagination 38 | /> 39 | 40 | ); 41 | } 42 | } 43 | 44 | // We have duplicated FeedRequestOptionsPropTypeShape at multiple places in codebase 45 | // We can't abstract it out since stylegudist doesn't work with imported types. 46 | const FeedRequestOptionsPropTypeShape = { 47 | withReactionCounts: PropTypes.bool, 48 | withRecentReactions: PropTypes.bool, 49 | withOwnReactions: PropTypes.bool, 50 | reactions: PropTypes.shape({ 51 | recent: PropTypes.bool, 52 | own: PropTypes.bool, 53 | counts: PropTypes.bool, 54 | }), 55 | limit: PropTypes.number, 56 | offset: PropTypes.number, 57 | id_lt: PropTypes.string, 58 | id_lte: PropTypes.string, 59 | id_gt: PropTypes.string, 60 | id_gte: PropTypes.string, 61 | ranking: PropTypes.string, 62 | mark_seen: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]), 63 | mark_read: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]), 64 | refresh: PropTypes.bool, 65 | }; 66 | 67 | SinglePost.propTypes = { 68 | activity: PropTypes.object, 69 | feedGroup: PropTypes.string, 70 | userId: PropTypes.string, 71 | options: PropTypes.shape(FeedRequestOptionsPropTypeShape), 72 | analyticsLocation: PropTypes.string, 73 | Activity: PropTypes.oneOfType([PropTypes.node, PropTypes.elementType]), 74 | Footer: PropTypes.oneOfType([PropTypes.node, PropTypes.elementType]), 75 | styles: PropTypes.object, 76 | navigation: PropTypes.object, 77 | /** 78 | * Override reaction add request 79 | * @param {*} kind 80 | * @param {*} activity 81 | * @param {*} data 82 | * @param {*} options 83 | */ 84 | doReactionAddRequest: PropTypes.func, 85 | /** 86 | * Override reaction delete request 87 | * @param {*} id 88 | */ 89 | doReactionDeleteRequest: PropTypes.func, 90 | /** 91 | * Override child reaction add request 92 | * @param {*} kind 93 | * @param {*} activity 94 | * @param {*} data 95 | * @param {*} options 96 | */ 97 | doChildReactionAddRequest: PropTypes.func, 98 | /** 99 | * Override child reaction delete request 100 | * @param {*} id 101 | */ 102 | doChildReactionDeleteRequest: PropTypes.func, 103 | /** 104 | * Override reactions filter request 105 | * @param {*} options 106 | */ 107 | doReactionsFilterRequest: PropTypes.func, 108 | /** 109 | * @param {*} ref 110 | */ 111 | setListRef: PropTypes.func, 112 | }; 113 | -------------------------------------------------------------------------------- /src/components/UploadImage.js: -------------------------------------------------------------------------------- 1 | // 2 | import React from 'react'; 3 | import { View, Image, TouchableOpacity } from 'react-native'; 4 | import PropTypes from 'prop-types'; 5 | 6 | import PickPhotoIcon from '../images/icons/pickphoto.png'; 7 | import { buildStylesheet } from '../styles'; 8 | 9 | const UploadImage = ({ onUploadButtonPress, ...props }) => { 10 | const styles = buildStylesheet('uploadImage', props.styles || {}); 11 | 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | ); 19 | }; 20 | 21 | UploadImage.propTypes = { 22 | onUploadButtonPress: PropTypes.func, 23 | styles: PropTypes.object, 24 | }; 25 | 26 | export default UploadImage; 27 | -------------------------------------------------------------------------------- /src/components/UrlPreview.js: -------------------------------------------------------------------------------- 1 | // 2 | import React from 'react'; 3 | import { Image, View, Text, TouchableOpacity } from 'react-native'; 4 | import PropTypes from 'prop-types'; 5 | 6 | import { buildStylesheet } from '../styles'; 7 | import _ from 'lodash'; 8 | 9 | /** 10 | * URL preview block with dismiss button (using open-graph attributes) 11 | * @example ./examples/UrlPreview.md 12 | */ 13 | export default class UrlPreview extends React.Component { 14 | render() { 15 | const styles = buildStylesheet('urlPreview', this.props.styles); 16 | 17 | return ( 18 | 19 | 20 | {this.props.og && this.props.og.images ? ( 21 | 31 | ) : null} 32 | 33 | 34 | 35 | {_.truncate(this.props.og.title, { length: 75 })} 36 | 37 | 38 | this.props.onPressDismiss(this.props.og.url)} 40 | > 41 | 45 | 46 | 47 | ); 48 | } 49 | } 50 | 51 | UrlPreview.propTypes = { 52 | og: PropTypes.shape({ 53 | images: PropTypes.arrayOf( 54 | PropTypes.shape({ 55 | image: PropTypes.string, 56 | }), 57 | ), 58 | title: PropTypes.string, 59 | url: PropTypes.string, 60 | }), 61 | styles: PropTypes.object, 62 | /** 63 | * Dismiss handler function. 64 | * @param {*} url 65 | */ 66 | onPressDismiss: PropTypes.func, 67 | }; 68 | -------------------------------------------------------------------------------- /src/components/UserBar.js: -------------------------------------------------------------------------------- 1 | // 2 | import React from 'react'; 3 | import { View, Text, Image, TouchableOpacity } from 'react-native'; 4 | import PropTypes from 'prop-types'; 5 | 6 | import { humanizeTimestamp } from '../utils'; 7 | 8 | import Avatar from './Avatar'; 9 | import FollowButton from './FollowButton'; 10 | 11 | import { buildStylesheet } from '../styles'; 12 | import { withTranslationContext } from '../Context'; 13 | 14 | /** 15 | * A compact horizontal user information box (it is used as activities' header) 16 | * @example ./examples/UserBar.md 17 | */ 18 | const UserBar = ({ 19 | username, 20 | subtitle, 21 | avatar, 22 | follow, 23 | onPressAvatar, 24 | icon, 25 | tDateTimeParser, 26 | ...props 27 | }) => { 28 | username = username || 'Unknown'; 29 | let time = props.time; 30 | if (time === undefined && props.timestamp != null) { 31 | time = humanizeTimestamp(props.timestamp, tDateTimeParser); 32 | } 33 | 34 | const styles = buildStylesheet('userBar', props.styles); 35 | 36 | return ( 37 | 38 | {avatar ? ( 39 | 40 | 48 | 49 | ) : null} 50 | 51 | 52 | {username} 53 | 54 | {icon !== undefined ? ( 55 | 59 | ) : null} 60 | {subtitle && {subtitle}} 61 | 62 | 63 | {time && ( 64 | 65 | {time} 66 | 67 | )} 68 | {follow && ( 69 | 70 | 71 | 72 | )} 73 | 74 | ); 75 | }; 76 | 77 | UserBar.propTypes = { 78 | username: PropTypes.string, 79 | avatar: PropTypes.string, 80 | subtitle: PropTypes.string, 81 | time: PropTypes.string, // text that should be displayed as the time 82 | timestamp: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // a timestamp that should be humanized 83 | icon: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 84 | onPressAvatar: PropTypes.func, 85 | follow: PropTypes.bool, 86 | styles: PropTypes.object, 87 | }; 88 | 89 | export default withTranslationContext(UserBar); 90 | -------------------------------------------------------------------------------- /src/components/UserCard.js: -------------------------------------------------------------------------------- 1 | // 2 | import React from 'react'; 3 | import { View, Text } from 'react-native'; 4 | import PropTypes from 'prop-types'; 5 | 6 | import Avatar from './Avatar'; 7 | import FollowButton from './FollowButton'; 8 | import { buildStylesheet } from '../styles'; 9 | 10 | export default class UserCard extends React.Component { 11 | static defaultProps = {}; 12 | render() { 13 | const styles = buildStylesheet('userCard', this.props.styles); 14 | const { user } = this.props; 15 | return ( 16 | 17 | 18 | {user.name} 19 | 20 | 21 | 22 | ); 23 | } 24 | } 25 | 26 | UserCard.propTypes = { 27 | user: PropTypes.shape({ 28 | profileImage: PropTypes.string, 29 | name: PropTypes.string, 30 | }), 31 | styles: PropTypes.object, 32 | }; 33 | -------------------------------------------------------------------------------- /src/components/examples/Activity.md: -------------------------------------------------------------------------------- 1 | #### Simple message with mention 2 | 3 | ```js 4 | import Activity from '../Activity'; 5 | 6 | const activity = { 7 | actor: { 8 | data: { 9 | name: 'Terry Walker', 10 | profileImage: 'https://randomuser.me/api/portraits/women/48.jpg', 11 | }, 12 | }, 13 | object: 'Hey @Thierry how are you doing?', 14 | verb: 'post', 15 | time: new Date(), 16 | }; 17 | 18 | ; 19 | ``` 20 | 21 | #### Activity with enriched URL 22 | 23 | ```js 24 | import Activity from '../Activity'; 25 | const activity = { 26 | actor: { 27 | data: { 28 | name: 'Nora Ferguson', 29 | profileImage: 'https://randomuser.me/api/portraits/women/72.jpg', 30 | }, 31 | }, 32 | verb: 'post', 33 | object: 'Oh snap!', 34 | attachments: { 35 | og: { 36 | description: 37 | 'Why choose one when you can wear both? These energizing pairings stand out from the crowd', 38 | title: 39 | "'Queen' rapper rescheduling dates to 2019 after deciding to “reevaluate elements of production on the 'NickiHndrxx Tour'", 40 | url: 41 | 'https://www.rollingstone.com/music/music-news/nicki-minaj-cancels-north-american-tour-with-future-714315/', 42 | images: [ 43 | { 44 | image: 45 | 'https://www.rollingstone.com/wp-content/uploads/2018/08/GettyImages-1020376858.jpg', 46 | }, 47 | ], 48 | }, 49 | }, 50 | time: new Date(), 51 | }; 52 | 53 | ; 54 | ``` 55 | 56 | #### Activity with attached image and hashtag 57 | 58 | ```js 59 | import Activity from '../Activity'; 60 | const activity = { 61 | actor: { 62 | data: { 63 | name: 'Nora Ferguson', 64 | profileImage: 'https://randomuser.me/api/portraits/women/72.jpg', 65 | }, 66 | }, 67 | verb: 'post', 68 | object: 'Just came back from this hike! #Hiking #Madeira', 69 | image: 70 | 'https://handluggageonly.co.uk/wp-content/uploads/2017/08/IMG_0777.jpg', 71 | time: new Date(), 72 | }; 73 | 74 | ; 75 | ``` 76 | 77 | #### Activity with custom header 78 | 79 | ```js 80 | import Activity from '../Activity'; 81 | const View = require('react-native').View; 82 | const RepostIcon = require('./resources/repost.png'); 83 | const HeartIcon = require('./resources/heart.png'); 84 | const HeartIconOutline = require('./resources/heart-outline.png'); 85 | const ReplyIcon = require('./resources/reply.png'); 86 | 87 | const reaction_counts = {}; 88 | 89 | const activity = { 90 | actor: { 91 | data: { 92 | name: 'Nora Ferguson', 93 | profileImage: 'https://randomuser.me/api/portraits/women/72.jpg', 94 | }, 95 | }, 96 | verb: 'post', 97 | object: 'I just missed my train 😤', 98 | time: new Date(), 99 | }; 100 | 101 | ( 105 | 106 | 107 | 114 | 122 | 129 | 130 | 131 | )} 132 | />; 133 | ``` 134 | -------------------------------------------------------------------------------- /src/components/examples/Avatar.md: -------------------------------------------------------------------------------- 1 | ```js 2 | import Avatar from '../Avatar'; 3 | 4 |

5 | 10 | 11 | 17 | 18 | 25 |
; 26 | ``` 27 | -------------------------------------------------------------------------------- /src/components/examples/BackButton.md: -------------------------------------------------------------------------------- 1 | ```js 2 | import BackButton from '../BackButton'; 3 | 4 | ; 5 | ``` 6 | -------------------------------------------------------------------------------- /src/components/examples/Card.md: -------------------------------------------------------------------------------- 1 | ```js 2 | import Card from '../Card'; 3 | 4 | ; 10 | ``` 11 | -------------------------------------------------------------------------------- /src/components/examples/CommentItem.md: -------------------------------------------------------------------------------- 1 | ```js 2 | import CommentItem from '../CommentItem'; 3 | 4 | ; 19 | ``` 20 | -------------------------------------------------------------------------------- /src/components/examples/CommentList.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/components/examples/CommentList.md -------------------------------------------------------------------------------- /src/components/examples/FollowButton.md: -------------------------------------------------------------------------------- 1 | ```js 2 | import FollowButton from '../FollowButton'; 3 | ; 4 | ``` 5 | 6 | ```js 7 | import FollowButton from '../FollowButton'; 8 | ; 9 | ``` 10 | -------------------------------------------------------------------------------- /src/components/examples/IconBadge.md: -------------------------------------------------------------------------------- 1 | ```js 2 | import { Image } from 'react-native'; 3 | const NotificationIcon = require('./resources/notifications.png'); 4 | 5 | import { StreamApp } from '../../Context/StreamApp'; 6 | import IconBadge from '../IconBadge'; 7 | 8 | 14 | ( 17 | 18 | )} 19 | /> 20 | ; 21 | ``` 22 | -------------------------------------------------------------------------------- /src/components/examples/LikeButton.md: -------------------------------------------------------------------------------- 1 | A version of [ReactionToggleIcon](#reactiontoggleicon) that's preconfigured for 2 | likes. If you need more freedom to configure it use 3 | [ReactionToggleIcon](#reactiontoggleicon) directly. Look at the source of this 4 | component for reference. 5 | 6 | ```js 7 | import { StreamApp } from '../../Context/StreamApp'; 8 | import LikeButton from '../LikeButton'; 9 | 10 | 16 | 23 | 24 | 30 | ; 31 | ``` 32 | -------------------------------------------------------------------------------- /src/components/examples/LikeList.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/components/examples/LikeList.md -------------------------------------------------------------------------------- /src/components/examples/NewActivitiesNotification.md: -------------------------------------------------------------------------------- 1 | ```js { "props": { "adds": [1] } } 2 | import NewActivitiesNotification from '../NewActivitiesNotification'; 3 | ; 4 | ``` 5 | 6 | ```js 7 | import NewActivitiesNotification from '../NewActivitiesNotification'; 8 | ; 13 | ``` 14 | -------------------------------------------------------------------------------- /src/components/examples/SectionHeader.md: -------------------------------------------------------------------------------- 1 | ```js 2 | import SectionHeader from '../SectionHeader'; 3 | 4 | Comments; 5 | ``` 6 | -------------------------------------------------------------------------------- /src/components/examples/SinglePost.md: -------------------------------------------------------------------------------- 1 | ```js 2 | import { View } from 'react-native'; 3 | import { StreamApp } from '../../Context/StreamApp'; 4 | import SinglePost from '../SinglePost'; 5 | import Activity from '../Activity'; 6 | import LikeButton from '../LikeButton'; 7 | import CommentList from '../CommentList'; 8 | import LikeList from '../LikeList'; 9 | 10 | // this is just an example, you get this from a feed 11 | const activity = { 12 | id: 'a727d86e-aa95-11e8-9d38-1231d51167b4', 13 | }; 14 | 15 | 21 | ( 24 | <> 25 | 29 | 30 | 31 | } 32 | /> 33 | 34 | 35 | 36 | 37 | 38 | 39 | )} 40 | /> 41 | ; 42 | ``` 43 | -------------------------------------------------------------------------------- /src/components/examples/UrlPreview.md: -------------------------------------------------------------------------------- 1 | ```js 2 | import UrlPreview from '../UrlPreview'; 3 | 4 | {}} 16 | />; 17 | ``` 18 | -------------------------------------------------------------------------------- /src/components/examples/UserBar.md: -------------------------------------------------------------------------------- 1 | ```js 2 | import UserBar from '../UserBar'; 3 | ; 8 | ``` 9 | -------------------------------------------------------------------------------- /src/components/examples/resources/heart-outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/components/examples/resources/heart-outline.png -------------------------------------------------------------------------------- /src/components/examples/resources/heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/components/examples/resources/heart.png -------------------------------------------------------------------------------- /src/components/examples/resources/notifications.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/components/examples/resources/notifications.png -------------------------------------------------------------------------------- /src/components/examples/resources/reply.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/components/examples/resources/reply.png -------------------------------------------------------------------------------- /src/components/examples/resources/repost.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/components/examples/resources/repost.png -------------------------------------------------------------------------------- /src/errors.js: -------------------------------------------------------------------------------- 1 | import { StreamApiError } from 'getstream'; 2 | export const handleError = (error, type, detail) => { 3 | console.warn(error); 4 | alert(getErrorMessage(error, type, detail)); 5 | }; 6 | 7 | export const getErrorMessage = (error, type, detail) => { 8 | console.warn(error); 9 | if (!(error instanceof StreamApiError)) { 10 | return fallbackErrorMessage(error, type, detail); 11 | } 12 | const response = error.response; 13 | 14 | if (!response.statusCode || !response.body || !response.body.detail) { 15 | return fallbackErrorMessage(error, type, detail); 16 | } 17 | const statusCode = response.statusCode; 18 | const text = response.body.detail; 19 | 20 | if (statusCode >= 400 && statusCode < 500) { 21 | return text; 22 | } else if (statusCode >= 500 && statusCode < 600) { 23 | return text; 24 | } 25 | 26 | return fallbackErrorMessage(error, type, detail); 27 | }; 28 | 29 | export const fallbackErrorMessage = (error, type, detail) => { 30 | let text = 'Something went wrong'; 31 | let suffix = ''; 32 | switch (type) { 33 | case 'get-user-info': 34 | text += ' when loading user info'; 35 | break; 36 | case 'get-feed': 37 | text += ' when loading the feed'; 38 | break; 39 | case 'get-feed-next-page': 40 | text += ' when loading the next page of the feed'; 41 | break; 42 | case 'get-notification-counts': 43 | text += ' when loading your unread notification counts'; 44 | break; 45 | case 'upload-image': 46 | text += ' when uploading your image'; 47 | suffix = ' If it is, the image is probably too big'; 48 | break; 49 | case 'add-activity': 50 | text += ' when submitting your post'; 51 | break; 52 | case 'add-reaction': 53 | text += ' when submitting your ' + detail.kind; 54 | break; 55 | case 'delete-reaction': 56 | text += ' when removing your ' + detail.kind; 57 | break; 58 | default: 59 | break; 60 | } 61 | 62 | text += '. Is your internet working?' + suffix; 63 | return text; 64 | }; 65 | -------------------------------------------------------------------------------- /src/i18n/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "1 comment": "1 comment", 3 | "1 like": "1 like", 4 | "1 repost": "1 repost", 5 | "Follow": "Follow", 6 | "Following": "Following", 7 | "Likes": "Likes", 8 | "Load more": "Load more", 9 | "Pick an image from camera roll": "Pick an image from camera roll", 10 | "Start Typing...": "Start Typing...", 11 | "Type your post...": "Type your post...", 12 | "You have 1 new notification": "You have 1 new notification", 13 | "You have {{ notificationCount }} new notifications": "You have {{ notificationCount }} new notifications", 14 | "{{ countComments }} comments": "{{ countComments }} comments", 15 | "{{ countLikes }} likes": "{{ countLikes }} likes", 16 | "{{ countReposts }} reposts": "{{ countReposts }} reposts" 17 | } 18 | -------------------------------------------------------------------------------- /src/i18n/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "1 comment": "1 commentaire", 3 | "1 like": "1 J'aime", 4 | "1 repost": "1 partage", 5 | "Follow": "S'abonner", 6 | "Following": "Déja abonné", 7 | "Likes": "J'aime", 8 | "Load more": "Voir plus", 9 | "Pick an image from camera roll": "Choisissez une image de votre gallerie", 10 | "Start Typing...": "Démarrez la saisie...", 11 | "Type your post...": "Tapez votre message", 12 | "You have 1 new notification": "Vous avez reçu 1 nouvelle notification", 13 | "You have {{ notificationCount }} new notifications": "Vous avez reçu {{ notificationCount }} nouvelles notifications", 14 | "{{ countComments }} comments": "{{ countComments }} commentaires", 15 | "{{ countLikes }} likes": "{{ countLikes }} J'aime", 16 | "{{ countReposts }} reposts": "{{ countReposts }} partages" 17 | } 18 | -------------------------------------------------------------------------------- /src/i18n/hi.json: -------------------------------------------------------------------------------- 1 | { 2 | "1 comment": "1 कमेंट", 3 | "1 like": "1 लाइक", 4 | "1 repost": "1 रिपोस्ट", 5 | "Follow": "फॉलो करे", 6 | "Following": "फॉलो कर रहे हो ", 7 | "Likes": "लाइक्स", 8 | "Load more": "एक्टिविटीज को लोड करें", 9 | "Pick an image from camera roll": "इमेज चुनिए ", 10 | "Start Typing...": "टाइप करना शुरू करें ...", 11 | "Type your post...": "अपनी पोस्ट लिखें ...", 12 | "You have 1 new notification": "आपके पास 1 नई नोटिफिकेशन है", 13 | "You have {{ notificationCount }} new notifications": "आपके पास {{ notificationCount }} नई नोटिफिकेशन्स है", 14 | "{{ countComments }} comments": "{{ countComments }} कमैंट्स", 15 | "{{ countLikes }} likes": "{{ countLikes }} लाइक्स", 16 | "{{ countReposts }} reposts": "{{ countReposts }} रेपोस्टस" 17 | } 18 | -------------------------------------------------------------------------------- /src/i18n/index.js: -------------------------------------------------------------------------------- 1 | import enTranslations from './en.json'; 2 | import nlTranslations from './nl.json'; 3 | import ruTranslations from './ru.json'; 4 | import trTranslations from './tr.json'; 5 | import frTranslations from './fr.json'; 6 | import hiTranslations from './hi.json'; 7 | import itTranslations from './it.json'; 8 | 9 | export { 10 | enTranslations, 11 | nlTranslations, 12 | ruTranslations, 13 | trTranslations, 14 | frTranslations, 15 | hiTranslations, 16 | itTranslations, 17 | }; 18 | -------------------------------------------------------------------------------- /src/i18n/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "1 comment": "1 commento", 3 | "1 like": "1 mi piace", 4 | "1 repost": "1 condivisione", 5 | "Follow": "Segui", 6 | "Following": "Segui giá", 7 | "Likes": "Mi piace", 8 | "Load more": "Carica altro", 9 | "Pick an image from camera roll": "Scegli un'immagine dal rullino", 10 | "Start Typing...": "Inizia a scrivere...", 11 | "Type your post...": "Scrivi il tuo post...", 12 | "You have 1 new notification": "Hai una nuova notifica", 13 | "You have {{ notificationCount }} new notifications": "Hai {{ notificationCount }} nuove notifiche", 14 | "{{ countComments }} comments": "{{ countComments }} commenti", 15 | "{{ countLikes }} likes": "{{ countLikes }} mi piace", 16 | "{{ countReposts }} reposts": "{{ countReposts }} condivisioni" 17 | } 18 | -------------------------------------------------------------------------------- /src/i18n/nl.json: -------------------------------------------------------------------------------- 1 | { 2 | "1 comment": "1 reactie", 3 | "1 like": "1 vind-ik-leuk", 4 | "1 repost": "1 keer gedeeld", 5 | "Follow": "Volgen", 6 | "Following": "Volgend", 7 | "Likes": "Vind-ik-leuks", 8 | "Load more": "Meer laden", 9 | "Pick an image from camera roll": "Kies een foto uit je gallerij", 10 | "Start Typing...": "Begin met typen...", 11 | "Type your post...": "Type je bericht...", 12 | "You have 1 new notification": "Je hebt 1 nieuw melding", 13 | "You have {{ notificationCount }} new notifications": "Je hebt {{ notificationCount }} nieuwe meldingen", 14 | "{{ countComments }} comments": "{{ countComments }} reacties", 15 | "{{ countLikes }} likes": "{{ countLikes }} vind-ik-leuks", 16 | "{{ countReposts }} reposts": "{{ countReposts }} keer gedeeld" 17 | } 18 | -------------------------------------------------------------------------------- /src/i18n/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "1 comment": "1 комментарий", 3 | "1 like": "1 лайк", 4 | "1 repost": "1 репост", 5 | "Follow": "Подписаться", 6 | "Following": "Подписаны", 7 | "Likes": "Лайков", 8 | "Load more": "Загрузить еще", 9 | "Pick an image from camera roll": "Выбрать изображение снятое на камеру", 10 | "Start Typing...": "Начните набирать...", 11 | "Type your post...": "Напишите ваш пост...", 12 | "You have 1 new notification": "У вас одно уведомление", 13 | "You have {{ notificationCount }} new notifications": "У вас {{ notificationCount }} новых уведомлений", 14 | "{{ countComments }} comments": "{{ countComments }} комментариев", 15 | "{{ countLikes }} likes": "{{ countLikes }} лайков", 16 | "{{ countReposts }} reposts": "{{ countReposts }} репостов" 17 | } 18 | -------------------------------------------------------------------------------- /src/i18n/tr.json: -------------------------------------------------------------------------------- 1 | { 2 | "1 comment": "1 yorum", 3 | "1 like": "1 beğeni", 4 | "1 repost": "1 paylaşım", 5 | "Follow": "Takip", 6 | "Following": "Takip ediliyor", 7 | "Likes": "Beğeni", 8 | "Load more": "Daha fazla yükle", 9 | "Pick an image from camera roll": "Galeriden resim seç", 10 | "Start Typing...": "Yazmaya Başla...", 11 | "Type your post...": "İletini yaz...", 12 | "You have 1 new notification": "1 yeni bildirimin var", 13 | "You have {{ notificationCount }} new notifications": "{{ notificationCount }} yeni bildirimin var", 14 | "{{ countComments }} comments": "{{ countComments }} yorum", 15 | "{{ countLikes }} likes": "{{ countLikes }} beğeni", 16 | "{{ countReposts }} reposts": "{{ countReposts }} paylaşım" 17 | } 18 | -------------------------------------------------------------------------------- /src/images/githubhero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/githubhero.png -------------------------------------------------------------------------------- /src/images/icons/backarrow-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/backarrow-blue.png -------------------------------------------------------------------------------- /src/images/icons/backarrow-blue@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/backarrow-blue@2x.png -------------------------------------------------------------------------------- /src/images/icons/backarrow-blue@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/backarrow-blue@3x.png -------------------------------------------------------------------------------- /src/images/icons/backarrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/backarrow.png -------------------------------------------------------------------------------- /src/images/icons/backarrow@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/backarrow@2x.png -------------------------------------------------------------------------------- /src/images/icons/backarrow@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/backarrow@3x.png -------------------------------------------------------------------------------- /src/images/icons/close-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/close-black.png -------------------------------------------------------------------------------- /src/images/icons/close-black@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/close-black@2x.png -------------------------------------------------------------------------------- /src/images/icons/close-black@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/close-black@3x.png -------------------------------------------------------------------------------- /src/images/icons/close-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/close-white.png -------------------------------------------------------------------------------- /src/images/icons/close-white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/close-white@2x.png -------------------------------------------------------------------------------- /src/images/icons/close-white@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/close-white@3x.png -------------------------------------------------------------------------------- /src/images/icons/gallery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/gallery.png -------------------------------------------------------------------------------- /src/images/icons/gallery@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/gallery@2x.png -------------------------------------------------------------------------------- /src/images/icons/gallery@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/gallery@3x.png -------------------------------------------------------------------------------- /src/images/icons/heart-outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/heart-outline.png -------------------------------------------------------------------------------- /src/images/icons/heart-outline@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/heart-outline@2x.png -------------------------------------------------------------------------------- /src/images/icons/heart-outline@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/heart-outline@3x.png -------------------------------------------------------------------------------- /src/images/icons/heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/heart.png -------------------------------------------------------------------------------- /src/images/icons/heart@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/heart@2x.png -------------------------------------------------------------------------------- /src/images/icons/heart@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/heart@3x.png -------------------------------------------------------------------------------- /src/images/icons/pickphoto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/pickphoto.png -------------------------------------------------------------------------------- /src/images/icons/pickphoto@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/pickphoto@2x.png -------------------------------------------------------------------------------- /src/images/icons/pickphoto@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/pickphoto@3x.png -------------------------------------------------------------------------------- /src/images/icons/send-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/send-disabled.png -------------------------------------------------------------------------------- /src/images/icons/send-disabled@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/send-disabled@2x.png -------------------------------------------------------------------------------- /src/images/icons/send-disabled@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/send-disabled@3x.png -------------------------------------------------------------------------------- /src/images/icons/send.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/send.png -------------------------------------------------------------------------------- /src/images/icons/send@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/send@2x.png -------------------------------------------------------------------------------- /src/images/icons/send@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/icons/send@3x.png -------------------------------------------------------------------------------- /src/images/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/placeholder.png -------------------------------------------------------------------------------- /src/images/stream_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/react-native-activity-feed/df3b94d73f55e3c04c42629302146e6190f07fc9/src/images/stream_logo.png -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // 2 | export { 3 | StreamApp, 4 | withTranslationContext, 5 | TranslationContext, 6 | StreamContext, 7 | Feed, 8 | FeedContext, 9 | } from './Context'; 10 | 11 | export { default as FlatFeed } from './components/FlatFeed'; 12 | export { default as NotificationFeed } from './components/NotificationFeed'; 13 | export { default as SinglePost } from './components/SinglePost'; 14 | 15 | export { default as Avatar } from './components/Avatar'; 16 | export { default as FollowButton } from './components/FollowButton'; 17 | export { default as UrlPreview } from './components/UrlPreview'; 18 | export { default as StatusUpdateForm } from './components/StatusUpdateForm'; 19 | export { default as UploadImage } from './components/UploadImage'; 20 | export { default as UserBar } from './components/UserBar'; 21 | export { default as UserCard } from './components/UserCard'; 22 | export { default as ReactionIcon } from './components/ReactionIcon'; 23 | export { default as ReactionToggleIcon } from './components/ReactionToggleIcon'; 24 | export { default as ReactionIconBar } from './components/ReactionIconBar'; 25 | export { default as CommentsContainer } from './components/CommentsContainer'; 26 | export { default as Card } from './components/Card'; 27 | 28 | export { default as ReactionList } from './components/ReactionList'; 29 | export { default as SectionHeader } from './components/SectionHeader'; 30 | 31 | export { default as CommentBox } from './components/CommentBox'; 32 | export { default as CommentItem } from './components/CommentItem'; 33 | export { default as CommentList } from './components/CommentList'; 34 | 35 | export { default as LikeList } from './components/LikeList'; 36 | 37 | export { default as BackButton } from './components/BackButton'; 38 | export { default as Activity } from './components/Activity'; 39 | export { default as LikeButton } from './components/LikeButton'; 40 | export { default as NewActivitiesNotification } from './components/NewActivitiesNotification'; 41 | export { default as IconBadge } from './components/IconBadge'; 42 | 43 | export { updateStyle, getStyle, buildStylesheet } from './styles'; 44 | export { humanizeTimestamp } from './utils'; 45 | export { 46 | registerNativeHandlers, 47 | setAndroidTranslucentStatusBar, 48 | } from './native'; 49 | -------------------------------------------------------------------------------- /src/native.js: -------------------------------------------------------------------------------- 1 | export let pickImage = () => { 2 | throw Error( 3 | 'Native handler was not registered, you should import expo-activity-feed or react-native-activity-feed', 4 | ); 5 | }; 6 | 7 | export const registerNativeHandlers = (handlers) => { 8 | if (handlers.pickImage) { 9 | pickImage = handlers.pickImage; 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /src/styleguideComponents/PathlineRenderer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import copy from 'clipboard-copy'; 3 | import { MdContentCopy } from 'react-icons/md'; 4 | import Link from 'react-styleguidist/lib/client/rsg-components/Link'; 5 | import ToolbarButton from 'react-styleguidist/lib/client/rsg-components/ToolbarButton'; 6 | import Styled from 'react-styleguidist/lib/client/rsg-components/Styled'; 7 | 8 | export const styles = ({ color, fontFamily, fontSize, space }) => ({ 9 | copyButton: { 10 | marginLeft: space[0], 11 | }, 12 | pathline: { 13 | color: color.light, 14 | fontFamily: fontFamily.monospace, 15 | fontSize: fontSize.small, 16 | wordBreak: 'break-all', 17 | }, 18 | }); 19 | 20 | export const PathlineRenderer = ({ children, classes }) => ( 21 |
22 | 30 | {children} 31 | 32 | children && copy(children.toString())} 35 | small 36 | title='Copy to clipboard' 37 | > 38 | 39 | 40 |
41 | ); 42 | 43 | export default Styled(styles)(PathlineRenderer); 44 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | // 2 | import * as React from 'react'; 3 | 4 | import Dayjs from 'dayjs'; 5 | 6 | export function humanizeTimestamp(timestamp, tDateTimeParser) { 7 | let time; 8 | // Following calculation is based on assumption that tDateTimeParser() 9 | // either returns momentjs or dayjs object. 10 | 11 | // When timestamp doesn't have z at the end, we are supposed to take it as UTC time. 12 | // Ideally we need to adhere to RFC3339. Unfortunately this needs to be fixed on backend. 13 | if ( 14 | typeof timestamp === 'string' && 15 | timestamp[timestamp.length - 1].toLowerCase() === 'z' 16 | ) { 17 | time = tDateTimeParser(timestamp); 18 | } else { 19 | time = tDateTimeParser(timestamp).add( 20 | Dayjs(timestamp).utcOffset(), 21 | 'minute', 22 | ); // parse time as UTC 23 | } 24 | 25 | const now = tDateTimeParser(); 26 | return time.from(now); 27 | } 28 | 29 | // https://reactnative.dev/docs/linking 30 | export const sanitizeUrlForLinking = (url) => { 31 | if (!/^https?:\/\//.test(url)) { 32 | url = `https://${url}`; 33 | } 34 | 35 | return url.replace(/(www\.)/, ''); 36 | }; 37 | 38 | export const smartRender = (ElementOrComponentOrLiteral, props, fallback) => { 39 | if (ElementOrComponentOrLiteral === undefined) { 40 | ElementOrComponentOrLiteral = fallback; 41 | } 42 | if (React.isValidElement(ElementOrComponentOrLiteral)) { 43 | // Flow cast through any, to make flow believe it's a React.Element 44 | const element = ElementOrComponentOrLiteral; 45 | return element; 46 | } 47 | 48 | // Flow cast through any to remove React.Element after previous check 49 | const ComponentOrLiteral = ElementOrComponentOrLiteral; 50 | if ( 51 | typeof ComponentOrLiteral === 'string' || 52 | typeof ComponentOrLiteral === 'number' || 53 | typeof ComponentOrLiteral === 'boolean' || 54 | ComponentOrLiteral == null 55 | ) { 56 | return ComponentOrLiteral; 57 | } 58 | return ; 59 | }; 60 | 61 | export function sleep(ms) { 62 | return new Promise((resolve) => setTimeout(resolve, ms)); 63 | } 64 | 65 | // https://stackoverflow.com/a/6860916/2570866 66 | export function generateRandomId() { 67 | // prettier-ignore 68 | return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4()); 69 | } 70 | 71 | function S4() { 72 | return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); 73 | } 74 | -------------------------------------------------------------------------------- /styleguide.config.js: -------------------------------------------------------------------------------- 1 | /* globals __dirname */ 2 | /* eslint-env commonjs*/ 3 | 4 | const path = require('path'); 5 | const webpack = require('webpack'); 6 | const notBabeledDeps = [ 7 | 'react-native-safe-area-view', 8 | 'react-native-sticky-keyboard-accessory', 9 | 'react-native-keyboard-spacer', 10 | ]; 11 | 12 | module.exports = { 13 | title: 'React native activity feeds - Docs', 14 | require: ['babel-polyfill'], 15 | styleguideDir: 'docs', 16 | serverPort: 6068, 17 | sortProps: (props) => props, 18 | resolver(ast, recast) { 19 | return require('react-docgen').resolver.findAllExportedComponentDefinitions( 20 | ast, 21 | recast, 22 | ); 23 | }, 24 | 25 | styleguideComponents: { 26 | PathlineRenderer: path.join( 27 | __dirname, 28 | 'src/styleguideComponents/PathlineRenderer.js', 29 | ), 30 | }, 31 | 32 | sections: [ 33 | { 34 | name: 'Introduction', 35 | content: 'docs/setup.md', 36 | }, 37 | { 38 | name: 'Top Level Components', 39 | content: 'docs/top-level-components.md', 40 | components: [ 41 | 'src/Context/StreamApp.js', 42 | 'src/components/FlatFeed.js', 43 | 'src/components/NotificationFeed.js', 44 | 'src/components/SinglePost.js', 45 | ], 46 | exampleMode: 'collapse', 47 | usageMode: 'expand', 48 | }, 49 | { 50 | name: 'UI Components', 51 | content: 'docs/other-components.md', 52 | components: 'src/components/[A-Z]*.js', 53 | ignore: [ 54 | '**/FlatFeed.js', 55 | '**/NotificationFeed.js', 56 | '**/SinglePost.js', 57 | '**/CommentsContainer.js', 58 | ], 59 | exampleMode: 'collapse', 60 | usageMode: 'expand', 61 | }, 62 | { 63 | name: 'Cookbook', 64 | content: 'docs/cookbook.md', 65 | }, 66 | { 67 | name: 'Internationalisation (i18n)', 68 | content: 'docs/Streami18n.md', 69 | }, 70 | { 71 | name: 'Styles', 72 | content: 'docs/styles.md', 73 | }, 74 | ], 75 | template: { 76 | favicon: 'https://getstream.imgix.net/images/favicons/favicon-96x96.png', 77 | }, 78 | webpackConfig: { 79 | devtool: 'source-map', 80 | resolve: { 81 | // auto resolves any react-native import as react-native-web 82 | alias: { 83 | 'react-native': 'react-native-web', 84 | 'react-native-gesture-handler': 'react-native-web', 85 | }, 86 | extensions: ['.web.js', '.js', '.ts', '.tsx'], 87 | }, 88 | devServer: { 89 | clientLogLevel: 'warn', 90 | }, 91 | module: { 92 | rules: [ 93 | { 94 | test: /\.(js|jsx|ts|tsx)$/, 95 | loader: 'babel-loader', 96 | include: [ 97 | path.join(__dirname, 'src'), 98 | ...notBabeledDeps.map((dep) => 99 | path.join(__dirname, 'node_modules', dep), 100 | ), 101 | ], 102 | options: { 103 | comments: true, 104 | plugins: ['module-resolver', 'react-native-web'], 105 | presets: ['module:metro-react-native-babel-preset'], 106 | babelrc: false, 107 | }, 108 | }, 109 | { 110 | test: /\.(jpe?g|png|gif|ttf)$/i, 111 | use: [ 112 | { 113 | loader: 'file-loader', 114 | options: { 115 | hash: 'sha512', 116 | digest: 'hex', 117 | name: '[hash].[ext]', 118 | outputPath: 'build/images', 119 | }, 120 | }, 121 | ], 122 | }, 123 | ], 124 | }, 125 | // Most react native projects will need some extra plugin configuration. 126 | plugins: [ 127 | // Add __DEV__ flag to browser example. 128 | new webpack.DefinePlugin({ 129 | // eslint-disable-next-line no-undef 130 | __DEV__: process.env, 131 | }), 132 | ], 133 | }, 134 | }; 135 | --------------------------------------------------------------------------------