├── .babelrc
├── .editorconfig
├── .eslintrc
├── .flowconfig
├── .gitignore
├── .tern-project
├── .watchmanconfig
├── Colors.json
├── LICENSE.md
├── android
├── HeyNeighbor.iml
├── app
│ ├── app.iml
│ ├── build.gradle
│ ├── fonts.gradle
│ ├── proguard-rules.pro
│ ├── react.gradle
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── assets
│ │ └── fonts
│ │ │ ├── Lato.ttf
│ │ │ ├── Lato_bold.ttf
│ │ │ ├── Lato_bold_talic.ttf
│ │ │ └── Lato_italic.ttf
│ │ ├── java
│ │ └── io
│ │ │ └── scrollback
│ │ │ └── neighborhoods
│ │ │ ├── AppState.java
│ │ │ ├── HeyNeighbor.java
│ │ │ ├── InstallReferrerReceiver.java
│ │ │ ├── MainActivity.java
│ │ │ ├── bundle
│ │ │ ├── Checksum.java
│ │ │ ├── IOHelpers.java
│ │ │ └── JSBundleManager.java
│ │ │ └── modules
│ │ │ ├── analytics
│ │ │ ├── AnalyticsPackage.java
│ │ │ ├── AnswersInstallTracker.java
│ │ │ ├── AnswersInstallTrackerModule.java
│ │ │ ├── AnswersModule.java
│ │ │ ├── LifeCycleTracker.java
│ │ │ └── Trackers.java
│ │ │ ├── core
│ │ │ ├── BuildConfigModule.java
│ │ │ ├── CorePackage.java
│ │ │ ├── GeolocationModule.java
│ │ │ ├── ShareModule.java
│ │ │ ├── URLResolverModule.java
│ │ │ └── VersionCodesModule.java
│ │ │ ├── facebook
│ │ │ ├── FacebookModule.java
│ │ │ └── FacebookPackage.java
│ │ │ ├── gcm
│ │ │ ├── HeyNeighborNotification.java
│ │ │ ├── PushNotificationBroadcastReceiver.java
│ │ │ ├── PushNotificationHandler.java
│ │ │ ├── PushNotificationIntentService.java
│ │ │ ├── PushNotificationModule.java
│ │ │ └── PushNotificationPackage.java
│ │ │ ├── google
│ │ │ ├── GoogleLoginModule.java
│ │ │ └── GoogleLoginPackage.java
│ │ │ └── places
│ │ │ ├── GooglePlacesModule.java
│ │ │ └── GooglePlacesPackage.java
│ │ └── res
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_status.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_status.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_status.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_status.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_status.png
│ │ ├── values-v19
│ │ └── styles.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
├── app
├── containers
│ ├── AccountContainer.js
│ ├── AppContainer.js
│ ├── AvatarContainer.js
│ ├── BannerOfflineContainer.js
│ ├── ChatContainer.js
│ ├── ChatItemContainer.js
│ ├── ChatMessagesContainer.js
│ ├── ChatSuggestionsContainer.js
│ ├── ChatTitleContainer.js
│ ├── Container.js
│ ├── DiscussionDetailsContainer.js
│ ├── DiscussionItemContainer.js
│ ├── DiscussionsContainer.js
│ ├── HomeContainer.js
│ ├── ImageUploadContainer.js
│ ├── LocalitiesContainer.js
│ ├── LocalitiesFilteredContainer.js
│ ├── LocalityTitleContainer.js
│ ├── MyPlacesContainer.js
│ ├── NotificationBadgeContainer.js
│ ├── NotificationCenterContainer.js
│ ├── NotificationClearIconContainer.js
│ ├── PeopleListContainer.js
│ ├── ShareButtonContainer.js
│ ├── SignUpContainer.js
│ ├── StartDiscussionContainer.js
│ ├── StatesFilteredContainer.js
│ └── UserIconContainer.js
├── extras
│ ├── analytics
│ │ ├── analytics.js
│ │ └── signin.js
│ ├── gcm
│ │ └── gcm.js
│ ├── oembed
│ │ ├── oembed-storage.js
│ │ ├── oembed-test.js
│ │ ├── oembed.js
│ │ ├── providers.js
│ │ └── regexes.js
│ ├── push-notification
│ │ └── gcm.js
│ └── syncjoin
│ │ └── syncjoin.js
├── lib
│ ├── URL.js
│ ├── __tests__
│ │ └── URL-test.js
│ ├── debounce.js
│ ├── generate.browser.js
│ ├── get-avatar.js
│ ├── group-objects-test.js
│ ├── group-objects.js
│ ├── location-utils.js
│ ├── obj-utils-test.js
│ ├── obj-utils.js
│ ├── pendingQueries.js
│ ├── range-ops.js
│ ├── smiley.js
│ ├── text-utils.js
│ ├── time-utils.js
│ ├── user-utils.js
│ ├── validator-strings.js
│ ├── validator-test.js
│ └── validator.js
├── mocks
│ ├── AsyncStorage.js
│ └── ReactNative.js
├── modules
│ ├── Answers.android.js
│ ├── BuildConfig.android.js
│ ├── Facebook.android.js
│ ├── Geolocation.android.js
│ ├── GoogleLogin.android.js
│ ├── ImageChooser.android.js
│ ├── Linking.android.js
│ ├── Linking.ios.js
│ ├── PushNotification.android.js
│ ├── Share.android.js
│ ├── SocialShare.android.js
│ ├── URLResolver.android.js
│ └── VersionCodes.android.js
├── navigation-rfc
│ ├── CustomComponents
│ │ ├── NavigationCard.js
│ │ ├── NavigationHeader.js
│ │ └── NavigationHeaderTitle.js
│ ├── Navigation
│ │ ├── NavigationActions.js
│ │ ├── NavigationAnimatedView.js
│ │ ├── NavigationContainer.js
│ │ ├── NavigationReducer.js
│ │ ├── NavigationRootContainer.js
│ │ ├── NavigationState.js
│ │ └── NavigationView.js
│ └── polyfill.js
├── navigation
│ ├── PersistentNavigator.js
│ ├── renderNavigator.js
│ ├── renderOverlay.js
│ └── renderScene.js
├── routes
│ ├── Route.js
│ ├── __tests__
│ │ └── Route-test.js
│ └── routeMapper.js
├── store.orig
│ ├── action-handler.js
│ ├── add-user-timezone.js
│ ├── bulkQuery.js
│ ├── entity-ops.js
│ ├── init-user-up-manager.js
│ ├── permissionWeights.js
│ ├── property-list.js
│ ├── rule-manager.js
│ ├── rules
│ │ ├── clearQueuedActions.js
│ │ ├── handleUserPresence.js
│ │ ├── loadRelatedUsers.js
│ │ ├── loadRooms.js
│ │ ├── loadTextsOnNav.js
│ │ ├── loadThread.js
│ │ ├── loadThreadsOnNav.js
│ │ ├── manageNotes.js
│ │ └── removeRelations.js
│ ├── session-manager.js
│ ├── socket.js
│ ├── state-manager.js
│ ├── store.js
│ └── test
│ │ ├── range-ops-test.js
│ │ ├── range-test.js
│ │ ├── state-manager-test.js
│ │ └── test.js
├── store
│ ├── actions.js
│ ├── config.js
│ ├── core.js
│ ├── init.js
│ └── store.js
└── views
│ ├── Account
│ ├── Account.js
│ ├── AccountButton.js
│ ├── AccountPhotoChooser.js
│ ├── AccountPhotoChooserItem.js
│ ├── MyPlaces.js
│ ├── PlaceButton.js
│ ├── PlaceItem.js
│ └── PlaceManager.js
│ ├── App.js
│ ├── AppText.js
│ ├── AppTextInput.js
│ ├── AppbarIcon.js
│ ├── AppbarSecondary.js
│ ├── AppbarTitle.js
│ ├── AppbarTouchable.js
│ ├── Avatar.android.js
│ ├── Avatar.js
│ ├── AvatarRound.js
│ ├── Banner.js
│ ├── BannerOffline.js
│ ├── BannerUnavailable.js
│ ├── Card.js
│ ├── CardAuthor.js
│ ├── CardSummary.js
│ ├── CardTitle.js
│ ├── Chat.js
│ ├── ChatBubble.js
│ ├── ChatInput.js
│ ├── ChatItem.js
│ ├── ChatMessages.js
│ ├── ChatSuggestions.js
│ ├── ChatTitle.js
│ ├── CloseButton.js
│ ├── DiscussionDetails.js
│ ├── DiscussionDetailsCard.js
│ ├── DiscussionFooter.js
│ ├── DiscussionItem.js
│ ├── DiscussionSummary.js
│ ├── Discussions.js
│ ├── Embed.js
│ ├── EmbedSummary.js
│ ├── EmbedThumbnail.js
│ ├── EmbedTitle.js
│ ├── FloatingActionButton.js
│ ├── GrowingTextInput.js
│ ├── Home.js
│ ├── Icon.android.js
│ ├── ImageUploadButton.js
│ ├── ImageUploadChat.js
│ ├── ImageUploadDiscussion.js
│ ├── KeyboardSpacer.js
│ ├── Link.js
│ ├── ListHeader.js
│ ├── ListItem.js
│ ├── Loading.android.js
│ ├── LoadingFancy.js
│ ├── LoadingItem.js
│ ├── Localities.js
│ ├── LocalitiesFiltered.js
│ ├── LocalityItem.js
│ ├── LocalityTitle.js
│ ├── Modal.js
│ ├── ModalSheet.js
│ ├── NotificationBadge.js
│ ├── NotificationCenter.js
│ ├── NotificationCenterItem.js
│ ├── NotificationClearIcon.js
│ ├── NotificationIcon.js
│ ├── Offline.js
│ ├── Onboard
│ ├── GetStarted.js
│ ├── LargeButton.js
│ ├── LocationDetails.js
│ ├── NextButton.js
│ ├── NextButtonLabel.js
│ ├── Onboard.js
│ ├── OnboardError.js
│ ├── OnboardParagraph.js
│ ├── OnboardTitle.js
│ ├── SignIn.js
│ ├── SignUp.js
│ └── UserDetails.js
│ ├── Page.js
│ ├── PageEmpty.js
│ ├── PageLoading.js
│ ├── PeopleList.js
│ ├── PeopleListItem.js
│ ├── RichText.js
│ ├── SearchButton.android.js
│ ├── SearchableList.js
│ ├── Searchbar.js
│ ├── ShareButton.js
│ ├── Splash.js
│ ├── StartDiscussion.js
│ ├── StartDiscussionButton.js
│ ├── StateItem.js
│ ├── StatesFiltered.js
│ ├── StatusbarWrapper.js
│ ├── Time.js
│ ├── TouchFeedback.js
│ ├── UpgradeBanner.js
│ └── UserIcon.js
├── assets
├── astronaut.png
├── astronaut@1.5x.png
├── astronaut@2x.png
├── astronaut@3x.png
├── astronaut@4x.png
├── logo.png
├── logo@1.5x.png
├── logo@2x.png
├── logo@3x.png
├── logo@4x.png
├── logotype.png
├── logotype@1.5x.png
├── logotype@2x.png
├── logotype@3x.png
├── logotype@4x.png
├── monkey-cool.png
├── monkey-cool@1.5x.png
├── monkey-cool@2x.png
├── monkey-cool@3x.png
├── monkey-cool@4x.png
├── monkey-happy.png
├── monkey-happy@1.5x.png
├── monkey-happy@2x.png
├── monkey-happy@3x.png
├── monkey-happy@4x.png
├── monkey-meh.png
├── monkey-meh@1.5x.png
├── monkey-meh@2x.png
├── monkey-meh@3x.png
├── monkey-meh@4x.png
├── monkey-sad.png
├── monkey-sad@1.5x.png
├── monkey-sad@2x.png
├── monkey-sad@3x.png
├── monkey-sad@4x.png
├── open-door.png
├── open-door@1.5x.png
├── open-door@2x.png
├── open-door@3x.png
├── open-door@4x.png
├── scrollback_logo.png
├── scrollback_logo@1.5x.png
├── scrollback_logo@2x.png
├── scrollback_logo@3x.png
├── scrollback_logo@4x.png
├── signin_bg.jpg
├── signin_bg@1.5x.jpg
├── signin_bg@2x.jpg
├── signin_bg@3x.jpg
├── triangle_left.png
├── triangle_left@1.5x.png
├── triangle_left@2x.png
├── triangle_left@3x.png
├── triangle_left@4x.png
├── triangle_right.png
├── triangle_right@1.5x.png
├── triangle_right@2x.png
├── triangle_right@3x.png
└── triangle_right@4x.png
├── config-defaults.json
├── config.json
├── index.android.js
├── index.ios.js
├── ios
├── HeyNeighbor.xcodeproj
│ ├── project.pbxproj
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── HeyNeighbor.xcscheme
├── HeyNeighbor
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Base.lproj
│ │ └── LaunchScreen.xib
│ ├── Images.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Info.plist
│ └── main.m
└── HeyNeighborTests
│ ├── HeyNeighborTests.m
│ └── Info.plist
├── package.json
└── tools
├── .eslintrc
└── bundle.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-native/packager/react-packager/.babelrc"
3 | }
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 |
10 | # change these settings to your own preference
11 | indent_style = tab
12 | indent_size = 4
13 |
14 | # we recommend you to keep these unchanged
15 | end_of_line = lf
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
23 | [*.{yml,yaml}]
24 | indent_style = space
25 | indent_size = 2
26 |
27 | [*.{java,css,less,scss}]
28 | indent_style = space
29 | indent_size = 4
30 |
31 | [{package,bower}.json]
32 | indent_style = space
33 | indent_size = 2
34 |
35 | [{.eslintrc,.babelrc}]
36 | indent_style = space
37 | indent_size = 2
38 |
--------------------------------------------------------------------------------
/.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 | project.xcworkspace
24 |
25 | # node.js
26 | #
27 | node_modules/
28 | npm-debug.log
29 |
30 | # Android
31 | #
32 |
33 | # Built application files
34 | *.apk
35 | *.ap_
36 |
37 | # Files for the Dalvik VM
38 | *.dex
39 |
40 | # Java class files
41 | *.class
42 |
43 | # Generated files
44 | bin/
45 | gen/
46 |
47 | # Gradle files
48 | .gradle/
49 | build/
50 | /*/build/
51 |
52 | # Local configuration file (sdk path, etc)
53 | local.properties
54 |
55 | # Proguard folder generated by Eclipse
56 | proguard/
57 |
58 | # Log Files
59 | *.log
60 |
61 | # Keystore
62 | *.jks
63 | *.keystore
64 |
65 | # IDE
66 | .idea
67 |
68 | # Fabric
69 | fabric.properties
70 | com_crashlytics_export_strings.xml
71 |
72 | # Appvirality
73 | appvirality.properties
74 |
75 | # Built files
76 | index.android.bundle
77 | index.ios.bundle
78 |
79 | # Hey Neighbor
80 | server-config.js
81 | client-config.js
82 |
--------------------------------------------------------------------------------
/.tern-project:
--------------------------------------------------------------------------------
1 | {
2 | "ecmaVersion": 6,
3 | "libs": [],
4 | "plugins": {
5 | "complete_strings": {},
6 | "doc_comment": {
7 | "fullDocs": true
8 | },
9 | "node": {},
10 | "modules": {},
11 | "es_modules": {}
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/Colors.json:
--------------------------------------------------------------------------------
1 | {
2 | "white": "#fff",
3 | "black": "#000",
4 | "grey": "#999",
5 | "lightGrey": "#eee",
6 | "darkGrey": "#444",
7 | "primary": "#673ab7",
8 | "accent": "#ff9419",
9 | "badge": "#e91e63",
10 | "info": "#2196f3",
11 | "error": "#f44336",
12 | "success": "#4caf50",
13 | "google": "#488ef1",
14 | "facebook": "#3b5998",
15 | "fadedWhite": "rgba(255, 255, 255, .5)",
16 | "fadedBlack": "rgba(0, 0, 0, .5)",
17 | "separator": "rgba(0, 0, 0, .08)",
18 | "underlay": "rgba(0, 0, 0, .12)",
19 | "placeholder": "rgba(0, 0, 0, .16)"
20 | }
21 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Askabt.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/android/HeyNeighbor.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/fonts.gradle:
--------------------------------------------------------------------------------
1 | /**
2 | * Task to copy icon font files
3 | */
4 | def iconFontsDir = '../../node_modules/react-native-vector-icons/Fonts';
5 | def iconFontNames = [ 'MaterialIcons.ttf', 'EvilIcons.ttf' ];
6 |
7 | task copyDebugIconFonts(type: Copy) {
8 | iconFontNames.each { name ->
9 | from("$iconFontsDir/$name")
10 | into("$buildDir/intermediates/assets/debug/fonts/")
11 | }
12 | }
13 |
14 | task copyReleaseIconFonts(type: Copy) {
15 | iconFontNames.each { name ->
16 | from("$iconFontsDir/$name")
17 | into("$buildDir/intermediates/assets/release/fonts/")
18 | }
19 | }
20 |
21 | gradle.projectsEvaluated {
22 | generateDebugAssets.dependsOn copyDebugIconFonts
23 | generateReleaseAssets.dependsOn copyReleaseIconFonts
24 | }
25 |
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Lato.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrollback/io.scrollback.neighborhoods/de5f8d0792cbd64230cdec7534c38fa29e906f22/android/app/src/main/assets/fonts/Lato.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Lato_bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrollback/io.scrollback.neighborhoods/de5f8d0792cbd64230cdec7534c38fa29e906f22/android/app/src/main/assets/fonts/Lato_bold.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Lato_bold_talic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrollback/io.scrollback.neighborhoods/de5f8d0792cbd64230cdec7534c38fa29e906f22/android/app/src/main/assets/fonts/Lato_bold_talic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Lato_italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrollback/io.scrollback.neighborhoods/de5f8d0792cbd64230cdec7534c38fa29e906f22/android/app/src/main/assets/fonts/Lato_italic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/java/io/scrollback/neighborhoods/AppState.java:
--------------------------------------------------------------------------------
1 | package io.scrollback.neighborhoods;
2 |
3 | import android.app.Activity;
4 | import android.app.Application;
5 | import android.os.Bundle;
6 |
7 | public class AppState implements Application.ActivityLifecycleCallbacks {
8 |
9 | private static AppState instance;
10 | private static boolean foreground;
11 |
12 | public static void init(Application app) {
13 | if (instance == null) {
14 | instance = new AppState();
15 |
16 | app.registerActivityLifecycleCallbacks(instance);
17 | }
18 | }
19 |
20 | public static boolean isForeground() {
21 | return foreground;
22 | }
23 |
24 | @Override
25 | public void onActivityPaused(Activity activity) {
26 | foreground = false;
27 | }
28 |
29 | @Override
30 | public void onActivityResumed(Activity activity) {
31 | foreground = true;
32 | }
33 |
34 | @Override
35 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
36 | }
37 |
38 | @Override
39 | public void onActivityStarted(Activity activity) {
40 | }
41 |
42 | @Override
43 | public void onActivityStopped(Activity activity) {
44 | }
45 |
46 | @Override
47 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
48 | }
49 |
50 | @Override
51 | public void onActivityDestroyed(Activity activity) {
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/android/app/src/main/java/io/scrollback/neighborhoods/HeyNeighbor.java:
--------------------------------------------------------------------------------
1 | package io.scrollback.neighborhoods;
2 |
3 | import android.app.Application;
4 |
5 | import com.crashlytics.android.Crashlytics;
6 |
7 | import io.fabric.sdk.android.Fabric;
8 | import io.scrollback.neighborhoods.modules.analytics.LifeCycleTracker;
9 |
10 | public class HeyNeighbor extends Application {
11 | @Override
12 | public void onCreate() {
13 | super.onCreate();
14 |
15 | Fabric.with(this, new Crashlytics());
16 |
17 | AppState.init(this);
18 | LifeCycleTracker.init(this);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/android/app/src/main/java/io/scrollback/neighborhoods/InstallReferrerReceiver.java:
--------------------------------------------------------------------------------
1 | package io.scrollback.neighborhoods;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | import io.scrollback.neighborhoods.modules.analytics.AnswersInstallTracker;
8 |
9 | public class InstallReferrerReceiver extends BroadcastReceiver {
10 |
11 | @Override
12 | public void onReceive(Context context, Intent intent) {
13 | if (intent != null && intent.getAction().equals("com.android.vending.INSTALL_REFERRER")) {
14 | final String referrer = intent.getStringExtra("referrer");
15 |
16 | if (referrer != null && referrer.length() != 0) {
17 | AnswersInstallTracker.getInstance(context).setReferrer(referrer);
18 | }
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/android/app/src/main/java/io/scrollback/neighborhoods/bundle/Checksum.java:
--------------------------------------------------------------------------------
1 | package io.scrollback.neighborhoods.bundle;
2 |
3 | import android.support.annotation.NonNull;
4 |
5 | import java.io.File;
6 | import java.io.FileInputStream;
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.math.BigInteger;
10 | import java.security.MessageDigest;
11 | import java.security.NoSuchAlgorithmException;
12 |
13 | public class Checksum {
14 | public static String MD5(@NonNull InputStream stream) throws IOException, NoSuchAlgorithmException {
15 | MessageDigest md = MessageDigest.getInstance("MD5");
16 |
17 | byte[] buffer = new byte[8192];
18 | int numOfBytesRead;
19 |
20 | while ((numOfBytesRead = stream.read(buffer)) > 0) {
21 | md.update(buffer, 0, numOfBytesRead);
22 | }
23 |
24 | byte[] hash = md.digest();
25 |
26 | return String.format("%032x", new BigInteger(1, hash));
27 | }
28 |
29 | public static String MD5(@NonNull File file) throws IOException, NoSuchAlgorithmException {
30 | InputStream stream = new FileInputStream(file);
31 |
32 | try {
33 | return MD5(stream);
34 | } finally {
35 | stream.close();
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/android/app/src/main/java/io/scrollback/neighborhoods/modules/analytics/AnalyticsPackage.java:
--------------------------------------------------------------------------------
1 | package io.scrollback.neighborhoods.modules.analytics;
2 |
3 | import com.facebook.react.ReactPackage;
4 | import com.facebook.react.bridge.JavaScriptModule;
5 | import com.facebook.react.bridge.NativeModule;
6 | import com.facebook.react.bridge.ReactApplicationContext;
7 | import com.facebook.react.uimanager.ViewManager;
8 |
9 | import java.util.Arrays;
10 | import java.util.Collections;
11 | import java.util.List;
12 |
13 | public class AnalyticsPackage implements ReactPackage {
14 |
15 | @Override
16 | public List createNativeModules(ReactApplicationContext reactContext) {
17 | return Arrays.asList(
18 | new AnswersModule(reactContext),
19 | new AnswersInstallTrackerModule(reactContext)
20 | );
21 | }
22 |
23 | @Override
24 | public List> createJSModules() {
25 | return Collections.emptyList();
26 | }
27 |
28 | @Override
29 | public List createViewManagers(ReactApplicationContext reactContext) {
30 | return Collections.emptyList();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/android/app/src/main/java/io/scrollback/neighborhoods/modules/analytics/LifeCycleTracker.java:
--------------------------------------------------------------------------------
1 | package io.scrollback.neighborhoods.modules.analytics;
2 |
3 | import android.app.Activity;
4 | import android.app.Application;
5 | import android.os.Bundle;
6 |
7 | import java.util.Date;
8 |
9 | public class LifeCycleTracker implements Application.ActivityLifecycleCallbacks {
10 |
11 | private static final String EVENT_NAME = "App Usage";
12 |
13 | private static LifeCycleTracker instance;
14 | private Date startTime;
15 |
16 | public static void init(Application app) {
17 | if (instance == null) {
18 | instance = new LifeCycleTracker();
19 |
20 | app.registerActivityLifecycleCallbacks(instance);
21 | }
22 | }
23 |
24 | @Override
25 | public void onActivityPaused(Activity activity) {
26 | Trackers.logTiming(EVENT_NAME, startTime, new Date());
27 | }
28 |
29 | @Override
30 | public void onActivityResumed(Activity activity) {
31 | startTime = new Date();
32 | }
33 |
34 | @Override
35 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
36 | }
37 |
38 | @Override
39 | public void onActivityStarted(Activity activity) {
40 | }
41 |
42 | @Override
43 | public void onActivityStopped(Activity activity) {
44 | }
45 |
46 | @Override
47 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
48 | }
49 |
50 | @Override
51 | public void onActivityDestroyed(Activity activity) {
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/android/app/src/main/java/io/scrollback/neighborhoods/modules/core/BuildConfigModule.java:
--------------------------------------------------------------------------------
1 | package io.scrollback.neighborhoods.modules.core;
2 |
3 | import com.facebook.react.bridge.ReactApplicationContext;
4 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
5 |
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | import io.scrollback.neighborhoods.BuildConfig;
10 |
11 | public class BuildConfigModule extends ReactContextBaseJavaModule {
12 |
13 | public BuildConfigModule(ReactApplicationContext reactContext) {
14 | super(reactContext);
15 | }
16 |
17 | @Override
18 | public String getName() {
19 | return "BuildConfigModule";
20 | }
21 |
22 | @Override
23 | public Map getConstants() {
24 | final Map constants = new HashMap<>();
25 |
26 | constants.put("APPLICATION_ID", BuildConfig.APPLICATION_ID);
27 | constants.put("BUILD_TYPE", BuildConfig.BUILD_TYPE);
28 | constants.put("DEBUG", BuildConfig.DEBUG);
29 | constants.put("FLAVOR", BuildConfig.FLAVOR);
30 | constants.put("VERSION_CODE", BuildConfig.VERSION_CODE);
31 | constants.put("VERSION_NAME", BuildConfig.VERSION_NAME);
32 |
33 | return constants;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/android/app/src/main/java/io/scrollback/neighborhoods/modules/core/CorePackage.java:
--------------------------------------------------------------------------------
1 | package io.scrollback.neighborhoods.modules.core;
2 |
3 | import com.facebook.react.ReactPackage;
4 | import com.facebook.react.bridge.JavaScriptModule;
5 | import com.facebook.react.bridge.NativeModule;
6 | import com.facebook.react.bridge.ReactApplicationContext;
7 | import com.facebook.react.uimanager.ViewManager;
8 |
9 | import java.util.Arrays;
10 | import java.util.Collections;
11 | import java.util.List;
12 |
13 | public class CorePackage implements ReactPackage {
14 |
15 | @Override
16 | public List createNativeModules(ReactApplicationContext reactContext) {
17 | return Arrays.asList(
18 | new BuildConfigModule(reactContext),
19 | new VersionCodesModule(reactContext),
20 | new URLResolverModule(reactContext),
21 | new GeolocationModule(reactContext),
22 | new ShareModule(reactContext)
23 | );
24 | }
25 |
26 | @Override
27 | public List> createJSModules() {
28 | return Collections.emptyList();
29 | }
30 |
31 | @Override
32 | public List createViewManagers(ReactApplicationContext reactContext) {
33 | return Arrays.asList();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/android/app/src/main/java/io/scrollback/neighborhoods/modules/core/ShareModule.java:
--------------------------------------------------------------------------------
1 | package io.scrollback.neighborhoods.modules.core;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 |
6 | import com.facebook.react.bridge.Promise;
7 | import com.facebook.react.bridge.ReactApplicationContext;
8 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
9 | import com.facebook.react.bridge.ReactMethod;
10 |
11 | public class ShareModule extends ReactContextBaseJavaModule {
12 |
13 | private static final String ACTIVITY_DOES_NOT_EXIST_ERROR = "Activity doesn't exist";
14 |
15 | public ShareModule(ReactApplicationContext reactContext) {
16 | super(reactContext);
17 | }
18 |
19 | @Override
20 | public String getName() {
21 | return "ShareModule";
22 | }
23 |
24 | @ReactMethod
25 | public void shareItem(final String title, final String content, final Promise promise) {
26 | Intent sharingIntent = new Intent(Intent.ACTION_SEND);
27 |
28 | sharingIntent.setType("text/plain");
29 | sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, content);
30 |
31 | Activity currentActivity = getCurrentActivity();
32 |
33 | if (currentActivity != null) {
34 | currentActivity.startActivity(Intent.createChooser(sharingIntent, title));
35 | promise.resolve(true);
36 | } else {
37 | promise.reject(ACTIVITY_DOES_NOT_EXIST_ERROR);
38 | }
39 |
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/android/app/src/main/java/io/scrollback/neighborhoods/modules/facebook/FacebookPackage.java:
--------------------------------------------------------------------------------
1 | package io.scrollback.neighborhoods.modules.facebook;
2 |
3 | import com.facebook.react.ReactPackage;
4 | import com.facebook.react.bridge.JavaScriptModule;
5 | import com.facebook.react.bridge.NativeModule;
6 | import com.facebook.react.bridge.ReactApplicationContext;
7 | import com.facebook.react.uimanager.ViewManager;
8 |
9 | import java.util.Arrays;
10 | import java.util.Collections;
11 | import java.util.List;
12 |
13 | public class FacebookPackage implements ReactPackage {
14 |
15 | @Override
16 | public List createNativeModules(ReactApplicationContext reactContext) {
17 | return Arrays.asList(new FacebookModule(reactContext));
18 | }
19 |
20 | @Override
21 | public List> createJSModules() {
22 | return Collections.emptyList();
23 | }
24 |
25 | @Override
26 | public List createViewManagers(ReactApplicationContext reactContext) {
27 | return Collections.emptyList();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/android/app/src/main/java/io/scrollback/neighborhoods/modules/gcm/PushNotificationBroadcastReceiver.java:
--------------------------------------------------------------------------------
1 | package io.scrollback.neighborhoods.modules.gcm;
2 |
3 | import android.app.Activity;
4 | import android.content.ComponentName;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.support.v4.content.WakefulBroadcastReceiver;
8 |
9 | public class PushNotificationBroadcastReceiver extends WakefulBroadcastReceiver {
10 | @Override
11 | public void onReceive(Context context, Intent intent) {
12 | // Explicitly specify that the IntentService will handle the intent.
13 | ComponentName comp = new ComponentName(context.getPackageName(), PushNotificationIntentService.class.getName());
14 |
15 | // Start the service, keeping the device awake while it is launching.
16 | startWakefulService(context, (intent.setComponent(comp)));
17 |
18 | setResultCode(Activity.RESULT_OK);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/android/app/src/main/java/io/scrollback/neighborhoods/modules/gcm/PushNotificationIntentService.java:
--------------------------------------------------------------------------------
1 | package io.scrollback.neighborhoods.modules.gcm;
2 |
3 | import android.app.IntentService;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.util.Log;
7 |
8 | import com.google.android.gms.gcm.GoogleCloudMessaging;
9 |
10 | public class PushNotificationIntentService extends IntentService {
11 |
12 | private static final String TAG = "GCM";
13 | private static final int NOTIFICATION_ID = 1;
14 |
15 | public PushNotificationIntentService() {
16 | super("PushNotificationIntentService");
17 | }
18 |
19 | @Override
20 | protected void onHandleIntent(Intent intent) {
21 | if (intent == null) {
22 | return;
23 | }
24 |
25 | Bundle extras = intent.getExtras();
26 | GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
27 |
28 | // The getMessageType() intent parameter must be the intent you received
29 | // in your BroadcastReceiver.
30 | String messageType = gcm.getMessageType(intent);
31 |
32 | if (!extras.isEmpty()) {
33 | if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
34 | Log.e(TAG, "Error in sending message: " + extras.toString());
35 | } else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType)) {
36 | Log.e(TAG, "Messages deleted on server: " + extras.toString());
37 | } else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
38 | // If it's a regular GCM message, do some work
39 | Log.d(TAG, "Payload received: " + extras.toString());
40 |
41 | new PushNotificationHandler(this).handleNotification(NOTIFICATION_ID, HeyNeighborNotification.fromBundle(this, extras));
42 | }
43 | }
44 |
45 | // Release the wake lock provided by the WakefulBroadcastReceiver.
46 | PushNotificationBroadcastReceiver.completeWakefulIntent(intent);
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/android/app/src/main/java/io/scrollback/neighborhoods/modules/gcm/PushNotificationPackage.java:
--------------------------------------------------------------------------------
1 | package io.scrollback.neighborhoods.modules.gcm;
2 |
3 | import com.facebook.react.ReactPackage;
4 | import com.facebook.react.bridge.JavaScriptModule;
5 | import com.facebook.react.bridge.NativeModule;
6 | import com.facebook.react.bridge.ReactApplicationContext;
7 | import com.facebook.react.uimanager.ViewManager;
8 |
9 | import java.util.Arrays;
10 | import java.util.Collections;
11 | import java.util.List;
12 |
13 | public class PushNotificationPackage implements ReactPackage {
14 |
15 | @Override
16 | public List createNativeModules(ReactApplicationContext reactContext) {
17 | return Arrays.asList(
18 | new PushNotificationModule(reactContext)
19 | );
20 | }
21 |
22 | @Override
23 | public List> createJSModules() {
24 | return Collections.emptyList();
25 | }
26 |
27 | @Override
28 | public List createViewManagers(ReactApplicationContext reactContext) {
29 | return Collections.emptyList();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/android/app/src/main/java/io/scrollback/neighborhoods/modules/google/GoogleLoginPackage.java:
--------------------------------------------------------------------------------
1 | package io.scrollback.neighborhoods.modules.google;
2 |
3 | import com.facebook.react.ReactPackage;
4 | import com.facebook.react.bridge.JavaScriptModule;
5 | import com.facebook.react.bridge.NativeModule;
6 | import com.facebook.react.bridge.ReactApplicationContext;
7 | import com.facebook.react.uimanager.ViewManager;
8 |
9 | import java.util.Arrays;
10 | import java.util.Collections;
11 | import java.util.List;
12 |
13 | public class GoogleLoginPackage implements ReactPackage {
14 |
15 | @Override
16 | public List createNativeModules(ReactApplicationContext reactContext) {
17 | return Arrays.asList(new GoogleLoginModule(reactContext));
18 | }
19 |
20 | @Override
21 | public List> createJSModules() {
22 | return Collections.emptyList();
23 | }
24 |
25 | @Override
26 | public List createViewManagers(ReactApplicationContext reactContext) {
27 | return Collections.emptyList();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/android/app/src/main/java/io/scrollback/neighborhoods/modules/places/GooglePlacesPackage.java:
--------------------------------------------------------------------------------
1 | package io.scrollback.neighborhoods.modules.places;
2 |
3 | import com.facebook.react.ReactPackage;
4 | import com.facebook.react.bridge.JavaScriptModule;
5 | import com.facebook.react.bridge.NativeModule;
6 | import com.facebook.react.bridge.ReactApplicationContext;
7 | import com.facebook.react.uimanager.ViewManager;
8 |
9 | import java.util.Arrays;
10 | import java.util.Collections;
11 | import java.util.List;
12 |
13 |
14 | public class GooglePlacesPackage implements ReactPackage {
15 |
16 | @Override
17 | public List createNativeModules(ReactApplicationContext reactContext) {
18 | return Arrays.asList(new GooglePlacesModule(reactContext));
19 | }
20 |
21 | @Override
22 | public List> createJSModules() {
23 | return Collections.emptyList();
24 | }
25 |
26 | @Override
27 | public List createViewManagers(ReactApplicationContext reactContext) {
28 | return Collections.emptyList();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrollback/io.scrollback.neighborhoods/de5f8d0792cbd64230cdec7534c38fa29e906f22/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_status.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrollback/io.scrollback.neighborhoods/de5f8d0792cbd64230cdec7534c38fa29e906f22/android/app/src/main/res/mipmap-hdpi/ic_status.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrollback/io.scrollback.neighborhoods/de5f8d0792cbd64230cdec7534c38fa29e906f22/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_status.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrollback/io.scrollback.neighborhoods/de5f8d0792cbd64230cdec7534c38fa29e906f22/android/app/src/main/res/mipmap-mdpi/ic_status.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrollback/io.scrollback.neighborhoods/de5f8d0792cbd64230cdec7534c38fa29e906f22/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_status.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrollback/io.scrollback.neighborhoods/de5f8d0792cbd64230cdec7534c38fa29e906f22/android/app/src/main/res/mipmap-xhdpi/ic_status.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrollback/io.scrollback.neighborhoods/de5f8d0792cbd64230cdec7534c38fa29e906f22/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_status.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrollback/io.scrollback.neighborhoods/de5f8d0792cbd64230cdec7534c38fa29e906f22/android/app/src/main/res/mipmap-xxhdpi/ic_status.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrollback/io.scrollback.neighborhoods/de5f8d0792cbd64230cdec7534c38fa29e906f22/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_status.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrollback/io.scrollback.neighborhoods/de5f8d0792cbd64230cdec7534c38fa29e906f22/android/app/src/main/res/mipmap-xxxhdpi/ic_status.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values-v19/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 | #673ab7
3 | #563099
4 | #ff9800
5 |
6 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | heyneighbor.chat
3 | https:
4 | Hey, Neighbor!
5 | 1389363534614084
6 |
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:1.3.1'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | mavenLocal()
18 | jcenter()
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/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.useDeprecatedNdk=true
21 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrollback/io.scrollback.neighborhoods/de5f8d0792cbd64230cdec7534c38fa29e906f22/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip
6 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'HeyNeighbor'
2 |
3 | include ':app'
4 |
5 | include ':react-native-image-chooser'
6 | project(':react-native-image-chooser').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-chooser/android')
7 |
--------------------------------------------------------------------------------
/app/containers/AccountContainer.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import React from 'react-native';
4 | import Account from '../views/Account/Account';
5 | import Container from './Container';
6 | import store from '../store/store';
7 |
8 | class AccountContainer extends React.Component {
9 | state = {
10 | user: 'missing'
11 | };
12 |
13 | componentDidMount() {
14 | this.runAfterInteractions(this._updateData);
15 |
16 | this.handle('statechange', changes => {
17 | const user = store.get('user');
18 |
19 | if (changes.entities && changes.entities[user]) {
20 | this._updateData();
21 | }
22 | });
23 | }
24 |
25 | _updateData = () => {
26 | this.setState({
27 | user: store.getUser()
28 | });
29 | };
30 |
31 | _saveUser = user => {
32 | this.dispatch('user', {
33 | to: user.id,
34 | user
35 | });
36 |
37 | this.setState({ user });
38 | };
39 |
40 | _signOut = () => {
41 | this.emit('logout');
42 | };
43 |
44 | render() {
45 | return (
46 |
52 | );
53 | }
54 | }
55 |
56 | export default Container(AccountContainer);
57 |
--------------------------------------------------------------------------------
/app/containers/AppContainer.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import React from 'react-native';
4 | import App from '../views/App';
5 | import Container from './Container';
6 | import store from '../store/store';
7 |
8 | class AppContainer extends React.Component {
9 | state = {
10 | user: 'missing',
11 | connectionStatus: 'connecting'
12 | };
13 |
14 | componentDidMount() {
15 | this.handle('statechange', changes => {
16 | if (changes && 'user' in changes || this.state.user === 'missing' && changes.app.connectionStatus) {
17 | this._updateData();
18 | }
19 | });
20 | }
21 |
22 | _updateData = () => {
23 | const user = store.get('user');
24 | const connectionStatus = store.get('app', 'connectionStatus') || 'connecting';
25 |
26 | if (user && user !== this.state.user) {
27 | this.setState({
28 | user,
29 | connectionStatus
30 | });
31 | } else if (connectionStatus !== this.state.connectionStatus) {
32 | this.setState({
33 | connectionStatus
34 | });
35 | }
36 | };
37 |
38 | render() {
39 | return ;
40 | }
41 | }
42 |
43 | export default Container(AppContainer);
44 |
--------------------------------------------------------------------------------
/app/containers/AvatarContainer.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import React from 'react-native';
4 | import Avatar from '../views/Avatar';
5 | import Container from './Container';
6 | import store from '../store/store';
7 | import config from '../store/config';
8 | import getAvatar from '../lib/get-avatar';
9 |
10 | const {
11 | PixelRatio
12 | } = React;
13 |
14 | class AvatarContainer extends React.Component {
15 |
16 | static propTypes = {
17 | nick: React.PropTypes.string.isRequired,
18 | size: React.PropTypes.number
19 | };
20 |
21 | static defaultProps = {
22 | size: 48
23 | };
24 |
25 | state = {
26 | uri: null
27 | };
28 |
29 | componentDidMount() {
30 | this.runAfterInteractions(this._updateData);
31 |
32 | this.handle('statechange', changes => {
33 | if (changes.entities && changes.entities[this.props.nick]) {
34 | this._updateData();
35 | }
36 | });
37 | }
38 |
39 | _updateData = () => {
40 | const { protocol, host } = config.server;
41 | const { nick, size } = this.props;
42 |
43 | const user = store.getUser(nick);
44 |
45 | let uri;
46 |
47 | if (user && user.picture) {
48 | uri = getAvatar(user.picture, (size * PixelRatio.get()));
49 | } else {
50 | uri = protocol + '//' + host + '/i/' + nick + '/picture?size=' + (size * PixelRatio.get());
51 | }
52 |
53 | this.setState({
54 | uri
55 | });
56 | };
57 |
58 | render() {
59 | return ;
60 | }
61 | }
62 |
63 | export default Container(AvatarContainer);
64 |
--------------------------------------------------------------------------------
/app/containers/BannerOfflineContainer.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import React from 'react-native';
4 | import BannerOffline from '../views/BannerOffline';
5 | import Container from './Container';
6 | import store from '../store/store';
7 |
8 | class BannerOfflineContainer extends React.Component {
9 | state = {
10 | connectionStatus: null
11 | };
12 |
13 | componentDidMount() {
14 | this.runAfterInteractions(this._updateData);
15 |
16 | this.handle('statechange', changes => {
17 | if (changes.app && changes.app.connectionStatus) {
18 | this._updateData();
19 | }
20 | });
21 | }
22 |
23 | _updateData = () => {
24 | this.setState({
25 | connectionStatus: store.get('app', 'connectionStatus')
26 | });
27 | };
28 |
29 | render() {
30 | return (
31 |
32 | );
33 | }
34 | }
35 |
36 | export default Container(BannerOfflineContainer);
37 |
--------------------------------------------------------------------------------
/app/containers/ChatContainer.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import React from 'react-native';
4 | import Chat from '../views/Chat';
5 | import Container from './Container';
6 | import store from '../store/store';
7 |
8 | class ChatContainer extends React.Component {
9 | static propTypes = {
10 | room: React.PropTypes.string.isRequired,
11 | thread: React.PropTypes.string.isRequired
12 | };
13 |
14 | state = {
15 | user: 'missing'
16 | };
17 |
18 | componentDidMount() {
19 | this.runAfterInteractions(this._updateData);
20 | }
21 |
22 | _updateData = () => {
23 | this.setState({
24 | user: store.get('user')
25 | });
26 | };
27 |
28 | _sendMessage = (text, textId) => {
29 | const textObj = {
30 | id: null,
31 | text: text.trim(),
32 | thread: this.props.thread,
33 | to: this.props.room,
34 | from: this.state.user
35 | };
36 |
37 | if (textId) {
38 | textObj.id = textId;
39 | }
40 |
41 | this.dispatch('text', textObj);
42 | };
43 |
44 | render() {
45 | return (
46 |
51 | );
52 | }
53 | }
54 |
55 | export default Container(ChatContainer);
56 |
--------------------------------------------------------------------------------
/app/containers/ChatItemContainer.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import React from 'react-native';
4 | import ChatItem from '../views/ChatItem';
5 | import Container from './Container';
6 | import store from '../store/store';
7 | import actions from '../store/actions';
8 |
9 | class ChatItemContainer extends React.Component {
10 | static propTypes = {
11 | text: React.PropTypes.shape({
12 | id: React.PropTypes.string.isRequired,
13 | from: React.PropTypes.string.isRequired,
14 | to: React.PropTypes.string.isRequired
15 | }).isRequired
16 | };
17 |
18 | _isUserAdmin = () => {
19 | return store.isUserAdmin(store.get('user'), this.props.text.to);
20 | };
21 |
22 | _isUserBanned = () => {
23 | return store.isUserBanned(this.props.text.from, this.props.text.to);
24 | };
25 |
26 | _hideText = () => {
27 | return actions.hideText(this.props.text);
28 | };
29 |
30 | _unhideText = () => {
31 | return actions.unhideText(this.props.text);
32 | };
33 |
34 | _banUser = () => {
35 | return actions.banUser(this.props.text);
36 | };
37 |
38 | _unbanUser = () => {
39 | return actions.unbanUser(this.props.text);
40 | };
41 |
42 | render() {
43 | return (
44 |
54 | );
55 | }
56 | }
57 |
58 | export default Container(ChatItemContainer);
59 |
--------------------------------------------------------------------------------
/app/containers/ChatTitleContainer.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import React from 'react-native';
4 | import ChatTitle from '../views/ChatTitle';
5 | import Container from './Container';
6 | import store from '../store/store';
7 |
8 | class ChatTitleContainer extends React.Component {
9 | static propTypes = {
10 | thread: React.PropTypes.string.isRequired
11 | };
12 |
13 | state = {
14 | thread: 'missing'
15 | };
16 |
17 | componentDidMount() {
18 | this.runAfterInteractions(this._updateData);
19 |
20 | this.handle('statechange', changes => {
21 | if (changes.indexes && changes.indexes.threadsById && changes.indexes.threadsById[this.props.thread]) {
22 | this._updateData();
23 | }
24 | });
25 | }
26 |
27 | _updateData = () => {
28 | const thread = store.getThreadById(this.props.thread);
29 |
30 | if (thread) {
31 | this.setState({
32 | thread
33 | });
34 | }
35 | };
36 |
37 | render() {
38 | return ;
39 | }
40 | }
41 |
42 | export default Container(ChatTitleContainer);
43 |
--------------------------------------------------------------------------------
/app/containers/DiscussionDetailsContainer.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import React from 'react-native';
4 | import DiscussionDetails from '../views/DiscussionDetails';
5 | import Container from './Container';
6 | import store from '../store/store';
7 |
8 | class DiscussionDetailsContainer extends React.Component {
9 | static propTypes = {
10 | thread: React.PropTypes.string.isRequired
11 | };
12 |
13 | state = {
14 | thread: 'missing'
15 | };
16 |
17 | componentDidMount() {
18 | this.runAfterInteractions(this._updateData);
19 |
20 | this.handle('statechange', changes => {
21 | if (changes.indexes && changes.indexes.threadsById && changes.indexes.threadsById[this.props.thread]) {
22 | this._updateData();
23 | }
24 | });
25 | }
26 |
27 | _updateData = () => {
28 | this.setState({
29 | thread: store.getThreadById(this.props.thread) || 'failed'
30 | });
31 | };
32 |
33 | render() {
34 | return ;
35 | }
36 | }
37 |
38 | export default Container(DiscussionDetailsContainer);
39 |
--------------------------------------------------------------------------------
/app/containers/DiscussionItemContainer.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import React from 'react-native';
4 | import DiscussionItem from '../views/DiscussionItem';
5 | import Container from './Container';
6 | import store from '../store/store';
7 | import actions from '../store/actions';
8 |
9 | class DiscussionItemContainer extends React.Component {
10 | static propTypes = {
11 | thread: React.PropTypes.shape({
12 | id: React.PropTypes.string.isRequired,
13 | from: React.PropTypes.string.isRequired,
14 | to: React.PropTypes.string.isRequired
15 | }).isRequired
16 | };
17 |
18 | _isCurrentUserAdmin = () => {
19 | return store.isUserAdmin(store.get('user'), this.props.thread.to);
20 | };
21 |
22 | _isUserBanned = () => {
23 | return store.isUserBanned(this.props.thread.from, this.props.thread.to);
24 | };
25 |
26 | _hideText = () => {
27 | return actions.hideText(this.props.thread);
28 | };
29 |
30 | _unhideText = () => {
31 | return actions.unhideText(this.props.thread);
32 | };
33 |
34 | _banUser = () => {
35 | return actions.banUser(this.props.thread);
36 | };
37 |
38 | _unbanUser = () => {
39 | return actions.unbanUser(this.props.thread);
40 | };
41 |
42 | render() {
43 | return (
44 |
55 | );
56 | }
57 | }
58 |
59 | export default Container(DiscussionItemContainer);
60 |
--------------------------------------------------------------------------------
/app/containers/DiscussionsContainer.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import React from 'react-native';
4 | import Discussions from '../views/Discussions';
5 | import Container from './Container';
6 | import store from '../store/store';
7 |
8 | class DiscussionsContainer extends React.Component {
9 | static propTypes = {
10 | room: React.PropTypes.string.isRequired
11 | };
12 |
13 | state = {
14 | data: [ 'missing' ],
15 | user: ''
16 | };
17 |
18 | componentDidMount() {
19 | this.handle('statechange', changes => {
20 | if (changes.threads && changes.threads[this.props.room]) {
21 | this._updateData();
22 | }
23 | });
24 |
25 | this.runAfterInteractions(async () => {
26 | this.emit('setstate', {
27 | nav: {
28 | room: this.props.room,
29 | mode: 'room'
30 | }
31 | });
32 |
33 | const requested = store.get('nav', this.props.room + '_requested');
34 |
35 | if (requested) {
36 | this._updateData();
37 | } else {
38 | this._handleEndReached();
39 | }
40 | });
41 | }
42 |
43 | _updateData = () => {
44 | const requested = store.get('nav', this.props.room + '_requested');
45 | const threads = store.getThreads(this.props.room, null, -requested);
46 |
47 | this.setState({
48 | data: threads.reverse(),
49 | user: store.get('user')
50 | });
51 | };
52 |
53 | _handleEndReached = () => {
54 | const key = this.props.room + '_requested';
55 | const requested = store.get('nav', key);
56 | const threads = store.getThreads(this.props.room, null, -requested);
57 |
58 | if (requested && requested > (threads.length + 1)) {
59 | return;
60 | }
61 |
62 | this.emit('setstate', {
63 | nav: {
64 | [key]: (requested || 0) + 20
65 | }
66 | });
67 | };
68 |
69 | render() {
70 | return (
71 |
76 | );
77 | }
78 | }
79 |
80 | export default Container(DiscussionsContainer);
81 |
--------------------------------------------------------------------------------
/app/containers/HomeContainer.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import React from 'react-native';
4 | import Home from '../views/Home';
5 | import Linking from '../modules/Linking';
6 | import Container from './Container';
7 |
8 | class AppContainer extends React.Component {
9 | state = {
10 | initialURL: 'missing'
11 | };
12 |
13 | componentDidMount() {
14 | this._setInitialURL();
15 | }
16 |
17 | _setInitialURL = () => {
18 | Linking.getInitialURL(url => {
19 | this.setState({
20 | initialURL: url
21 | });
22 | });
23 | };
24 |
25 | render() {
26 | if (this.state.initialURL === 'missing') {
27 | return null;
28 | } else {
29 | return ;
30 | }
31 | }
32 | }
33 |
34 | export default Container(AppContainer);
35 |
--------------------------------------------------------------------------------
/app/containers/LocalitiesContainer.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import React from 'react-native';
4 | import Localities from '../views/Localities';
5 | import Container from './Container';
6 | import store from '../store/store';
7 |
8 | class LocalitiesContainer extends React.Component {
9 | state = {
10 | data: [ 'missing' ]
11 | };
12 |
13 | componentDidMount() {
14 | this.runAfterInteractions(this._updateData);
15 |
16 | this.handle('statechange', changes => {
17 | const user = store.get('user');
18 |
19 | if ((changes.indexes && changes.indexes.userRooms && changes.indexes.userRooms[user]) || (changes.app && changes.app.nearByRooms)) {
20 | this._updateData();
21 | }
22 | });
23 |
24 | this.runAfterInteractions(() => {
25 | this.emit('setstate', {
26 | nav: { mode: 'home' }
27 | });
28 | });
29 | }
30 |
31 | _updateData = () => {
32 |
33 | const following = store.getRelatedRooms().filter(room => room.role && room.role !== 'none');
34 | const user = store.getUser();
35 | const available = user && user.params ? (user.params.places && user.params.places.length || user.params.skipped !== true) : false;
36 |
37 | let data;
38 |
39 | if (available) {
40 | data = following;
41 | } else {
42 | data = [ {
43 | id: 'open-house',
44 | guides: {
45 | displayName: 'Open House'
46 | }
47 | } ];
48 | }
49 |
50 | this.setState({
51 | data,
52 | available
53 | });
54 | };
55 |
56 | render() {
57 | return (
58 |
62 | );
63 | }
64 | }
65 |
66 | export default Container(LocalitiesContainer);
67 |
--------------------------------------------------------------------------------
/app/containers/LocalitiesFilteredContainer.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import React from 'react-native';
4 | import LocalitiesFiltered from '../views/LocalitiesFiltered';
5 | import Geolocation from '../modules/Geolocation';
6 | import Container from './Container';
7 |
8 | class LocalitiesFilteredContainer extends React.Component {
9 | static propTypes = {
10 | excludeList: React.PropTypes.arrayOf(React.PropTypes.string)
11 | };
12 |
13 | _getResults = async filter => {
14 | const opts: {
15 | ref?: string;
16 | limit?: number;
17 | location?: {
18 | lat: number;
19 | lon: number
20 | }
21 | } = filter ? {
22 | ref: filter.trim().replace(/\W+/g, '-').toLowerCase() + '*'
23 | } : { limit: 5 };
24 |
25 | try {
26 | const position = await Geolocation.getCurrentPosition();
27 | const { latitude: lat, longitude: lon } = position.coords;
28 |
29 | opts.location = {
30 | lat,
31 | lon
32 | };
33 | } catch (e) {
34 | // Ignore
35 | }
36 |
37 | if (opts.ref || opts.location) {
38 | const { excludeList } = this.props;
39 | const results = await this.query('getRooms', opts);
40 |
41 | if (excludeList && excludeList.length) {
42 | return results.filter(room => room && excludeList.indexOf(room.id) === -1);
43 | } else {
44 | return results;
45 | }
46 | } else {
47 | return [];
48 | }
49 | };
50 |
51 | render() {
52 | return (
53 |
54 | );
55 | }
56 | }
57 |
58 | export default Container(LocalitiesFilteredContainer);
59 |
--------------------------------------------------------------------------------
/app/containers/LocalityTitleContainer.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import React from 'react-native';
4 | import LocalityTitle from '../views/LocalityTitle';
5 | import Container from './Container';
6 | import store from '../store/store';
7 |
8 | class LocalityTitleContainer extends React.Component {
9 | static propTypes = {
10 | room: React.PropTypes.string.isRequired
11 | };
12 |
13 | constructor(props) {
14 | super(props);
15 |
16 | const displayName = this.props.room.replace(/-+/g, ' ').replace(/\w\S*/g, s => s.charAt(0).toUpperCase() + s.slice(1)).trim();
17 |
18 | this.state = {
19 | room: {
20 | guides: { displayName }
21 | }
22 | };
23 | }
24 |
25 | componentDidMount() {
26 | this.runAfterInteractions(this._updateData);
27 |
28 | this.handle('statechange', changes => {
29 | if (changes.entities && changes.entities[this.props.room]) {
30 | this._updateData();
31 | }
32 | });
33 | }
34 |
35 | _updateData = () => {
36 | const room = store.getRoom(this.props.room);
37 |
38 | if (room.guides && room.guides.displayName) {
39 | this.setState({ room });
40 | }
41 | };
42 |
43 | render() {
44 | return ;
45 | }
46 | }
47 |
48 | export default Container(LocalityTitleContainer);
49 |
--------------------------------------------------------------------------------
/app/containers/MyPlacesContainer.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import React from 'react-native';
4 | import MyPlaces from '../views/Account/MyPlaces';
5 | import Container from './Container';
6 | import store from '../store/store';
7 |
8 | class MyPlacesContainer extends React.Component {
9 | state = {
10 | user: null,
11 | places: [ 'missing' ]
12 | };
13 |
14 | componentDidMount() {
15 | this.runAfterInteractions(this._updateData);
16 | }
17 |
18 | _updateData = () => {
19 | const user = store.getUser();
20 |
21 | this.setState({
22 | user,
23 | places: this._getPlacesData(user)
24 | });
25 | };
26 |
27 | _getPlacesData = (user) => {
28 | if (user && user.params && user.params.places) {
29 | return user.params.places.map(it => {
30 | const room = store.getRoom(it.id);
31 |
32 | return {
33 | place: typeof room === 'object' ? room : { id: it.id },
34 | type: it.type
35 | };
36 | });
37 | } else {
38 | return [];
39 | }
40 | };
41 |
42 | _saveParams = (params: Object) => {
43 | const user = {
44 | ...this.state.user,
45 | params: {
46 | ...this.state.user.params,
47 | ...params
48 | }
49 | };
50 |
51 | return this.dispatch('user', {
52 | from: user.id,
53 | to: user.id,
54 | user
55 | });
56 | };
57 |
58 | _savePlaces = (places: Array