├── .watchmanconfig
├── .gitattributes
├── app.json
├── android
├── .settings
│ └── org.eclipse.buildship.core.prefs
├── app
│ ├── .settings
│ │ └── org.eclipse.buildship.core.prefs
│ ├── debug.keystore
│ ├── src
│ │ ├── main
│ │ │ ├── assets
│ │ │ │ └── fonts
│ │ │ │ │ ├── Entypo.ttf
│ │ │ │ │ ├── Feather.ttf
│ │ │ │ │ ├── Zocial.ttf
│ │ │ │ │ ├── AntDesign.ttf
│ │ │ │ │ ├── EvilIcons.ttf
│ │ │ │ │ ├── Fontisto.ttf
│ │ │ │ │ ├── Foundation.ttf
│ │ │ │ │ ├── Ionicons.ttf
│ │ │ │ │ ├── Octicons.ttf
│ │ │ │ │ ├── FontAwesome.ttf
│ │ │ │ │ ├── MaterialIcons.ttf
│ │ │ │ │ ├── SimpleLineIcons.ttf
│ │ │ │ │ ├── SFProDisplay-Bold.ttf
│ │ │ │ │ ├── FontAwesome5_Brands.ttf
│ │ │ │ │ ├── FontAwesome5_Regular.ttf
│ │ │ │ │ ├── FontAwesome5_Solid.ttf
│ │ │ │ │ ├── SFProDisplay-Light.ttf
│ │ │ │ │ ├── SFProDisplay-Regular.ttf
│ │ │ │ │ └── MaterialCommunityIcons.ttf
│ │ │ ├── res
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ ├── splash.png
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ ├── ic_launcher_round.png
│ │ │ │ │ └── ic_notification.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ ├── splash.png
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ ├── ic_launcher_round.png
│ │ │ │ │ └── ic_notification.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ ├── splash.png
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ ├── ic_notification.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ ├── splash.png
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ ├── ic_notification.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ ├── splash.png
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ ├── ic_notification.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── values
│ │ │ │ │ ├── colors.xml
│ │ │ │ │ ├── strings.xml
│ │ │ │ │ └── styles.xml
│ │ │ │ └── layout
│ │ │ │ │ └── launch_screen.xml
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ │ └── proximity
│ │ │ │ │ └── app
│ │ │ │ │ ├── MainActivity.java
│ │ │ │ │ └── MainApplication.java
│ │ │ └── AndroidManifest.xml
│ │ └── debug
│ │ │ └── AndroidManifest.xml
│ ├── .classpath
│ ├── proguard-rules.pro
│ ├── .project
│ ├── build_defs.bzl
│ ├── google-services.json
│ └── _BUCK
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── settings.gradle
├── .project
├── gradle.properties
├── build.gradle
└── gradlew.bat
├── react-native.config.js
├── assets
├── images
│ └── proximity-logo.png
└── fonts
│ ├── SFProDisplay-Bold.ttf
│ ├── SFProDisplay-Light.ttf
│ └── SFProDisplay-Regular.ttf
├── ios
├── Proximity
│ ├── Images.xcassets
│ │ ├── Contents.json
│ │ ├── Splash.imageset
│ │ │ ├── splash-1.png
│ │ │ ├── splash-2.png
│ │ │ ├── splash.png
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ ├── icon_20@2x.png
│ │ │ ├── icon_20@3x.png
│ │ │ ├── icon_29@2x.png
│ │ │ ├── icon_29@3x.png
│ │ │ ├── icon_40@2x.png
│ │ │ ├── icon_40@3x.png
│ │ │ ├── icon_60@2x.png
│ │ │ ├── icon_60@3x.png
│ │ │ ├── icon_1024@1x.png
│ │ │ └── Contents.json
│ ├── Proximity.entitlements
│ ├── AppDelegate.h
│ ├── main.m
│ ├── Base.lproj
│ │ └── LaunchScreen.xib
│ ├── AppDelegate.m
│ └── Info.plist
├── Proximity.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── Config.xcconfig
├── ProximityTests
│ ├── Info.plist
│ └── ProximityTests.m
├── Proximity-tvOSTests
│ └── Info.plist
├── GoogleService-Info.plist
├── Proximity-tvOS
│ └── Info.plist
└── Podfile
├── jest.config.js
├── .buckconfig
├── .prettierrc.js
├── declarations.d.ts
├── index.js
├── __tests__
└── App-test.js
├── app
├── theme
│ ├── index.ts
│ └── Typography.ts
├── screens
│ ├── ConversationScreen
│ │ └── components
│ │ │ ├── CustomScrollToBottom.tsx
│ │ │ ├── ChatHeaderAvatar.tsx
│ │ │ ├── CustomSend.tsx
│ │ │ ├── CustomMessageText.tsx
│ │ │ ├── CustomComposer.tsx
│ │ │ ├── CustomInputToolbar.tsx
│ │ │ └── CustomBubble.tsx
│ ├── ExploreScreen
│ │ ├── hooks
│ │ │ ├── useDebounce.ts
│ │ │ └── useExploreFeed.ts
│ │ └── components
│ │ │ ├── UserSearchResults.tsx
│ │ │ ├── ExploreGrid.tsx
│ │ │ └── ExplorePostCard.tsx
│ ├── index.ts
│ ├── ProfileViewScreen
│ │ └── components
│ │ │ └── ProfileOptionsBottomSheet.tsx
│ ├── PostViewScreen
│ │ └── components
│ │ │ ├── Comments.tsx
│ │ │ ├── LikeBounceAnimation.tsx
│ │ │ ├── PostOptionsBottomSheet.tsx
│ │ │ ├── EditPostBottomSheet.tsx
│ │ │ ├── LikesBottomSheet.tsx
│ │ │ ├── CommentCard.tsx
│ │ │ └── CommentInput.tsx
│ ├── NotificationScreen
│ │ └── index.tsx
│ ├── UploadScreen
│ │ └── components
│ │ │ └── UploadBanner.tsx
│ ├── MessageScreen
│ │ └── components
│ │ │ └── NewMessageBottomSheet.tsx
│ └── ProfileScreen
│ │ └── components
│ │ └── SettingsBottomSheet.tsx
├── layout
│ ├── misc
│ │ ├── LoadingIndicator.tsx
│ │ ├── NativeImage.tsx
│ │ ├── SvgBanner.tsx
│ │ ├── ListEmptyComponent.tsx
│ │ └── BounceView.tsx
│ ├── placeholders
│ │ ├── PlaceholderAnimation.tsx
│ │ ├── ConversationScreen.Placeholder.tsx
│ │ ├── PostCard.Placeholder.tsx
│ │ ├── MessageScreen.Placeholder.tsx
│ │ ├── NotificationScreen.Placeholder.tsx
│ │ ├── Connections.Placeholder.tsx
│ │ ├── SearchUsers.Placeholder.tsx
│ │ ├── PostViewScreen.Placeholder.tsx
│ │ └── ExploreScreen.Placeholder.tsx
│ ├── headers
│ │ ├── Header.tsx
│ │ ├── BottomSheetHeader.tsx
│ │ ├── SearchBar.tsx
│ │ ├── HomeHeader.tsx
│ │ ├── GoBackHeader.tsx
│ │ └── AnimatedSearchBar.tsx
│ ├── shared
│ │ ├── PostThumbnail.tsx
│ │ ├── DeleteCardRightActions.tsx
│ │ ├── Option.tsx
│ │ ├── UserCard.tsx
│ │ ├── ConfirmationModal.tsx
│ │ └── ConnectionsBottomSheet.tsx
│ ├── controls
│ │ ├── IconButton.tsx
│ │ ├── Button.tsx
│ │ └── FormInput.tsx
│ └── index.ts
├── types
│ ├── auth.ts
│ ├── theme.ts
│ ├── screens.ts
│ └── constants.ts
├── navigation
│ ├── Routes.ts
│ ├── TabBarComponent
│ │ ├── TabIcon.tsx
│ │ └── index.tsx
│ ├── Transition.ts
│ └── index.tsx
├── utils
│ ├── authentication.ts
│ ├── storage.ts
│ ├── notifications.ts
│ └── firebase.ts
├── config
│ └── index.ts
├── graphql
│ ├── subscription
│ │ └── index.ts
│ └── client
│ │ └── index.ts
├── context
│ └── index.tsx
└── constants
│ └── index.ts
├── appcenter-pre-build.sh
├── metro.config.js
├── .eslintrc.js
├── tsconfig.json
├── babel.config.js
├── .gitignore
├── .flowconfig
├── App.tsx
└── package.json
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.pbxproj -text
2 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Proximity",
3 | "displayName": "Proximity"
4 | }
--------------------------------------------------------------------------------
/android/.settings/org.eclipse.buildship.core.prefs:
--------------------------------------------------------------------------------
1 | connection.project.dir=
2 | eclipse.preferences.version=1
3 |
--------------------------------------------------------------------------------
/android/app/.settings/org.eclipse.buildship.core.prefs:
--------------------------------------------------------------------------------
1 | connection.project.dir=..
2 | eclipse.preferences.version=1
3 |
--------------------------------------------------------------------------------
/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/debug.keystore
--------------------------------------------------------------------------------
/react-native.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | assets: ['react-native-vector-icons', './assets/fonts']
3 | };
4 |
--------------------------------------------------------------------------------
/assets/images/proximity-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/assets/images/proximity-logo.png
--------------------------------------------------------------------------------
/ios/Proximity/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/assets/fonts/SFProDisplay-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/assets/fonts/SFProDisplay-Bold.ttf
--------------------------------------------------------------------------------
/assets/fonts/SFProDisplay-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/assets/fonts/SFProDisplay-Light.ttf
--------------------------------------------------------------------------------
/assets/fonts/SFProDisplay-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/assets/fonts/SFProDisplay-Regular.ttf
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'react-native',
3 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
4 | };
5 |
--------------------------------------------------------------------------------
/.buckconfig:
--------------------------------------------------------------------------------
1 |
2 | [android]
3 | target = Google Inc.:Google APIs:23
4 |
5 | [maven_repositories]
6 | central = https://repo1.maven.org/maven2
7 |
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Entypo.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/assets/fonts/Entypo.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Feather.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/assets/fonts/Feather.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Zocial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/assets/fonts/Zocial.ttf
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | bracketSpacing: false,
3 | jsxBracketSameLine: true,
4 | singleQuote: true,
5 | trailingComma: 'all',
6 | };
7 |
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/AntDesign.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/assets/fonts/AntDesign.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/EvilIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/assets/fonts/EvilIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Fontisto.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/assets/fonts/Fontisto.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Foundation.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/assets/fonts/Foundation.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Ionicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/assets/fonts/Ionicons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Octicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/assets/fonts/Octicons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-hdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-mdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-xhdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/FontAwesome.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/assets/fonts/FontAwesome.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-xxhdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-xxxhdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/MaterialIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/assets/fonts/MaterialIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/SimpleLineIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/assets/fonts/SimpleLineIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/SFProDisplay-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/assets/fonts/SFProDisplay-Bold.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/SFProDisplay-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/assets/fonts/SFProDisplay-Light.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/SFProDisplay-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/assets/fonts/SFProDisplay-Regular.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-hdpi/ic_notification.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-mdpi/ic_notification.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_notification.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_notification.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 | #846BE2
3 | #FFFFFF
4 |
5 |
--------------------------------------------------------------------------------
/ios/Proximity/Images.xcassets/Splash.imageset/splash-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/ios/Proximity/Images.xcassets/Splash.imageset/splash-1.png
--------------------------------------------------------------------------------
/ios/Proximity/Images.xcassets/Splash.imageset/splash-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/ios/Proximity/Images.xcassets/Splash.imageset/splash-2.png
--------------------------------------------------------------------------------
/ios/Proximity/Images.xcassets/Splash.imageset/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/ios/Proximity/Images.xcassets/Splash.imageset/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_notification.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/ios/Proximity/Images.xcassets/AppIcon.appiconset/icon_20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/ios/Proximity/Images.xcassets/AppIcon.appiconset/icon_20@2x.png
--------------------------------------------------------------------------------
/ios/Proximity/Images.xcassets/AppIcon.appiconset/icon_20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/ios/Proximity/Images.xcassets/AppIcon.appiconset/icon_20@3x.png
--------------------------------------------------------------------------------
/ios/Proximity/Images.xcassets/AppIcon.appiconset/icon_29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/ios/Proximity/Images.xcassets/AppIcon.appiconset/icon_29@2x.png
--------------------------------------------------------------------------------
/ios/Proximity/Images.xcassets/AppIcon.appiconset/icon_29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/ios/Proximity/Images.xcassets/AppIcon.appiconset/icon_29@3x.png
--------------------------------------------------------------------------------
/ios/Proximity/Images.xcassets/AppIcon.appiconset/icon_40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/ios/Proximity/Images.xcassets/AppIcon.appiconset/icon_40@2x.png
--------------------------------------------------------------------------------
/ios/Proximity/Images.xcassets/AppIcon.appiconset/icon_40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/ios/Proximity/Images.xcassets/AppIcon.appiconset/icon_40@3x.png
--------------------------------------------------------------------------------
/ios/Proximity/Images.xcassets/AppIcon.appiconset/icon_60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/ios/Proximity/Images.xcassets/AppIcon.appiconset/icon_60@2x.png
--------------------------------------------------------------------------------
/ios/Proximity/Images.xcassets/AppIcon.appiconset/icon_60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/ios/Proximity/Images.xcassets/AppIcon.appiconset/icon_60@3x.png
--------------------------------------------------------------------------------
/ios/Proximity/Images.xcassets/AppIcon.appiconset/icon_1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/karanpratapsingh/Proximity/HEAD/ios/Proximity/Images.xcassets/AppIcon.appiconset/icon_1024@1x.png
--------------------------------------------------------------------------------
/declarations.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.png';
2 | declare module '*.svg' {
3 | import { SvgProps } from 'react-native-svg';
4 | const content: React.StatelessComponent;
5 | export default content;
6 | }
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'Proximity'
2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
3 | include ':app'
4 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import 'react-native-gesture-handler';
2 | import { AppRegistry } from 'react-native';
3 | import App from './App';
4 | import { name as appName } from './app.json';
5 |
6 | AppRegistry.registerComponent(appName, () => App);
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Proximity
3 |
4 | DeploymentKey
5 |
6 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.5-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/__tests__/App-test.js:
--------------------------------------------------------------------------------
1 | import 'react-native';
2 | import React from 'react';
3 | import App from '../App';
4 |
5 | // Note: test renderer must be required after react-native.
6 | import renderer from 'react-test-renderer';
7 |
8 | it('renders correctly', () => {
9 | renderer.create();
10 | });
11 |
--------------------------------------------------------------------------------
/ios/Proximity.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Proximity.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/android/app/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/theme/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Theme,
3 | ThemeStatic,
4 | ThemeVariant,
5 | MaterialColors,
6 | HandleAvailableColor,
7 | OnlineDotColor
8 | } from './Colors';
9 | import Typography from './Typography';
10 |
11 | export {
12 | Theme,
13 | ThemeStatic,
14 | ThemeVariant,
15 | MaterialColors,
16 | HandleAvailableColor,
17 | OnlineDotColor,
18 | Typography
19 | };
20 |
--------------------------------------------------------------------------------
/ios/Config.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // Config.xcconfig
3 | // Proximity
4 | //
5 | // Created by Vertigo 101 on 30/11/19.
6 | // Copyright © 2019 Facebook. All rights reserved.
7 | //
8 |
9 | // Configuration settings file format documentation can be found at:
10 | // https://help.apple.com/xcode/#/dev745c5c974
11 |
12 | GOOGLE_REVERSED_CLIENT_ID=com.googleusercontent.apps.649469906394-cunn0sogttuea6fhvmekebm85h70rqeq
13 |
--------------------------------------------------------------------------------
/ios/Proximity/Proximity.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | aps-environment
6 | development
7 | com.apple.developer.applesignin
8 |
9 | Default
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/screens/ConversationScreen/components/CustomScrollToBottom.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Entypo from 'react-native-vector-icons/Entypo';
3 | import { IconSizes } from '@app/constants';
4 | import { ThemeStatic } from '@app/theme';
5 |
6 | const CustomScrollToBottom = () => ;
11 |
12 | export default CustomScrollToBottom;
--------------------------------------------------------------------------------
/app/layout/misc/LoadingIndicator.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { DotIndicator } from 'react-native-indicators';
3 |
4 | interface LoadingIndicatorProps {
5 | size: number,
6 | color?: string,
7 | count?: number
8 | };
9 |
10 | const LoadingIndicator: React.FC = ({ size, color, count }) =>
11 | ;
12 |
13 | export default LoadingIndicator;
--------------------------------------------------------------------------------
/app/types/auth.ts:
--------------------------------------------------------------------------------
1 | export type GoogleAuthResult = {
2 | idToken: string | null;
3 | accessToken: string | undefined;
4 | };
5 |
6 | export type AppleAuthResult = {
7 | identityToken: string | null;
8 | nonce: string;
9 | };
10 |
11 | export enum SocialSignInType {
12 | GOOGLE,
13 | APPLE,
14 | }
15 |
16 | export type SocialSignInResult = {
17 | token: string;
18 | avatar: string | null;
19 | name: string;
20 | email: string;
21 | };
22 |
--------------------------------------------------------------------------------
/ios/Proximity/AppDelegate.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | #import
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (nonatomic, strong) UIWindow *window;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/ios/Proximity/main.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | #import
9 |
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/screens/ExploreScreen/hooks/useDebounce.ts:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 |
3 | export default function useDebounce(value: string, delay: number) {
4 | const [debouncedValue, setDebouncedValue] = useState(value);
5 |
6 | useEffect(() => {
7 | const handler = setTimeout(() => {
8 | setDebouncedValue(value);
9 | }, delay);
10 |
11 | return () => {
12 | clearTimeout(handler);
13 | };
14 | }, [value]);
15 |
16 | return debouncedValue;
17 | }
18 |
--------------------------------------------------------------------------------
/android/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | Proximity
4 | Project android created by Buildship.
5 |
6 |
7 |
8 |
9 | org.eclipse.buildship.core.gradleprojectbuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.buildship.core.gradleprojectnature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/layout/misc/NativeImage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View } from 'react-native';
3 | import FastImage from 'react-native-fast-image';
4 |
5 | interface NativeImageProps {
6 | uri: string,
7 | style: any
8 | };
9 |
10 | const NativeImage: React.FC = ({ uri, style }) => {
11 |
12 | if (!uri || !uri.includes('http')) return ;
13 | return
14 | };
15 |
16 | export default NativeImage;
--------------------------------------------------------------------------------
/ios/Proximity/Images.xcassets/Splash.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "splash.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "splash-1.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "splash-2.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/appcenter-pre-build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | node node_modules/jetifier/bin/jetify
4 |
5 | if [[ ! -z "$GOOGLE_SERVICES_JSON" ]]; then
6 | echo "$GOOGLE_SERVICES_JSON" | base64 --decode > android/app/google-services.json
7 | fi
8 |
9 | if [[ ! -z "$GOOGLE_SERVICES_PLIST" ]]; then
10 | echo "$GOOGLE_SERVICES_PLIST" | base64 --decode > ios/GoogleService-Info.plist
11 | echo "$XCCONFIG" | base64 --decode > ios/Config.xcconfig
12 | fi
13 |
14 | mkdir app/config
15 | echo "$APP_CONFIG" | base64 --decode > app/config/index.ts
16 |
--------------------------------------------------------------------------------
/metro.config.js:
--------------------------------------------------------------------------------
1 | const { getDefaultConfig } = require('metro-config');
2 |
3 | module.exports = (async () => {
4 | const {
5 | resolver: { sourceExts, assetExts }
6 | } = await getDefaultConfig();
7 | return {
8 | transformer: {
9 | experimentalImportSupport: false,
10 | inlineRequires: false,
11 | babelTransformerPath: require.resolve('react-native-svg-transformer'),
12 | },
13 | resolver: {
14 | assetExts: assetExts.filter(ext => ext !== 'svg'),
15 | sourceExts: [...sourceExts, 'svg']
16 | }
17 | };
18 | })();
19 |
--------------------------------------------------------------------------------
/app/navigation/Routes.ts:
--------------------------------------------------------------------------------
1 | import {
2 | HomeScreen,
3 | ExploreScreen,
4 | UploadScreen,
5 | NotificationScreen,
6 | ProfileScreen,
7 | ProfileViewScreen,
8 | MessageScreen,
9 | ConversationScreen,
10 | PostViewScreen,
11 | LoginScreen
12 | } from '@app/screens';
13 |
14 | export const TabBarRoutes = {
15 | HomeScreen,
16 | ExploreScreen,
17 | UploadScreen,
18 | NotificationScreen,
19 | ProfileScreen
20 | };
21 |
22 | export const StackRoutes = {
23 | MessageScreen,
24 | ConversationScreen,
25 | ProfileViewScreen,
26 | PostViewScreen,
27 | LoginScreen
28 | };
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'parser': 'babel-eslint',
3 | 'env': {
4 | 'jest': true,
5 | },
6 | 'parserOptions': {
7 | 'ecmaVersion': 7,
8 | 'sourceType': 'module',
9 | 'ecmaFeatures': {
10 | 'jsx': true,
11 | }
12 | },
13 | 'rules': {
14 | 'no-use-before-define': 'off',
15 | 'react/jsx-filename-extension': 'off',
16 | 'react/prop-types': 'off',
17 | 'comma-dangle': 'off',
18 | 'quotes': ['error', 'single'],
19 | 'semi': ['error', 'always'],
20 | 'indent': ['error', 2]
21 | },
22 | 'globals': {
23 | 'fetch': false
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/android/app/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | app
4 | Project app created by Buildship.
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | org.eclipse.buildship.core.gradleprojectbuilder
15 |
16 |
17 |
18 |
19 |
20 | org.eclipse.jdt.core.javanature
21 | org.eclipse.buildship.core.gradleprojectnature
22 |
23 |
24 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/layout/placeholders/PlaceholderAnimation.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { Shine, ShineOverlay } from 'rn-placeholder';
3 | import { AppContext } from '@app/context';
4 | import { ThemeVariant } from '@app/theme';
5 |
6 | const AnimationBackground = {
7 | light: '#DFDFDF',
8 | dark: '#242424'
9 | };
10 |
11 | const PlaceholderAnimation = props => {
12 | const { themeType } = useContext(AppContext);
13 | const backgroundColor = AnimationBackground[themeType];
14 |
15 | if (themeType === ThemeVariant.light) {
16 | return ;
17 | }
18 |
19 | return ;
20 | };
21 |
22 | export default PlaceholderAnimation;
--------------------------------------------------------------------------------
/app/layout/placeholders/ConversationScreen.Placeholder.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View } from 'react-native';
3 | import { IconSizes } from '@app/constants';
4 | import { ThemeStatic } from '@app/theme';
5 | import LoadingIndicator from '../misc/LoadingIndicator';
6 |
7 | const ConversationScreenPlaceholder = () => (
8 |
9 |
10 |
11 | );
12 |
13 | const styles = StyleSheet.create({
14 | container: {
15 | flex: 1,
16 | paddingHorizontal: 10,
17 | alignItems: 'center',
18 | justifyContent: 'center'
19 | }
20 | });
21 |
22 | export default ConversationScreenPlaceholder;
--------------------------------------------------------------------------------
/android/app/src/main/res/layout/launch_screen.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
18 |
--------------------------------------------------------------------------------
/app/screens/ConversationScreen/components/ChatHeaderAvatar.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, TouchableOpacity } from 'react-native';
3 | import { NativeImage } from '@app/layout';
4 |
5 | const ChatHeaderAvatar = ({ avatar, onPress }) => (
6 |
7 |
8 |
9 | );
10 |
11 | const styles = StyleSheet.create({
12 | container: {
13 | height: 36,
14 | width: 36,
15 | overflow: 'hidden',
16 | marginHorizontal: 10,
17 | borderRadius: 20
18 | },
19 | avatarImage: {
20 | flex: 1
21 | }
22 | });
23 |
24 | export default ChatHeaderAvatar;
--------------------------------------------------------------------------------
/app/utils/authentication.ts:
--------------------------------------------------------------------------------
1 | import { messaging, crashlytics, auth } from './firebase';
2 | import { StorageErrorTypes, removeToken } from './storage';
3 | import { Errors } from '@app/constants';
4 |
5 | export const handleLoginError = async (errorType: string) => {
6 | switch (errorType) {
7 | case StorageErrorTypes.Expired:
8 | await signOut();
9 | break;
10 |
11 | case StorageErrorTypes.NotFound:
12 | break;
13 |
14 | default:
15 | break;
16 | }
17 | };
18 |
19 | export const signOut = async () => {
20 | try {
21 | await messaging.deleteToken();
22 | await removeToken();
23 | await auth.signOut();
24 | } catch ({ message }) {
25 | crashlytics.recordCustomError(Errors.SIGN_OUT, message);
26 | }
27 | };
--------------------------------------------------------------------------------
/app/screens/index.ts:
--------------------------------------------------------------------------------
1 | import HomeScreen from './HomeScreen';
2 | import ExploreScreen from './ExploreScreen';
3 | import UploadScreen from './UploadScreen';
4 | import NotificationScreen from './NotificationScreen';
5 | import ProfileScreen from './ProfileScreen';
6 | import ProfileViewScreen from './ProfileViewScreen';
7 | import MessageScreen from './MessageScreen';
8 | import ConversationScreen from './ConversationScreen';
9 | import PostViewScreen from './PostViewScreen';
10 | import LoginScreen from './LoginScreen';
11 |
12 | export {
13 | HomeScreen,
14 | ExploreScreen,
15 | UploadScreen,
16 | NotificationScreen,
17 | ProfileScreen,
18 | ProfileViewScreen,
19 | MessageScreen,
20 | ConversationScreen,
21 | PostViewScreen,
22 | LoginScreen
23 | };
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowJs": true,
4 | "allowSyntheticDefaultImports": true,
5 | "esModuleInterop": true,
6 | "isolatedModules": true,
7 | "jsx": "react",
8 | "lib": [
9 | "es6"
10 | ],
11 | "moduleResolution": "node",
12 | "noEmit": true,
13 | "strict": true,
14 | "target": "esnext",
15 | "baseUrl": ".",
16 | "noImplicitAny": false,
17 | "paths": {
18 | "*": [
19 | "./app/*",
20 | ],
21 | "@app/*": [
22 | "./app/*"
23 | ],
24 | "@app/assets/*": [
25 | "./assets/*"
26 | ],
27 | },
28 | },
29 | "exclude": [
30 | "node_modules",
31 | "babel.config.js",
32 | "metro.config.js",
33 | "jest.config.js"
34 | ]
35 | }
--------------------------------------------------------------------------------
/app/screens/ConversationScreen/components/CustomSend.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet } from 'react-native';
3 | import { Send } from 'react-native-gifted-chat';
4 | import FontAwesome from 'react-native-vector-icons/FontAwesome';
5 | import { IconSizes } from '@app/constants';
6 | import { ThemeStatic } from '@app/theme';
7 |
8 | const CustomSend: React.FC = sendProps => (
9 |
12 |
17 |
18 | );
19 |
20 | const styles = StyleSheet.create({
21 | container: {
22 | alignItems: 'center',
23 | justifyContent: 'center',
24 | paddingHorizontal: 20
25 | }
26 | });
27 |
28 | export default CustomSend;
--------------------------------------------------------------------------------
/ios/ProximityTests/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 |
--------------------------------------------------------------------------------
/app/screens/ConversationScreen/components/CustomMessageText.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet } from 'react-native';
3 | import { MessageText } from 'react-native-gifted-chat';
4 | import { Typography, ThemeStatic } from '@app/theme';
5 |
6 | const { FontWeights, FontSizes } = Typography;
7 |
8 | const CustomMessageText: React.FC = messageTextProps => (
9 |
16 | );
17 |
18 | const styles = StyleSheet.create({
19 | left: {
20 | ...FontWeights.Light,
21 | ...FontSizes.Body,
22 | color: ThemeStatic.black
23 | },
24 | right: {
25 | ...FontWeights.Light,
26 | ...FontSizes.Body,
27 | color: ThemeStatic.white
28 | }
29 | });
30 |
31 | export default CustomMessageText;
--------------------------------------------------------------------------------
/ios/Proximity-tvOSTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)
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 |
--------------------------------------------------------------------------------
/app/theme/Typography.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Typography:
3 | * This contains all the typography config for the application
4 | * #Note: color and font size are defaulted as they can be overridden
5 | * as required.
6 | */
7 |
8 | const FontWeights = {
9 | Bold: {
10 | fontFamily: 'SFProDisplay-Bold',
11 | color: '#000'
12 | },
13 | Regular: {
14 | fontFamily: 'SFProDisplay-Regular',
15 | color: '#000'
16 | },
17 | Light: {
18 | fontFamily: 'SFProDisplay-Light',
19 | color: '#000'
20 | }
21 | };
22 |
23 | const FontSizes = {
24 | Heading: {
25 | fontSize: 32
26 | },
27 | SubHeading: {
28 | fontSize: 24
29 | },
30 | Label: {
31 | fontSize: 20
32 | },
33 | Body: {
34 | fontSize: 16
35 | },
36 | Caption: {
37 | fontSize: 14
38 | }
39 | };
40 |
41 | const Typography = { FontWeights, FontSizes };
42 |
43 | export default Typography;
44 |
--------------------------------------------------------------------------------
/app/config/index.ts:
--------------------------------------------------------------------------------
1 | import { Platform } from 'react-native';
2 |
3 | const {
4 | author: {
5 | name,
6 | email,
7 | url
8 | },
9 | repository: {
10 | url: repository
11 | },
12 | version
13 | } = require('../../package.json');
14 |
15 | const codepush = {
16 | staging: Platform.select({
17 | ios: '',
18 | android: ''
19 | }),
20 | production: Platform.select({
21 | ios: '',
22 | android: ''
23 | })
24 | };
25 |
26 | const webClientId: string = '649469906394-0d9hghf2jv5n9n0t0o763e5bheer720f.apps.googleusercontent.com';
27 |
28 | const Config = {
29 | author: { name, email, url },
30 | repository,
31 | version,
32 | codepush,
33 | url: {
34 | https: 'https://proximity-development.herokuapp.com/',
35 | wss: 'wss://proximity-development.herokuapp.com/',
36 | },
37 | webClientId
38 | };
39 |
40 | export default Config;
41 |
--------------------------------------------------------------------------------
/app/screens/ConversationScreen/components/CustomComposer.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet, Platform } from 'react-native';
3 | import { Composer } from 'react-native-gifted-chat';
4 | import { AppContext } from '@app/context';
5 | import { Typography } from '@app/theme';
6 | import { ThemeColors } from '@app/types/theme';
7 |
8 | const { FontWeights, FontSizes } = Typography;
9 |
10 | const CustomComposer: React.FC = composerProps => {
11 | const { theme } = useContext(AppContext);
12 | return (
13 |
18 | );
19 | };
20 |
21 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
22 | inputStyle: {
23 | ...FontWeights.Light,
24 | ...FontSizes.Body,
25 | color: theme.text01,
26 | paddingTop: 10,
27 | paddingLeft: 10
28 | }
29 | });
30 |
31 | export default CustomComposer;
--------------------------------------------------------------------------------
/app/screens/ConversationScreen/components/CustomInputToolbar.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet } from 'react-native';
3 | import { InputToolbar } from 'react-native-gifted-chat';
4 | import { ThemeStatic } from '@app/theme';
5 |
6 | const CustomInputToolbar: React.FC = inputToolbarProps => (
7 |
12 | );
13 |
14 | const styles = StyleSheet.create({
15 | container: {
16 | height: 40,
17 | alignItems: 'center',
18 | justifyContent: 'center',
19 | borderTopWidth: 0,
20 | marginBottom: 10,
21 | backgroundColor: 'transparent'
22 | },
23 | primary: {
24 | alignItems: 'center',
25 | justifyContent: 'center',
26 | borderRadius: 40,
27 | marginHorizontal: 10,
28 | borderWidth: StyleSheet.hairlineWidth,
29 | borderColor: ThemeStatic.text02
30 | }
31 | });
32 |
33 | export default CustomInputToolbar;
--------------------------------------------------------------------------------
/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 | android.useAndroidX=true
21 | android.enableJetifier=true
22 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:metro-react-native-babel-preset'],
3 | plugins: [
4 | [
5 | 'module-resolver',
6 | {
7 | root: ['.'],
8 | extensions: [
9 | '.ios.ts',
10 | '.android.ts',
11 | '.ts',
12 | '.ios.tsx',
13 | '.android.tsx',
14 | '.tsx',
15 | '.jsx',
16 | '.js',
17 | '.json',
18 | ],
19 | alias: {
20 | '@app/config': './app/config',
21 | '@app/context': './app/context',
22 | '@app/graphql': './app/graphql',
23 | '@app/constants': './app/constants',
24 | '@app/layout': './app/layout',
25 | '@app/navigation': './app/navigation',
26 | '@app/screens': './app/screens',
27 | '@app/theme': './app/theme',
28 | '@app/types': './app/types',
29 | '@app/utils': './app/utils',
30 | '@app/assets': './assets',
31 | },
32 | },
33 | ],
34 | ],
35 | };
36 |
--------------------------------------------------------------------------------
/app/types/theme.ts:
--------------------------------------------------------------------------------
1 | import { StatusBarStyle } from 'react-native';
2 |
3 | export type ThemeStaticType = {
4 | accent: string,
5 | white: string,
6 | black: string,
7 | text01: string,
8 | text02: string,
9 | placeholder: string,
10 | like: string,
11 | unlike: string,
12 | translucent: string,
13 | delete: string,
14 | badge: string
15 | };
16 |
17 | export type ThemeColors = {
18 | accent: string,
19 | base: string,
20 | text01: string,
21 | text02: string,
22 | placeholder: string,
23 | white: string
24 | };
25 |
26 | export type ThemeVariantType = {
27 | light: string,
28 | dark: string
29 | };
30 |
31 | export type ThemeType = {
32 | type: string,
33 | colors: ThemeColors
34 | };
35 |
36 | export type HandleAvailableColorType = {
37 | true: string,
38 | false: string
39 | };
40 |
41 | export type OnlineDotColorType = {
42 | true: string,
43 | false: string
44 | };
45 |
46 | export type DynamicStatusBarType = {
47 | [key: string]: {
48 | barStyle: StatusBarStyle,
49 | backgroundColor: string
50 | }
51 | };
--------------------------------------------------------------------------------
/app/layout/headers/Header.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet, Text, View } from 'react-native';
3 | import { Typography } from '@app/theme';
4 | import { AppContext } from '@app/context';
5 | import { ThemeColors } from '@app/types/theme';
6 |
7 | const { FontWeights, FontSizes } = Typography;
8 |
9 | interface HeaderProps {
10 | title: string,
11 | IconRight?: React.FC
12 | };
13 |
14 | const Header: React.FC = ({ title, IconRight }) => {
15 |
16 | const { theme } = useContext(AppContext);
17 | return (
18 |
19 | {title}
20 | {IconRight && }
21 |
22 | );
23 | };
24 |
25 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
26 | container: {
27 | flexDirection: 'row',
28 | alignItems: 'center',
29 | justifyContent: 'space-between',
30 | paddingVertical: 10,
31 | paddingHorizontal: 20
32 | },
33 | title: {
34 | ...FontWeights.Bold,
35 | ...FontSizes.Heading,
36 | color: theme.text01
37 | }
38 | });
39 |
40 | export default Header;
--------------------------------------------------------------------------------
/app/layout/headers/BottomSheetHeader.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { View, Text, StyleSheet } from 'react-native';
3 | import { AppContext } from '@app/context';
4 | import { ThemeColors } from '@app/types/theme';
5 | import { Typography } from '@app/theme';
6 |
7 | const { FontWeights, FontSizes } = Typography;
8 |
9 | interface BottomSheetHeaderProps {
10 | heading: string,
11 | subHeading: string
12 | };
13 |
14 | const BottomSheetHeader: React.FC = ({ heading, subHeading }) => {
15 | const { theme } = useContext(AppContext);
16 | return (
17 |
18 | {heading}
19 | {subHeading}
20 |
21 | );
22 | };
23 |
24 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
25 | container: {},
26 | heading: {
27 | ...FontWeights.Bold,
28 | ...FontSizes.Label,
29 | color: theme.text01
30 | },
31 | subHeading: {
32 | ...FontWeights.Regular,
33 | ...FontSizes.Body,
34 | marginTop: 2,
35 | color: theme.text02
36 | }
37 | });
38 |
39 | export default BottomSheetHeader;
--------------------------------------------------------------------------------
/app/screens/ExploreScreen/hooks/useExploreFeed.ts:
--------------------------------------------------------------------------------
1 | import { useQuery } from '@apollo/react-hooks';
2 |
3 | import { QUERY_POSTS } from '@app/graphql/query';
4 | import { Pagination } from '@app/constants';
5 |
6 | export default function useExploreFeed(userId: string) {
7 | const defaultInput = {
8 | userId,
9 | last: Pagination.PAGE_SIZE,
10 | };
11 |
12 | const {
13 | data: postsData,
14 | loading: postsLoading,
15 | error: postsError,
16 | fetchMore,
17 | refetch: refetchPosts,
18 | } = useQuery(QUERY_POSTS, {
19 | variables: defaultInput,
20 | });
21 |
22 | const fetchMorePosts = async () => {
23 | const { posts } = postsData;
24 | const variables = {
25 | ...defaultInput,
26 | last: posts.length + Pagination.CURSOR_SIZE,
27 | };
28 |
29 | const updateQuery = (prev, { fetchMoreResult }) => {
30 | if (!fetchMoreResult) return prev;
31 |
32 | return { posts: [...fetchMoreResult.posts] };
33 | };
34 |
35 | if (fetchMore) {
36 | // @ts-ignore
37 | fetchMore({ variables, updateQuery });
38 | }
39 | };
40 |
41 | return { postsData, postsLoading, postsError, fetchMorePosts, refetchPosts };
42 | }
43 |
--------------------------------------------------------------------------------
/app/layout/placeholders/PostCard.Placeholder.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet, View } from 'react-native';
3 | import { Placeholder, PlaceholderLine } from 'rn-placeholder';
4 | import PlaceholderAnimation from './PlaceholderAnimation';
5 | import { AppContext } from '@app/context';
6 | import { PostDimensions } from '@app/constants';
7 |
8 | const PostCardPlaceholder = () => {
9 | const { theme } = useContext(AppContext);
10 |
11 | return (
12 |
13 |
14 | {new Array(4)
15 | .fill({})
16 | .map((_, index) =>
17 |
24 | )}
25 |
26 |
27 | );
28 | };
29 |
30 | const styles = StyleSheet.create({
31 | container: {
32 | paddingTop: 2,
33 | paddingHorizontal: 20
34 | },
35 | card: {
36 | marginTop: 20,
37 | borderRadius: 10
38 | }
39 | });
40 |
41 | export default PostCardPlaceholder;
--------------------------------------------------------------------------------
/app/utils/storage.ts:
--------------------------------------------------------------------------------
1 | import Storage from 'react-native-storage';
2 | import AsyncStorage from '@react-native-community/async-storage';
3 |
4 | const storage = new Storage({
5 | size: 1000,
6 | storageBackend: AsyncStorage,
7 | defaultExpires: 7 * 1000 * 3600 * 24, // 7 days, eg: 1 day (1000 * 3600 * 24 milliseconds)
8 | enableCache: true
9 | });
10 |
11 | export const StorageErrorTypes = {
12 | Expired: 'ExpiredError',
13 | NotFound: 'NotFoundError'
14 | };
15 |
16 | export const saveToken = (token: string) => {
17 | return storage.save({
18 | key: 'proximity:token',
19 | data: token
20 | });
21 | };
22 |
23 | export const loadToken = () => {
24 | return storage.load({
25 | key: 'proximity:token'
26 | });
27 | };
28 |
29 | export const removeToken = () => {
30 | return storage.remove({
31 | key: 'proximity:token'
32 | });
33 | };
34 |
35 | export const saveThemeType = (themeType: string) => {
36 | return storage.save({
37 | key: 'proximity:theme',
38 | data: themeType,
39 | expires: null // never expires until changed
40 | });
41 | };
42 |
43 | export const loadThemeType = () => {
44 | return storage.load({
45 | key: 'proximity:theme'
46 | });
47 | };
48 |
49 | export default storage;
50 |
--------------------------------------------------------------------------------
/.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 |
61 | # Firebase
62 | # /android/app/google-services.json
63 | # /ios/GoogleService-Info.plist
64 |
65 | # Config
66 | # /ios/Config.xcconfig
67 |
68 | # App
69 | # /app/config/index.ts
--------------------------------------------------------------------------------
/app/screens/ConversationScreen/components/CustomBubble.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet } from 'react-native';
3 | import { Bubble } from 'react-native-gifted-chat';
4 | import { ThemeStatic } from '@app/theme';
5 | import posed, { Transition } from 'react-native-pose';
6 |
7 | const TransitionBubble = posed.View({
8 | enter: { opacity: 1, x: 0 },
9 | exit: { opacity: 0.5, x: ({ offset }) => offset }
10 | });
11 |
12 | const CustomBubble: React.FC = bubbleProps => {
13 | // @ts-ignore
14 | const { user: { _id: authorId }, currentMessage: { user: { _id: currentId } } } = bubbleProps;
15 |
16 | const offset = authorId === currentId ? 20 : -20;
17 |
18 | return (
19 |
20 |
21 |
26 |
27 |
28 | );
29 | };
30 |
31 | const styles = StyleSheet.create({
32 | container: {
33 | flex: 1
34 | },
35 | right: {
36 | marginVertical: 4,
37 | backgroundColor: ThemeStatic.accent
38 | }
39 | });
40 |
41 | export default CustomBubble;
--------------------------------------------------------------------------------
/android/app/src/main/java/com/proximity/app/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.proximity.app;
2 |
3 | import android.os.Bundle;
4 | import com.facebook.react.ReactActivity;
5 | import com.facebook.react.ReactActivityDelegate;
6 | import com.facebook.react.ReactRootView;
7 | import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
8 |
9 | import org.devio.rn.splashscreen.SplashScreen;
10 |
11 | public class MainActivity extends ReactActivity {
12 |
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | SplashScreen.show(this);
16 | super.onCreate(savedInstanceState);
17 | }
18 |
19 | /**
20 | * Returns the name of the main component registered from JavaScript. This is
21 | * used to schedule rendering of the component.
22 | */
23 | @Override
24 | protected String getMainComponentName() {
25 | return "Proximity";
26 | }
27 |
28 | @Override
29 | protected ReactActivityDelegate createReactActivityDelegate() {
30 | return new ReactActivityDelegate(this, getMainComponentName()) {
31 | @Override
32 | protected ReactRootView createRootView() {
33 | return new RNGestureHandlerEnabledRootView(MainActivity.this);
34 | }
35 | };
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/navigation/TabBarComponent/TabIcon.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import AntDesign from 'react-native-vector-icons/AntDesign';
3 | import Entypo from 'react-native-vector-icons/Entypo';
4 | import Feather from 'react-native-vector-icons/Feather';
5 | import { IconSizes } from '@app/constants';
6 | import { AppContext } from '@app/context';
7 |
8 | const TabIcon = ({ route, isActive }) => {
9 | const { theme } = useContext(AppContext);
10 |
11 | switch (route) {
12 | case 'HomeScreen':
13 |
14 | return ;
15 |
16 | case 'ExploreScreen':
17 |
18 | return ;
19 |
20 | case 'UploadScreen':
21 |
22 | return ;
23 |
24 | case 'NotificationScreen':
25 |
26 | return ;
27 |
28 | case 'ProfileScreen':
29 |
30 | return ;
31 |
32 | default:
33 | return null;
34 | };
35 | };
36 |
37 | export default TabIcon;
--------------------------------------------------------------------------------
/app/layout/misc/SvgBanner.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleProp, StyleSheet, Text, TextStyle, View } from 'react-native';
3 | import { responsiveHeight } from 'react-native-responsive-dimensions';
4 | import { AppContext } from '@app/context';
5 | import { Typography } from '@app/theme';
6 | import { ThemeColors } from '@app/types/theme';
7 |
8 | const { FontWeights, FontSizes } = Typography;
9 |
10 | interface SvgBannerProps {
11 | Svg: any,
12 | placeholder: string,
13 | spacing?: number,
14 | textStyle?: StyleProp
15 | };
16 |
17 | const SvgBanner: React.FC = ({ Svg, placeholder, spacing, textStyle }) => {
18 | const { theme } = useContext(AppContext);
19 |
20 | return (
21 |
22 |
23 | {placeholder}
24 |
25 | );
26 | };
27 |
28 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
29 | container: {
30 | alignItems: 'center',
31 | justifyContent: 'center',
32 | marginHorizontal: 10
33 | },
34 | placeholderText: {
35 | ...FontWeights.Light,
36 | ...FontSizes.Label,
37 | color: theme.text02,
38 | marginTop: 40
39 | }
40 | });
41 |
42 | export default SvgBanner;
--------------------------------------------------------------------------------
/app/graphql/subscription/index.ts:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag';
2 |
3 | export const SUBSCRIPTION_CHAT = gql`
4 | subscription Chat($chatId: String!) {
5 | chat(chatId: $chatId) {
6 | messages(last: 40) {
7 | id
8 | body
9 | createdAt
10 | author {
11 | id
12 | name
13 | avatar
14 | }
15 | }
16 | }
17 | }
18 | `;
19 |
20 | export const SUBSCRIPTION_POST = gql`
21 | subscription Post($postId: String!) {
22 | post(postId: $postId) {
23 | author {
24 | id
25 | handle
26 | avatar
27 | }
28 | comments {
29 | id
30 | body
31 | author {
32 | id
33 | avatar
34 | handle
35 | }
36 | createdAt
37 | }
38 | uri
39 | likes
40 | caption
41 | createdAt
42 | }
43 | }
44 | `;
45 |
46 | // Legacy
47 | // export const SUBSCRIPTION_USER = gql`
48 | // subscription User($userId: String!) {
49 | // user(userId: $userId) {
50 | // id
51 | // avatar
52 | // name
53 | // handle
54 | // about
55 | // following {
56 | // id
57 | // }
58 | // followers {
59 | // id
60 | // }
61 | // posts {
62 | // id
63 | // uri
64 | // }
65 | // }
66 | // }
67 | // `;
--------------------------------------------------------------------------------
/app/navigation/TabBarComponent/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { View, TouchableOpacity, StyleSheet } from 'react-native';
3 | import { ThemeColors } from '@app/types/theme';
4 | import TabIcon from './TabIcon';
5 | import { TabBarRoutes } from '../Routes';
6 | import { AppContext } from '@app/context';
7 |
8 | const TabBarComponent = ({ navigation, ...data }) => {
9 | const { theme } = useContext(AppContext);
10 | const currentRouteName = navigation.state.routes[navigation.state.index].key;
11 | return (
12 |
13 | {Object.keys(TabBarRoutes).map(key => (
14 | navigation.navigate(key)}>
15 |
16 |
17 | ))}
18 |
19 | );
20 | };
21 |
22 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
23 | container: {
24 | flexDirection: 'row',
25 | alignItems: 'center',
26 | justifyContent: 'space-between',
27 | paddingVertical: 12,
28 | paddingHorizontal: 10,
29 | backgroundColor: theme.base
30 | },
31 | icon: {
32 | flex: 1,
33 | alignItems: 'center',
34 | justifyContent: 'center'
35 | }
36 | });
37 |
38 | export default TabBarComponent;
--------------------------------------------------------------------------------
/ios/GoogleService-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CLIENT_ID
6 | 649469906394-cunn0sogttuea6fhvmekebm85h70rqeq.apps.googleusercontent.com
7 | REVERSED_CLIENT_ID
8 | com.googleusercontent.apps.649469906394-cunn0sogttuea6fhvmekebm85h70rqeq
9 | API_KEY
10 | AIzaSyBUDLaueKfWfkbhgxTDOFMDuSh-ReUAxjU
11 | GCM_SENDER_ID
12 | 649469906394
13 | PLIST_VERSION
14 | 1
15 | BUNDLE_ID
16 | com.proximity.app
17 | PROJECT_ID
18 | proximity-developement
19 | STORAGE_BUCKET
20 | proximity-developement.appspot.com
21 | IS_ADS_ENABLED
22 |
23 | IS_ANALYTICS_ENABLED
24 |
25 | IS_APPINVITE_ENABLED
26 |
27 | IS_GCM_ENABLED
28 |
29 | IS_SIGNIN_ENABLED
30 |
31 | GOOGLE_APP_ID
32 | 1:649469906394:ios:13504c0882dfc87c30c4e3
33 | DATABASE_URL
34 | https://proximity-developement.firebaseio.com
35 |
36 |
--------------------------------------------------------------------------------
/app/layout/shared/PostThumbnail.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet, TouchableOpacity } from 'react-native';
3 | import { useNavigation } from 'react-navigation-hooks';
4 | import { Routes } from '@app/constants';
5 | import { AppContext } from '@app/context';
6 | import { ThemeColors } from '@app/types/theme';
7 | import { Dimensions } from '@app/types/constants';
8 | import NativeImage from '../misc/NativeImage';
9 |
10 | interface PostThumbnailProps {
11 | id: string,
12 | uri: string,
13 | dimensions: Dimensions
14 | };
15 |
16 | const PostThumbnail: React.FC = ({ id, uri, dimensions }) => {
17 | const { theme } = useContext(AppContext);
18 | const { navigate } = useNavigation();
19 |
20 | const navigateToPost = () => navigate(Routes.PostViewScreen, { postId: id });
21 |
22 | return (
23 |
24 |
25 |
26 | );
27 | };
28 |
29 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
30 | container: {
31 | backgroundColor: theme.placeholder,
32 | overflow: 'hidden',
33 | borderRadius: 5
34 | },
35 | thumbnailImage: {
36 | flex: 1
37 | }
38 | });
39 |
40 | export default PostThumbnail;
--------------------------------------------------------------------------------
/app/layout/controls/IconButton.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleProp, StyleSheet, Text, TouchableOpacity, View, ViewStyle } from 'react-native';
3 | import { ThemeStatic, Typography } from '@app/theme';
4 |
5 | const { FontWeights, FontSizes } = Typography;
6 |
7 | interface IconButtonProps {
8 | Icon: React.FC,
9 | onPress: () => void,
10 | style?: StyleProp,
11 | hasBadge?: boolean
12 | badgeCount?: number
13 | };
14 |
15 | const IconButton: React.FC = ({ Icon, onPress, style, hasBadge, badgeCount }) => (
16 |
17 |
18 | {hasBadge &&
19 | {badgeCount}
20 | }
21 |
22 | );
23 |
24 | const styles = StyleSheet.create({
25 | container: {
26 | alignItems: 'flex-end',
27 | width: 40
28 | },
29 | badge: {
30 | position: 'absolute',
31 | height: 20,
32 | width: 20,
33 | alignItems: 'center',
34 | justifyContent: 'center',
35 | top: -10,
36 | right: -10,
37 | borderRadius: 20,
38 | backgroundColor: ThemeStatic.badge
39 | },
40 | badgeText: {
41 | ...FontWeights.Regular,
42 | ...FontSizes.Caption,
43 | color: ThemeStatic.white
44 | }
45 | });
46 |
47 | export default IconButton;
--------------------------------------------------------------------------------
/app/graphql/client/index.ts:
--------------------------------------------------------------------------------
1 | import { InMemoryCache } from 'apollo-cache-inmemory';
2 | import ApolloClient from 'apollo-client';
3 | import { ApolloLink, split } from 'apollo-link';
4 | import { onError } from 'apollo-link-error';
5 | import { HttpLink } from 'apollo-link-http';
6 | import { WebSocketLink } from 'apollo-link-ws';
7 | import { getMainDefinition } from 'apollo-utilities';
8 | import Config from '@app/config';
9 |
10 | const httpLink = new HttpLink({ uri: Config.url.https });
11 |
12 | const wsLink = new WebSocketLink({
13 | uri: Config.url.wss,
14 | options: { reconnect: true }
15 | });
16 |
17 | const link = split(
18 | ({ query }) => {
19 | const definition = getMainDefinition(query);
20 | return (
21 | definition.kind === 'OperationDefinition' &&
22 | definition.operation === 'subscription'
23 | );
24 | },
25 | wsLink,
26 | httpLink,
27 | );
28 |
29 | const client = new ApolloClient({
30 | link: ApolloLink.from([
31 | onError(({ graphQLErrors, networkError }) => {
32 | if (graphQLErrors)
33 | graphQLErrors.forEach(({ message, locations, path }) =>
34 | console.log(
35 | `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
36 | ),
37 | );
38 | if (networkError) console.log(`[Network error]: ${networkError}`);
39 | }),
40 | link
41 | ]),
42 | cache: new InMemoryCache()
43 | });
44 |
45 | export default client;
46 |
--------------------------------------------------------------------------------
/app/layout/headers/SearchBar.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { Platform, StyleSheet, TextInput } from 'react-native';
3 | import { AppContext } from '@app/context';
4 | import { Typography } from '@app/theme';
5 | import { ThemeColors } from '@app/types/theme';
6 |
7 | const { FontWeights, FontSizes } = Typography;
8 |
9 | interface SearchBarProps {
10 | value: string,
11 | onChangeText: any,
12 | onFocus?: any,
13 | onBlur?: any,
14 | placeholder: string,
15 | style?: object
16 | };
17 |
18 | const SearchBar: React.FC = ({ value, onChangeText, onFocus, onBlur, placeholder, style }) => {
19 | const { theme } = useContext(AppContext);
20 |
21 | return (
22 |
31 | );
32 | };
33 |
34 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
35 | container: {
36 | ...FontWeights.Light,
37 | ...FontSizes.Body,
38 | width: '90%',
39 | alignSelf: 'center',
40 | paddingVertical: Platform.select({ ios: 10, android: 5 }),
41 | paddingHorizontal: 20,
42 | backgroundColor: theme.placeholder,
43 | color: theme.text01,
44 | borderRadius: 20,
45 | marginVertical: 5
46 | }
47 | });
48 |
49 | export default SearchBar;
--------------------------------------------------------------------------------
/ios/Proximity/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "icon_20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "icon_20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "icon_29@2x.png",
19 | "scale" : "2x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "icon_29@3x.png",
25 | "scale" : "3x"
26 | },
27 | {
28 | "size" : "40x40",
29 | "idiom" : "iphone",
30 | "filename" : "icon_40@2x.png",
31 | "scale" : "2x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "icon_40@3x.png",
37 | "scale" : "3x"
38 | },
39 | {
40 | "size" : "60x60",
41 | "idiom" : "iphone",
42 | "filename" : "icon_60@2x.png",
43 | "scale" : "2x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "icon_60@3x.png",
49 | "scale" : "3x"
50 | },
51 | {
52 | "size" : "1024x1024",
53 | "idiom" : "ios-marketing",
54 | "filename" : "icon_1024@1x.png",
55 | "scale" : "1x"
56 | }
57 | ],
58 | "info" : {
59 | "version" : 1,
60 | "author" : "xcode"
61 | }
62 | }
--------------------------------------------------------------------------------
/app/layout/shared/DeleteCardRightActions.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Animated, StyleSheet, StyleProp, ViewStyle } from 'react-native';
3 | import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
4 | import { IconSizes } from '@app/constants';
5 | import { ThemeStatic } from '@app/theme';
6 |
7 | interface DeleteCardRightActionsProps {
8 | progress: any,
9 | dragX: any,
10 | onDelete: () => void,
11 | style?: StyleProp
12 | };
13 |
14 | const DeleteCardRightActions: React.FC = ({ progress, dragX, onDelete, style }) => {
15 | const translateX = dragX.interpolate({
16 | inputRange: [0, 80, 100, 101],
17 | outputRange: [-10, 0, 0, 1]
18 | });
19 |
20 | const slideEffect = { opacity: progress, transform: [{ translateX }] };
21 |
22 | return (
23 |
24 |
31 |
32 | );
33 | };
34 |
35 | const styles = StyleSheet.create({
36 | container: {
37 | width: 80,
38 | alignItems: 'center',
39 | justifyContent: 'center',
40 | backgroundColor: ThemeStatic.delete,
41 | borderTopRightRadius: 5,
42 | borderBottomRightRadius: 5
43 | },
44 | delete: {
45 | width: 50,
46 | textAlign: 'center'
47 | }
48 | });
49 |
50 | export default DeleteCardRightActions;
--------------------------------------------------------------------------------
/android/app/google-services.json:
--------------------------------------------------------------------------------
1 | {
2 | "project_info": {
3 | "project_number": "649469906394",
4 | "firebase_url": "https://proximity-developement.firebaseio.com",
5 | "project_id": "proximity-developement",
6 | "storage_bucket": "proximity-developement.appspot.com"
7 | },
8 | "client": [
9 | {
10 | "client_info": {
11 | "mobilesdk_app_id": "1:649469906394:android:c134c41d6e0cc61330c4e3",
12 | "android_client_info": {
13 | "package_name": "com.proximity.app"
14 | }
15 | },
16 | "oauth_client": [
17 | {
18 | "client_id": "649469906394-0d9hghf2jv5n9n0t0o763e5bheer720f.apps.googleusercontent.com",
19 | "client_type": 3
20 | }
21 | ],
22 | "api_key": [
23 | {
24 | "current_key": "AIzaSyBlJ_2T901-PkDvy0_upe7rocbPjewmjbc"
25 | }
26 | ],
27 | "services": {
28 | "appinvite_service": {
29 | "other_platform_oauth_client": [
30 | {
31 | "client_id": "649469906394-0d9hghf2jv5n9n0t0o763e5bheer720f.apps.googleusercontent.com",
32 | "client_type": 3
33 | },
34 | {
35 | "client_id": "649469906394-cunn0sogttuea6fhvmekebm85h70rqeq.apps.googleusercontent.com",
36 | "client_type": 2,
37 | "ios_info": {
38 | "bundle_id": "com.proximity.app"
39 | }
40 | }
41 | ]
42 | }
43 | }
44 | }
45 | ],
46 | "configuration_version": "1"
47 | }
--------------------------------------------------------------------------------
/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.boilerplate",
39 | )
40 |
41 | android_resource(
42 | name = "res",
43 | package = "com.boilerplate",
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 |
--------------------------------------------------------------------------------
/app/layout/headers/HomeHeader.tsx:
--------------------------------------------------------------------------------
1 | import dateFormat from 'dateformat';
2 | import React, { useContext } from 'react';
3 | import { Platform, StyleSheet, Text, View } from 'react-native';
4 | import { AppContext } from '@app/context';
5 | import { Typography } from '@app/theme';
6 | import { ThemeColors } from '@app/types/theme';
7 |
8 | const { FontWeights, FontSizes } = Typography;
9 |
10 | interface HomeHeaderProps {
11 | IconRight?: React.FC
12 | };
13 |
14 | const HomeHeader: React.FC = ({ IconRight }) => {
15 |
16 | const { theme } = useContext(AppContext);
17 | const weekdayNow = dateFormat(new Date(), 'dddd');
18 | return (
19 |
20 |
21 | PROXIMITY
22 | {weekdayNow}
23 |
24 | {IconRight && }
25 |
26 | );
27 | };
28 |
29 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
30 | container: {
31 | flexDirection: 'row',
32 | alignItems: 'center',
33 | justifyContent: 'space-between',
34 | paddingVertical: 10,
35 | paddingHorizontal: 20
36 | },
37 | topTitle: {
38 | ...FontWeights.Light,
39 | ...FontSizes.Caption,
40 | color: theme.text02,
41 | letterSpacing: 4
42 | },
43 | title: {
44 | ...FontWeights.Light,
45 | ...FontSizes.Heading,
46 | marginTop: Platform.select({ ios: 2, android: 0 }),
47 | color: theme.text01
48 | }
49 | });
50 |
51 | export default HomeHeader;
--------------------------------------------------------------------------------
/app/layout/misc/ListEmptyComponent.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { View, Text, StyleSheet, StyleProp, TextStyle, ViewStyle } from 'react-native';
3 | import { AppContext } from '@app/context';
4 | import { ThemeColors } from '@app/types/theme';
5 | import { Typography } from '@app/theme';
6 | import { responsiveHeight } from 'react-native-responsive-dimensions';
7 |
8 | const { FontWeights, FontSizes } = Typography;
9 |
10 | interface ListEmptyComponentProps {
11 | listType?: string,
12 | spacing: number,
13 | style?: StyleProp,
14 | placeholder?: string,
15 | placeholderStyle?: StyleProp
16 | };
17 |
18 | const ListEmptyComponent: React.FC = ({ listType, spacing, style, placeholder, placeholderStyle }) => {
19 | const { theme } = useContext(AppContext);
20 | let content = `No ${listType} yet`;
21 | if (placeholder) {
22 | content = placeholder;
23 | }
24 |
25 | return (
26 |
27 | {content}
28 |
29 | );
30 | };
31 |
32 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
33 | container: {
34 | flex: 1,
35 | alignItems: 'center',
36 | justifyContent: 'center',
37 | marginHorizontal: 10
38 | },
39 | placeholderText: {
40 | ...FontWeights.Light,
41 | ...FontSizes.Label,
42 | color: theme.text02
43 | }
44 | });
45 |
46 | export default ListEmptyComponent;
--------------------------------------------------------------------------------
/app/screens/ExploreScreen/components/UserSearchResults.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet } from 'react-native';
3 | import { responsiveWidth } from 'react-native-responsive-dimensions';
4 | import { FlatGrid } from 'react-native-super-grid';
5 | import { ListEmptyComponent } from '@app/layout';
6 | import { UserCard } from '@app/layout';
7 | import { AppContext } from '@app/context';
8 | import { SearchResult } from '@app/types/screens';
9 |
10 | interface UserSearchResultsProps {
11 | searchResults: SearchResult[]
12 | };
13 |
14 | const UserSearchResults: React.FC = ({ searchResults }) => {
15 | const { user } = useContext(AppContext);
16 |
17 | const filteredSearchResults = [...searchResults].filter(result => result.id !== user.id);
18 |
19 | const renderItem = ({ item }) => {
20 | const { id, avatar, handle, name } = item;
21 | return (
22 |
28 | );
29 | };
30 |
31 | return (
32 | }
37 | style={styles.container}
38 | spacing={20}
39 | renderItem={renderItem}
40 | />
41 | );
42 | };
43 |
44 | const styles = StyleSheet.create({
45 | container: {
46 | flex: 1,
47 | width: responsiveWidth(100)
48 | }
49 | });
50 |
51 | export default UserSearchResults;
--------------------------------------------------------------------------------
/app/layout/controls/Button.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { TouchableOpacity, Text, StyleSheet, StyleProp, ViewStyle, TextStyle } from 'react-native';
3 | import LoadingIndicator from '../misc/LoadingIndicator';
4 | import { ThemeStatic, Typography } from '@app/theme';
5 |
6 | const { FontWeights, FontSizes } = Typography;
7 |
8 | interface ButtonProps {
9 | Icon?: React.FC,
10 | label: string,
11 | onPress: any,
12 | loading: boolean,
13 | containerStyle?: StyleProp,
14 | labelStyle?: StyleProp,
15 | indicatorColor?: string
16 | };
17 |
18 | const Button: React.FC = ({ Icon, label, onPress, loading, containerStyle, labelStyle, indicatorColor }) => {
19 |
20 | let content = ;
21 | if (!loading) content = (
22 | <>
23 | {Icon && }
24 | {label}
25 | >
26 | );
27 |
28 | return (
29 |
30 | {content}
31 |
32 | );
33 | };
34 |
35 | const styles = StyleSheet.create({
36 | container: {
37 | height: 40,
38 | width: '100%',
39 | flexDirection: 'row',
40 | alignItems: 'center',
41 | justifyContent: 'center',
42 | paddingVertical: 10,
43 | borderRadius: 40,
44 | backgroundColor: ThemeStatic.accent
45 | },
46 | label: {
47 | ...FontWeights.Light,
48 | ...FontSizes.Body,
49 | marginLeft: 5,
50 | color: ThemeStatic.white
51 | }
52 | });
53 |
54 | export default Button;
--------------------------------------------------------------------------------
/app/types/screens.ts:
--------------------------------------------------------------------------------
1 | import { NotificationTextType } from './constants';
2 |
3 | // Profile Screen
4 |
5 | export type Connection = {
6 | id: string,
7 | avatar: string,
8 | name: string,
9 | handle: string
10 | }
11 |
12 | // Notification Screen
13 |
14 | export type ActionUser = {
15 | id: string,
16 | avatar: string,
17 | handle: string
18 | };
19 |
20 | export type Notification = {
21 | actionUser: ActionUser,
22 | type: keyof NotificationTextType,
23 | createdAt: string
24 | };
25 |
26 | // Post View Screen
27 |
28 | export type Author = {
29 | id: string,
30 | avatar: string,
31 | handle: string
32 | };
33 |
34 | export type Comment = {
35 | author: Author,
36 | body: string,
37 | createdAt: string
38 | };
39 |
40 | export type PostViewType = {
41 | author: Author,
42 | comments: Comment[],
43 | uri: string,
44 | likes: string,
45 | caption: string,
46 | createdAt: string
47 | };
48 |
49 | // Explore Screen
50 |
51 | export type ExplorePost = {
52 | id: string,
53 | uri: string
54 | };
55 |
56 | export type SearchResult = {
57 | id: string,
58 | avatar: string,
59 | name: string,
60 | handle: string
61 | };
62 |
63 | // Message Screen
64 |
65 | export type Participant = {
66 | id: string,
67 | avatar: string,
68 | handle: string,
69 | };
70 |
71 | export type MessageAuthor = {
72 | id: string
73 | };
74 |
75 | export type Message = {
76 | id: string,
77 | body: string,
78 | seen: boolean,
79 | author: MessageAuthor,
80 | createdAt: string
81 | };
82 |
83 | export type Chat = {
84 | id: string,
85 | participants: Participant[],
86 | messages: Message[]
87 | };
88 |
--------------------------------------------------------------------------------
/app/layout/shared/Option.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
3 | import Ionicons from 'react-native-vector-icons/Ionicons';
4 | import { IconSizes } from '@app/constants';
5 | import { AppContext } from '@app/context';
6 | import { Typography } from '@app/theme';
7 | import { ThemeColors } from '@app/types/theme';
8 |
9 | const { FontWeights, FontSizes } = Typography;
10 |
11 | interface OptionProps {
12 | label?: string,
13 | iconName: string,
14 | onPress?: () => void,
15 | children?: any,
16 | color?: string
17 | };
18 |
19 | const Option: React.FC = ({ label, iconName, onPress, children, color }) => {
20 | const { theme } = useContext(AppContext);
21 |
22 | if (children)
23 | return (
24 |
25 |
26 | {children}
27 |
28 | );
29 |
30 | return (
31 |
32 |
33 | {label}
34 |
35 | );
36 | };
37 |
38 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
39 | container: {
40 | flexDirection: 'row',
41 | alignItems: 'center',
42 | marginVertical: 8
43 | },
44 | label: {
45 | ...FontWeights.Light,
46 | ...FontSizes.Body,
47 | marginLeft: 10
48 | }
49 | });
50 |
51 | export default Option;
--------------------------------------------------------------------------------
/app/screens/ProfileViewScreen/components/ProfileOptionsBottomSheet.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet, View } from 'react-native';
3 | import { Modalize } from 'react-native-modalize';
4 | import { AppContext } from '@app/context';
5 | import { BottomSheetHeader, Option } from '@app/layout';
6 | import { ThemeStatic } from '@app/theme';
7 | import { ThemeColors } from '@app/types/theme';
8 |
9 | interface ProfileOptionsBottomSheetProps {
10 | ref: React.Ref,
11 | onBlockUser: () => void
12 | };
13 |
14 | const ProfileOptionsBottomSheet: React.FC = React.forwardRef(({ onBlockUser }, ref) => {
15 |
16 | const { theme } = useContext(AppContext);
17 |
18 | return (
19 |
25 |
29 |
30 |
36 |
37 |
38 | );
39 | });
40 |
41 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
42 | container: {
43 | padding: 20,
44 | backgroundColor: theme.base
45 | },
46 | content: {
47 | flex: 1,
48 | paddingTop: 20,
49 | paddingBottom: 16
50 | }
51 | });
52 |
53 | export default ProfileOptionsBottomSheet;
--------------------------------------------------------------------------------
/app/navigation/Transition.ts:
--------------------------------------------------------------------------------
1 | import { Animated, Easing, I18nManager } from 'react-native';
2 |
3 | function forInitial(props) {
4 |
5 | const { navigation, scene } = props;
6 |
7 | const focused = navigation.state.index === scene.index;
8 | const opacity = focused ? 1 : 0;
9 | const translate = focused ? 0 : 1000000;
10 | return {
11 | opacity,
12 | transform: [{ translateX: translate }, { translateY: translate }],
13 | };
14 | };
15 |
16 | function forHorizontal(props) {
17 |
18 | const { layout, position, scene } = props;
19 |
20 | if (!layout.isMeasured) {
21 | return forInitial(props);
22 | }
23 |
24 | const index = scene.index;
25 | const inputRange = [index - 1, index, index + 1];
26 |
27 | const width = layout.initWidth;
28 | const outputRange = I18nManager.isRTL
29 | ? [-width, 0, width * 0.3]
30 | : [width, 0, width * -0.3];
31 |
32 | const opacity = position.interpolate({
33 | inputRange: [
34 | index - 1,
35 | index - 0.99,
36 | index,
37 | index + 0.99,
38 | index + 1,
39 | ],
40 | outputRange: [0, 1, 1, 0.3, 0],
41 | });
42 |
43 | const translateY = 0;
44 | const translateX = position.interpolate({
45 | inputRange,
46 | outputRange,
47 | });
48 |
49 | return {
50 | opacity,
51 | transform: [{ translateX }, { translateY }],
52 | };
53 | };
54 |
55 | export default function getSlideFromRightTransitionConfig() {
56 | return {
57 | transitionSpec: {
58 | duration: 400,
59 | easing: Easing.out(Easing.poly(4)),
60 | timing: Animated.timing,
61 | useNativeDriver: true
62 | },
63 | screenInterpolator: forHorizontal,
64 | };
65 | };
--------------------------------------------------------------------------------
/app/context/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, createContext, } from 'react';
2 | import { Theme } from '@app/theme';
3 | import { ThemeColors } from '@app/types/theme';
4 | import { saveThemeType } from '@app/utils/storage';
5 |
6 | type UserType = {
7 | id: string,
8 | avatar: string,
9 | handle: string
10 | };
11 |
12 | type AppContextType = {
13 | user: UserType,
14 | updateUser: (user: UserType) => void
15 | theme: ThemeColors,
16 | themeType: string,
17 | toggleTheme: (type: string) => void,
18 | unreadMessages: number,
19 | updateUnreadMessages: (count: number) => void,
20 | };
21 |
22 | export const AppContext = createContext({} as AppContextType);
23 |
24 | export const AppContextProvider = props => {
25 | const [user, setUser] = useState({
26 | id: '',
27 | avatar: '',
28 | handle: ''
29 | });
30 | const [theme, setTheme] = useState(Theme.light.colors);
31 | const [themeType, setThemeType] = useState(Theme.light.type);
32 | const [unreadMessages, setUnreadMessages] = useState(0);
33 |
34 | const updateUser = (user: UserType) => {
35 | setUser(user);
36 | };
37 |
38 | const toggleTheme = (type: string) => {
39 | setTheme(Theme[type].colors);
40 | setThemeType(type);
41 | saveThemeType(type);
42 | };
43 |
44 | const updateUnreadMessages = (count: number) => {
45 | setUnreadMessages(count);
46 | };
47 |
48 | const value = {
49 | user,
50 | updateUser,
51 | theme,
52 | themeType,
53 | toggleTheme,
54 | unreadMessages,
55 | updateUnreadMessages
56 | };
57 |
58 | return (
59 |
60 | {props.children}
61 |
62 | );
63 | };
--------------------------------------------------------------------------------
/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 = "28.0.3"
6 | minSdkVersion = 16
7 | compileSdkVersion = 28
8 | targetSdkVersion = 28
9 | supportLibVersion = "28.0.0"
10 | googlePlayServicesAuthVersion = "16.0.1"
11 | }
12 | repositories {
13 | google()
14 | jcenter()
15 | // Firebase Crashlytics
16 | maven {
17 | url 'https://maven.fabric.io/public'
18 | }
19 | }
20 | dependencies {
21 | classpath("com.android.tools.build:gradle:3.4.2")
22 |
23 | classpath 'com.google.gms:google-services:4.2.0'
24 | // Firebase Crashlytics [CUSTOM]
25 | classpath 'io.fabric.tools:gradle:1.28.1'
26 | // NOTE: Do not place your application dependencies here; they belong
27 | // in the individual module build.gradle files
28 | }
29 | }
30 |
31 | allprojects {
32 | repositories {
33 | mavenLocal()
34 | google()
35 | maven {
36 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
37 | url("$rootDir/../node_modules/react-native/android")
38 | }
39 | maven {
40 | // Android JSC is installed from npm
41 | url("$rootDir/../node_modules/jsc-android/dist")
42 | }
43 |
44 | google()
45 | jcenter()
46 | maven { url 'https://jitpack.io' }
47 |
48 | // react-native-image-crop-picker [CUSTOM]
49 | maven { url 'https://maven.google.com' }
50 | maven { url "https://www.jitpack.io" }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/ios/Proximity-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 |
--------------------------------------------------------------------------------
/app/layout/placeholders/MessageScreen.Placeholder.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet, View } from 'react-native';
3 | import { Placeholder, PlaceholderLine, PlaceholderMedia } from 'rn-placeholder';
4 | import PlaceholderAnimation from './PlaceholderAnimation';
5 | import { AppContext } from '@app/context';
6 |
7 | const MessageScreenPlaceholder: React.FC = () => {
8 | const { theme } = useContext(AppContext);
9 |
10 | return (
11 |
12 |
13 | {new Array(20)
14 | .fill({})
15 | .map((_, index) =>
16 |
17 |
18 |
19 |
25 |
31 |
32 |
33 | )}
34 |
35 |
36 | );
37 | };
38 |
39 | const styles = StyleSheet.create({
40 | container: {
41 | flex: 1,
42 | padding: 20,
43 | paddingBottom: 0
44 | },
45 | cardContainer: {
46 | flexDirection: 'row',
47 | marginBottom: 20
48 | },
49 | infoContainer: {
50 | flex: 1,
51 | paddingLeft: 10
52 | },
53 | userChatPlaceholder: {
54 | borderRadius: 10,
55 | marginTop: 10
56 | }
57 | });
58 |
59 | export default MessageScreenPlaceholder;
--------------------------------------------------------------------------------
/app/layout/headers/GoBackHeader.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet, Text, View, StyleProp, TextProps, TextStyle } from 'react-native';
3 | import { Typography } from '@app/theme';
4 | import { AppContext } from '@app/context';
5 | import { ThemeColors } from '@app/types/theme';
6 | import Entypo from 'react-native-vector-icons/Entypo';
7 | import { useNavigation } from 'react-navigation-hooks';
8 |
9 | const { FontWeights, FontSizes } = Typography;
10 |
11 | interface GoBackHeaderProps {
12 | title?: string,
13 | onTitlePress?: () => void,
14 | ContentLeft?: React.FC,
15 | IconRight?: React.FC,
16 | iconSize: number,
17 | titleStyle?: StyleProp
18 | };
19 |
20 | const GoBackHeader: React.FC = ({ title, onTitlePress, ContentLeft, IconRight, iconSize, titleStyle }) => {
21 |
22 | const { theme } = useContext(AppContext);
23 | const { goBack } = useNavigation();
24 | const navigateBack = () => goBack();
25 |
26 | return (
27 |
28 |
29 | {ContentLeft && }
30 | {title && {title}}
31 | {IconRight && }
32 |
33 | );
34 | };
35 |
36 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
37 | container: {
38 | flexDirection: 'row',
39 | alignItems: 'center',
40 | padding: 10
41 | },
42 | title: {
43 | ...FontWeights.Bold,
44 | ...FontSizes.Label,
45 | color: theme.text01,
46 | marginLeft: 20
47 | },
48 | underline: {
49 | marginTop: 16,
50 | height: 2,
51 | width: 20,
52 | borderRadius: 10,
53 | backgroundColor: theme.text01
54 | }
55 | });
56 |
57 | export default GoBackHeader;
--------------------------------------------------------------------------------
/app/layout/controls/FormInput.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet } from 'react-native';
3 | import { TextField } from 'react-native-material-textfield';
4 | import { AppContext } from '@app/context';
5 | import { ThemeColors } from '@app/types/theme';
6 | import { Typography } from '@app/theme';
7 |
8 | const { FontWeights, FontSizes } = Typography;
9 |
10 | interface FormInputProps {
11 | ref: React.Ref,
12 | label: string,
13 | placeholder: string,
14 | value: string,
15 | onChangeText: any,
16 | multiline?: boolean,
17 | characterRestriction?: number,
18 | children?: any,
19 | error?: string
20 | };
21 |
22 | const FormInput: React.FC = React.forwardRef(({ label, placeholder, value, onChangeText, children, multiline, characterRestriction, error }, ref) => {
23 | const { theme } = useContext(AppContext);
24 | return (
25 | children}
45 | />
46 | );
47 | });
48 |
49 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
50 | labelTextStyle: {
51 | ...FontWeights.Regular
52 | },
53 | textStyle: {
54 | ...FontWeights.Light,
55 | color: theme.text01
56 | }
57 | });
58 |
59 | export default FormInput;
--------------------------------------------------------------------------------
/app/layout/placeholders/NotificationScreen.Placeholder.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet, View } from 'react-native';
3 | import { Placeholder, PlaceholderLine, PlaceholderMedia } from 'rn-placeholder';
4 | import PlaceholderAnimation from './PlaceholderAnimation';
5 | import { AppContext } from '@app/context';
6 |
7 | const NotificationScreenPlaceholder: React.FC = () => {
8 |
9 | const { theme } = useContext(AppContext);
10 |
11 | return (
12 |
13 |
14 | {new Array(20)
15 | .fill({})
16 | .map((_, index) =>
17 |
18 |
19 |
20 |
26 |
32 |
33 |
34 | )}
35 |
36 |
37 | );
38 | };
39 |
40 | const styles = StyleSheet.create({
41 | container: {
42 | flex: 1,
43 | padding: 20,
44 | paddingBottom: 0
45 | },
46 | cardContainer: {
47 | flexDirection: 'row',
48 | marginBottom: 20
49 | },
50 | infoContainer: {
51 | flex: 1,
52 | paddingLeft: 10
53 | },
54 | notificationCardPlaceholder: {
55 | borderRadius: 10,
56 | marginTop: 10
57 | }
58 | });
59 |
60 | export default NotificationScreenPlaceholder;
--------------------------------------------------------------------------------
/app/layout/placeholders/Connections.Placeholder.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet, View } from 'react-native';
3 | import { responsiveWidth } from 'react-native-responsive-dimensions';
4 | import { Placeholder, PlaceholderLine, PlaceholderMedia } from 'rn-placeholder';
5 | import PlaceholderAnimation from './PlaceholderAnimation';
6 | import { AppContext } from '@app/context';
7 |
8 | const ConnectionsPlaceholder: React.FC = () => {
9 | const { theme } = useContext(AppContext);
10 |
11 | return (
12 |
13 |
14 | {new Array(10)
15 | .fill({})
16 | .map((_, index) =>
17 |
18 |
19 |
20 |
26 |
32 |
33 |
34 | )}
35 |
36 |
37 | );
38 | };
39 |
40 | const styles = StyleSheet.create({
41 | container: {
42 | flex: 1,
43 | marginTop: 20,
44 | paddingBottom: 0
45 | },
46 | cardContainer: {
47 | flexDirection: 'row',
48 | marginBottom: 20
49 | },
50 | infoContainer: {
51 | flex: 1,
52 | paddingLeft: 10
53 | },
54 | userInfoPlaceholder: {
55 | borderRadius: 10,
56 | marginTop: 10
57 | }
58 | });
59 |
60 | export default ConnectionsPlaceholder;
--------------------------------------------------------------------------------
/app/layout/placeholders/SearchUsers.Placeholder.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet, View } from 'react-native';
3 | import { responsiveWidth } from 'react-native-responsive-dimensions';
4 | import { Placeholder, PlaceholderLine, PlaceholderMedia } from 'rn-placeholder';
5 | import PlaceholderAnimation from './PlaceholderAnimation';
6 | import { AppContext } from '@app/context';
7 |
8 | const SearchUsersPlaceholder: React.FC = () => {
9 | const { theme } = useContext(AppContext);
10 |
11 | return (
12 |
13 |
14 | {new Array(20)
15 | .fill({})
16 | .map((_, index) =>
17 |
18 |
19 |
20 |
26 |
32 |
33 |
34 | )}
35 |
36 |
37 | );
38 | };
39 |
40 | const styles = StyleSheet.create({
41 | container: {
42 | flex: 1,
43 | padding: 20,
44 | paddingBottom: 0
45 | },
46 | cardContainer: {
47 | flexDirection: 'row',
48 | marginBottom: 20
49 | },
50 | infoContainer: {
51 | flex: 1,
52 | paddingLeft: 10
53 | },
54 | userInfoPlaceholder: {
55 | borderRadius: 10,
56 | marginTop: 10
57 | }
58 | });
59 |
60 | export default SearchUsersPlaceholder;
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/navigation/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Platform } from 'react-native';
3 | import { createAppContainer } from 'react-navigation';
4 | import createAnimatedSwitchNavigator from 'react-navigation-animated-switch';
5 | import { createStackNavigator } from 'react-navigation-stack';
6 | import { createBottomTabNavigator } from 'react-navigation-tabs';
7 | import { StackRoutes, TabBarRoutes } from './Routes';
8 | import TabBarComponent from './TabBarComponent';
9 | import getSlideFromRightTransitionConfig from './Transition';
10 |
11 | const TabNavigator = createBottomTabNavigator({
12 | HomeScreen: {
13 | screen: TabBarRoutes.HomeScreen
14 | },
15 | ExploreScreen: {
16 | screen: TabBarRoutes.ExploreScreen
17 | },
18 | UploadScreen: {
19 | screen: TabBarRoutes.UploadScreen
20 | },
21 | NotificationScreen: {
22 | screen: TabBarRoutes.NotificationScreen
23 | },
24 | ProfileScreen: {
25 | screen: TabBarRoutes.ProfileScreen
26 | },
27 | }, {
28 | lazy: true,
29 | tabBarOptions: {
30 | showLabel: false,
31 | },
32 | tabBarComponent: props =>
33 | });
34 |
35 | const AuthStack = createStackNavigator({
36 | LoginScreen: {
37 | screen: StackRoutes.LoginScreen
38 | }
39 | }, { headerMode: 'none' });
40 |
41 | const AppStack = createStackNavigator({
42 | TabNavigator: {
43 | screen: TabNavigator
44 | },
45 | MessageScreen: {
46 | screen: StackRoutes.MessageScreen
47 | },
48 | ConversationScreen: {
49 | screen: StackRoutes.ConversationScreen
50 | },
51 | ProfileViewScreen: {
52 | screen: StackRoutes.ProfileViewScreen
53 | },
54 | PostViewScreen: {
55 | screen: StackRoutes.PostViewScreen
56 | }
57 | }, {
58 | headerMode: 'none',
59 | transitionConfig: Platform.OS === 'ios' ? undefined : getSlideFromRightTransitionConfig
60 | });
61 |
62 | const SwitchNavigator = createAnimatedSwitchNavigator({
63 | Auth: AuthStack,
64 | App: AppStack
65 | }, {
66 | initialRouteName: 'Auth'
67 | });
68 |
69 | const AppNavigator = createAppContainer(SwitchNavigator as any);
70 |
71 | export default AppNavigator;
--------------------------------------------------------------------------------
/app/layout/shared/UserCard.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleProp, StyleSheet, Text, TouchableOpacity, View, ViewStyle } from 'react-native';
3 | import { useNavigation } from 'react-navigation-hooks';
4 | import { Routes } from '@app/constants';
5 | import { AppContext } from '@app/context';
6 | import { Typography } from '@app/theme';
7 | import { ThemeColors } from '@app/types/theme';
8 | import NativeImage from '../misc/NativeImage';
9 |
10 | const { FontWeights, FontSizes } = Typography;
11 |
12 | interface UserCardProps {
13 | userId: string,
14 | avatar: string,
15 | handle: string,
16 | name: string,
17 | style?: StyleProp,
18 | onPress?: any
19 | };
20 |
21 | const UserCard: React.FC = ({ userId, avatar, handle, name, onPress, style }) => {
22 |
23 | const { user, theme } = useContext(AppContext);
24 | const { navigate } = useNavigation();
25 |
26 | const navigateToProfile = () => {
27 | if (userId === user.id) return;
28 | navigate(Routes.ProfileViewScreen, { userId });
29 | };
30 |
31 | return (
32 |
33 |
34 |
35 | {handle}
36 | {name}
37 |
38 |
39 | );
40 | };
41 |
42 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
43 | container: {
44 | flexDirection: 'row',
45 | borderRadius: 5,
46 | width: '100%',
47 | },
48 | avatarImage: {
49 | height: 50,
50 | width: 50,
51 | borderRadius: 50,
52 | backgroundColor: theme.placeholder
53 | },
54 | info: {
55 | flex: 1,
56 | justifyContent: 'center',
57 | paddingLeft: 10
58 | },
59 | handleText: {
60 | ...FontWeights.Regular,
61 | ...FontSizes.Body,
62 | color: theme.text01
63 | },
64 | nameText: {
65 | ...FontWeights.Light,
66 | ...FontSizes.Caption,
67 | color: theme.text02,
68 | marginTop: 5
69 | }
70 | });
71 |
72 | export default UserCard;
--------------------------------------------------------------------------------
/app/layout/misc/BounceView.tsx:
--------------------------------------------------------------------------------
1 | // @ts-nocheck
2 | import React, { Component } from 'react';
3 | import { Animated, PanResponder, View } from 'react-native';
4 |
5 | const truthy = () => true;
6 | const noop = () => { };
7 |
8 | class BounceView extends Component {
9 |
10 | static defaultProps = {
11 | onPress: noop,
12 | scale: 1.1, // Max scale of animation
13 | moveSlop: 15, // Slop area for press
14 | delay: 40 // Animation delay in milliseconds
15 | };
16 |
17 | state = {
18 | scale: new Animated.Value(1)
19 | };
20 |
21 | UNSAFE_componentWillMount() {
22 | this.panResponder = PanResponder.create({
23 | onStartShouldSetPanResponder: truthy,
24 | onStartShouldSetPanResponderCapture: truthy,
25 | onMoveShouldSetPanResponder: truthy,
26 | onMoveShouldSetPanResponderCapture: truthy,
27 | onPanResponderTerminationRequest: truthy,
28 | onPanResponderTerminate: noop,
29 | onPanResponderGrant: () => {
30 | Animated.timing(this.state.scale, {
31 | toValue: this.props.scale,
32 | friction: 1,
33 | duration: 200,
34 | useNativeDriver: true
35 | }).start();
36 | },
37 | onPanResponderRelease: (evt, gestureState) => {
38 | const { moveSlop, delay, onPress } = this.props;
39 |
40 | const isOutOfRange = gestureState.dy > moveSlop || gestureState.dy < (-moveSlop) || gestureState.dx > moveSlop || gestureState.dx < (-moveSlop);
41 |
42 | if (!isOutOfRange) {
43 | setTimeout(() => {
44 | Animated.spring(this.state.scale, {
45 | toValue: 1,
46 | friction: 1,
47 | duration: 200,
48 | useNativeDriver: true
49 | }).start();
50 | onPress(evt);
51 | }, delay);
52 | }
53 | }
54 | });
55 | };
56 |
57 | render() {
58 | const { scale } = this.state;
59 | const { children, style, ...rest } = this.props;
60 |
61 | return (
62 |
66 |
67 | {children}
68 |
69 |
70 | );
71 | };
72 | };
73 |
74 | export default BounceView;
--------------------------------------------------------------------------------
/ios/Proximity/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/ios/ProximityTests/ProximityTests.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | #import
9 | #import
10 |
11 | #import
12 | #import
13 |
14 | #define TIMEOUT_SECONDS 600
15 | #define TEXT_TO_LOOK_FOR @"Welcome to React"
16 |
17 | @interface ProximityTests : XCTestCase
18 |
19 | @end
20 |
21 | @implementation ProximityTests
22 |
23 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
24 | {
25 | if (test(view)) {
26 | return YES;
27 | }
28 | for (UIView *subview in [view subviews]) {
29 | if ([self findSubviewInView:subview matching:test]) {
30 | return YES;
31 | }
32 | }
33 | return NO;
34 | }
35 |
36 | - (void)testRendersWelcomeScreen
37 | {
38 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
39 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
40 | BOOL foundElement = NO;
41 |
42 | __block NSString *redboxError = nil;
43 | #ifdef DEBUG
44 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
45 | if (level >= RCTLogLevelError) {
46 | redboxError = message;
47 | }
48 | });
49 | #endif
50 |
51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
54 |
55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
57 | return YES;
58 | }
59 | return NO;
60 | }];
61 | }
62 |
63 | #ifdef DEBUG
64 | RCTSetLogFunction(RCTDefaultLogFunction);
65 | #endif
66 |
67 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
68 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
69 | }
70 |
71 |
72 | @end
73 |
--------------------------------------------------------------------------------
/app/screens/PostViewScreen/components/Comments.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet, Text, FlatList } from 'react-native';
3 | import { AppContext } from '@app/context';
4 | import { ListEmptyComponent } from '@app/layout';
5 | import { Typography } from '@app/theme';
6 | import { ThemeColors } from '@app/types/theme';
7 | import CommentCard from './CommentCard';
8 | import { Comment } from '@app/types/screens';
9 |
10 | const { FontWeights, FontSizes } = Typography;
11 |
12 | interface CommentsProps {
13 | postId: string,
14 | comments: Comment[]
15 | };
16 |
17 | const Comments: React.FC = ({ postId, comments }) => {
18 | const { theme } = useContext(AppContext);
19 |
20 | const renderItem = ({ item }) => {
21 | const {
22 | id: commentId,
23 | author: {
24 | id: authorId,
25 | avatar,
26 | handle
27 | },
28 | body,
29 | createdAt
30 | } = item;
31 |
32 | return (
33 |
42 | );
43 | };
44 |
45 | const ListHeaderComponent = () => Comments
46 |
47 | const marginBottom = comments.length === 0 ? 0 : 20;
48 |
49 | return (
50 |
58 |
63 | }
64 | />
65 | );
66 | };
67 |
68 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
69 | commentsHeader: {
70 | ...FontWeights.Regular,
71 | ...FontSizes.Body,
72 | color: theme.text01
73 | },
74 | listStyle: {
75 | marginBottom: 10
76 | },
77 | placeholderStyle: {
78 | ...FontSizes.Body
79 | }
80 | });
81 |
82 | export default Comments;
--------------------------------------------------------------------------------
/app/layout/shared/ConfirmationModal.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { View, Text, StyleSheet } from 'react-native';
3 | import Modal from 'react-native-modal';
4 | import { Typography } from '@app/theme';
5 | import { AppContext } from '@app/context';
6 | import { ThemeColors } from '@app/types/theme';
7 | import Button from '../controls/Button';
8 |
9 | const { FontWeights, FontSizes } = Typography;
10 |
11 | interface ConfirmationModalModal {
12 | title: string,
13 | description: string,
14 | isVisible: boolean,
15 | toggle: () => void,
16 | label: string,
17 | color?: string,
18 | onConfirm: () => void
19 | };
20 |
21 | const ConfirmationModal: React.FC = ({ title, description, isVisible, toggle, label, color, onConfirm }) => {
22 |
23 | const { theme } = useContext(AppContext);
24 |
25 | return (
26 |
35 |
36 | {title}
37 | {description}
38 |
44 |
51 |
52 |
53 | );
54 | };
55 |
56 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
57 | container: {
58 | padding: 20,
59 | borderRadius: 10,
60 | backgroundColor: theme.base
61 | },
62 | heading: {
63 | ...FontWeights.Regular,
64 | ...FontSizes.Label,
65 | color: theme.text01
66 | },
67 | subHeading: {
68 | ...FontWeights.Light,
69 | ...FontSizes.Body,
70 | marginTop: 10,
71 | color: theme.text02
72 | },
73 | confirm: { marginTop: 40 },
74 | cancel: { marginTop: 10 }
75 | });
76 |
77 | export default ConfirmationModal;
--------------------------------------------------------------------------------
/.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/Libraries/react-native/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/Libraries/react-native/react-native-implementation'
40 | module.name_mapper='^react-native/\(.*\)$' -> '/node_modules/react-native/\1'
41 | 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'
42 |
43 | suppress_type=$FlowIssue
44 | suppress_type=$FlowFixMe
45 | suppress_type=$FlowFixMeProps
46 | suppress_type=$FlowFixMeState
47 |
48 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)
49 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+
50 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
51 |
52 | [lints]
53 | sketchy-null-number=warn
54 | sketchy-null-mixed=warn
55 | sketchy-number=warn
56 | untyped-type-import=warn
57 | nonstrict-import=warn
58 | deprecated-type=warn
59 | unsafe-getters-setters=warn
60 | inexact-spread=warn
61 | unnecessary-invariant=warn
62 | signature-verification-failure=warn
63 | deprecated-utility=error
64 |
65 | [strict]
66 | deprecated-type
67 | nonstrict-import
68 | sketchy-null
69 | unclear-type
70 | unsafe-getters-setters
71 | untyped-import
72 | untyped-type-import
73 |
74 | [version]
75 | ^0.105.0
76 |
--------------------------------------------------------------------------------
/app/screens/NotificationScreen/index.tsx:
--------------------------------------------------------------------------------
1 | import { useQuery } from '@apollo/react-hooks';
2 | import React, { useContext } from 'react';
3 | import { StyleSheet, View } from 'react-native';
4 | import { responsiveWidth } from 'react-native-responsive-dimensions';
5 | import { FlatGrid } from 'react-native-super-grid';
6 | import EmptyNotifications from '@app/assets/svg/empty-notifications.svg';
7 | import { PollIntervals } from '@app/constants';
8 | import { AppContext } from '@app/context';
9 | import { QUERY_NOTIFICATION } from '@app/graphql/query';
10 | import { Header, NotificationScreenPlaceholder, SvgBanner } from '@app/layout';
11 | import { ThemeColors } from '@app/types/theme';
12 | import NotificationCard from './components/NotificationCard';
13 |
14 | const NotificationScreen: React.FC = () => {
15 | const { user, theme } = useContext(AppContext);
16 |
17 | const { data, loading, error } = useQuery(QUERY_NOTIFICATION, {
18 | variables: { userId: user.id },
19 | pollInterval: PollIntervals.notification
20 | });
21 |
22 | const renderItem = ({ item }) => {
23 | const { id: notificationId, actionUser, type, resourceId, createdAt } = item;
24 |
25 | return (
26 |
34 | );
35 | };
36 |
37 | let content = ;
38 |
39 | if (!loading && !error) {
40 | const { notifications } = data;
41 | content = (
42 | }
47 | style={styles().notificationList}
48 | spacing={20}
49 | renderItem={renderItem}
50 | />
51 | );
52 | }
53 |
54 | return (
55 |
56 |
57 | {content}
58 |
59 | );
60 | };
61 |
62 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
63 | container: {
64 | flex: 1,
65 | backgroundColor: theme.base
66 | },
67 | notificationList: {
68 | flex: 1,
69 | paddingHorizontal: 4
70 | }
71 | });
72 |
73 | export default NotificationScreen;
--------------------------------------------------------------------------------
/app/screens/UploadScreen/components/UploadBanner.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Image, StyleSheet, TouchableOpacity } from 'react-native';
3 | import AntDesign from 'react-native-vector-icons/AntDesign';
4 | import UploadSvg from '@app/assets/svg/upload.svg';
5 | import { IconSizes, PostDimensions } from '@app/constants';
6 | import { IconButton } from '@app/layout';
7 | import SvgBanner from '@app/layout/misc/SvgBanner';
8 | import { ThemeStatic } from '@app/theme';
9 | import { getImageFromLibrary } from '@app/utils/shared';
10 |
11 | interface UploadBannerProps {
12 | pickedAsset: string,
13 | onAsset: (path: string) => void
14 | };
15 |
16 | const UploadBanner: React.FC = ({ pickedAsset, onAsset }) => {
17 | const discardImage = () => onAsset('');
18 |
19 | const pickImage = async () => {
20 | //@ts-ignore
21 | const { path } = await getImageFromLibrary(340, 340);
22 | onAsset(path);
23 | };
24 |
25 | let content = (
26 |
31 | );
32 |
33 | if (pickedAsset) {
34 | content = (
35 | <>
36 |
40 | }
45 | />
46 |
47 | >
48 | );
49 | }
50 |
51 | return (
52 |
53 | {content}
54 |
55 | );
56 | };
57 |
58 | const styles = StyleSheet.create({
59 | container: {
60 | ...PostDimensions.Large,
61 | alignSelf: 'center',
62 | alignItems: 'center',
63 | justifyContent: 'center',
64 | borderStyle: 'dashed',
65 | borderColor: ThemeStatic.accent,
66 | marginVertical: 20,
67 | borderRadius: 10
68 | },
69 | discardImageButton: {
70 | position: 'absolute',
71 | top: 10,
72 | right: 10,
73 | paddingBottom: 10,
74 | zIndex: 100
75 | },
76 | pickedImage: {
77 | ...PostDimensions.Large,
78 | borderRadius: 10
79 | },
80 | placeholderText: {
81 | color: ThemeStatic.accent
82 | }
83 | });
84 |
85 | export default UploadBanner;
--------------------------------------------------------------------------------
/app/types/constants.ts:
--------------------------------------------------------------------------------
1 | export type RoutesType = {
2 | App: string;
3 | Auth: string;
4 | HomeScreen: string;
5 | ExploreScreen: string;
6 | UploadScreen: string;
7 | NotificationScreen: string;
8 | ProfileScreen: string;
9 | TabNavigator: string;
10 | ProfileViewScreen: string;
11 | MessageScreen: string;
12 | ConversationScreen: string;
13 | PostViewScreen: string;
14 | LoginScreen: string;
15 | };
16 |
17 | export type NotificationTextType = {
18 | FOLLOW: string;
19 | COMMENT: string;
20 | LIKE: string;
21 | };
22 |
23 | export type FollowInteractionType = {
24 | FOLLOW: string;
25 | UNFOLLOW: string;
26 | };
27 |
28 | export type LikeActionType = {
29 | LIKE: string;
30 | UNLIKE: string;
31 | };
32 |
33 | export type ConnectionsType = {
34 | FOLLOWING: string;
35 | FOLLOWERS: string;
36 | };
37 |
38 | export type IconSizesType = {
39 | x00: number;
40 | x0: number;
41 | x1: number;
42 | x2: number;
43 | x3: number;
44 | x4: number;
45 | x5: number;
46 | x6: number;
47 | x7: number;
48 | x8: number;
49 | x9: number;
50 | x10: number;
51 | x11: number;
52 | x12: number;
53 | };
54 |
55 | export type Dimensions = {
56 | height: number;
57 | width: number;
58 | };
59 |
60 | export type PostDimensionsType = {
61 | Small: Dimensions;
62 | Medium: Dimensions;
63 | Large: Dimensions;
64 | };
65 |
66 | export type PollIntervalsType = {
67 | messages: number;
68 | profile: number;
69 | profileView: number;
70 | postView: number;
71 | interaction: number;
72 | notification: number;
73 | lastSeen: number;
74 | blockList: number;
75 | };
76 |
77 | export type StoragePathsType = {
78 | avatars: string;
79 | posts: string;
80 | };
81 |
82 | export type AssetType = {
83 | avatar: string;
84 | post: string;
85 | };
86 |
87 | export type TimeoutsType = {
88 | online: number;
89 | };
90 |
91 | export type ErrorsType = {
92 | SIGN_IN: string;
93 | SIGN_OUT: string;
94 | UPDATE_LAST_SEEN: string;
95 | LOAD_THEME: string;
96 | INITIALIZE_FCM: string;
97 | INITIALIZE_CHAT: string;
98 | UPDATE_FCM_TOKEN: string;
99 | ASSET_UPLOAD: string;
100 | EDIT_POST: string;
101 | };
102 |
103 | export type AuthDefaultsType = {
104 | avatar: string;
105 | name: string;
106 | };
107 |
108 | export type PaginationType = {
109 | PAGE_SIZE: number;
110 | CURSOR_SIZE: number;
111 | };
112 |
113 | export type DebounceType = {
114 | EXPLORE_SEARCH: number;
115 | };
--------------------------------------------------------------------------------
/app/screens/ExploreScreen/components/ExploreGrid.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet, View, RefreshControl } from 'react-native';
3 | import { FlatGrid } from 'react-native-super-grid';
4 | import { ListEmptyComponent, LoadingIndicator } from '@app/layout';
5 | import { PrimaryImageGroup, SecondaryImageGroup } from './ExplorePostCard';
6 | import { ExplorePost } from '@app/types/screens';
7 | import { responsiveWidth } from 'react-native-responsive-dimensions';
8 | import { parseGridImages } from '@app/utils/shared';
9 | import { AppContext } from '@app/context';
10 |
11 | interface ExploreGridProps {
12 | posts: ExplorePost[],
13 | onRefresh: () => void,
14 | tintColor: string,
15 | onEndReached: () => void
16 | };
17 |
18 | const ExploreGrid: React.FC = ({ posts, onRefresh, tintColor, onEndReached }) => {
19 | const { theme } = useContext(AppContext);
20 |
21 | const renderItem = ({ item, index }) => {
22 | const isSecondaryImageGroup = index % 3 === 2;
23 |
24 | if (isSecondaryImageGroup) {
25 | const reversed = index % 2 === 1;
26 |
27 | return ;
28 | }
29 |
30 | return
31 | };
32 |
33 | const refreshControl = () => (
34 |
38 | );
39 |
40 | const ListFooterComponent = () => (
41 |
42 |
43 |
44 | );
45 |
46 | const parsedGridImages = parseGridImages(posts);
47 |
48 | return (
49 |
50 | }
57 | spacing={5}
58 | onEndReached={onEndReached}
59 | onEndReachedThreshold={0.5}
60 | ListFooterComponent={ListFooterComponent}
61 | renderItem={renderItem}
62 | />
63 |
64 | );
65 | };
66 |
67 | const styles = StyleSheet.create({
68 | container: {
69 | flex: 1,
70 | alignItems: 'center',
71 | justifyContent: 'center'
72 | },
73 | listLoader: {
74 | marginBottom: 12
75 | }
76 | });
77 |
78 | export default ExploreGrid;
--------------------------------------------------------------------------------
/app/layout/index.ts:
--------------------------------------------------------------------------------
1 | import HomeHeader from './headers/HomeHeader';
2 | import Header from './headers/Header';
3 | import SearchBar from './headers/SearchBar';
4 | import BottomSheetHeader from './headers/BottomSheetHeader';
5 | import AnimatedSearchBar from './headers/AnimatedSearchBar';
6 | import GoBackHeader from './headers/GoBackHeader';
7 |
8 | import FormInput from './controls/FormInput';
9 | import Button from './controls/Button';
10 | import IconButton from './controls/IconButton';
11 |
12 | import SvgBanner from './misc/SvgBanner';
13 | import ListEmptyComponent from './misc/ListEmptyComponent';
14 | import LoadingIndicator from './misc/LoadingIndicator';
15 | import NativeImage from './misc/NativeImage';
16 | import BounceView from './misc/BounceView';
17 |
18 | import MessageScreenPlaceholder from './placeholders/MessageScreen.Placeholder';
19 | import ConversationScreenPlaceholder from './placeholders/ConversationScreen.Placeholder';
20 | import NotificationScreenPlaceholder from './placeholders/NotificationScreen.Placeholder';
21 | import ProfileScreenPlaceholder from './placeholders/ProfileScreen.Placeholder';
22 | import SearchUsersPlaceholder from './placeholders/SearchUsers.Placeholder';
23 | import ExploreScreenPlaceholder from './placeholders/ExploreScreen.Placeholder';
24 | import PostCardPlaceholder from './placeholders/PostCard.Placeholder';
25 | import PostViewScreenPlaceholder from './placeholders/PostViewScreen.Placeholder';
26 | import ConnectionsPlaceholder from './placeholders/Connections.Placeholder';
27 |
28 | import PostThumbnail from './shared/PostThumbnail';
29 | import ProfileCard from './shared/ProfileCard';
30 | import UserCard from './shared/UserCard';
31 | import ConnectionsBottomSheet from './shared/ConnectionsBottomSheet';
32 | import Option from './shared/Option';
33 | import ConfirmationModal from './shared/ConfirmationModal';
34 | import DeleteCardRightActions from './shared/DeleteCardRightActions';
35 |
36 | export {
37 | HomeHeader,
38 | Header,
39 | SearchBar,
40 | BottomSheetHeader,
41 | AnimatedSearchBar,
42 | GoBackHeader,
43 | FormInput,
44 | Button,
45 | IconButton,
46 | SvgBanner,
47 | ListEmptyComponent,
48 | LoadingIndicator,
49 | NativeImage,
50 | BounceView,
51 | MessageScreenPlaceholder,
52 | ConversationScreenPlaceholder,
53 | NotificationScreenPlaceholder,
54 | ProfileScreenPlaceholder,
55 | PostThumbnail,
56 | ProfileCard,
57 | UserCard,
58 | ConnectionsBottomSheet,
59 | Option,
60 | ConfirmationModal,
61 | DeleteCardRightActions,
62 | SearchUsersPlaceholder,
63 | ExploreScreenPlaceholder,
64 | PostCardPlaceholder,
65 | PostViewScreenPlaceholder,
66 | ConnectionsPlaceholder
67 | };
--------------------------------------------------------------------------------
/ios/Proximity/AppDelegate.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | #import "AppDelegate.h"
9 | #import
10 | #import
11 | #import
12 |
13 | #import
14 | #import "RNFirebaseNotifications.h"
15 | #import "RNFirebaseMessaging.h"
16 |
17 | #import "RNSplashScreen.h"
18 | #import
19 |
20 | @implementation AppDelegate
21 |
22 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
23 | {
24 | [FIRApp configure];
25 | [RNFirebaseNotifications configure];
26 |
27 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
28 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
29 | moduleName:@"Proximity"
30 | initialProperties:nil];
31 |
32 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
33 |
34 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
35 | UIViewController *rootViewController = [UIViewController new];
36 | rootViewController.view = rootView;
37 | self.window.rootViewController = rootViewController;
38 | [self.window makeKeyAndVisible];
39 |
40 | [RNSplashScreen show];
41 |
42 | return YES;
43 | }
44 |
45 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
46 | {
47 | #if DEBUG
48 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
49 | #else
50 | return [CodePush bundleURL];
51 | #endif
52 | }
53 |
54 | - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
55 | [[RNFirebaseNotifications instance] didReceiveLocalNotification:notification];
56 | }
57 |
58 | - (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo
59 | fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler{
60 | [[RNFirebaseNotifications instance] didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
61 | }
62 |
63 | - (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
64 | [[RNFirebaseMessaging instance] didRegisterUserNotificationSettings:notificationSettings];
65 | }
66 |
67 | @end
68 |
--------------------------------------------------------------------------------
/app/screens/PostViewScreen/components/LikeBounceAnimation.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Animated, StyleSheet } from 'react-native';
3 | import AntDesign from 'react-native-vector-icons/AntDesign';
4 | import { IconSizes, PostDimensions } from '@app/constants';
5 | import { ThemeStatic } from '@app/theme';
6 |
7 | const AnimationValues = {
8 | opacity: { initial: 0, final: 1 },
9 | bounce: { initial: 0.4, final: 1 }
10 | };
11 |
12 | interface LikeBounceAnimationProps {
13 | ref: React.Ref
14 | };
15 |
16 | const LikeBounceAnimation: React.FC = React.forwardRef((_, ref) => {
17 |
18 | const [animating, setAnimating] = useState(false);
19 | const [likeOpacity] = useState(new Animated.Value(AnimationValues.opacity.initial));
20 | const [likeBounce] = useState(new Animated.Value(AnimationValues.bounce.initial));
21 |
22 | // @ts-ignore
23 | ref.current = {
24 | animate: () => {
25 |
26 | if (animating) return;
27 |
28 | const opacityConfig = {
29 | duration: 250,
30 | useNativeDriver: true
31 | };
32 |
33 | const fadeInConfig = {
34 | toValue: AnimationValues.opacity.final,
35 | ...opacityConfig
36 | };
37 |
38 | const fadeOutConfig = {
39 | toValue: AnimationValues.opacity.initial,
40 | ...opacityConfig
41 | };
42 |
43 | const springConfig = {
44 | toValue: AnimationValues.bounce.final,
45 | friction: 2,
46 | useNativeDriver: true
47 | };
48 |
49 | setAnimating(true);
50 |
51 | const parallelAnimations = [
52 | Animated.spring(likeBounce, springConfig),
53 | Animated.timing(likeOpacity, fadeInConfig)
54 | ];
55 |
56 | Animated.parallel(parallelAnimations).start(() => {
57 | Animated
58 | .timing(likeOpacity, fadeOutConfig)
59 | .start(() => {
60 | likeBounce.setValue(AnimationValues.bounce.initial);
61 | setAnimating(false);
62 | });
63 | });
64 | }
65 | };
66 |
67 | return (
68 |
72 |
77 |
78 | );
79 | });
80 |
81 | const styles = StyleSheet.create({
82 | container: {
83 | position: 'absolute',
84 | top: PostDimensions.Large.height / 2.25,
85 | alignSelf: 'center',
86 | alignItems: 'center',
87 | justifyContent: 'center'
88 | }
89 | });
90 |
91 | export default LikeBounceAnimation;
--------------------------------------------------------------------------------
/app/screens/PostViewScreen/components/PostOptionsBottomSheet.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet, View } from 'react-native';
3 | import { Modalize } from 'react-native-modalize';
4 | import { AppContext } from '@app/context';
5 | import { BottomSheetHeader, Option } from '@app/layout';
6 | import { ThemeColors } from '@app/types/theme';
7 | import { postReportedNotification } from '@app/utils/notifications';
8 | import { useMutation } from '@apollo/react-hooks';
9 | import { MUTATION_REPORT_POST } from '@app/graphql/mutation';
10 | import { ThemeStatic } from '@app/theme';
11 |
12 | interface PostOptionsBottomSheetProps {
13 | ref: React.Ref,
14 | authorId: string,
15 | postId: string,
16 | onPostEdit: () => void,
17 | onPostDelete: () => void
18 | };
19 |
20 | const PostOptionsBottomSheet: React.FC = React.forwardRef(({ authorId, postId, onPostEdit, onPostDelete }, ref) => {
21 |
22 | const { user, theme } = useContext(AppContext);
23 | const [reportPost] = useMutation(MUTATION_REPORT_POST, { variables: { postId } });
24 | const isOwnPost = user.id === authorId;
25 |
26 | const onPostReport = () => {
27 | reportPost();
28 | postReportedNotification();
29 | // @ts-ignore
30 | ref.current.close();
31 | };
32 |
33 | let subHeading = 'Tell us what you think';
34 | let content = (
35 |
41 | );
42 |
43 | if (isOwnPost) {
44 | subHeading = 'Manage your post'
45 | content = (
46 | <>
47 |
52 |
58 | >
59 | );
60 | }
61 |
62 | return (
63 |
69 |
73 |
74 | {content}
75 |
76 |
77 | );
78 | });
79 |
80 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
81 | container: {
82 | padding: 20,
83 | backgroundColor: theme.base
84 | },
85 | content: {
86 | flex: 1,
87 | paddingTop: 20,
88 | paddingBottom: 16
89 | }
90 | });
91 |
92 | export default PostOptionsBottomSheet;
--------------------------------------------------------------------------------
/app/layout/placeholders/PostViewScreen.Placeholder.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet, View } from 'react-native';
3 | import { Placeholder, PlaceholderLine, PlaceholderMedia } from 'rn-placeholder';
4 | import PlaceholderAnimation from './PlaceholderAnimation';
5 | import { AppContext } from '@app/context';
6 | import { PostDimensions } from '@app/constants';
7 |
8 | const PostViewScreenPlaceholder = () => {
9 | const { theme } = useContext(AppContext);
10 |
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
23 |
29 |
30 |
31 |
37 |
43 |
49 |
55 |
56 |
57 | );
58 | };
59 | const styles = StyleSheet.create({
60 | container: {
61 | paddingTop: 2
62 | },
63 | postHeader: {
64 | alignItems: 'center',
65 | flexDirection: 'row'
66 | },
67 | author: {
68 | flex: 1,
69 | justifyContent: 'center'
70 | },
71 | handlePlaceholder: {
72 | marginLeft: 12,
73 | borderRadius: 10
74 | },
75 | timePlaceholder: {
76 | marginTop: 10,
77 | marginLeft: 12,
78 | borderRadius: 10
79 | },
80 | card: {
81 | marginTop: 25,
82 | borderRadius: 10
83 | },
84 | likesPlaceholder: {
85 | marginTop: 20,
86 | marginBottom: 5,
87 | borderRadius: 10
88 | },
89 | captionPlaceholder: {
90 | marginTop: 8,
91 | borderRadius: 10
92 | }
93 | });
94 |
95 | export default PostViewScreenPlaceholder;
--------------------------------------------------------------------------------
/app/layout/placeholders/ExploreScreen.Placeholder.tsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useContext } from 'react';
2 | import { StyleSheet, View } from 'react-native';
3 | import { responsiveWidth } from 'react-native-responsive-dimensions';
4 | import { Placeholder, PlaceholderLine } from 'rn-placeholder';
5 | import { AppContext } from '@app/context';
6 | import PlaceholderAnimation from './PlaceholderAnimation';
7 |
8 | const ExploreScreenPlaceholder: React.FC = () => (
9 |
10 |
11 | {new Array(2).fill({}).map((_, index) => (
12 |
13 | {new Array(2).fill({}).map((__, index) => (
14 |
15 | ))}
16 |
17 |
18 | ))}
19 |
20 |
21 | );
22 |
23 |
24 | const PrimaryImageGroupPlaceholder = (): React.ReactElement => {
25 | const { theme } = useContext(AppContext);
26 | return (
27 |
28 |
29 |
30 |
31 |
32 | );
33 | }
34 |
35 | const SecondaryImageGroupPlaceholder = (): React.ReactElement => {
36 | const { theme } = useContext(AppContext);
37 | const secondaryTileOffset: number = 5;
38 | return (
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | );
47 | }
48 |
49 |
50 | const styles = StyleSheet.create({
51 | container: {
52 | flex: 1,
53 | paddingTop: 5,
54 | paddingHorizontal: 20
55 | },
56 | postContainer: {
57 | flexDirection: 'row',
58 | alignItems: 'center',
59 | marginBottom: 5
60 | },
61 | postPlaceholder: {
62 | marginRight: 5,
63 | borderRadius: 4
64 | },
65 | secondaryImageGroup: {
66 | flexDirection: 'row',
67 | marginBottom: 5
68 | },
69 | secondaryLeftColumn: {
70 | flex: 1,
71 | }
72 | });
73 |
74 | export default ExploreScreenPlaceholder;
--------------------------------------------------------------------------------
/app/layout/headers/AnimatedSearchBar.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from 'react';
2 | import { Keyboard, Platform, StyleProp, StyleSheet, Text, TextInput, TouchableOpacity, View, ViewStyle } from 'react-native';
3 | import posed from 'react-native-pose';
4 | import { AppContext } from '@app/context';
5 | import { Typography } from '@app/theme';
6 | import { ThemeColors } from '@app/types/theme';
7 |
8 | const { FontWeights, FontSizes } = Typography;
9 |
10 | interface AnimatedSearchBarProps {
11 | value: string,
12 | onChangeText: (text: string) => void,
13 | onFocus?: any,
14 | onBlur?: any,
15 | placeholder: string,
16 | style?: StyleProp
17 | };
18 |
19 | const TransitionInput = posed(TextInput)({
20 | focused: { width: '75%' },
21 | notFocused: { width: '90%' }
22 | });
23 |
24 | const TransitionTouchableOpacity = posed(TouchableOpacity)({
25 | focused: { width: 70 },
26 | notFocused: { width: 0 }
27 | });
28 |
29 | const AnimatedSearchBar: React.FC = ({ value, onChangeText, onFocus, onBlur, placeholder, style }) => {
30 |
31 | const { theme } = useContext(AppContext);
32 | const [focused, setFocused] = useState(false);
33 |
34 | const onOpen = () => {
35 | setFocused(true);
36 | onFocus();
37 | };
38 |
39 | const onCancel = () => {
40 | setFocused(false);
41 | Keyboard.dismiss();
42 | onChangeText('');
43 | onBlur();
44 | };
45 |
46 | const pose = focused ? 'focused' : 'notFocused';
47 |
48 | return (
49 |
50 |
59 |
64 | Cancel
65 |
66 |
67 | );
68 | };
69 |
70 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
71 | container: {
72 | flexDirection: 'row',
73 | alignItems: 'center',
74 | paddingBottom: 5
75 | },
76 | animatedSearchBar: {
77 | ...FontWeights.Light,
78 | ...FontSizes.Body,
79 | marginLeft: 20,
80 | paddingVertical: Platform.select({ ios: 10, android: 5 }),
81 | paddingHorizontal: 20,
82 | backgroundColor: theme.placeholder,
83 | color: theme.text01,
84 | borderRadius: 20,
85 | marginVertical: 5
86 | },
87 | cancel: {
88 | height: 20,
89 | alignItems: 'center',
90 | justifyContent: 'center'
91 | },
92 | cancelText: {
93 | height: 20,
94 | ...FontWeights.Light,
95 | ...FontSizes.Body,
96 | color: theme.text01
97 | }
98 | });
99 |
100 | export default AnimatedSearchBar;
--------------------------------------------------------------------------------
/ios/Proximity/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | Proximity
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 | $(MARKETING_VERSION)
21 | CFBundleSignature
22 | ????
23 | CFBundleURLTypes
24 |
25 |
26 | CFBundleTypeRole
27 | Editor
28 | CFBundleURLSchemes
29 |
30 | $(GOOGLE_REVERSED_CLIENT_ID)
31 |
32 |
33 |
34 | CFBundleVersion
35 | 1
36 | LSRequiresIPhoneOS
37 |
38 | NSAppTransportSecurity
39 |
40 | NSAllowsArbitraryLoads
41 |
42 | NSExceptionDomains
43 |
44 | localhost
45 |
46 | NSExceptionAllowsInsecureHTTPLoads
47 |
48 |
49 |
50 |
51 | NSLocationWhenInUseUsageDescription
52 |
53 | NSPhotoLibraryUsageDescription
54 | $(PRODUCT_NAME) would like access to your photo gallery for your profile picture
55 | UIAppFonts
56 |
57 | AntDesign.ttf
58 | Entypo.ttf
59 | EvilIcons.ttf
60 | Feather.ttf
61 | FontAwesome.ttf
62 | FontAwesome5_Brands.ttf
63 | FontAwesome5_Regular.ttf
64 | FontAwesome5_Solid.ttf
65 | Fontisto.ttf
66 | Foundation.ttf
67 | Ionicons.ttf
68 | MaterialCommunityIcons.ttf
69 | MaterialIcons.ttf
70 | Octicons.ttf
71 | SimpleLineIcons.ttf
72 | Zocial.ttf
73 | SFProDisplay-Bold.ttf
74 | SFProDisplay-Light.ttf
75 | SFProDisplay-Regular.ttf
76 |
77 | UIBackgroundModes
78 |
79 | remote-notification
80 |
81 | UILaunchStoryboardName
82 | LaunchScreen
83 | UIRequiredDeviceCapabilities
84 |
85 | armv7
86 |
87 | UISupportedInterfaceOrientations
88 |
89 | UIInterfaceOrientationPortrait
90 |
91 | UIViewControllerBasedStatusBarAppearance
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/app/screens/PostViewScreen/components/EditPostBottomSheet.tsx:
--------------------------------------------------------------------------------
1 | import { useMutation } from '@apollo/react-hooks';
2 | import React, { useContext, useRef, useState } from 'react';
3 | import { StyleSheet, View } from 'react-native';
4 | import { Modalize } from 'react-native-modalize';
5 | import Feather from 'react-native-vector-icons/Feather';
6 | import { Errors, IconSizes } from '@app/constants';
7 | import { AppContext } from '@app/context';
8 | import { MUTATION_EDIT_POST } from '@app/graphql/mutation';
9 | import { BottomSheetHeader, Button, FormInput } from '@app/layout';
10 | import { ThemeStatic } from '@app/theme';
11 | import { ThemeColors } from '@app/types/theme';
12 | import { crashlytics } from '@app/utils/firebase';
13 | import { postUpdatedNotification } from '@app/utils/notifications';
14 |
15 | interface EditPostBottomSheetProps {
16 | ref: React.Ref,
17 | postId: string,
18 | caption: string
19 | };
20 |
21 | const EditPostBottomSheet: React.FC = React.forwardRef(({ postId, caption }, ref) => {
22 |
23 | const { theme } = useContext(AppContext);
24 | const [editableCaption, setEditableCaption] = useState(caption);
25 | const [isUpdating, setIsUpdating] = useState(false);
26 | const editableCaptionRef = useRef();
27 |
28 | const [editPost] = useMutation(MUTATION_EDIT_POST);
29 |
30 | const updatePost = async () => {
31 | setIsUpdating(true);
32 | try {
33 | await editPost({ variables: { postId, caption: editableCaption } });
34 | } catch ({ message }) {
35 | crashlytics.recordCustomError(Errors.EDIT_POST, message);
36 | }
37 | setIsUpdating(false);
38 | postUpdatedNotification();
39 | // @ts-ignore
40 | ref.current.close();
41 | };
42 |
43 | const Icon = () => ;
48 |
49 | return (
50 |
56 |
60 |
61 |
70 |
77 |
78 |
79 | );
80 | });
81 |
82 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
83 | container: {
84 | padding: 20,
85 | backgroundColor: theme.base
86 | },
87 | content: {
88 | flex: 1,
89 | paddingTop: 20,
90 | paddingBottom: 20
91 | },
92 | updateButton: {
93 | marginVertical: 20
94 | }
95 | });
96 |
97 | export default EditPostBottomSheet;
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '9.0'
2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
3 |
4 | target 'Proximity' do
5 | # Pods for Proximity
6 | pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector"
7 | pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec"
8 | pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired"
9 | pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety"
10 | pod 'React', :path => '../node_modules/react-native/'
11 | pod 'React-Core', :path => '../node_modules/react-native/'
12 | pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules'
13 | pod 'React-Core/DevSupport', :path => '../node_modules/react-native/'
14 | pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS'
15 | pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation'
16 | pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob'
17 | pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image'
18 | pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS'
19 | pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network'
20 | pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings'
21 | pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text'
22 | pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration'
23 | pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/'
24 |
25 | pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact'
26 | pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi'
27 | pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor'
28 | pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector'
29 | pod 'ReactCommon/jscallinvoker', :path => "../node_modules/react-native/ReactCommon"
30 | pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon"
31 | pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
32 |
33 | pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
34 | pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
35 | pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
36 |
37 | pod 'Firebase/Core', '~> 6.13.0'
38 | pod 'Firebase/Storage', '~> 6.13.0'
39 | pod 'Firebase/Messaging', '~> 6.13.0'
40 | pod 'Firebase/Auth', '~> 6.13.0'
41 | pod 'Fabric', '~> 1.10.2'
42 | pod 'Crashlytics', '~> 3.14.0'
43 |
44 | target 'ProximityTests' do
45 | inherit! :search_paths
46 | # Pods for testing
47 | end
48 |
49 | use_native_modules!
50 | end
51 |
52 | target 'Proximity-tvOS' do
53 | # Pods for Proximity-tvOS
54 |
55 | target 'Proximity-tvOSTests' do
56 | inherit! :search_paths
57 | # Pods for testing
58 | end
59 |
60 | end
61 |
--------------------------------------------------------------------------------
/app/screens/PostViewScreen/components/LikesBottomSheet.tsx:
--------------------------------------------------------------------------------
1 | import { useQuery } from '@apollo/react-hooks';
2 | import React, { useContext } from 'react';
3 | import { StyleSheet, View } from 'react-native';
4 | import { Modalize } from 'react-native-modalize';
5 | import { responsiveHeight, responsiveWidth } from 'react-native-responsive-dimensions';
6 | import { FlatGrid } from 'react-native-super-grid';
7 | import EmptyLikesBanner from '@app/assets/svg/empty-likes.svg';
8 | import { AppContext } from '@app/context';
9 | import { QUERY_LIKE_USERS } from '@app/graphql/query';
10 | import { BottomSheetHeader, ConnectionsPlaceholder, SvgBanner, UserCard } from '@app/layout';
11 | import { ThemeColors } from '@app/types/theme';
12 |
13 | interface LikesBottomSheetProps {
14 | ref: React.Ref,
15 | likes: string[],
16 | onUserPress: (userId: string) => void
17 | };
18 |
19 | const LikesBottomSheet: React.FC = React.forwardRef(({ likes, onUserPress }, ref) => {
20 |
21 | const { theme } = useContext(AppContext);
22 |
23 | const { data, loading, error } = useQuery(QUERY_LIKE_USERS, {
24 | variables: { likes },
25 | fetchPolicy: 'network-only'
26 | });
27 |
28 | let content = ;
29 |
30 | const ListEmptyComponent = () => (
31 |
36 | );
37 |
38 | const renderItem = ({ item }) => {
39 | const { id, avatar, handle, name } = item;
40 | return (
41 | onUserPress(id)}
47 | />
48 | );
49 | };
50 |
51 | if (!loading && !error) {
52 | const { likeUsers } = data;
53 |
54 | content = (
55 |
67 | );
68 | }
69 |
70 | return (
71 |
76 |
80 |
81 | {content}
82 |
83 |
84 | );
85 | });
86 |
87 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
88 | container: {
89 | marginTop: 40,
90 | padding: 20,
91 | backgroundColor: theme.base
92 | },
93 | content: {
94 | flex: 1,
95 | paddingBottom: responsiveHeight(5)
96 | },
97 | listContainer: {
98 | flex: 1
99 | },
100 | listItemContainer: {
101 | width: '106%'
102 | },
103 | listContentContainer: {
104 | alignItems: 'center',
105 | justifyContent: 'flex-start'
106 | }
107 | });
108 |
109 | export default LikesBottomSheet;
--------------------------------------------------------------------------------
/App.tsx:
--------------------------------------------------------------------------------
1 | import { ApolloProvider, useMutation } from '@apollo/react-hooks';
2 | import { GoogleSignin } from '@react-native-community/google-signin';
3 | import React, { useContext, useEffect } from 'react';
4 | import { StatusBar, StyleSheet } from 'react-native';
5 | import codePush from 'react-native-code-push';
6 | import FlashMessage from 'react-native-flash-message';
7 | import { SafeAreaView } from 'react-navigation';
8 | import Config from './app/config';
9 | import { Errors, PollIntervals } from './app/constants';
10 | import { AppContext, AppContextProvider } from './app/context';
11 | import client from './app/graphql/client';
12 | import { MUTATION_LAST_SEEN } from './app/graphql/mutation';
13 | import AppNavigator from './app/navigation';
14 | import { ThemeStatic, Typography } from './app/theme';
15 | import { DynamicStatusBar } from './app/theme/Colors';
16 | import { ThemeColors } from './app/types/theme';
17 | import { crashlytics } from './app/utils/firebase';
18 | import { loadThemeType } from './app/utils/storage';
19 | import { computeUnreadMessages } from './app/utils/shared';
20 |
21 | const { webClientId } = Config;
22 |
23 | GoogleSignin.configure({
24 | webClientId,
25 | forceConsentPrompt: true
26 | });
27 |
28 | const SafeAreaApp = () => {
29 | const { user, theme, themeType, toggleTheme, updateUnreadMessages } = useContext(AppContext);
30 | const { barStyle, backgroundColor } = DynamicStatusBar[themeType];
31 | const [updateLastSeen] = useMutation(MUTATION_LAST_SEEN);
32 |
33 | const initializeTheme = async () => {
34 | try {
35 | const themeType = await loadThemeType();
36 | toggleTheme(themeType);
37 | } catch ({ message }) {
38 | crashlytics.recordCustomError(Errors.LOAD_THEME, message);
39 | }
40 | };
41 |
42 | useEffect(() => {
43 | initializeTheme();
44 | }, []);
45 |
46 | useEffect(() => {
47 | setInterval(async () => {
48 | if (user.id) {
49 | try {
50 | const { data: { updateLastSeen: { chats } } } = await updateLastSeen({ variables: { userId: user.id } });
51 | const unreadMessages = computeUnreadMessages(chats, user.id);
52 |
53 | updateUnreadMessages(unreadMessages);
54 | } catch ({ message }) {
55 | crashlytics.recordCustomError(Errors.UPDATE_LAST_SEEN, message);
56 | }
57 | }
58 | }, PollIntervals.lastSeen);
59 | }, [user.id]);
60 |
61 | return (
62 |
63 |
64 |
65 |
66 |
67 | );
68 | };
69 |
70 | const App = () => {
71 | return (
72 |
73 |
74 |
75 |
76 |
77 | );
78 | };
79 |
80 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
81 | container: {
82 | flex: 1,
83 | backgroundColor: theme.base
84 | },
85 | flashMessageTitle: {
86 | ...Typography.FontWeights.Light,
87 | ...Typography.FontSizes.Body,
88 | color: ThemeStatic.white
89 | }
90 | });
91 |
92 | const CodepushApp = codePush({
93 | deploymentKey: Config.codepush.production,
94 | checkFrequency: codePush.CheckFrequency.ON_APP_START
95 | })(App);
96 |
97 | export default CodepushApp;
98 |
--------------------------------------------------------------------------------
/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 http://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 Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
34 |
35 | @rem Find java.exe
36 | if defined JAVA_HOME goto findJavaFromJavaHome
37 |
38 | set JAVA_EXE=java.exe
39 | %JAVA_EXE% -version >NUL 2>&1
40 | if "%ERRORLEVEL%" == "0" goto init
41 |
42 | echo.
43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
44 | echo.
45 | echo Please set the JAVA_HOME variable in your environment to match the
46 | echo location of your Java installation.
47 |
48 | goto fail
49 |
50 | :findJavaFromJavaHome
51 | set JAVA_HOME=%JAVA_HOME:"=%
52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
53 |
54 | if exist "%JAVA_EXE%" goto init
55 |
56 | echo.
57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
58 | echo.
59 | echo Please set the JAVA_HOME variable in your environment to match the
60 | echo location of your Java installation.
61 |
62 | goto fail
63 |
64 | :init
65 | @rem Get command-line arguments, handling Windows variants
66 |
67 | if not "%OS%" == "Windows_NT" goto win9xME_args
68 |
69 | :win9xME_args
70 | @rem Slurp the command line arguments.
71 | set CMD_LINE_ARGS=
72 | set _SKIP=2
73 |
74 | :win9xME_args_slurp
75 | if "x%~1" == "x" goto execute
76 |
77 | set CMD_LINE_ARGS=%*
78 |
79 | :execute
80 | @rem Setup the command line
81 |
82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
83 |
84 | @rem Execute Gradle
85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
86 |
87 | :end
88 | @rem End local scope for the variables with windows NT shell
89 | if "%ERRORLEVEL%"=="0" goto mainEnd
90 |
91 | :fail
92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
93 | rem the _cmd.exe /c_ return code!
94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
95 | exit /b 1
96 |
97 | :mainEnd
98 | if "%OS%"=="Windows_NT" endlocal
99 |
100 | :omega
101 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/proximity/app/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.proximity.app;
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.ReactNativeHost;
8 | import com.facebook.react.ReactPackage;
9 | import com.facebook.soloader.SoLoader;
10 | import java.lang.reflect.InvocationTargetException;
11 | import java.util.List;
12 |
13 | import io.invertase.firebase.storage.RNFirebaseStoragePackage;
14 | import io.invertase.firebase.messaging.RNFirebaseMessagingPackage;
15 | import io.invertase.firebase.notifications.RNFirebaseNotificationsPackage;
16 | import io.invertase.firebase.fabric.crashlytics.RNFirebaseCrashlyticsPackage;
17 | import io.invertase.firebase.auth.RNFirebaseAuthPackage;
18 | import com.microsoft.codepush.react.CodePush;
19 |
20 | public class MainApplication extends Application implements ReactApplication {
21 |
22 | private final ReactNativeHost mReactNativeHost =
23 | new ReactNativeHost(this) {
24 | @Override
25 | public boolean getUseDeveloperSupport() {
26 | return BuildConfig.DEBUG;
27 | }
28 |
29 | // Codepush [CUSTOM]
30 | @Override
31 | protected String getJSBundleFile() {
32 | return CodePush.getJSBundleFile();
33 | }
34 |
35 | @Override
36 | protected List getPackages() {
37 | @SuppressWarnings("UnnecessaryLocalVariable")
38 | List packages = new PackageList(this).getPackages();
39 | // Packages that cannot be autolinked yet can be added manually here, for example:
40 | // packages.add(new MyReactNativePackage());
41 | packages.add(new RNFirebaseStoragePackage());
42 | packages.add(new RNFirebaseMessagingPackage());
43 | packages.add(new RNFirebaseNotificationsPackage());
44 | packages.add(new RNFirebaseCrashlyticsPackage());
45 | packages.add(new RNFirebaseAuthPackage());
46 | return packages;
47 | }
48 |
49 | @Override
50 | protected String getJSMainModuleName() {
51 | return "index";
52 | }
53 | };
54 |
55 | @Override
56 | public ReactNativeHost getReactNativeHost() {
57 | return mReactNativeHost;
58 | }
59 |
60 | @Override
61 | public void onCreate() {
62 | super.onCreate();
63 | SoLoader.init(this, /* native exopackage */ false);
64 | initializeFlipper(this); // Remove this line if you don't want Flipper enabled
65 | }
66 |
67 | /**
68 | * Loads Flipper in React Native templates.
69 | *
70 | * @param context
71 | */
72 | private static void initializeFlipper(Context context) {
73 | if (BuildConfig.DEBUG) {
74 | try {
75 | /*
76 | We use reflection here to pick up the class that initializes Flipper,
77 | since Flipper library is not available in release mode
78 | */
79 | Class> aClass = Class.forName("com.facebook.flipper.ReactNativeFlipper");
80 | aClass.getMethod("initializeFlipper", Context.class).invoke(null, context);
81 | } catch (ClassNotFoundException e) {
82 | e.printStackTrace();
83 | } catch (NoSuchMethodException e) {
84 | e.printStackTrace();
85 | } catch (IllegalAccessException e) {
86 | e.printStackTrace();
87 | } catch (InvocationTargetException e) {
88 | e.printStackTrace();
89 | }
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/app/utils/notifications.ts:
--------------------------------------------------------------------------------
1 | import { showMessage } from 'react-native-flash-message';
2 | import { ThemeStatic } from '@app/theme';
3 |
4 | export const welcomeNotification = () => showMessage({
5 | message: 'Welcome to Proximity',
6 | icon: 'success',
7 | type: 'success',
8 | duration: 2000
9 | });
10 |
11 | export const postUploadedNotification = () => showMessage({
12 | message: 'Upload complete, your post is live',
13 | icon: 'success',
14 | type: 'success',
15 | duration: 2000
16 | });
17 |
18 | export const uploadErrorNotification = (asset: string) => showMessage({
19 | message: `${asset} upload failed, please try again later`,
20 | icon: 'danger',
21 | type: 'danger',
22 | duration: 2000
23 | });
24 |
25 | export const inputLimitErrorNotification = (type: string, condition: string, limit: number) => showMessage({
26 | message: `${type} should be ${condition} than ${limit} characters`,
27 | icon: 'danger',
28 | type: 'danger',
29 | duration: 4000
30 | });
31 |
32 | export const somethingWentWrongErrorNotification = () => showMessage({
33 | message: 'Oops, please try again later',
34 | icon: 'danger',
35 | type: 'danger',
36 | duration: 4000
37 | });
38 |
39 |
40 | export const showErrorNotification = (message: string) => showMessage({
41 | message,
42 | icon: 'danger',
43 | type: 'danger',
44 | duration: 4000
45 | });
46 |
47 | export const noAssetInfoNotification = () => showMessage({
48 | message: 'Please pick an image before uploading',
49 | icon: 'info',
50 | type: 'info',
51 | backgroundColor: ThemeStatic.accent,
52 | duration: 2000
53 | });
54 |
55 | export const noPermissionNotification = () => showMessage({
56 | message: 'Please allow photo gallery permissions',
57 | icon: 'danger',
58 | type: 'danger',
59 | duration: 4000
60 | });
61 |
62 | export const longPressDeleteNotification = onLongPress => showMessage({
63 | message: 'Long press this notification to delete',
64 | icon: 'danger',
65 | type: 'danger',
66 | duration: 4000,
67 | backgroundColor: ThemeStatic.delete,
68 | onLongPress
69 | });
70 |
71 | export const tryAgainLaterNotification = () => showMessage({
72 | message: 'Please try again later',
73 | icon: 'danger',
74 | type: 'danger',
75 | duration: 4000
76 | });
77 |
78 | export const postReportedNotification = () => showMessage({
79 | message: 'Post has been reported and submitted for review',
80 | icon: 'info',
81 | type: 'info',
82 | backgroundColor: ThemeStatic.accent,
83 | duration: 4000
84 | });
85 |
86 | export const postUpdatedNotification = () => showMessage({
87 | message: 'Post has been updated',
88 | icon: 'success',
89 | type: 'success',
90 | duration: 2000
91 | });
92 |
93 | export const postDeletedNotification = () => showMessage({
94 | message: 'Post has been deleted',
95 | icon: 'info',
96 | type: 'info',
97 | backgroundColor: ThemeStatic.accent,
98 | duration: 2000
99 | });
100 |
101 | export const userBlockedNotification = (handle: string = 'User') => showMessage({
102 | message: `${handle} has been blocked, please refresh your feed`,
103 | icon: 'info',
104 | type: 'info',
105 | backgroundColor: ThemeStatic.accent,
106 | duration: 4000
107 | });
108 |
109 | export const longPressUnblockNotification = (onLongPress, handle) => showMessage({
110 | message: `Long press this notification to unblock ${handle}`,
111 | icon: 'danger',
112 | type: 'danger',
113 | duration: 4000,
114 | backgroundColor: ThemeStatic.delete,
115 | onLongPress
116 | });
--------------------------------------------------------------------------------
/app/screens/MessageScreen/components/NewMessageBottomSheet.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet, View } from 'react-native';
3 | import { Modalize } from 'react-native-modalize';
4 | import { responsiveHeight, responsiveWidth } from 'react-native-responsive-dimensions';
5 | import { FlatGrid } from 'react-native-super-grid';
6 | import EmptyConnectionsBanner from '@app/../assets/svg/empty-connections.svg';
7 |
8 | import { AppContext } from '@app/context';
9 | import { BottomSheetHeader, SvgBanner, ConnectionsPlaceholder, UserCard } from '@app/layout';
10 | import { ThemeColors } from '@app/types/theme';
11 | import { useQuery } from '@apollo/react-hooks';
12 | import { QUERY_USER_CONNECTIONS } from '@app/graphql/query';
13 | import { Connections } from '@app/constants';
14 |
15 | interface NewMessageBottomSheetProps {
16 | ref: React.Ref,
17 | onConnectionSelect: (targetId: string, avatar: string, handle: string) => void
18 | };
19 |
20 | const NewMessageBottomSheet: React.FC = React.forwardRef(({ onConnectionSelect }, ref) => {
21 |
22 | const { user, theme } = useContext(AppContext);
23 |
24 | const { data, loading, error } = useQuery(QUERY_USER_CONNECTIONS, {
25 | variables: { userId: user.id, type: Connections.FOLLOWING },
26 | fetchPolicy: 'network-only'
27 | });
28 |
29 | let content = ;
30 |
31 | const ListEmptyComponent = () => (
32 |
37 | );
38 |
39 | const renderItem = ({ item }) => {
40 | const { id, avatar, handle, name } = item;
41 | return (
42 | onConnectionSelect(id, avatar, handle)}
48 | />
49 | );
50 | };
51 |
52 | if (!loading && !error) {
53 | const { userConnections } = data;
54 | content = (
55 |
67 | );
68 | }
69 |
70 | return (
71 |
76 |
80 |
81 | {content}
82 |
83 |
84 | );
85 | });
86 |
87 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
88 | container: {
89 | marginTop: 40,
90 | padding: 20,
91 | backgroundColor: theme.base
92 | },
93 | content: {
94 | flex: 1,
95 | paddingBottom: responsiveHeight(5)
96 | },
97 | listContainer: {
98 | flex: 1
99 | },
100 | listItemContainer: {
101 | width: '106%'
102 | },
103 | listContentContainer: {
104 | alignItems: 'center',
105 | justifyContent: 'flex-start'
106 | }
107 | });
108 |
109 | export default NewMessageBottomSheet;
--------------------------------------------------------------------------------
/app/utils/firebase.ts:
--------------------------------------------------------------------------------
1 | import firebase, { AuthCredential } from 'react-native-firebase';
2 | import { generateUUID } from './shared';
3 | import { Asset, StoragePaths, Errors, AuthDefaults } from '@app/constants';
4 | import { Platform } from 'react-native';
5 | import { SocialSignInType, SocialSignInResult } from '@app/types/auth';
6 |
7 | export const storage = firebase.storage();
8 | export const messaging = firebase.messaging();
9 | export const notifications = firebase.notifications();
10 | export const crashlytics = firebase.crashlytics();
11 | export const auth = firebase.auth();
12 |
13 | const getGoogleCredentials = (
14 | idToken: string | null,
15 | accessToken: string | undefined,
16 | ): AuthCredential =>
17 | firebase.auth.GoogleAuthProvider.credential(idToken, accessToken);
18 |
19 | const getAppleCredentials = (
20 | identityToken: string | null,
21 | nonce: string,
22 | ): AuthCredential =>
23 | firebase.auth.AppleAuthProvider.credential(identityToken, nonce);
24 |
25 | export const processSocialSignIn = async (
26 | authResult: any,
27 | type: SocialSignInType,
28 | ): Promise => {
29 | let credentials: AuthCredential;
30 |
31 | if (type === SocialSignInType.GOOGLE) {
32 | const { idToken, accessToken } = authResult;
33 | credentials = getGoogleCredentials(idToken, accessToken);
34 | } else {
35 | const { identityToken, nonce } = authResult;
36 | credentials = getAppleCredentials(identityToken, nonce);
37 | }
38 |
39 | const {
40 | user: { providerData, providerId },
41 | } = await auth.signInWithCredential(credentials);
42 | const [
43 | { uid: token, displayName, photoURL, email },
44 | ] = providerData;
45 |
46 | const name = displayName || AuthDefaults.name;
47 | const avatar = photoURL || AuthDefaults.avatar;
48 | const safeEmail = email || `${token}@${providerId}`;
49 |
50 | const socialSignInResult = {
51 | token,
52 | name,
53 | avatar,
54 | email: safeEmail,
55 | };
56 |
57 | return socialSignInResult;
58 | };
59 |
60 | export const initializeFCM = async () => {
61 | try {
62 | if (Platform.OS === 'android') {
63 | const channel = new firebase.notifications.Android.Channel(
64 | 'proximity-channel',
65 | 'Notification Channel',
66 | firebase.notifications.Android.Importance.Max,
67 | )
68 | .setDescription('Proximity Notification Channel')
69 | .setSound('default');
70 |
71 | notifications.android.createChannel(channel);
72 | }
73 | const hasPermission = await messaging.hasPermission();
74 | if (!hasPermission) {
75 | await messaging.requestPermission();
76 | return null;
77 | } else if (hasPermission) {
78 | return await messaging.getToken();
79 | }
80 | } catch ({ message }) {
81 | crashlytics.recordCustomError(Errors.INITIALIZE_FCM, message);
82 | }
83 | };
84 |
85 | export const uploadToStorage = (asset: string, uri: string, userId: string) => {
86 | const [type] = getMediaType(uri);
87 | let storageRef: string | undefined;
88 |
89 | if (asset === Asset.avatar) {
90 | storageRef = `${StoragePaths.avatars}/${userId}.${type}`;
91 | } else if (asset === Asset.post) {
92 | storageRef = `${StoragePaths.posts}/${userId}/${generateUUID()}.${type}`;
93 | }
94 |
95 | return storage.ref(storageRef).putFile(uri);
96 | };
97 |
98 | export const getMediaType = (uri: string) => uri.split('.').slice(-1);
99 |
100 | export const deleteFromStorage = (uri: string) =>
101 | storage.refFromURL(uri).delete();
102 |
--------------------------------------------------------------------------------
/app/screens/ProfileScreen/components/SettingsBottomSheet.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useState } from 'react';
2 | import { StyleSheet, View } from 'react-native';
3 | import { Modalize } from 'react-native-modalize';
4 | import Checkbox from 'react-native-modest-checkbox';
5 | import { responsiveWidth } from 'react-native-responsive-dimensions';
6 | import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
7 | import { useNavigation } from 'react-navigation-hooks';
8 | import { IconSizes, Routes } from '@app/constants';
9 | import { AppContext } from '@app/context';
10 | import { BottomSheetHeader, Option } from '@app/layout';
11 | import { ThemeStatic, ThemeVariant, Typography } from '@app/theme';
12 | import { ThemeColors } from '@app/types/theme';
13 | import { signOut } from '@app/utils/authentication';
14 |
15 | const { FontWeights, FontSizes } = Typography;
16 |
17 | interface SettingsBottomSheetProps {
18 | ref: React.Ref,
19 | onBlockListPress: () => void,
20 | onAboutPress: () => void
21 | };
22 |
23 | const SettingsBottomSheet: React.FC = React.forwardRef(({ onBlockListPress, onAboutPress }, ref) => {
24 |
25 | const { toggleTheme, theme, themeType } = useContext(AppContext);
26 | const { navigate } = useNavigation();
27 | const [isChecked, setIsChecked] = useState(false);
28 |
29 | useEffect(() => {
30 | setIsChecked(themeType === ThemeVariant.dark);
31 | }, []);
32 |
33 | const onChange = ({ checked }) => {
34 | if (checked) toggleTheme(ThemeVariant.dark);
35 | else toggleTheme(ThemeVariant.light);
36 | setIsChecked(checked);
37 | };
38 |
39 | const logOut = async () => {
40 | await signOut();
41 | navigate(Routes.Auth);
42 | };
43 |
44 | return (
45 |
51 |
55 |
56 |
61 |
72 |
77 |
82 |
83 |
84 | );
85 | });
86 |
87 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
88 | container: {
89 | padding: 20,
90 | backgroundColor: theme.base
91 | },
92 | content: {
93 | flex: 1,
94 | paddingTop: 20
95 | },
96 | label: {
97 | ...FontWeights.Light,
98 | ...FontSizes.Body,
99 | width: responsiveWidth(74),
100 | color: theme.text01
101 | }
102 | });
103 |
104 | export default SettingsBottomSheet;
--------------------------------------------------------------------------------
/app/layout/shared/ConnectionsBottomSheet.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet, View } from 'react-native';
3 | import { Modalize } from 'react-native-modalize';
4 | import { responsiveHeight, responsiveWidth } from 'react-native-responsive-dimensions';
5 | import { FlatGrid } from 'react-native-super-grid';
6 | import BottomSheetHeader from '../headers/BottomSheetHeader';
7 | import SvgBanner from '../misc/SvgBanner';
8 | import EmptyConnectionsBanner from '@app/assets/svg/empty-connections.svg';
9 | import { Connections } from '@app/constants';
10 | import { AppContext } from '@app/context';
11 | import { ThemeColors } from '@app/types/theme';
12 | import UserCard from './UserCard';
13 | import { Connection } from '@app/types/screens';
14 |
15 | interface ConnectionsBottomSheetProps {
16 | ref: React.Ref,
17 | viewMode?: boolean,
18 | data: Connection[],
19 | handle?: string,
20 | type: string
21 | };
22 |
23 | const ConnectionsBottomSheet: React.FC = React.forwardRef(({ viewMode, handle, data, type }, ref) => {
24 |
25 | const { theme } = useContext(AppContext);
26 |
27 | let heading;
28 | let subHeading;
29 |
30 | if (type === Connections.FOLLOWING) {
31 | heading = 'Following';
32 | if (viewMode) {
33 | subHeading = `People ${handle} is following`;
34 | } else {
35 | subHeading = 'People you are following';
36 | }
37 | } else if (type === Connections.FOLLOWERS) {
38 | heading = 'Followers';
39 | if (viewMode) {
40 | subHeading = `People who are following ${handle}`;
41 | } else {
42 | subHeading = 'People who are following you';
43 | }
44 | }
45 |
46 | const ListEmptyComponent = () => (
47 |
52 | );
53 |
54 | const renderItem = ({ item }) => {
55 | const { id, avatar, handle, name } = item;
56 | return (
57 |
63 | );
64 | };
65 |
66 | return (
67 |
72 |
76 |
77 |
89 |
90 |
91 | );
92 | });
93 |
94 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
95 | container: {
96 | marginTop: 40,
97 | padding: 20,
98 | backgroundColor: theme.base
99 | },
100 | content: {
101 | flex: 1,
102 | paddingBottom: responsiveHeight(5)
103 | },
104 | listContainer: {
105 | flex: 1
106 | },
107 | listItemContainer: {
108 | width: '106%'
109 | },
110 | listContentContainer: {
111 | alignItems: 'center',
112 | justifyContent: 'flex-start'
113 | }
114 | });
115 |
116 | export default ConnectionsBottomSheet;
--------------------------------------------------------------------------------
/app/constants/index.ts:
--------------------------------------------------------------------------------
1 | import { responsiveWidth } from 'react-native-responsive-dimensions';
2 | import {
3 | ConnectionsType,
4 | FollowInteractionType,
5 | IconSizesType,
6 | LikeActionType,
7 | NotificationTextType,
8 | PollIntervalsType,
9 | PostDimensionsType,
10 | RoutesType,
11 | StoragePathsType,
12 | AssetType,
13 | TimeoutsType,
14 | ErrorsType,
15 | AuthDefaultsType,
16 | PaginationType,
17 | DebounceType,
18 | } from '@app/types/constants';
19 |
20 | export const Routes: RoutesType = {
21 | App: 'App',
22 | Auth: 'Auth',
23 | HomeScreen: 'HomeScreen,',
24 | ExploreScreen: 'ExploreScreen,',
25 | UploadScreen: 'UploadScreen,',
26 | NotificationScreen: 'NotificationScreen,',
27 | ProfileScreen: 'ProfileScreen,',
28 | TabNavigator: 'TabNavigator',
29 | ProfileViewScreen: 'ProfileViewScreen',
30 | MessageScreen: 'MessageScreen',
31 | ConversationScreen: 'ConversationScreen',
32 | PostViewScreen: 'PostViewScreen',
33 | LoginScreen: 'LoginScreen'
34 | };
35 |
36 | export const NotificationType: NotificationTextType = {
37 | FOLLOW: 'FOLLOW',
38 | COMMENT: 'COMMENT',
39 | LIKE: 'LIKE'
40 | };
41 |
42 | export const NotificationText: NotificationTextType = {
43 | FOLLOW: 'has started following you',
44 | COMMENT: 'commented on your post',
45 | LIKE: 'liked your post'
46 | };
47 |
48 | export const FollowInteraction: FollowInteractionType = {
49 | FOLLOW: 'FOLLOW',
50 | UNFOLLOW: 'UNFOLLOW'
51 | };
52 |
53 | export const LikeAction: LikeActionType = {
54 | LIKE: 'LIKE',
55 | UNLIKE: 'UNLIKE'
56 | };
57 |
58 | export const Connections: ConnectionsType = {
59 | FOLLOWING: 'FOLLOWING',
60 | FOLLOWERS: 'FOLLOWERS'
61 | };
62 |
63 | export const IconSizes: IconSizesType = {
64 | x00: 4,
65 | x0: 6,
66 | x1: 10,
67 | x2: 12,
68 | x3: 14,
69 | x4: 16,
70 | x5: 20,
71 | x6: 24,
72 | x7: 28,
73 | x8: 32,
74 | x9: 40,
75 | x10: 50,
76 | x11: 60,
77 | x12: 100
78 | };
79 |
80 | export const PostDimensions: PostDimensionsType = {
81 | Small: { height: responsiveWidth(29), width: responsiveWidth(29) },
82 | Medium: { height: responsiveWidth(43), width: responsiveWidth(43) },
83 | Large: { height: responsiveWidth(90), width: responsiveWidth(90) }
84 | };
85 |
86 | export const PollIntervals: PollIntervalsType = {
87 | messages: 2 * 1000,
88 | profile: 1000,
89 | profileView: 1000,
90 | postView: 2 * 1000,
91 | interaction: 1000,
92 | notification: 2 * 1000,
93 | lastSeen: 10 * 1000,
94 | blockList: 1000
95 | };
96 |
97 | export const StoragePaths: StoragePathsType = {
98 | avatars: 'avatars',
99 | posts: 'posts'
100 | };
101 |
102 | export const Asset: AssetType = {
103 | avatar: 'avatar',
104 | post: 'post'
105 | };
106 |
107 | export const Timeouts: TimeoutsType = {
108 | online: 12
109 | };
110 |
111 | export const Errors: ErrorsType = {
112 | SIGN_IN: 'SIGN_IN',
113 | SIGN_OUT: 'SIGN_OUT',
114 | UPDATE_LAST_SEEN: 'UPDATE_LAST_SEEN',
115 | LOAD_THEME: 'LOAD_THEME',
116 | INITIALIZE_FCM: 'INITIALIZE_FCM',
117 | INITIALIZE_CHAT: 'INITIALIZE_CHAT',
118 | UPDATE_FCM_TOKEN: 'UPDATE_FCM_TOKEN',
119 | ASSET_UPLOAD: 'ASSET_UPLOAD',
120 | EDIT_POST: 'EDIT_POST'
121 | };
122 |
123 | export const AuthDefaults: AuthDefaultsType = {
124 | avatar: 'https://firebasestorage.googleapis.com/v0/b/proximity-406e5.appspot.com/o/defaults%2Favatar-default.png?alt=media&token=23a37081-e594-4184-ba9f-c986ee9d3c41',
125 | name: 'Proximity user'
126 | };
127 |
128 | export const Pagination: PaginationType = {
129 | PAGE_SIZE: 15,
130 | CURSOR_SIZE: 9
131 | };
132 |
133 | export const Debounce: DebounceType = {
134 | EXPLORE_SEARCH: 400
135 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Proximity",
3 | "version": "2.1.0",
4 | "private": true,
5 | "author": {
6 | "name": "Karan Pratap Singh",
7 | "email": "karan.pratapsingh686@gmail.com",
8 | "url": "http://karanpratapsingh.com"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/karanpratapsingh/Proximity.git"
13 | },
14 | "scripts": {
15 | "start": "react-native start",
16 | "test": "jest",
17 | "cocoapods": "cd ios && pod install && cd ..",
18 | "ios": "react-native run-ios --simulator=\"iPhone 8\"",
19 | "device": "react-native run-ios --device=\"iPhone\"",
20 | "android": "react-native run-android",
21 | "emulator": "emulator @PIXEL_API_28",
22 | "codepush:ios": "appcenter codepush release-react -a Karan-Pratap-Singh/Proximity -d Production",
23 | "codepush:android": "appcenter codepush release-react -a Karan-Pratap-Singh/Proximity-Android -d Production"
24 | },
25 | "dependencies": {
26 | "@apollo/react-hooks": "^3.1.3",
27 | "@gorhom/sticky-item": "^2.0.0",
28 | "@invertase/react-native-apple-authentication": "^0.1.1",
29 | "@react-native-community/async-storage": "^1.7.0",
30 | "@react-native-community/google-signin": "^3.0.3",
31 | "@types/jest": "^24.0.21",
32 | "@types/react": "^16.9.11",
33 | "@types/react-native": "^0.60.22",
34 | "@types/react-redux": "^7.1.5",
35 | "@types/react-test-renderer": "^16.9.1",
36 | "apollo-boost": "^0.4.4",
37 | "apollo-cache-inmemory": "^1.6.3",
38 | "apollo-client": "^2.6.4",
39 | "apollo-link": "^1.2.13",
40 | "apollo-link-error": "^1.1.12",
41 | "apollo-link-http": "^1.5.16",
42 | "apollo-link-ws": "^1.0.19",
43 | "apollo-utilities": "^1.3.2",
44 | "dateformat": "^3.0.3",
45 | "graphql": "^14.5.8",
46 | "graphql-tag": "^2.10.1",
47 | "react": "16.9.0",
48 | "react-native": "0.61.5",
49 | "react-native-code-push": "^6.0.0",
50 | "react-native-fast-image": "^7.0.2",
51 | "react-native-firebase": "^5.6.0",
52 | "react-native-flash-message": "^0.1.15",
53 | "react-native-gesture-handler": "^1.6.0",
54 | "react-native-gifted-chat": "^0.12.0",
55 | "react-native-image-crop-picker": "^0.26.2",
56 | "react-native-indicators": "^0.17.0",
57 | "react-native-iphone-x-helper": "^1.2.1",
58 | "react-native-material-textfield": "^0.16.1",
59 | "react-native-modal": "^11.5.4",
60 | "react-native-modalize": "^1.3.6",
61 | "react-native-modest-checkbox": "^3.3.0",
62 | "react-native-pose": "^0.9.1",
63 | "react-native-reanimated": "^1.7.0",
64 | "react-native-responsive-dimensions": "^3.0.0",
65 | "react-native-splash-screen": "^3.2.0",
66 | "react-native-storage": "^1.0.1",
67 | "react-native-story": "^0.1.3",
68 | "react-native-super-grid": "^3.1.1",
69 | "react-native-svg": "^9.13.3",
70 | "react-native-vector-icons": "^6.6.0",
71 | "react-navigation": "^4.0.10",
72 | "react-navigation-animated-switch": "^0.3.2",
73 | "react-navigation-hooks": "^1.1.0",
74 | "react-navigation-stack": "^1.10.3",
75 | "react-navigation-tabs": "^2.5.6",
76 | "rn-placeholder": "^3.0.0",
77 | "subscriptions-transport-ws": "^0.9.16",
78 | "typescript": "^3.8.3"
79 | },
80 | "devDependencies": {
81 | "@babel/core": "^7.5.0",
82 | "@babel/runtime": "^7.5.0",
83 | "@react-native-community/eslint-config": "^0.0.3",
84 | "appcenter-cli": "^2.3.3",
85 | "babel-jest": "^24.1.0",
86 | "babel-plugin-module-resolver": "^4.0.0",
87 | "jest": "^24.1.0",
88 | "metro-react-native-babel-preset": "0.51.1",
89 | "react-native-svg-transformer": "^0.14.0",
90 | "react-test-renderer": "16.8.1"
91 | },
92 | "jest": {
93 | "preset": "react-native"
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/app/screens/ExploreScreen/components/ExplorePostCard.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react';
2 | import { StyleSheet, View, TouchableOpacity, StyleProp, ViewStyle } from 'react-native';
3 | import { useNavigation } from 'react-navigation-hooks';
4 | import { Routes, PostDimensions } from '@app/constants';
5 | import { NativeImage } from '@app/layout';
6 | import { AppContext } from '@app/context';
7 | import { ThemeColors } from '@app/types/theme';
8 | import { ExplorePost } from '@app/types/screens';
9 |
10 | interface ExplorePostCardProps {
11 | postId: string,
12 | uri: string,
13 | large?: boolean
14 | style?: StyleProp
15 | };
16 |
17 | const ExplorePostCard: React.FC = ({ postId, uri, large, style }): React.ReactElement => {
18 |
19 | const { theme } = useContext(AppContext);
20 | const { navigate } = useNavigation();
21 |
22 | const navigateToPost = () => {
23 | navigate(Routes.PostViewScreen, { postId });
24 | };
25 |
26 | const containerStyle = large ? { height: '100%', width: '100%' } : {};
27 |
28 | return (
29 |
30 |
31 |
32 | );
33 | };
34 |
35 | export const PrimaryImageGroup = ({ imageGroup }: { imageGroup: ExplorePost[] }): React.ReactElement => (
36 |
37 | {
38 | imageGroup.map(({ id: postId, uri }) => (
39 |
44 | ))
45 | }
46 |
47 | );
48 |
49 | type SecondaryImageGroupProps = {
50 | imageGroup: ExplorePost[];
51 | reversed: boolean;
52 | };
53 |
54 | export const SecondaryImageGroup: React.FC = ({ imageGroup, reversed }): React.ReactElement => {
55 | const leftColumns = imageGroup.slice(0, 2);
56 | const rightColumn = imageGroup.slice(2, 3);
57 |
58 | const reverseDirection: ViewStyle = reversed ? { flexDirection: 'row-reverse' } : {};
59 | const reverseMargin: ViewStyle = reversed ? { alignItems: 'flex-end', marginLeft: 2 } : { marginRight: 2 };
60 |
61 | return (
62 |
63 |
64 | {
65 | leftColumns.map(({ id: postId, uri }, index) => {
66 | const style = index ? { marginTop: 5 } : {};
67 | return (
68 |
74 | )
75 | })
76 | }
77 |
78 |
79 | {
80 | rightColumn.map(({ id: postId, uri }) => (
81 |
87 | ))
88 | }
89 |
90 |
91 | );
92 | };
93 |
94 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
95 | container: {
96 | ...PostDimensions.Small,
97 | overflow: 'hidden',
98 | borderRadius: 4
99 | },
100 | postImage: {
101 | flex: 1,
102 | backgroundColor: theme.placeholder
103 | },
104 | gridImageGroup: {
105 | flex: 1,
106 | flexDirection: 'row',
107 | alignItems: 'center',
108 | justifyContent: 'space-between'
109 | },
110 | secondaryLeftColumn: {
111 | flex: 1
112 | },
113 | secondaryRightColumn: {
114 | flex: 2
115 | },
116 | });
117 |
118 | export default ExplorePostCard;
--------------------------------------------------------------------------------
/app/screens/PostViewScreen/components/CommentCard.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useRef } from 'react';
2 | import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';
3 | import { AppContext } from '@app/context';
4 | import { NativeImage, DeleteCardRightActions } from '@app/layout';
5 | import { Typography } from '@app/theme';
6 | import { ThemeColors } from '@app/types/theme';
7 | import { parseTimeElapsed } from '@app/utils/shared';
8 | import { useNavigation } from 'react-navigation-hooks';
9 | import { Routes } from '@app/constants';
10 | import Swipeable from 'react-native-gesture-handler/Swipeable';
11 | import { useMutation } from '@apollo/react-hooks';
12 | import { MUTATION_DELETE_COMMENT } from '@app/graphql/mutation';
13 | import { longPressDeleteNotification } from '@app/utils/notifications';
14 |
15 | const { FontWeights, FontSizes } = Typography;
16 |
17 | interface CommentCardProps {
18 | postId: string,
19 | commentId: string,
20 | authorId: string,
21 | avatar: string,
22 | handle: string,
23 | body: string,
24 | time: string
25 | };
26 |
27 | const CommentCard: React.FC = ({ postId, commentId, authorId, avatar, handle, body, time }) => {
28 |
29 | const { user, theme } = useContext(AppContext);
30 | const { navigate } = useNavigation();
31 | const { parsedTime } = parseTimeElapsed(time);
32 |
33 | const [deleteComment, { loading: deleteCommentLoading, called: deleteCommentCalled }] = useMutation(MUTATION_DELETE_COMMENT);
34 |
35 | const swipeableRef = useRef();
36 |
37 | const navigateToProfile = () => {
38 | if (authorId === user.id) return;
39 | navigate(Routes.ProfileViewScreen, { userId: authorId });
40 | };
41 |
42 | const onDelete = () => {
43 | if (!deleteCommentLoading && !deleteCommentCalled) {
44 | longPressDeleteNotification(() => {
45 | // @ts-ignore
46 | swipeableRef.current.close();
47 | deleteComment({ variables: { postId, commentId } });
48 | });
49 | }
50 | };
51 |
52 | const renderRightActions = (progress, dragX) => {
53 | if (authorId !== user.id) return null;
54 | return (
55 |
60 | );
61 | };
62 |
63 | return (
64 | // @ts-ignore
65 |
66 |
67 |
68 |
69 |
70 | {handle}{' '}
71 | {body}
72 |
73 | {parsedTime}
74 |
75 |
76 |
77 | );
78 | };
79 |
80 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
81 | swipeable: {
82 | marginVertical: 5
83 | },
84 | container: {
85 | flexDirection: 'row',
86 | borderRadius: 5,
87 | marginVertical: 5
88 | },
89 | avatarImage: {
90 | height: 40,
91 | width: 40,
92 | borderRadius: 50,
93 | backgroundColor: theme.placeholder
94 | },
95 | info: {
96 | flex: 1,
97 | justifyContent: 'center',
98 | paddingLeft: 10
99 | },
100 | handleText: {
101 | ...FontWeights.Regular,
102 | ...FontSizes.Body,
103 | color: theme.text01
104 | },
105 | commentText: {
106 | ...FontWeights.Light,
107 | ...FontSizes.Body,
108 | color: theme.text01
109 | },
110 | timeText: {
111 | ...FontWeights.Light,
112 | ...FontSizes.Caption,
113 | color: theme.text02,
114 | paddingTop: 2
115 | }
116 | });
117 |
118 | export default CommentCard;
--------------------------------------------------------------------------------
/app/screens/PostViewScreen/components/CommentInput.tsx:
--------------------------------------------------------------------------------
1 | import { useMutation } from '@apollo/react-hooks';
2 | import React, { useContext, useState } from 'react';
3 | import { Keyboard, Platform, StyleSheet, TextInput, View } from 'react-native';
4 | import { ifIphoneX } from 'react-native-iphone-x-helper';
5 | import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
6 | import { IconSizes } from '@app/constants';
7 | import { AppContext } from '@app/context';
8 | import { MUTATION_ADD_COMMENT } from '@app/graphql/mutation';
9 | import { IconButton, LoadingIndicator, NativeImage } from '@app/layout';
10 | import { ThemeStatic, Typography } from '@app/theme';
11 | import { ThemeColors } from '@app/types/theme';
12 | import { inputLimitErrorNotification } from '@app/utils/notifications';
13 | import { createAsyncDelay } from '@app/utils/shared';
14 |
15 | const { FontWeights, FontSizes } = Typography;
16 |
17 | interface CommentInputProps {
18 | postId: string,
19 | scrollViewRef: React.MutableRefObject
20 | };
21 |
22 | const CommentInput: React.FC = ({ postId, scrollViewRef }) => {
23 |
24 | const { user, theme } = useContext(AppContext);
25 | const [comment, setComment] = useState('');
26 | const [addComment, { loading }] = useMutation(MUTATION_ADD_COMMENT);
27 |
28 | const postComment = async () => {
29 | if (comment.length < 1) {
30 | inputLimitErrorNotification('Comment', 'more', 1);
31 | return;
32 | }
33 | if (comment.length > 200) {
34 | inputLimitErrorNotification('Comment', 'less', 200);
35 | return;
36 | }
37 | await addComment({ variables: { userId: user.id, postId, body: comment } });
38 | Keyboard.dismiss();
39 | setComment('');
40 | await createAsyncDelay(1200);
41 | scrollViewRef.current.scrollToEnd();
42 | };
43 |
44 | const Icon = () =>
45 | ;
49 |
50 | let content = (
51 |
52 |
53 |
54 | );
55 |
56 | if (!loading) {
57 | content = (
58 |
63 | );
64 | }
65 |
66 | return (
67 |
68 |
72 |
79 | {content}
80 |
81 | );
82 | };
83 |
84 | const styles = (theme = {} as ThemeColors) => StyleSheet.create({
85 | container: {
86 | flexDirection: 'row',
87 | alignItems: 'center',
88 | paddingVertical: 8,
89 | paddingHorizontal: 20,
90 | borderTopColor: ThemeStatic.translucent,
91 | borderTopWidth: StyleSheet.hairlineWidth,
92 | backgroundColor: theme.base,
93 | ...ifIphoneX({
94 | paddingBottom: 32
95 | }, {})
96 | },
97 | commentAvatarImage: {
98 | height: 36,
99 | width: 36,
100 | backgroundColor: theme.placeholder,
101 | marginRight: 10,
102 | borderRadius: 50
103 | },
104 | commentTextInput: {
105 | flex: 1,
106 | ...FontWeights.Light,
107 | ...FontSizes.Body,
108 | paddingVertical: Platform.select({ ios: 8, android: 6 }),
109 | paddingHorizontal: 20,
110 | backgroundColor: theme.placeholder,
111 | color: theme.text01,
112 | borderRadius: 20,
113 | marginVertical: 5
114 | },
115 | loading: {
116 | marginLeft: 10
117 | },
118 | postButton: {
119 | width: undefined,
120 | marginLeft: 10
121 | }
122 | });
123 |
124 | export default CommentInput;
--------------------------------------------------------------------------------