diff --git a/RootNavigationContainer.js b/RootNavigationContainer.js index 5b155d86..2030f387 100644 --- a/RootNavigationContainer.js +++ b/RootNavigationContainer.js @@ -1,10 +1,10 @@ import React from 'react'; -import { View, Dimensions, Easing, Animated } from 'react-native'; +import { View, Platform } from 'react-native'; import { Root } from 'native-base'; import { createSwitchNavigator, createAppContainer } from 'react-navigation'; import { createStackNavigator } from 'react-navigation-stack'; +import NavigationAnimation from "./src/helpers/NavigationAnimation"; import { createBottomTabNavigator } from 'react-navigation-tabs'; -import deepGet from 'lodash/get'; import NavigationService from './src/services/NavigationService'; import AuthLoading from './src/components/AuthLoading'; @@ -39,6 +39,7 @@ import { NotificationToastComponent } from './src/theme/components/NotificationT import SocketManager from './src/services/SocketManager'; import SearchScreen from './src/components/Search'; import FanVideoDetails from './src/components/FanVideoDetails'; +import FanVideoReplyDetails from './src/components/FanVideoReplyDetails'; import WalletSettingScreen from './src/components/WalletSetting'; import StoreProductsScreen from './src/components/StoreProducts'; import PaymentWorker from './src/components/PaymentWorker'; @@ -56,14 +57,24 @@ import CouchMarks from './src/components/CouchMarks'; import RedemptiomScreen from './src/components/Redemption'; import VideoTags from './src/components/VideoTags'; import FullScreenVideoCollection from './src/components/FullScreenVideoCollection'; +import VideoReplies from './src/components/VideoReplies'; +import FullScreenReplyCollection from './src/components/FullScreenReplyCollection'; +import VideoReplyPlayer from './src/components/CommonComponents/VideoReplyPlayer'; const customTabHiddenRoutes = [ 'CaptureVideo', 'FanVideoDetails', + 'FanVideoReplyDetails', 'InviteCodeScreen', 'AddEmailScreen', 'InAppBrowserComponent', - 'CouchMarks' + 'CouchMarks', + 'VideoReplies', + 'FullScreenReplyCollection', + 'FullScreenVideoCollection', + 'UserVideoHistory', + 'VideoPlayer', + 'VideoReplyPlayer' ]; const modalStackConfig = { @@ -80,38 +91,44 @@ const modalStackConfig = { const txModalConfig = { transparentCard: true, - cardStyle: { backgroundColor: 'rgba(0,0,0,0.5)' }, + cardStyle: { backgroundColor: 'rgba(0,0,0,0)' }, gesturesEnabled: false, - transitionConfig: () => ({ - transitionSpec: { - duration: 300, - easing: Easing.out(Easing.poly(4)), - timing: Animated.timing - }, - screenInterpolator: (sceneProps) => { - const { layout, position, scene } = sceneProps; - const { index } = scene; + transitionConfig: (transitionProps, prevTransitionProps) => { + return NavigationAnimation.defaultTransition(); + } +}; - const height = layout.initHeight; - const translateY = position.interpolate({ - inputRange: [index - 1, index, index + 1], - outputRange: [height, 0, 0] - }); +const cardStackConfig = { + transparentCard: true, + cardStyle: { backgroundColor: 'rgba(0,0,0,0)' }, + transitionConfig: ( transitionProps, prevTransitionProps ) => { + const scenes = transitionProps["scenes"]; + const prevScene = scenes[scenes.length - 2]; + const nextScene = scenes[scenes.length - 1]; - const opacity = position.interpolate({ - inputRange: [index - 1, index - 0.99, index], - outputRange: [0, 1, 1] - }); + if (nextScene.route.routeName === 'VideoReplies') { + return NavigationAnimation.fromBottom(); + } - return { opacity, transform: [{ translateY }] }; + if(Platform.OS == "ios"){ + if (prevScene + && prevScene.route.routeName === 'VideoReplies' + && nextScene.route.routeName === 'FullScreenReplyCollection') { + return NavigationAnimation.fromBottom(); + } } - }) -}; + + } +} + const CaptureVideoStack = createStackNavigator( { CaptureVideo: CaptureVideo, - FanVideoDetails: FanVideoDetails + FanVideoDetails: FanVideoDetails, + FanVideoReplyDetails: FanVideoReplyDetails, + WalletSettingScreen: WalletSettingScreen, + WalletDetails: WalletDetails }, { headerLayoutPreset: 'center' @@ -134,11 +151,19 @@ const HomePushStack = createStackNavigator( UserVideoHistory: UserVideoHistory, SupportingListScreen: SupportingListScreen, SupportersListScreen: SupportersListScreen, + VideoPlayer: VideoPlayer, + VideoReplyPlayer: VideoReplyPlayer, VideoTags: VideoTags, - FullScreenVideoCollection: FullScreenVideoCollection + WalletSettingScreen: WalletSettingScreen, + WalletDetails: WalletDetails, + FullScreenVideoCollection: FullScreenVideoCollection, + VideoReplies:VideoReplies , + FullScreenReplyCollection: FullScreenReplyCollection }, { - headerLayoutPreset: 'center' + initialRouteName: 'HomeScreen', + headerLayoutPreset: 'center' , + ...cardStackConfig } ); @@ -166,13 +191,19 @@ const NotificationPushStack = createStackNavigator( UsersProfileScreen: UsersProfileScreen, UserVideoHistory: UserVideoHistory, VideoPlayer: VideoPlayer, + VideoReplyPlayer: VideoReplyPlayer, SupportingListScreen: SupportingListScreen, SupportersListScreen: SupportersListScreen, VideoTags: VideoTags, - FullScreenVideoCollection: FullScreenVideoCollection + WalletSettingScreen: WalletSettingScreen, + WalletDetails: WalletDetails, + FullScreenVideoCollection: FullScreenVideoCollection, + VideoReplies:VideoReplies , + FullScreenReplyCollection: FullScreenReplyCollection }, { - headerLayoutPreset: 'center' + headerLayoutPreset: 'center', + ...cardStackConfig } ); @@ -199,14 +230,19 @@ const ProfilePushStack = createStackNavigator( BioScreen: BioScreen, EmailScreen: EmailScreen, ReferAndEarn: ReferAndEarn, + VideoPlayer: VideoPlayer, + VideoReplyPlayer: VideoReplyPlayer, Invites: Invites, WalletSettingScreen: WalletSettingScreen, WalletDetails: WalletDetails, VideoTags: VideoTags, - FullScreenVideoCollection: FullScreenVideoCollection + FullScreenVideoCollection: FullScreenVideoCollection, + VideoReplies:VideoReplies , + FullScreenReplyCollection: FullScreenReplyCollection }, { - headerLayoutPreset: 'center' + headerLayoutPreset: 'center', + ...cardStackConfig } ); @@ -222,17 +258,7 @@ const ProfileStack = createStackNavigator( StoreProductsScreen: StoreProductsScreen, RedemptiomScreen: RedemptiomScreen }, - { - headerLayoutPreset: 'center', - headerMode: 'none', - mode: 'modal', - navigationOptions: ({ navigation }) => { - return { - tabBarVisible: deepGet(navigation, 'state.index') === 0 - }; - }, - ...txModalConfig - } + { ...modalStackConfig, ...txModalConfig } ); const SearchPushStack = createStackNavigator( @@ -242,11 +268,18 @@ const SearchPushStack = createStackNavigator( SupportingListScreen: SupportingListScreen, SupportersListScreen: SupportersListScreen, UserVideoHistory: UserVideoHistory, + VideoPlayer: VideoPlayer, + VideoReplyPlayer: VideoReplyPlayer, VideoTags: VideoTags, - FullScreenVideoCollection: FullScreenVideoCollection + WalletSettingScreen: WalletSettingScreen, + WalletDetails: WalletDetails, + FullScreenVideoCollection: FullScreenVideoCollection, + VideoReplies:VideoReplies , + FullScreenReplyCollection: FullScreenReplyCollection, }, { - headerLayoutPreset: 'center' + headerLayoutPreset: 'center', + ...cardStackConfig } ); diff --git a/android/app/build.gradle b/android/app/build.gradle index 9a553b9c..1e055172 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -126,8 +126,8 @@ android { applicationId "com.pepo.staging" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 19 - versionName "0.9.6" + versionCode 20 + versionName "1.0.0" missingDimensionStrategy 'react-native-camera', 'general' } splits { diff --git a/index.js b/index.js index 83b27449..226255de 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,6 @@ /** * @format */ -import * as foo from "./src/helpers/Logger"; import { AppRegistry } from 'react-native'; import AppContainer from './AppContainer'; import { name as appName } from './app.json'; diff --git a/ios/Cartfile b/ios/Cartfile index 6ad59fcb..50d55200 100644 --- a/ios/Cartfile +++ b/ios/Cartfile @@ -1 +1 @@ -github "ostdotcom/ost-wallet-sdk-ios" == 2.3.4 +github "ostdotcom/ost-wallet-sdk-ios" == 2.3.5 diff --git a/ios/Pepo2.xcodeproj/project.pbxproj b/ios/Pepo2.xcodeproj/project.pbxproj index 53da8e0c..3c15fe5c 100644 --- a/ios/Pepo2.xcodeproj/project.pbxproj +++ b/ios/Pepo2.xcodeproj/project.pbxproj @@ -5,7 +5,6 @@ }; objectVersion = 46; objects = { - /* Begin PBXBuildFile section */ 00029B4323329B55009E6919 /* TrustKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B76D86A2318FAAC007316D4 /* TrustKit.framework */; }; 00029B4423329B63009E6919 /* OstWalletSdk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0050FDA022A8226800C2634F /* OstWalletSdk.framework */; }; @@ -2314,7 +2313,7 @@ "$(inherited)", "\"$(SRCROOT)/Pepo2\"", ); - MARKETING_VERSION = 0.9.7; + MARKETING_VERSION = 1.0.0; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -2377,7 +2376,7 @@ "$(inherited)", "\"$(SRCROOT)/Pepo2\"", ); - MARKETING_VERSION = 0.9.7; + MARKETING_VERSION = 1.0.0; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/ios/Pepo2/AppDelegate.m b/ios/Pepo2/AppDelegate.m index 0920b269..4bd3bbec 100644 --- a/ios/Pepo2/AppDelegate.m +++ b/ios/Pepo2/AppDelegate.m @@ -146,6 +146,7 @@ - (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull N restorationHandler:restorationHandler]; return handled; + } diff --git a/ios/Pepo2/InfoPlist/com.pepo.staging-Info.plist b/ios/Pepo2/InfoPlist/com.pepo.staging-Info.plist index e0f6f95d..37629cd6 100644 --- a/ios/Pepo2/InfoPlist/com.pepo.staging-Info.plist +++ b/ios/Pepo2/InfoPlist/com.pepo.staging-Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.9.7 + 1.0.0 CFBundleSignature ???? CFBundleURLTypes diff --git a/ios/Pepo2/InfoPlist/com.pepo.v2.production-Info.plist b/ios/Pepo2/InfoPlist/com.pepo.v2.production-Info.plist index 1b3349e1..7c27f3fa 100644 --- a/ios/Pepo2/InfoPlist/com.pepo.v2.production-Info.plist +++ b/ios/Pepo2/InfoPlist/com.pepo.v2.production-Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.9.7 + 1.0.0 CFBundleSignature ???? CFBundleURLTypes diff --git a/ios/Pepo2/InfoPlist/com.pepo.v2.sandbox-Info.plist b/ios/Pepo2/InfoPlist/com.pepo.v2.sandbox-Info.plist index daf3861c..7d1e6a53 100644 --- a/ios/Pepo2/InfoPlist/com.pepo.v2.sandbox-Info.plist +++ b/ios/Pepo2/InfoPlist/com.pepo.v2.sandbox-Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.9.7 + 1.0.0 CFBundleSignature ???? CFBundleURLTypes diff --git a/package-lock.json b/package-lock.json index 842076a8..93f5ac3e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,11 +35,11 @@ } }, "@babel/generator": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.2.tgz", - "integrity": "sha512-WthSArvAjYLz4TcbKOi88me+KmDJdKSlfwwN8CnUYn9jBkzhq0ZEPuBfkAWIvjJ3AdEV1Cf/+eSQTnp3IDJKlQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", + "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", "requires": { - "@babel/types": "^7.7.2", + "@babel/types": "^7.7.4", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" @@ -53,70 +53,70 @@ } }, "@babel/helper-annotate-as-pure": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.7.0.tgz", - "integrity": "sha512-k50CQxMlYTYo+GGyUGFwpxKVtxVJi9yh61sXZji3zYHccK9RYliZGSTOgci85T+r+0VFN2nWbGM04PIqwfrpMg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.7.4.tgz", + "integrity": "sha512-2BQmQgECKzYKFPpiycoF9tlb5HA4lrVyAmLLVK177EcQAqjVLciUb2/R+n1boQ9y5ENV3uz2ZqiNw7QMBBw1Og==", "requires": { - "@babel/types": "^7.7.0" + "@babel/types": "^7.7.4" } }, "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.7.0.tgz", - "integrity": "sha512-Cd8r8zs4RKDwMG/92lpZcnn5WPQ3LAMQbCw42oqUh4s7vsSN5ANUZjMel0OOnxDLq57hoDDbai+ryygYfCTOsw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.7.4.tgz", + "integrity": "sha512-Biq/d/WtvfftWZ9Uf39hbPBYDUo986m5Bb4zhkeYDGUllF43D+nUe5M6Vuo6/8JDK/0YX/uBdeoQpyaNhNugZQ==", "requires": { - "@babel/helper-explode-assignable-expression": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/helper-explode-assignable-expression": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/helper-builder-react-jsx": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.7.0.tgz", - "integrity": "sha512-LSln3cexwInTMYYoFeVLKnYPPMfWNJ8PubTBs3hkh7wCu9iBaqq1OOyW+xGmEdLxT1nhsl+9SJ+h2oUDYz0l2A==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.7.4.tgz", + "integrity": "sha512-kvbfHJNN9dg4rkEM4xn1s8d1/h6TYNvajy9L1wx4qLn9HFg0IkTsQi4rfBe92nxrPUFcMsHoMV+8rU7MJb3fCA==", "requires": { - "@babel/types": "^7.7.0", + "@babel/types": "^7.7.4", "esutils": "^2.0.0" } }, "@babel/helper-call-delegate": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.7.0.tgz", - "integrity": "sha512-Su0Mdq7uSSWGZayGMMQ+z6lnL00mMCnGAbO/R0ZO9odIdB/WNU/VfQKqMQU0fdIsxQYbRjDM4BixIa93SQIpvw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.7.4.tgz", + "integrity": "sha512-8JH9/B7J7tCYJ2PpWVpw9JhPuEVHztagNVuQAFBVFYluRMlpG7F1CgKEgGeL6KFqcsIa92ZYVj6DSc0XwmN1ZA==", "requires": { - "@babel/helper-hoist-variables": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/helper-hoist-variables": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/helper-create-class-features-plugin": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.7.0.tgz", - "integrity": "sha512-MZiB5qvTWoyiFOgootmRSDV1udjIqJW/8lmxgzKq6oDqxdmHUjeP2ZUOmgHdYjmUVNABqRrHjYAYRvj8Eox/UA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.7.4.tgz", + "integrity": "sha512-l+OnKACG4uiDHQ/aJT8dwpR+LhCJALxL0mJ6nzjB25e5IPwqV1VOsY7ah6UB1DG+VOXAIMtuC54rFJGiHkxjgA==", "requires": { - "@babel/helper-function-name": "^7.7.0", - "@babel/helper-member-expression-to-functions": "^7.7.0", - "@babel/helper-optimise-call-expression": "^7.7.0", + "@babel/helper-function-name": "^7.7.4", + "@babel/helper-member-expression-to-functions": "^7.7.4", + "@babel/helper-optimise-call-expression": "^7.7.4", "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.7.0", - "@babel/helper-split-export-declaration": "^7.7.0" + "@babel/helper-replace-supers": "^7.7.4", + "@babel/helper-split-export-declaration": "^7.7.4" } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.7.2.tgz", - "integrity": "sha512-pAil/ZixjTlrzNpjx+l/C/wJk002Wo7XbbZ8oujH/AoJ3Juv0iN/UTcPUHXKMFLqsfS0Hy6Aow8M31brUYBlQQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.7.4.tgz", + "integrity": "sha512-Mt+jBKaxL0zfOIWrfQpnfYCN7/rS6GKx6CCCfuoqVVd+17R8zNDlzVYmIi9qyb2wOk002NsmSTDymkIygDUH7A==", "requires": { "@babel/helper-regex": "^7.4.4", "regexpu-core": "^4.6.0" } }, "@babel/helper-define-map": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.7.0.tgz", - "integrity": "sha512-kPKWPb0dMpZi+ov1hJiwse9dWweZsz3V9rP4KdytnX1E7z3cTNmFGglwklzFPuqIcHLIY3bgKSs4vkwXXdflQA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.7.4.tgz", + "integrity": "sha512-v5LorqOa0nVQUvAUTUF3KPastvUt/HzByXNamKQ6RdJRTV7j8rLL+WB5C/MzzWAwOomxDhYFb1wLLxHqox86lg==", "requires": { - "@babel/helper-function-name": "^7.7.0", - "@babel/types": "^7.7.0", + "@babel/helper-function-name": "^7.7.4", + "@babel/types": "^7.7.4", "lodash": "^4.17.13" }, "dependencies": { @@ -128,66 +128,66 @@ } }, "@babel/helper-explode-assignable-expression": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.7.0.tgz", - "integrity": "sha512-CDs26w2shdD1urNUAji2RJXyBFCaR+iBEGnFz3l7maizMkQe3saVw9WtjG1tz8CwbjvlFnaSLVhgnu1SWaherg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.7.4.tgz", + "integrity": "sha512-2/SicuFrNSXsZNBxe5UGdLr+HZg+raWBLE9vC98bdYOKX/U6PY0mdGlYUJdtTDPSU0Lw0PNbKKDpwYHJLn2jLg==", "requires": { - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/traverse": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/helper-function-name": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.0.tgz", - "integrity": "sha512-tDsJgMUAP00Ugv8O2aGEua5I2apkaQO7lBGUq1ocwN3G23JE5Dcq0uh3GvFTChPa4b40AWiAsLvCZOA2rdnQ7Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", + "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", "requires": { - "@babel/helper-get-function-arity": "^7.7.0", - "@babel/template": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/helper-get-function-arity": "^7.7.4", + "@babel/template": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/helper-get-function-arity": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.0.tgz", - "integrity": "sha512-tLdojOTz4vWcEnHWHCuPN5P85JLZWbm5Fx5ZsMEMPhF3Uoe3O7awrbM2nQ04bDOUToH/2tH/ezKEOR8zEYzqyw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", + "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", "requires": { - "@babel/types": "^7.7.0" + "@babel/types": "^7.7.4" } }, "@babel/helper-hoist-variables": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.7.0.tgz", - "integrity": "sha512-LUe/92NqsDAkJjjCEWkNe+/PcpnisvnqdlRe19FahVapa4jndeuJ+FBiTX1rcAKWKcJGE+C3Q3tuEuxkSmCEiQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.7.4.tgz", + "integrity": "sha512-wQC4xyvc1Jo/FnLirL6CEgPgPCa8M74tOdjWpRhQYapz5JC7u3NYU1zCVoVAGCE3EaIP9T1A3iW0WLJ+reZlpQ==", "requires": { - "@babel/types": "^7.7.0" + "@babel/types": "^7.7.4" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.7.0.tgz", - "integrity": "sha512-QaCZLO2RtBcmvO/ekOLp8p7R5X2JriKRizeDpm5ChATAFWrrYDcDxPuCIBXKyBjY+i1vYSdcUTMIb8psfxHDPA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.7.4.tgz", + "integrity": "sha512-9KcA1X2E3OjXl/ykfMMInBK+uVdfIVakVe7W7Lg3wfXUNyS3Q1HWLFRwZIjhqiCGbslummPDnmb7vIekS0C1vw==", "requires": { - "@babel/types": "^7.7.0" + "@babel/types": "^7.7.4" } }, "@babel/helper-module-imports": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.7.0.tgz", - "integrity": "sha512-Dv3hLKIC1jyfTkClvyEkYP2OlkzNvWs5+Q8WgPbxM5LMeorons7iPP91JM+DU7tRbhqA1ZeooPaMFvQrn23RHw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.7.4.tgz", + "integrity": "sha512-dGcrX6K9l8258WFjyDLJwuVKxR4XZfU0/vTUgOQYWEnRD8mgr+p4d6fCUMq/ys0h4CCt/S5JhbvtyErjWouAUQ==", "requires": { - "@babel/types": "^7.7.0" + "@babel/types": "^7.7.4" } }, "@babel/helper-module-transforms": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.7.0.tgz", - "integrity": "sha512-rXEefBuheUYQyX4WjV19tuknrJFwyKw0HgzRwbkyTbB+Dshlq7eqkWbyjzToLrMZk/5wKVKdWFluiAsVkHXvuQ==", - "requires": { - "@babel/helper-module-imports": "^7.7.0", - "@babel/helper-simple-access": "^7.7.0", - "@babel/helper-split-export-declaration": "^7.7.0", - "@babel/template": "^7.7.0", - "@babel/types": "^7.7.0", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.7.4.tgz", + "integrity": "sha512-ehGBu4mXrhs0FxAqN8tWkzF8GSIGAiEumu4ONZ/hD9M88uHcD+Yu2ttKfOCgwzoesJOJrtQh7trI5YPbRtMmnA==", + "requires": { + "@babel/helper-module-imports": "^7.7.4", + "@babel/helper-simple-access": "^7.7.4", + "@babel/helper-split-export-declaration": "^7.7.4", + "@babel/template": "^7.7.4", + "@babel/types": "^7.7.4", "lodash": "^4.17.13" }, "dependencies": { @@ -199,11 +199,11 @@ } }, "@babel/helper-optimise-call-expression": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.7.0.tgz", - "integrity": "sha512-48TeqmbazjNU/65niiiJIJRc5JozB8acui1OS7bSd6PgxfuovWsvjfWSzlgx+gPFdVveNzUdpdIg5l56Pl5jqg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.7.4.tgz", + "integrity": "sha512-VB7gWZ2fDkSuqW6b1AKXkJWO5NyNI3bFL/kK79/30moK57blr6NbH8xcl2XcKCwOmJosftWunZqfO84IGq3ZZg==", "requires": { - "@babel/types": "^7.7.0" + "@babel/types": "^7.7.4" } }, "@babel/helper-plugin-utils": { @@ -227,64 +227,64 @@ } }, "@babel/helper-remap-async-to-generator": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.7.0.tgz", - "integrity": "sha512-pHx7RN8X0UNHPB/fnuDnRXVZ316ZigkO8y8D835JlZ2SSdFKb6yH9MIYRU4fy/KPe5sPHDFOPvf8QLdbAGGiyw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.7.4.tgz", + "integrity": "sha512-Sk4xmtVdM9sA/jCI80f+KS+Md+ZHIpjuqmYPk1M7F/upHou5e4ReYmExAiu6PVe65BhJPZA2CY9x9k4BqE5klw==", "requires": { - "@babel/helper-annotate-as-pure": "^7.7.0", - "@babel/helper-wrap-function": "^7.7.0", - "@babel/template": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/helper-annotate-as-pure": "^7.7.4", + "@babel/helper-wrap-function": "^7.7.4", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/helper-replace-supers": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.7.0.tgz", - "integrity": "sha512-5ALYEul5V8xNdxEeWvRsBzLMxQksT7MaStpxjJf9KsnLxpAKBtfw5NeMKZJSYDa0lKdOcy0g+JT/f5mPSulUgg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.7.4.tgz", + "integrity": "sha512-pP0tfgg9hsZWo5ZboYGuBn/bbYT/hdLPVSS4NMmiRJdwWhP0IznPwN9AE1JwyGsjSPLC364I0Qh5p+EPkGPNpg==", "requires": { - "@babel/helper-member-expression-to-functions": "^7.7.0", - "@babel/helper-optimise-call-expression": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/helper-member-expression-to-functions": "^7.7.4", + "@babel/helper-optimise-call-expression": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/helper-simple-access": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.7.0.tgz", - "integrity": "sha512-AJ7IZD7Eem3zZRuj5JtzFAptBw7pMlS3y8Qv09vaBWoFsle0d1kAn5Wq6Q9MyBXITPOKnxwkZKoAm4bopmv26g==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.7.4.tgz", + "integrity": "sha512-zK7THeEXfan7UlWsG2A6CI/L9jVnI5+xxKZOdej39Y0YtDYKx9raHk5F2EtK9K8DHRTihYwg20ADt9S36GR78A==", "requires": { - "@babel/template": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/template": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/helper-split-export-declaration": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.0.tgz", - "integrity": "sha512-HgYSI8rH08neWlAH3CcdkFg9qX9YsZysZI5GD8LjhQib/mM0jGOZOVkoUiiV2Hu978fRtjtsGsW6w0pKHUWtqA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", + "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", "requires": { - "@babel/types": "^7.7.0" + "@babel/types": "^7.7.4" } }, "@babel/helper-wrap-function": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.7.0.tgz", - "integrity": "sha512-sd4QjeMgQqzshSjecZjOp8uKfUtnpmCyQhKQrVJBBgeHAB/0FPi33h3AbVlVp07qQtMD4QgYSzaMI7VwncNK/w==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.7.4.tgz", + "integrity": "sha512-VsfzZt6wmsocOaVU0OokwrIytHND55yvyT4BPB9AIIgwr8+x7617hetdJTsuGwygN5RC6mxA9EJztTjuwm2ofg==", "requires": { - "@babel/helper-function-name": "^7.7.0", - "@babel/template": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/helper-function-name": "^7.7.4", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/helpers": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.7.0.tgz", - "integrity": "sha512-VnNwL4YOhbejHb7x/b5F39Zdg5vIQpUUNzJwx0ww1EcVRt41bbGRZWhAURrfY32T5zTT3qwNOQFWpn+P0i0a2g==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.7.4.tgz", + "integrity": "sha512-ak5NGZGJ6LV85Q1Zc9gn2n+ayXOizryhjSUBTdu5ih1tlVCJeuQENzc4ItyCVhINVXvIT/ZQ4mheGIsfBkpskg==", "requires": { - "@babel/template": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/highlight": { @@ -298,182 +298,182 @@ } }, "@babel/parser": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.3.tgz", - "integrity": "sha512-bqv+iCo9i+uLVbI0ILzKkvMorqxouI+GbV13ivcARXn9NNEabi2IEz912IgNpT/60BNXac5dgcfjb94NjsF33A==" + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", + "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==" }, "@babel/plugin-external-helpers": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-external-helpers/-/plugin-external-helpers-7.2.0.tgz", - "integrity": "sha512-QFmtcCShFkyAsNtdCM3lJPmRe1iB+vPZymlB4LnDIKEBj2yKQLQKtoxXxJ8ePT5fwMl4QGg303p4mB0UsSI2/g==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-external-helpers/-/plugin-external-helpers-7.7.4.tgz", + "integrity": "sha512-RVGNajLaFlknbZLutaP/uv7Q+xmVs2LMlEWFXbcjLnwtBdPqAVpV3nzYIAJqri/VjJCUrhG5nALijtg0aND+XA==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-proposal-class-properties": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.7.0.tgz", - "integrity": "sha512-tufDcFA1Vj+eWvwHN+jvMN6QsV5o+vUlytNKrbMiCeDL0F2j92RURzUsUMWE5EJkLyWxjdUslCsMQa9FWth16A==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.7.4.tgz", + "integrity": "sha512-EcuXeV4Hv1X3+Q1TsuOmyyxeTRiSqurGJ26+I/FW1WbymmRRapVORm6x1Zl3iDIHyRxEs+VXWp6qnlcfcJSbbw==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.7.0", + "@babel/helper-create-class-features-plugin": "^7.7.4", "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-proposal-export-default-from": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.5.2.tgz", - "integrity": "sha512-wr9Itk05L1/wyyZKVEmXWCdcsp/e185WUNl6AfYZeEKYaUPPvHXRDqO5K1VH7/UamYqGJowFRuCv30aDYZawsg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.7.4.tgz", + "integrity": "sha512-1t6dh7BHYUz4zD1m4pozYYEZy/3m8dgOr9owx3r0mPPI3iGKRUKUbIxfYmcJ4hwljs/dhd0qOTr1ZDUp43ix+w==", "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-export-default-from": "^7.2.0" + "@babel/plugin-syntax-export-default-from": "^7.7.4" } }, "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.4.4.tgz", - "integrity": "sha512-Amph7Epui1Dh/xxUxS2+K22/MUi6+6JVTvy3P58tja3B6yKTSjwwx0/d83rF7551D6PVSSoplQb8GCwqec7HRw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.7.4.tgz", + "integrity": "sha512-TbYHmr1Gl1UC7Vo2HVuj/Naci5BEGNZ0AJhzqD2Vpr6QPFWpUmBRLrIDjedzx7/CShq0bRDS2gI4FIs77VHLVQ==", "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.2.0" + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.7.4" } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.6.2.tgz", - "integrity": "sha512-LDBXlmADCsMZV1Y9OQwMc0MyGZ8Ta/zlD9N67BfQT8uYwkRswiu2hU6nJKrjrt/58aH/vqfQlR/9yId/7A2gWw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.7.4.tgz", + "integrity": "sha512-rnpnZR3/iWKmiQyJ3LKJpSwLDcX/nSXhdLk4Aq/tXOApIvyu7qoabrige0ylsAJffaUC51WiBu209Q0U+86OWQ==", "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.2.0" + "@babel/plugin-syntax-object-rest-spread": "^7.7.4" } }, "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz", - "integrity": "sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.7.4.tgz", + "integrity": "sha512-DyM7U2bnsQerCQ+sejcTNZh8KQEUuC3ufzdnVnSiUv/qoGJp2Z3hanKL18KDhsBT5Wj6a7CMT5mdyCNJsEaA9w==", "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.2.0" + "@babel/plugin-syntax-optional-catch-binding": "^7.7.4" } }, "@babel/plugin-proposal-optional-chaining": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.6.0.tgz", - "integrity": "sha512-kj4gkZ6qUggkprRq3Uh5KP8XnE1MdIO0J7MhdDX8+rAbB6dJ2UrensGIS+0NPZAaaJ1Vr0PN6oLUgXMU1uMcSg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.7.4.tgz", + "integrity": "sha512-JmgaS+ygAWDR/STPe3/7y0lNlHgS+19qZ9aC06nYLwQ/XB7c0q5Xs+ksFU3EDnp9EiEsO0dnRAOKeyLHTZuW3A==", "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-optional-chaining": "^7.2.0" + "@babel/plugin-syntax-optional-chaining": "^7.7.4" } }, "@babel/plugin-syntax-class-properties": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.2.0.tgz", - "integrity": "sha512-UxYaGXYQ7rrKJS/PxIKRkv3exi05oH7rokBAsmCSsCxz1sVPZ7Fu6FzKoGgUvmY+0YgSkYHgUoCh5R5bCNBQlw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.7.4.tgz", + "integrity": "sha512-JH3v5ZOeKT0qqdJ9BeBcZTFQiJOMax8RopSr1bH6ASkZKo2qWsvBML7W1mp89sszBRDBBRO8snqcByGdrMTdMg==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-syntax-dynamic-import": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz", - "integrity": "sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.7.4.tgz", + "integrity": "sha512-jHQW0vbRGvwQNgyVxwDh4yuXu4bH1f5/EICJLAhl1SblLs2CDhrsmCk+v5XLdE9wxtAFRyxx+P//Iw+a5L/tTg==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-syntax-export-default-from": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.2.0.tgz", - "integrity": "sha512-c7nqUnNST97BWPtoe+Ssi+fJukc9P9/JMZ71IOMNQWza2E+Psrd46N6AEvtw6pqK+gt7ChjXyrw4SPDO79f3Lw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.7.4.tgz", + "integrity": "sha512-j888jpjATLEzOWhKawq46UrpXnCRDbdhBd5io4jgwjJ3+CHHGCRb6PNAVEgs+BXIb+dNRAmnkv36zfB992PRVw==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-syntax-flow": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.7.0.tgz", - "integrity": "sha512-vQMV07p+L+jZeUnvX3pEJ9EiXGCjB5CTTvsirFD9rpEuATnoAvLBLoYbw1v5tyn3d2XxSuvEKi8cV3KqYUa0vQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.7.4.tgz", + "integrity": "sha512-2AMAWl5PsmM5KPkB22cvOkUyWk6MjUaqhHNU5nSPUl/ns3j5qLfw2SuYP5RbVZ0tfLvePr4zUScbICtDP2CUNw==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-syntax-jsx": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.2.0.tgz", - "integrity": "sha512-VyN4QANJkRW6lDBmENzRszvZf3/4AXaj9YR7GwrWeeN9tEBPuXbmDYVU9bYBN0D70zCWVwUy0HWq2553VCb6Hw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.7.4.tgz", + "integrity": "sha512-wuy6fiMe9y7HeZBWXYCGt2RGxZOj0BImZ9EyXJVnVGBKO/Br592rbR3rtIQn0eQhAk9vqaKP5n8tVqEFBQMfLg==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.2.0.tgz", - "integrity": "sha512-lRCEaKE+LTxDQtgbYajI04ddt6WW0WJq57xqkAZ+s11h4YgfRHhVA/Y2VhfPzzFD4qeLHWg32DMp9HooY4Kqlg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.7.4.tgz", + "integrity": "sha512-XKh/yIRPiQTOeBg0QJjEus5qiSKucKAiApNtO1psqG7D17xmE+X2i5ZqBEuSvo0HRuyPaKaSN/Gy+Ha9KFQolw==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-syntax-object-rest-spread": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", - "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.7.4.tgz", + "integrity": "sha512-mObR+r+KZq0XhRVS2BrBKBpr5jqrqzlPvS9C9vuOf5ilSwzloAl7RPWLrgKdWS6IreaVrjHxTjtyqFiOisaCwg==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz", - "integrity": "sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.7.4.tgz", + "integrity": "sha512-4ZSuzWgFxqHRE31Glu+fEr/MirNZOMYmD/0BhBWyLyOOQz/gTAl7QmWm2hX1QxEIXsr2vkdlwxIzTyiYRC4xcQ==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-syntax-optional-chaining": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.2.0.tgz", - "integrity": "sha512-HtGCtvp5Uq/jH/WNUPkK6b7rufnCPLLlDAFN7cmACoIjaOOiXxUt3SswU5loHqrhtqTsa/WoLQ1OQ1AGuZqaWA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.7.4.tgz", + "integrity": "sha512-2MqYD5WjZSbJdUagnJvIdSfkb/ucOC9/1fRJxm7GAxY6YQLWlUvkfxoNbUPcPLHJyetKUDQ4+yyuUyAoc0HriA==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-syntax-typescript": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz", - "integrity": "sha512-dGwbSMA1YhVS8+31CnPR7LB4pcbrzcV99wQzby4uAfrkZPYZlQ7ImwdpzLqi6Z6IL02b8IAL379CaMwo0x5Lag==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.7.4.tgz", + "integrity": "sha512-77blgY18Hud4NM1ggTA8xVT/dBENQf17OpiToSa2jSmEY3fWXD2jwrdVlO4kq5yzUTeF15WSQ6b4fByNvJcjpQ==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz", - "integrity": "sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.7.4.tgz", + "integrity": "sha512-zUXy3e8jBNPiffmqkHRNDdZM2r8DWhCB7HhcoyZjiK1TxYEluLHAvQuYnTT+ARqRpabWqy/NHkO6e3MsYB5YfA==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.7.0.tgz", - "integrity": "sha512-vLI2EFLVvRBL3d8roAMqtVY0Bm9C1QzLkdS57hiKrjUBSqsQYrBsMCeOg/0KK7B0eK9V71J5mWcha9yyoI2tZw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.7.4.tgz", + "integrity": "sha512-zpUTZphp5nHokuy8yLlyafxCJ0rSlFoSHypTUWgpdwoDXWQcseaect7cJ8Ppk6nunOM6+5rPMkod4OYKPR5MUg==", "requires": { - "@babel/helper-module-imports": "^7.7.0", + "@babel/helper-module-imports": "^7.7.4", "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.7.0" + "@babel/helper-remap-async-to-generator": "^7.7.4" } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz", - "integrity": "sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.7.4.tgz", + "integrity": "sha512-kqtQzwtKcpPclHYjLK//3lH8OFsCDuDJBaFhVwf8kqdnF6MN4l618UDlcA7TfRs3FayrHj+svYnSX8MC9zmUyQ==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.6.3.tgz", - "integrity": "sha512-7hvrg75dubcO3ZI2rjYTzUrEuh1E9IyDEhhB6qfcooxhDA33xx2MasuLVgdxzcP6R/lipAC6n9ub9maNW6RKdw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.7.4.tgz", + "integrity": "sha512-2VBe9u0G+fDt9B5OV5DQH4KBf5DoiNkwFKOz0TCvBWvdAN2rOykCTkrL+jTLxfCAm76l9Qo5OqL7HBOx2dWggg==", "requires": { "@babel/helper-plugin-utils": "^7.0.0", "lodash": "^4.17.13" @@ -487,236 +487,236 @@ } }, "@babel/plugin-transform-classes": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.7.0.tgz", - "integrity": "sha512-/b3cKIZwGeUesZheU9jNYcwrEA7f/Bo4IdPmvp7oHgvks2majB5BoT5byAql44fiNQYOPzhk2w8DbgfuafkMoA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.7.0", - "@babel/helper-define-map": "^7.7.0", - "@babel/helper-function-name": "^7.7.0", - "@babel/helper-optimise-call-expression": "^7.7.0", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.7.4.tgz", + "integrity": "sha512-sK1mjWat7K+buWRuImEzjNf68qrKcrddtpQo3swi9j7dUcG6y6R6+Di039QN2bD1dykeswlagupEmpOatFHHUg==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.7.4", + "@babel/helper-define-map": "^7.7.4", + "@babel/helper-function-name": "^7.7.4", + "@babel/helper-optimise-call-expression": "^7.7.4", "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.7.0", - "@babel/helper-split-export-declaration": "^7.7.0", + "@babel/helper-replace-supers": "^7.7.4", + "@babel/helper-split-export-declaration": "^7.7.4", "globals": "^11.1.0" } }, "@babel/plugin-transform-computed-properties": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz", - "integrity": "sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.7.4.tgz", + "integrity": "sha512-bSNsOsZnlpLLyQew35rl4Fma3yKWqK3ImWMSC/Nc+6nGjC9s5NFWAer1YQ899/6s9HxO2zQC1WoFNfkOqRkqRQ==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-destructuring": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.6.0.tgz", - "integrity": "sha512-2bGIS5P1v4+sWTCnKNDZDxbGvEqi0ijeqM/YqHtVGrvG2y0ySgnEEhXErvE9dA0bnIzY9bIzdFK0jFA46ASIIQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.7.4.tgz", + "integrity": "sha512-4jFMXI1Cu2aXbcXXl8Lr6YubCn6Oc7k9lLsu8v61TZh+1jny2BWmdtvY9zSUlLdGUvcy9DMAWyZEOqjsbeg/wA==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz", - "integrity": "sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.7.4.tgz", + "integrity": "sha512-MCqiLfCKm6KEA1dglf6Uqq1ElDIZwFuzz1WH5mTf8k2uQSxEJMbOIEh7IZv7uichr7PMfi5YVSrr1vz+ipp7AQ==", "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.1.0", + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.7.4", "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-flow-strip-types": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.6.3.tgz", - "integrity": "sha512-l0ETkyEofkqFJ9LS6HChNIKtVJw2ylKbhYMlJ5C6df+ldxxaLIyXY4yOdDQQspfFpV8/vDiaWoJlvflstlYNxg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.7.4.tgz", + "integrity": "sha512-w9dRNlHY5ElNimyMYy0oQowvQpwt/PRHI0QS98ZJCTZU2bvSnKXo5zEiD5u76FBPigTm8TkqzmnUTg16T7qbkA==", "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-flow": "^7.2.0" + "@babel/plugin-syntax-flow": "^7.7.4" } }, "@babel/plugin-transform-for-of": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz", - "integrity": "sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.7.4.tgz", + "integrity": "sha512-zZ1fD1B8keYtEcKF+M1TROfeHTKnijcVQm0yO/Yu1f7qoDoxEIc/+GX6Go430Bg84eM/xwPFp0+h4EbZg7epAA==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-function-name": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.7.0.tgz", - "integrity": "sha512-P5HKu0d9+CzZxP5jcrWdpe7ZlFDe24bmqP6a6X8BHEBl/eizAsY8K6LX8LASZL0Jxdjm5eEfzp+FIrxCm/p8bA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.7.4.tgz", + "integrity": "sha512-E/x09TvjHNhsULs2IusN+aJNRV5zKwxu1cpirZyRPw+FyyIKEHPXTsadj48bVpc1R5Qq1B5ZkzumuFLytnbT6g==", "requires": { - "@babel/helper-function-name": "^7.7.0", + "@babel/helper-function-name": "^7.7.4", "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-literals": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz", - "integrity": "sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.7.4.tgz", + "integrity": "sha512-X2MSV7LfJFm4aZfxd0yLVFrEXAgPqYoDG53Br/tCKiKYfX0MjVjQeWPIhPHHsCqzwQANq+FLN786fF5rgLS+gw==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.2.0.tgz", - "integrity": "sha512-HiU3zKkSU6scTidmnFJ0bMX8hz5ixC93b4MHMiYebmk2lUVNGOboPsqQvx5LzooihijUoLR/v7Nc1rbBtnc7FA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.7.4.tgz", + "integrity": "sha512-9VMwMO7i69LHTesL0RdGy93JU6a+qOPuvB4F4d0kR0zyVjJRVJRaoaGjhtki6SzQUu8yen/vxPKN6CWnCUw6bA==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.0.tgz", - "integrity": "sha512-KEMyWNNWnjOom8vR/1+d+Ocz/mILZG/eyHHO06OuBQ2aNhxT62fr4y6fGOplRx+CxCSp3IFwesL8WdINfY/3kg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.4.tgz", + "integrity": "sha512-k8iVS7Jhc367IcNF53KCwIXtKAH7czev866ThsTgy8CwlXjnKZna2VHwChglzLleYrcHz1eQEIJlGRQxB53nqA==", "requires": { - "@babel/helper-module-transforms": "^7.7.0", + "@babel/helper-module-transforms": "^7.7.4", "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-simple-access": "^7.7.0", + "@babel/helper-simple-access": "^7.7.4", "babel-plugin-dynamic-import-node": "^2.3.0" } }, "@babel/plugin-transform-object-assign": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.2.0.tgz", - "integrity": "sha512-nmE55cZBPFgUktbF2OuoZgPRadfxosLOpSgzEPYotKSls9J4pEPcembi8r78RU37Rph6UApCpNmsQA4QMWK9Ng==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.7.4.tgz", + "integrity": "sha512-0TpeUlnhQDwKxPLTIckdaWt46L2s61c/5w5snw1OUod5ehOJywZD98Ha3dFHVjeqkfOFtOTH7cqxddjxUuvcmg==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-object-super": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.5.5.tgz", - "integrity": "sha512-un1zJQAhSosGFBduPgN/YFNvWVpRuHKU7IHBglLoLZsGmruJPOo6pbInneflUdmq7YvSVqhpPs5zdBvLnteltQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.7.4.tgz", + "integrity": "sha512-ho+dAEhC2aRnff2JCA0SAK7V2R62zJd/7dmtoe7MHcso4C2mS+vZjn1Pb1pCVZvJs1mgsvv5+7sT+m3Bysb6eg==", "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.5.5" + "@babel/helper-replace-supers": "^7.7.4" } }, "@babel/plugin-transform-parameters": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz", - "integrity": "sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.7.4.tgz", + "integrity": "sha512-VJwhVePWPa0DqE9vcfptaJSzNDKrWU/4FbYCjZERtmqEs05g3UMXnYMZoXja7JAJ7Y7sPZipwm/pGApZt7wHlw==", "requires": { - "@babel/helper-call-delegate": "^7.4.4", - "@babel/helper-get-function-arity": "^7.0.0", + "@babel/helper-call-delegate": "^7.7.4", + "@babel/helper-get-function-arity": "^7.7.4", "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-property-literals": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz", - "integrity": "sha512-9q7Dbk4RhgcLp8ebduOpCbtjh7C0itoLYHXd9ueASKAG/is5PQtMR5VJGka9NKqGhYEGn5ITahd4h9QeBMylWQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.7.4.tgz", + "integrity": "sha512-MatJhlC4iHsIskWYyawl53KuHrt+kALSADLQQ/HkhTjX954fkxIEh4q5slL4oRAnsm/eDoZ4q0CIZpcqBuxhJQ==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-react-display-name": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.2.0.tgz", - "integrity": "sha512-Htf/tPa5haZvRMiNSQSFifK12gtr/8vwfr+A9y69uF0QcU77AVu4K7MiHEkTxF7lQoHOL0F9ErqgfNEAKgXj7A==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.7.4.tgz", + "integrity": "sha512-sBbIvqYkthai0X0vkD2xsAwluBp+LtNHH+/V4a5ydifmTtb8KOVOlrMIk/MYmIc4uTYDnjZUHQildYNo36SRJw==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-react-jsx": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.7.0.tgz", - "integrity": "sha512-mXhBtyVB1Ujfy+0L6934jeJcSXj/VCg6whZzEcgiiZHNS0PGC7vUCsZDQCxxztkpIdF+dY1fUMcjAgEOC3ZOMQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.7.4.tgz", + "integrity": "sha512-LixU4BS95ZTEAZdPaIuyg/k8FiiqN9laQ0dMHB4MlpydHY53uQdWCUrwjLr5o6ilS6fAgZey4Q14XBjl5tL6xw==", "requires": { - "@babel/helper-builder-react-jsx": "^7.7.0", + "@babel/helper-builder-react-jsx": "^7.7.4", "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.2.0" + "@babel/plugin-syntax-jsx": "^7.7.4" } }, "@babel/plugin-transform-react-jsx-source": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.5.0.tgz", - "integrity": "sha512-58Q+Jsy4IDCZx7kqEZuSDdam/1oW8OdDX8f+Loo6xyxdfg1yF0GE2XNJQSTZCaMol93+FBzpWiPEwtbMloAcPg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.7.4.tgz", + "integrity": "sha512-5ZU9FnPhqtHsOXxutRtXZAzoEJwDaP32QcobbMP1/qt7NYcsCNK8XgzJcJfoEr/ZnzVvUNInNjIW22Z6I8p9mg==", "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.2.0" + "@babel/plugin-syntax-jsx": "^7.7.4" } }, "@babel/plugin-transform-regenerator": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.0.tgz", - "integrity": "sha512-AXmvnC+0wuj/cFkkS/HFHIojxH3ffSXE+ttulrqWjZZRaUOonfJc60e1wSNT4rV8tIunvu/R3wCp71/tLAa9xg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.4.tgz", + "integrity": "sha512-e7MWl5UJvmPEwFJTwkBlPmqixCtr9yAASBqff4ggXTNicZiwbF8Eefzm6NVgfiBp7JdAGItecnctKTgH44q2Jw==", "requires": { "regenerator-transform": "^0.14.0" } }, "@babel/plugin-transform-runtime": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.6.2.tgz", - "integrity": "sha512-cqULw/QB4yl73cS5Y0TZlQSjDvNkzDbu0FurTZyHlJpWE5T3PCMdnyV+xXoH1opr1ldyHODe3QAX3OMAii5NxA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.7.4.tgz", + "integrity": "sha512-O8kSkS5fP74Ad/8pfsCMGa8sBRdLxYoSReaARRNSz3FbFQj3z/QUvoUmJ28gn9BO93YfnXc3j+Xyaqe8cKDNBQ==", "requires": { - "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-module-imports": "^7.7.4", "@babel/helper-plugin-utils": "^7.0.0", "resolve": "^1.8.1", "semver": "^5.5.1" } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz", - "integrity": "sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.7.4.tgz", + "integrity": "sha512-q+suddWRfIcnyG5YiDP58sT65AJDZSUhXQDZE3r04AuqD6d/XLaQPPXSBzP2zGerkgBivqtQm9XKGLuHqBID6Q==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-spread": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.6.2.tgz", - "integrity": "sha512-DpSvPFryKdK1x+EDJYCy28nmAaIMdxmhot62jAXF/o99iA33Zj2Lmcp3vDmz+MUh0LNYVPvfj5iC3feb3/+PFg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.7.4.tgz", + "integrity": "sha512-8OSs0FLe5/80cndziPlg4R0K6HcWSM0zyNhHhLsmw/Nc5MaA49cAsnoJ/t/YZf8qkG7fD+UjTRaApVDB526d7Q==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz", - "integrity": "sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.7.4.tgz", + "integrity": "sha512-Ls2NASyL6qtVe1H1hXts9yuEeONV2TJZmplLONkMPUG158CtmnrzW5Q5teibM5UVOFjG0D3IC5mzXR6pPpUY7A==", "requires": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/helper-regex": "^7.0.0" } }, "@babel/plugin-transform-template-literals": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz", - "integrity": "sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.7.4.tgz", + "integrity": "sha512-sA+KxLwF3QwGj5abMHkHgshp9+rRz+oY9uoRil4CyLtgEuE/88dpkeWgNk5qKVsJE9iSfly3nvHapdRiIS2wnQ==", "requires": { - "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-annotate-as-pure": "^7.7.4", "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-typescript": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.7.2.tgz", - "integrity": "sha512-UWhDaJRqdPUtdK1s0sKYdoRuqK0NepjZto2UZltvuCgMoMZmdjhgz5hcRokie/3aYEaSz3xvusyoayVaq4PjRg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.7.4.tgz", + "integrity": "sha512-X8e3tcPEKnwwPVG+vP/vSqEShkwODOEeyQGod82qrIuidwIrfnsGn11qPM1jBLF4MqguTXXYzm58d0dY+/wdpg==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.7.0", + "@babel/helper-create-class-features-plugin": "^7.7.4", "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-typescript": "^7.2.0" + "@babel/plugin-syntax-typescript": "^7.7.4" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.7.0.tgz", - "integrity": "sha512-RrThb0gdrNwFAqEAAx9OWgtx6ICK69x7i9tCnMdVrxQwSDp/Abu9DXFU5Hh16VP33Rmxh04+NGW28NsIkFvFKA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.7.4.tgz", + "integrity": "sha512-N77UUIV+WCvE+5yHw+oks3m18/umd7y392Zv7mYTpFqHtkpcc+QUz+gLJNTWVlWROIWeLqY0f3OjZxV5TcXnRw==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.0", + "@babel/helper-create-regexp-features-plugin": "^7.7.4", "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/register": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.7.0.tgz", - "integrity": "sha512-HV3GJzTvSoyOMWGYn2TAh6uL6g+gqKTgEZ99Q3+X9UURT1VPT/WcU46R61XftIc5rXytcOHZ4Z0doDlsjPomIg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.7.4.tgz", + "integrity": "sha512-/fmONZqL6ZMl9KJUYajetCrID6m0xmL4odX7v+Xvoxcv0DdbP/oO0TWIeLUCHqczQ6L6njDMqmqHFy2cp3FFsA==", "requires": { "find-cache-dir": "^2.0.0", "lodash": "^4.17.13", @@ -750,26 +750,26 @@ } }, "@babel/template": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.0.tgz", - "integrity": "sha512-OKcwSYOW1mhWbnTBgQY5lvg1Fxg+VyfQGjcBduZFljfc044J5iDlnDSfhQ867O17XHiSCxYHUxHg2b7ryitbUQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", + "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/parser": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/traverse": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.2.tgz", - "integrity": "sha512-TM01cXib2+rgIZrGJOLaHV/iZUAxf4A0dt5auY6KNZ+cm6aschuJGqKJM3ROTt3raPUdIDk9siAufIFEleRwtw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", + "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.2", - "@babel/helper-function-name": "^7.7.0", - "@babel/helper-split-export-declaration": "^7.7.0", - "@babel/parser": "^7.7.2", - "@babel/types": "^7.7.2", + "@babel/generator": "^7.7.4", + "@babel/helper-function-name": "^7.7.4", + "@babel/helper-split-export-declaration": "^7.7.4", + "@babel/parser": "^7.7.4", + "@babel/types": "^7.7.4", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" @@ -783,9 +783,9 @@ } }, "@babel/types": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.2.tgz", - "integrity": "sha512-YTf6PXoh3+eZgRCBzzP25Bugd2ngmpQVrk7kXX0i5N9BO7TFBtIgZYs7WtxtOGs8e6A4ZI7ECkbBCEHeXocvOA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", + "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", "requires": { "esutils": "^2.0.2", "lodash": "^4.17.13", @@ -878,9 +878,9 @@ } }, "@google-cloud/paginator": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-2.0.1.tgz", - "integrity": "sha512-HZ6UTGY/gHGNriD7OCikYWL/Eu0sTEur2qqse2w6OVsz+57se3nTkqH14JIPxtf0vlEJ8IJN5w3BdZ22pjCB8g==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-2.0.2.tgz", + "integrity": "sha512-PCddVtZWvw0iZ3BLIsCXMBQvxUcS9O5CgfHBu8Zd8T3DCiML+oQED1odsbl3CQ9d3RrvBaj+eIh7Dv12D15PbA==", "dev": true, "optional": true, "requires": { @@ -889,16 +889,16 @@ } }, "@google-cloud/projectify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-1.0.1.tgz", - "integrity": "sha512-xknDOmsMgOYHksKc1GPbwDLsdej8aRNIA17SlSZgQdyrcC0lx0OGo4VZgYfwoEU1YS8oUxF9Y+6EzDOb0eB7Xg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-1.0.2.tgz", + "integrity": "sha512-WnkGxvk4U1kAJpoS/Ehk+3MZXVW+XHHhwc/QyD6G8Za4xml3Fv+NRn/bYffl1TxSg+gE0N0mj9Shgc7e8+fl8A==", "dev": true, "optional": true }, "@google-cloud/promisify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-1.0.2.tgz", - "integrity": "sha512-7WfV4R/3YV5T30WRZW0lqmvZy9hE2/p9MvpI34WuKa2Wz62mLu5XplGTFEMK6uTbJCLWUxTcZ4J4IyClKucE5g==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-1.0.3.tgz", + "integrity": "sha512-Rufgfl3TnkIil3CjsH33Q6093zeoVqyqCdvtvgHuCqRJxCZYfaVPIyr8JViMeLTD4Ja630pRKKZVSjKggoVbNg==", "dev": true, "optional": true }, @@ -2946,9 +2946,9 @@ } }, "@ostdotcom/ost-wallet-sdk-react-native": { - "version": "2.3.7-alpha.3", - "resolved": "https://registry.npmjs.org/@ostdotcom/ost-wallet-sdk-react-native/-/ost-wallet-sdk-react-native-2.3.7-alpha.3.tgz", - "integrity": "sha512-GAshH+6nO/gC7ToKNCscuamos3Vbhhpo8UtjjIz5seKYNWVRMJnHuD4VEvisgJXJ0ARZnm8v2vFWCGW7467f8w==" + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@ostdotcom/ost-wallet-sdk-react-native/-/ost-wallet-sdk-react-native-2.3.10.tgz", + "integrity": "sha512-SthTSY/CiJoA6/wSsGdzcP/9quk+XYxJ39DOvkIWQvaVx7jkNF+lL26GzVXNT1sbrkBuNCWTal+pwXnHYIF8Fg==" }, "@protobufjs/aspromise": { "version": "1.1.2", @@ -3035,9 +3035,9 @@ "integrity": "sha512-cvJewReV2l65exdsccveqnqXHGFuTgwdbjr+0MASxq6zQ+eeHLx9g/wYk8YrXHWXJqf5B/jZnNDpcg/y23atmw==" }, "@react-native-community/cli": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-1.11.2.tgz", - "integrity": "sha512-5NuYd30f5PCTrGUbZLnusZKv5nfTWvTDTRa/3Q4vwdMnUQrhm9sZXWGQ5CnFoQ7cE58EAqhj6/ShXeJF3DZ9uQ==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-1.12.0.tgz", + "integrity": "sha512-GAs4JgVP8QkEYeZks/T7cCrBuwFJKxd9ksBLRdQ058uvLGkOEeS4g3y4GsVM/9C1zat5h6Z6QwU0h/hj7G3tzg==", "requires": { "chalk": "^1.1.1", "commander": "^2.19.0", @@ -3168,9 +3168,9 @@ }, "dependencies": { "hoist-non-react-statics": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz", - "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw==", "requires": { "react-is": "^16.7.0" } @@ -3188,9 +3188,9 @@ }, "dependencies": { "hoist-non-react-statics": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz", - "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw==", "requires": { "react-is": "^16.7.0" } @@ -3230,9 +3230,9 @@ } }, "@types/babel__traverse": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.7.tgz", - "integrity": "sha512-CeBpmX1J8kWLcDEnI3Cl2Eo6RfbGvzUctA+CjZUhOKDFbLfcr7fc4usEqLNWetrlJd7RhAkyYe2czXop4fICpw==", + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.8.tgz", + "integrity": "sha512-yGeB2dHEdvxjP0y4UbRtQaSkXJ9649fYCmIdRoul5kfAoGCwxuCbMhag0k3RPfnuh9kPGm8x89btcfDEXdVWGw==", "dev": true, "requires": { "@babel/types": "^7.3.0" @@ -3298,9 +3298,9 @@ "dev": true }, "abab": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.2.tgz", - "integrity": "sha512-2scffjvioEmNz0OyDSLGWDfKCVwaKc6l9Pm9kOIREU13ClXZvHpg/nRL5xyjSSSLhOnXqft2HpsAzNEEA8cFFg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz", + "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==", "dev": true }, "abort-controller": { @@ -3353,9 +3353,9 @@ }, "dependencies": { "acorn": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", - "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", + "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==", "dev": true } } @@ -3877,9 +3877,9 @@ "dev": true }, "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.0.tgz", + "integrity": "sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==", "dev": true }, "babel-jest": { @@ -4131,9 +4131,9 @@ } }, "big-integer": { - "version": "1.6.47", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.47.tgz", - "integrity": "sha512-9t9f7X3as2XGX8b52GqG6ox0GvIdM86LyIXASJnDCFhYNgt+A+MByQZ3W2PyMRZjEvG5f8TEbSPfEotVuMJnQg==" + "version": "1.6.48", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", + "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==" }, "bignumber.js": { "version": "8.1.1", @@ -4824,9 +4824,9 @@ "integrity": "sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ=" }, "deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.0.tgz", - "integrity": "sha512-ZbfWJq/wN1Z273o7mUSjILYqehAktR2NVoSrOukDkU9kg2v/Uv89yU4Cvz8seJeAmtN5oqiefKq8FPuXOboqLw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", "dev": true, "optional": true, "requires": { @@ -5134,18 +5134,18 @@ } }, "es-abstract": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.0.tgz", - "integrity": "sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.2.tgz", + "integrity": "sha512-jYo/J8XU2emLXl3OLwfwtuFfuF2w6DYPs+xy9ZfVyPkDcrauu6LYrw/q2TyCtrbc/KUdCiC5e9UajRhgNkVopA==", "dev": true, "requires": { - "es-to-primitive": "^1.2.0", + "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.0", + "has-symbols": "^1.0.1", "is-callable": "^1.1.4", "is-regex": "^1.0.4", - "object-inspect": "^1.6.0", + "object-inspect": "^1.7.0", "object-keys": "^1.1.1", "string.prototype.trimleft": "^2.1.0", "string.prototype.trimright": "^2.1.0" @@ -5446,17 +5446,17 @@ }, "dependencies": { "@babel/core": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.2.tgz", - "integrity": "sha512-eeD7VEZKfhK1KUXGiyPFettgF3m513f8FoBSWiQ1xTvl1RAopLs42Wp9+Ze911I6H0N9lNqJMDgoZT7gHsipeQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.4.tgz", + "integrity": "sha512-+bYbx56j4nYBmpsWtnPUsKW3NdnYxbqyfrP2w9wILBuHzdfIKz9prieZK0DFPyIzkjYVUe4QkusGL07r5pXznQ==", "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.2", - "@babel/helpers": "^7.7.0", - "@babel/parser": "^7.7.2", - "@babel/template": "^7.7.0", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.7.2", + "@babel/generator": "^7.7.4", + "@babel/helpers": "^7.7.4", + "@babel/parser": "^7.7.4", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@babel/types": "^7.7.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "json5": "^2.1.0", @@ -6146,9 +6146,9 @@ } }, "gcp-metadata": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.2.1.tgz", - "integrity": "sha512-JjDedBWnbXVXWwTpjBdpb9RpVLiowXG4/50rra4hPH8REXAi2si6Xbb48B2SwkQBLz9Wu6+o32GDTvVy2kkLoQ==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.2.2.tgz", + "integrity": "sha512-vR7kcJMCYJG/mYWp/a1OszdOqnLB/XW1GorWW1hc1lWVNL26L497zypWb9cG0CYDQ4Bl1Wk0+fSZFFjwJlTQgQ==", "dev": true, "optional": true, "requires": { @@ -6157,9 +6157,9 @@ } }, "gcs-resumable-upload": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-2.3.0.tgz", - "integrity": "sha512-PclXJiEngrVx0c4K0LfE1XOxhmOkBEy39Rrhspdn6jAbbwe4OQMZfjo7Z1LHBrh57+bNZeIN4M+BooYppCoHSg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-2.3.1.tgz", + "integrity": "sha512-zEO7L+jz99VznQsbsF7vFTnIFbSu+CjdJqt5htnjIrfsp5j+QCVBvbbKdqpaTfCPzpUPYj1Q9O9DhIh/8newfA==", "dev": true, "optional": true, "requires": { @@ -6276,14 +6276,15 @@ } }, "google-gax": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-1.8.0.tgz", - "integrity": "sha512-gDlL1mcyMSde7guPVzwpWi9V8XP+4F8UwFKoUiINEGRDNDbEj18OorL016XU0Kyt8Aizcf5dgb3q8UKegYAOmQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-1.11.1.tgz", + "integrity": "sha512-v/APF2G5h2nS5R/1DW2vsgloaMu2/B3xjHdAptR1yUwZpEd9rxPTlhqosrjl/VRu+gWGr9JZN19ZgJTXQ/Db6Q==", "dev": true, "optional": true, "requires": { "@grpc/grpc-js": "0.6.9", "@grpc/proto-loader": "^0.5.1", + "@types/long": "^4.0.0", "abort-controller": "^3.0.0", "duplexify": "^3.6.0", "google-auth-library": "^5.0.0", @@ -6307,9 +6308,9 @@ } }, "google-p12-pem": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.2.tgz", - "integrity": "sha512-UfnEARfJKI6pbmC1hfFFm+UAcZxeIwTiEcHfqKe/drMsXD/ilnVjF7zgOGpHXyhuvX6jNJK3S8A0hOQjwtFxEw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.3.tgz", + "integrity": "sha512-Tq2kBCANxYYPxaBpTgCpRfdoPs9+/lNzc/Iaee4kuMVW5ascD+HwhpBsTLwH85C9Ev4qfB8KKHmpPQYyD2vg2w==", "dev": true, "optional": true, "requires": { @@ -6336,9 +6337,9 @@ "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=" }, "gtoken": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.1.tgz", - "integrity": "sha512-2FEmEDGi4NdM6u+mtaLjSDDtHiw5wT+nBsI+yrSeFO6fVqPEytYVF6uiIpRaOaZhRP+ozjYWuwwtMlrjAyTcYA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.3.tgz", + "integrity": "sha512-ofW+FiXjswyKdkjMcDbe6E4K7cDDdE82dGDhZIc++kUECqaE7MSErf6arJPAjcnYn1qxE1/Ti06qQuqgVusovQ==", "dev": true, "optional": true, "requires": { @@ -6358,9 +6359,9 @@ } }, "handlebars": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.1.tgz", - "integrity": "sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", + "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", "dev": true, "requires": { "neo-async": "^2.6.0", @@ -6376,9 +6377,9 @@ "dev": true }, "uglify-js": { - "version": "3.6.8", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.8.tgz", - "integrity": "sha512-XhHJ3S3ZyMwP8kY1Gkugqx3CJh2C3O0y8NPiSxtm1tyD/pktLAkFZsFGpuNfTZddKDQ/bbDBLAd2YyA1pbi8HQ==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.0.tgz", + "integrity": "sha512-PC/ee458NEMITe1OufAjal65i6lB58R1HWMRcxwvdz1UopW0DYqlRL3xdu3IcTvTXsB02CRHykidkTRL+A3hQA==", "dev": true, "optional": true, "requires": { @@ -6447,9 +6448,9 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, "has-unicode": { "version": "2.0.1", @@ -6914,12 +6915,12 @@ "optional": true }, "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", "dev": true, "requires": { - "has-symbols": "^1.0.0" + "has-symbols": "^1.0.1" } }, "is-typedarray": { @@ -9946,17 +9947,17 @@ }, "dependencies": { "@babel/core": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.2.tgz", - "integrity": "sha512-eeD7VEZKfhK1KUXGiyPFettgF3m513f8FoBSWiQ1xTvl1RAopLs42Wp9+Ze911I6H0N9lNqJMDgoZT7gHsipeQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.4.tgz", + "integrity": "sha512-+bYbx56j4nYBmpsWtnPUsKW3NdnYxbqyfrP2w9wILBuHzdfIKz9prieZK0DFPyIzkjYVUe4QkusGL07r5pXznQ==", "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.2", - "@babel/helpers": "^7.7.0", - "@babel/parser": "^7.7.2", - "@babel/template": "^7.7.0", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.7.2", + "@babel/generator": "^7.7.4", + "@babel/helpers": "^7.7.4", + "@babel/parser": "^7.7.4", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@babel/types": "^7.7.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "json5": "^2.1.0", @@ -10116,17 +10117,17 @@ }, "dependencies": { "@babel/core": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.2.tgz", - "integrity": "sha512-eeD7VEZKfhK1KUXGiyPFettgF3m513f8FoBSWiQ1xTvl1RAopLs42Wp9+Ze911I6H0N9lNqJMDgoZT7gHsipeQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.4.tgz", + "integrity": "sha512-+bYbx56j4nYBmpsWtnPUsKW3NdnYxbqyfrP2w9wILBuHzdfIKz9prieZK0DFPyIzkjYVUe4QkusGL07r5pXznQ==", "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.2", - "@babel/helpers": "^7.7.0", - "@babel/parser": "^7.7.2", - "@babel/template": "^7.7.0", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.7.2", + "@babel/generator": "^7.7.4", + "@babel/helpers": "^7.7.4", + "@babel/parser": "^7.7.4", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@babel/types": "^7.7.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "json5": "^2.1.0", @@ -10152,17 +10153,17 @@ }, "dependencies": { "@babel/core": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.2.tgz", - "integrity": "sha512-eeD7VEZKfhK1KUXGiyPFettgF3m513f8FoBSWiQ1xTvl1RAopLs42Wp9+Ze911I6H0N9lNqJMDgoZT7gHsipeQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.4.tgz", + "integrity": "sha512-+bYbx56j4nYBmpsWtnPUsKW3NdnYxbqyfrP2w9wILBuHzdfIKz9prieZK0DFPyIzkjYVUe4QkusGL07r5pXznQ==", "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.2", - "@babel/helpers": "^7.7.0", - "@babel/parser": "^7.7.2", - "@babel/template": "^7.7.0", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.7.2", + "@babel/generator": "^7.7.4", + "@babel/helpers": "^7.7.4", + "@babel/parser": "^7.7.4", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@babel/types": "^7.7.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "json5": "^2.1.0", @@ -10296,17 +10297,17 @@ }, "dependencies": { "@babel/core": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.2.tgz", - "integrity": "sha512-eeD7VEZKfhK1KUXGiyPFettgF3m513f8FoBSWiQ1xTvl1RAopLs42Wp9+Ze911I6H0N9lNqJMDgoZT7gHsipeQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.4.tgz", + "integrity": "sha512-+bYbx56j4nYBmpsWtnPUsKW3NdnYxbqyfrP2w9wILBuHzdfIKz9prieZK0DFPyIzkjYVUe4QkusGL07r5pXznQ==", "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.2", - "@babel/helpers": "^7.7.0", - "@babel/parser": "^7.7.2", - "@babel/template": "^7.7.0", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.7.2", + "@babel/generator": "^7.7.4", + "@babel/helpers": "^7.7.4", + "@babel/parser": "^7.7.4", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@babel/types": "^7.7.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "json5": "^2.1.0", @@ -10423,16 +10424,16 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + "version": "1.42.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", + "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==" }, "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "version": "2.1.25", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", + "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", "requires": { - "mime-db": "1.40.0" + "mime-db": "1.42.0" } }, "mimic-fn": { @@ -10500,6 +10501,14 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" }, + "moment-timezone": { + "version": "0.5.27", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.27.tgz", + "integrity": "sha512-EIKQs7h5sAsjhPCqN6ggx6cEbs94GK050254TIJySD1bzoM5JTYDwAU1IoVOeTOL6Gm27kYJ51/uuvq1kIlrbw==", + "requires": { + "moment": ">= 2.9.0" + } + }, "morgan": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", @@ -10761,9 +10770,9 @@ } }, "object-inspect": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", - "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", "dev": true }, "object-is": { @@ -11334,9 +11343,9 @@ }, "dependencies": { "@types/node": { - "version": "10.17.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.5.tgz", - "integrity": "sha512-RElZIr/7JreF1eY6oD5RF3kpmdcreuQPjg5ri4oQ5g9sq7YWU8HkfB3eH8GwAwxf5OaCh0VPi7r4N/yoTGelrA==", + "version": "10.17.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.6.tgz", + "integrity": "sha512-0a2X6cgN3RdPBL2MIlR6Lt0KlM7fOFsutuXcdglcOq6WvLnYXgPQSh0Mx6tO1KCAE8MxbHSOSTWDoUxRq+l3DA==", "dev": true, "optional": true } @@ -11413,9 +11422,9 @@ "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" }, "query-string": { - "version": "6.8.3", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.8.3.tgz", - "integrity": "sha512-llcxWccnyaWlODe7A9hRjkvdCKamEKTh+wH8ITdTc3OhchaqUZteiSCX/2ablWHVrkVIe04dntnaZJ7BdyW0lQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.9.0.tgz", + "integrity": "sha512-KG4bhCFYapExLsUHrFt+kQVEegF2agm4cpF/VNc6pZVthIfCc/GK8t8VyNIE3nyXG9DK3Tf2EGkxjR6/uRdYsA==", "requires": { "decode-uri-component": "^0.2.0", "split-on-first": "^1.0.0", @@ -11505,9 +11514,9 @@ } }, "react-is": { - "version": "16.11.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.11.0.tgz", - "integrity": "sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw==" + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", + "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==" }, "react-native": { "version": "0.59.9", @@ -11567,9 +11576,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.2.tgz", - "integrity": "sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.4.tgz", + "integrity": "sha512-r24eVUUr0QqNZa+qrImUk8fn5SPhHq+IfYvIoIMg0do3GdK9sMdiLKP3GYVVaxpPKORgm8KRKaNTEhAjgIpLMw==", "requires": { "regenerator-runtime": "^0.13.2" }, @@ -11917,17 +11926,17 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.2.tgz", - "integrity": "sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.4.tgz", + "integrity": "sha512-r24eVUUr0QqNZa+qrImUk8fn5SPhHq+IfYvIoIMg0do3GdK9sMdiLKP3GYVVaxpPKORgm8KRKaNTEhAjgIpLMw==", "requires": { "regenerator-runtime": "^0.13.2" } }, "hoist-non-react-statics": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz", - "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw==", "requires": { "react-is": "^16.7.0" } @@ -12241,9 +12250,9 @@ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" }, "resolve": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", - "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.13.1.tgz", + "integrity": "sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w==", "requires": { "path-parse": "^1.0.6" } @@ -12312,6 +12321,15 @@ "glob": "^7.1.3" } }, + "rn-sliding-up-panel": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/rn-sliding-up-panel/-/rn-sliding-up-panel-2.3.3.tgz", + "integrity": "sha512-LcIaU/qbriu5i0aLtwBDJRl2b26idj9XJQZYEdvSCxtNEhm5R3+OEHFwq0YF14a4tnfGmnlHIALi9sKBIj6mcw==", + "requires": { + "clamp": "^1.0.1", + "prop-types": "^15.7.2" + } + }, "rsvp": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz", diff --git a/package.json b/package.json index 38e46d64..5daab109 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "test": "jest" }, "dependencies": { - "@ostdotcom/ost-wallet-sdk-react-native": "2.3.9", + "@ostdotcom/ost-wallet-sdk-react-native": "2.3.10", "@react-native-community/async-storage": "1.6.1", "@react-native-community/cameraroll": "1.1.1", "@react-native-community/netinfo": "3.2.1", @@ -16,6 +16,7 @@ "lodash": "4.17.11", "metro-react-native-babel-preset": "0.54.1", "moment": "2.24.0", + "moment-timezone": "0.5.27", "native-base": "2.12.1", "numeral": "2.0.6", "qs": "6.7.0", @@ -52,6 +53,7 @@ "redux": "4.0.1", "redux-actions": "2.6.5", "redux-logger": "3.0.6", + "rn-sliding-up-panel": "2.3.3", "socket.io-client": "2.2.0", "react-native-keep-awake": "4.0.0" }, diff --git a/src/actions/constants.js b/src/actions/constants.js index 3e2d58de..3eec4023 100644 --- a/src/actions/constants.js +++ b/src/actions/constants.js @@ -4,6 +4,7 @@ const SHOW_MODAL_COVER = 'SHOW_MODAL_COVER'; const HIDE_MODAL_COVER = 'HIDE_MODAL_COVER'; const SHOW_LOGIN_POPOVER = 'SHOW_LOGIN_POPOVER'; const HIDE_LOGIN_POPOVER = 'HIDE_LOGIN_POPOVER'; +const SHOW_CONNECTING_LOGIN_POPOVER = 'SHOW_CONNECTING_LOGIN_POPOVER'; const UPSERT_USER_ENTITIES = 'UPSERT_USER_ENTITIES'; const LOGOUT_USER = 'LOGOUT_USER'; const UPSERT_ACTIVITIES_ENTITIES = 'UPSERT_ACTIVITIES_ENTITIES'; @@ -16,12 +17,16 @@ const UPSERT_USER_STAT_ENTITIES = 'UPSERT_USER_STAT_ENTITIES'; const UPSERT_LINK_ENTITIES = 'UPSERT_LINK_ENTITIES'; const UPSERT_VIDEO_ENTITIES = 'UPSERT_VIDEO_ENTITIES'; const UPSERT_VIDEO_STAT_ENTITIES = 'UPSERT_VIDEO_STAT_ENTITIES'; +const UPSERT_REPLY_DETAIL_ENTITIES = 'UPSERT_REPLY_DETAIL_ENTITIES'; +const UPSERT_CURRENT_USER_VIDEO_RELATION_ENTITIES = 'UPSERT_CURRENT_USER_VIDEO_RELATION_ENTITIES'; +const UPSERT_CURRENT_USER_REPLY_DETAIL_RELATION_ENTITIES = 'UPSERT_CURRENT_USER_REPLY_DETAIL_RELATION_ENTITIES'; const UPSERT_HOME_FEED_ENTITIES = 'UPSERT_HOME_FEED_ENTITIES'; const UPSERT_IMAGE_ENTITIES = 'UPSERT_IMAGE_ENTITIES'; const UPDATE_BALANCE = 'UPDATE_BALANCE'; const UPDATE_IS_PURCHASE = 'UPDATE_IS_PURCHASE'; const UPDATE_EXECUTE_TRANSACTION_STATUS = 'UPDATE_EXECUTE_TRANSACTION_STATUS'; const UPSERT_VIDEO_CONTRIBUTION_ENTITIES = 'UPSERT_VIDEO_CONTRIBUTION_ENTITIES'; +const UPSERT_REPLY_CONTRIBUTION_ENTITIES = 'UPSERT_REPLY_CONTRIBUTION_ENTITIES'; const UPSERT_USER_CONTRIBUTION_ENTITIES = 'UPSERT_USER_CONTRIBUTION_ENTITIES'; const UPSERT_USER_VIDEO_ENTITIES = 'UPSERT_USER_VIDEO_ENTITIES'; const UPDATE_PRICE_POINTS = 'UPDATE_PRICE_POINTS'; @@ -50,6 +55,7 @@ export { SHOW_MODAL_COVER, HIDE_MODAL_COVER, SHOW_LOGIN_POPOVER, + SHOW_CONNECTING_LOGIN_POPOVER, HIDE_LOGIN_POPOVER, UPSERT_USER_ENTITIES, UPSERT_ACTIVITIES_ENTITIES, @@ -59,6 +65,9 @@ export { UPSERT_TAG_ENTITIES, UPSERT_USER_PROFILE_ENTITIES, UPSERT_USER_STAT_ENTITIES, + UPSERT_REPLY_DETAIL_ENTITIES, + UPSERT_CURRENT_USER_VIDEO_RELATION_ENTITIES, + UPSERT_CURRENT_USER_REPLY_DETAIL_RELATION_ENTITIES, UPSERT_LINK_ENTITIES, UPSERT_VIDEO_ENTITIES, UPSERT_VIDEO_STAT_ENTITIES, @@ -68,6 +77,7 @@ export { UPDATE_IS_PURCHASE, UPDATE_EXECUTE_TRANSACTION_STATUS, UPSERT_VIDEO_CONTRIBUTION_ENTITIES, + UPSERT_REPLY_CONTRIBUTION_ENTITIES, UPSERT_USER_CONTRIBUTION_ENTITIES, UPSERT_USER_VIDEO_ENTITIES, UPDATE_PRICE_POINTS, diff --git a/src/actions/index.js b/src/actions/index.js index f056d3dd..26a1571b 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -27,19 +27,35 @@ export const showLoginPopover = () => ({ type: types.SHOW_LOGIN_POPOVER, payload: { login_popover: { - show: true + show: true, + isTwitterConnecting: false } } }); -export const hideLoginPopover = () => ({ - type: types.HIDE_LOGIN_POPOVER, - payload: { - login_popover: { - show: false +export const hideLoginPopover = () => { + return { + type: types.HIDE_LOGIN_POPOVER, + payload: { + login_popover: { + show: false, + isTwitterConnecting: false + } } - } -}); + }; +}; + +export const showConnectingLoginPopover = () => { + return { + type: types.SHOW_CONNECTING_LOGIN_POPOVER, + payload: { + login_popover: { + show: true, + isTwitterConnecting: true + } + } + }; +}; export const updateCurrentUser = (current_user) => ({ type: types.UPDATE_CURRENT_USER, @@ -107,6 +123,21 @@ export const upsertVideoStatEntities = (data) => ({ payload: { video_stat_entities: data } }); +export const upsertReplyDetailEntities = (data) => ({ + type: types.UPSERT_REPLY_DETAIL_ENTITIES, + payload: { reply_detail_entities: data } +}); + +export const upsertCurrentUserVideoRelationEntities = (data) => ({ + type: types.UPSERT_CURRENT_USER_VIDEO_RELATION_ENTITIES, + payload: { current_user_video_relation_entities: data } +}); + +export const upsertCurrentUserReplyDetailRelationEntities = (data) => ({ + type: types.UPSERT_CURRENT_USER_REPLY_DETAIL_RELATION_ENTITIES, + payload: { current_user_reply_detail_relation_entities: data } +}); + export const upsertHomeFeedEntities = (data) => ({ type: types.UPSERT_HOME_FEED_ENTITIES, payload: { home_feed_entities: data } @@ -129,6 +160,13 @@ export const upsertVideoContributionEntities = (data) => ({ payload: { video_contribution_entities: data } }); + +export const upsertReplyContributionEntities = (data) => ({ + type: types.UPSERT_REPLY_CONTRIBUTION_ENTITIES, + payload: { reply_contribution_entities: data } +}); + + export const upsertUserContributionEntities = (data) => ({ type: types.UPSERT_USER_CONTRIBUTION_ENTITIES, payload: { user_contribution_entities: data } diff --git a/src/assets/UnicornSmall.png b/src/assets/UnicornSmall.png index bf146545..831194a5 100644 Binary files a/src/assets/UnicornSmall.png and b/src/assets/UnicornSmall.png differ diff --git a/src/assets/cameraIconWhite.png b/src/assets/cameraIconWhite.png new file mode 100644 index 00000000..2d94f7b6 Binary files /dev/null and b/src/assets/cameraIconWhite.png differ diff --git a/src/assets/cross-icon-white.png b/src/assets/cross-icon-white.png new file mode 100644 index 00000000..97a9f86b Binary files /dev/null and b/src/assets/cross-icon-white.png differ diff --git a/src/assets/reply-video-white.png b/src/assets/reply-video-white.png new file mode 100644 index 00000000..2d94f7b6 Binary files /dev/null and b/src/assets/reply-video-white.png differ diff --git a/src/assets/reply-with-pepo.png b/src/assets/reply-with-pepo.png new file mode 100644 index 00000000..b65c104c Binary files /dev/null and b/src/assets/reply-with-pepo.png differ diff --git a/src/assets/reply_video.png b/src/assets/reply_video.png new file mode 100644 index 00000000..a792e83c Binary files /dev/null and b/src/assets/reply_video.png differ diff --git a/src/assets/reply_video_icon.png b/src/assets/reply_video_icon.png new file mode 100644 index 00000000..695dd2c5 Binary files /dev/null and b/src/assets/reply_video_icon.png differ diff --git a/src/assets/top-up-icon.png b/src/assets/top-up-icon.png index f157d055..f6f593fd 100644 Binary files a/src/assets/top-up-icon.png and b/src/assets/top-up-icon.png differ diff --git a/src/assets/video-reply.png b/src/assets/video-reply.png new file mode 100644 index 00000000..d78e5a67 Binary files /dev/null and b/src/assets/video-reply.png differ diff --git a/src/components/AddEmail/index.js b/src/components/AddEmail/index.js index 16f5cf85..5e60e9f7 100644 --- a/src/components/AddEmail/index.js +++ b/src/components/AddEmail/index.js @@ -14,6 +14,7 @@ import { ostErrors } from '../../services/OstErrors'; import Colors from '../../theme/styles/Colors'; import CurrentUser from '../../models/CurrentUser'; import { navigateTo } from '../../helpers/navigateTo'; +import CommonStyle from '../../theme/styles/Common'; const bottomSpace = getBottomSpace([true]), extraPadding = 10, @@ -113,7 +114,7 @@ class AddEmailScreen extends React.Component { }; onSuccess(res) { - //TODO show success screen + this.setState({ emailSent: true }); @@ -214,7 +215,7 @@ class AddEmailScreen extends React.Component { render() { return ( - + {!this.state.emailSent ? this.emailSignUp() : this.confirmEmail()} diff --git a/src/components/AuthLoading/index.js b/src/components/AuthLoading/index.js index 6ff180bf..1278f4ff 100644 --- a/src/components/AuthLoading/index.js +++ b/src/components/AuthLoading/index.js @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import { View, StatusBar, Alert, Platform, Linking } from 'react-native'; +import { View, StatusBar, Alert } from 'react-native'; import Toast from '../../theme/components/NotificationToast'; import styles from './styles'; diff --git a/src/components/Bio/index.js b/src/components/Bio/index.js index c268dfaa..f2662634 100644 --- a/src/components/Bio/index.js +++ b/src/components/Bio/index.js @@ -1,10 +1,11 @@ import React, { PureComponent } from 'react'; -import { View, Text, ScrollView } from 'react-native'; +import {View, Text} from 'react-native'; import { withNavigation } from 'react-navigation'; import BackArrow from '../CommonComponents/BackArrow'; import TagsInput from '../CommonComponents/TagsInput'; import Colors from '../../theme/styles/Colors'; +import CommonStyle from '../../theme/styles/Common'; import inlineStyles from './style'; class BioScreen extends PureComponent { @@ -51,29 +52,24 @@ class BioScreen extends PureComponent { render() { return ( - - + + + {this.state.count} /300 - - + + ); } } -const SearchResultRowComponent = (props) => ( - - {`#${props.val}`} - -); - export default withNavigation(BioScreen); diff --git a/src/components/Bio/style.js b/src/components/Bio/style.js index 792eb83a..682e2989 100644 --- a/src/components/Bio/style.js +++ b/src/components/Bio/style.js @@ -5,18 +5,27 @@ let stylesMap = { multilineTextInput:{ height : 100 , textAlignVertical:'top', - paddingTop:20 + padding: 15, + paddingTop: 15, + marginTop: 0 }, - suggestionText:{ - fontWeight:"normal", - color:Colors.midNightblue, - fontSize:18 - }, - suggestionTextWrapper : { - marginTop:20 + dropDownStyle: { + position: 'absolute', + top: 99, + width: '100%', + zIndex: 1, + shadowColor: '#000', + shadowOffset: { + width: 0, + height: 1 + }, + shadowOpacity: 0.2, + shadowRadius: 1.5, + elevation: 1 }, countStyle:{ fontSize:10, + marginTop: 5, textAlign:'right' } }; diff --git a/src/components/BottomStatus/Base.js b/src/components/BottomStatus/Base.js new file mode 100644 index 00000000..9d8d788d --- /dev/null +++ b/src/components/BottomStatus/Base.js @@ -0,0 +1,183 @@ +import React, { PureComponent } from 'react'; +import { View, Text } from 'react-native'; + +import inlineStyles from './styles'; +import { TouchableWithoutFeedback } from 'react-native-gesture-handler'; +import reduxGetter from '../../services/ReduxGetters'; + +import multipleClickHandler from '../../services/MultipleClickHandler'; +import InAppBrowser from '../../services/InAppBrowser'; +import Utilities from '../../services/Utilities'; +import CurrentUser from '../../models/CurrentUser'; + +class BottomStatus extends PureComponent { + constructor(props) { + super(props); + } + + onLinkClick = () => { + InAppBrowser.openBrowser(this.props.link); + }; + + navigateToUserProfile = () => { + if(!this.props.isUserNavigate) return; + if (Utilities.checkActiveUser()) { + if (this.props.userId == CurrentUser.getUserId()) { + this.props.navigation.navigate('ProfileScreen'); + } else { + this.props.navigation.push('UsersProfileScreen', { userId: this.props.userId }); + } + } + }; + + isValidTag(descriptionId, tappedText) { + if(tappedText.indexOf('#') != 0 ) return false; + return this.isValidIncludes( descriptionId, tappedText ); + } + + isValidMention(descriptionId, tappedText) { + if(tappedText.indexOf('@') != 0 ) return false; + return this.isValidIncludes( descriptionId, tappedText ); + } + + isValidIncludes(descriptionId, tappedText){ + return !!reduxGetter.getTappedIncludesEntity(descriptionId, tappedText); + } + + getHashTagMarkup( item , prevText ){ + const tagText = item.replace("#", ""); + return ( + + {prevText} + { + this.onIncludesPressed(item) + }}> + # + {tagText} + + + ); + } + + getMentionMarkup( item , prevText ){ + const mentionText = item.replace("@", ""); + return ( + + {prevText} + { + this.onIncludesPressed(item) + }}> + @ + {mentionText} + + + ); + } + + onIncludesPressed = (tag) => { + if(!Utilities.checkActiveUser()) return; + + let tapEntity = reduxGetter.getTappedIncludesEntity(this.props.entityDescriptionId, tag); + if (!tapEntity) { + return; + } + + if( tapEntity.kind === 'tags'){ + this.props.navigation.push('VideoTags', { "tagId": tapEntity.id }); + return; + } + + if( tapEntity.kind === 'users'){ + if (tapEntity.id == CurrentUser.getUserId()) { + this.props.navigation.navigate('ProfileScreen'); + }else{ + this.props.navigation.push('UsersProfileScreen', { userId: tapEntity.id }); + } + return; + } + }; + + getTextMarkup( item , prevText){ + return ( + + {prevText + item} + + ) + } + + render() { + + let processingString = this.props.description; + let includesArray = processingString.match(/(#|@)\w+/g) || []; + + return ( + + + + this.navigateToUserProfile())} pointerEvents={'auto'}> + {!!this.props.userName && + + {`@${this.props.userName}`} + } + + {this.props.description ? ( + + + {(includesArray.map((item) => { + let tagLocation = processingString.search(item); + let prevText = processingString.slice(0, tagLocation); + processingString = processingString.slice(tagLocation + item.length); + if (this.isValidTag(this.props.entityDescriptionId, item)) { + return this.getHashTagMarkup(item, prevText); + } if( this.isValidMention(this.props.entityDescriptionId , item) ){ + return this.getMentionMarkup(item, prevText); + }else { + return this.getTextMarkup(item, prevText); + } + }))} + + {processingString} + + + ) : ( + + ) + } + + + {this.props.link ? ( + { + this.onLinkClick(); + })} + pointerEvents={'auto'} + > + + {this.props.link.replace(/^(?:https?:\/\/)?(?:www\.)?/i, '')} + + + ) : ( + + )} + + ); + }; +} + +BottomStatus.defaultProps = { + isUserNavigate : true +} + +export default BottomStatus; diff --git a/src/components/BottomStatus/ReplyVideoBottomStatus.js b/src/components/BottomStatus/ReplyVideoBottomStatus.js new file mode 100644 index 00000000..a352a1d6 --- /dev/null +++ b/src/components/BottomStatus/ReplyVideoBottomStatus.js @@ -0,0 +1,20 @@ +import reduxGetter from "../../services/ReduxGetters"; +import { connect } from 'react-redux'; +import Base from "./Base" +import { withNavigation } from "react-navigation"; + +const mapStateToProps = (state, ownProps) => { + return { + userName: reduxGetter.getUserName(ownProps.userId, state), + name: reduxGetter.getName(ownProps.userId, state), + entityDescriptionId : reduxGetter.getReplyDescriptionId(ownProps.entityId), + description: reduxGetter.getVideoDescription(reduxGetter.getReplyDescriptionId(ownProps.entityId, state), state), + link: reduxGetter.getVideoLink(reduxGetter.getReplyLinkId(ownProps.entityId, state), state), + supporters: reduxGetter.getReplySupporters(ownProps.entityId), + totalBt: reduxGetter.getReplyBt(ownProps.entityId, state) + }; + }; + +const ReplyVideoBottomStatus = connect(mapStateToProps)( withNavigation( Base )); + +export default ReplyVideoBottomStatus; diff --git a/src/components/BottomStatus/VideoBottomStatus.js b/src/components/BottomStatus/VideoBottomStatus.js new file mode 100644 index 00000000..f3741ffa --- /dev/null +++ b/src/components/BottomStatus/VideoBottomStatus.js @@ -0,0 +1,20 @@ +import reduxGetter from "../../services/ReduxGetters"; +import { connect } from 'react-redux'; +import Base from "./Base" +import { withNavigation } from "react-navigation"; + +const mapStateToProps = (state, ownProps) => { + return { + userName: reduxGetter.getUserName(ownProps.userId, state), + name: reduxGetter.getName(ownProps.userId, state), + entityDescriptionId : reduxGetter.getVideoDescriptionId(ownProps.entityId), + description: reduxGetter.getVideoDescription(reduxGetter.getVideoDescriptionId(ownProps.entityId, state), state), + link: reduxGetter.getVideoLink(reduxGetter.getVideoLinkId(ownProps.entityId, state), state), + supporters: reduxGetter.getVideoSupporters(ownProps.entityId), + totalBt: reduxGetter.getVideoBt(ownProps.entityId, state) + }; + }; + +const VideoBottomStatus = connect(mapStateToProps)( withNavigation( Base )); + +export default VideoBottomStatus ; diff --git a/src/components/BottomStatus/styles.js b/src/components/BottomStatus/styles.js new file mode 100644 index 00000000..f06df144 --- /dev/null +++ b/src/components/BottomStatus/styles.js @@ -0,0 +1,30 @@ +import { Dimensions} from 'react-native'; +import DefaultStyleGenerator from '../../theme/styles/DefaultStyleGenerator'; +import Colors from '../../theme/styles/Colors'; + +const { height} = Dimensions.get('window'); + +let stylesMap = { + + bottomBg: { + backgroundColor: 'rgba(0, 0, 0, 0.6)', + borderTopLeftRadius: 20, + minHeight: height * 0.05, + paddingHorizontal: 12 + }, + + bottomBgTxt: { + color: Colors.white + }, + handle: { + fontSize: 15, + paddingBottom: 3, + color: Colors.white, + fontFamily: 'AvenirNext-DemiBold', + fontWeight: '700' + } +} + +; + +export default styles = DefaultStyleGenerator.generate(stylesMap); diff --git a/src/components/CaptureVideo/index.js b/src/components/CaptureVideo/index.js index ba824740..23208b81 100644 --- a/src/components/CaptureVideo/index.js +++ b/src/components/CaptureVideo/index.js @@ -2,9 +2,15 @@ import React, { Component } from 'react'; import VideoRecorder from '../VideoRecorder'; import { View } from 'react-native'; import PreviewRecordedVideo from '../PreviewRecordedVideo'; +import Store from "../../store"; +import {upsertRecordedVideo} from "../../actions"; +import utilities from '../../services/Utilities'; +import CurrentUser from '../../models/CurrentUser'; +import AppConfig from '../../constants/AppConfig'; +import deepGet from 'lodash/get'; import FanVideoDetails from '../FanVideoDetails'; import KeepAwake from 'react-native-keep-awake'; - +import reduxGetters from "../../services/ReduxGetters"; class CaptureVideo extends Component { @@ -13,34 +19,124 @@ class CaptureVideo extends Component { }; constructor(props) { super(props); + let isChargeble = props.navigation.getParam("isChargeble"); this.state = { recordingScreen: true, videoUri: '', actionSheetOnRecordVideo: true, modalVisible: true, - acceptedCameraTnC: null + acceptedCameraTnC: null, + showLightBoxOnReply: isChargeble }; + this.replyReceiverUserId = null; + this.replyReceiverVideoId = null; + this.amountToSendWithReply = null; + this.videoType = null; + this.proceedWithExisting = null; + this.setReplyVideoParams(); + this.showCoachForVideoRecord(); + + } + + componentDidMount () {} + + + showCoachForVideoRecord() { + const oThis = this; + if (this.isVideoTypePost()) { + utilities.getItem(`${CurrentUser.getUserId()}-accepted-camera-t-n-c`).then((terms) => { + oThis.setState({ acceptedCameraTnC: terms }); + }); + } + } + + setReplyVideoParams(){ + this.videoType = this.props.navigation.getParam("videoType"); + if (this.isVideoTypeReply()){ + this.replyReceiverUserId = this.props.navigation.getParam("userId"); + this.replyReceiverVideoId = this.props.navigation.getParam("videoId"); + this.amountToSendWithReply = this.props.navigation.getParam("amount"); + this.isChargeble = this.props.navigation.getParam("isChargeble"); + this.toTokenHolderAddress = this.props.navigation.getParam("toTokenHolderAddress"); + + } else { + // Do nothing. + } } + isVideoTypePost(){ + return this.videoType === AppConfig.videoTypes.post; + } + + isVideoTypeReply = () => { + return this.videoType === AppConfig.videoTypes.reply; + }; + goToRecordScreen() { this.setState({ recordingScreen: true, actionSheetOnRecordVideo: false, - acceptedCameraTnC: 'true' + acceptedCameraTnC: 'true', + showLightBoxOnReply: false }); } - goToPreviewScreen(videoUri) { - this.setState({ + + proceedWithExistingVideo = (recordedVideoObj) => { + this.proceedWithExisting = true; + this.videoType = recordedVideoObj.video_type || AppConfig.videoTypes.post ; + this.setState ({ recordingScreen: false, - videoUri + videoUri: recordedVideoObj.raw_video }); + }; + + + saveVideoPrimaryInfo = () => { + this.proceedWithExisting = false; + + Store.dispatch(upsertRecordedVideo(this.getPrimaryVideoInfo())); + }; + + + getPrimaryVideoInfo = () => { + if (this.proceedWithExisting) return {}; + + if (this.isVideoTypeReply()){ + return { video_type: AppConfig.videoTypes.reply , reply_obj: this.getReplyOptions()}; + } else if (this.isVideoTypePost()) { + return { video_type: AppConfig.videoTypes.post }; + } + + }; + + + goToDetailsScreen () { + if (this.videoType === AppConfig.videoTypes.post ){ + this.props.navigation.push('FanVideoDetails', this.getPrimaryVideoInfo()); + } else if (this.videoType === AppConfig.videoTypes.reply ){ + this.props.navigation.push('FanVideoReplyDetails', this.getPrimaryVideoInfo()); + } + } + + getReplyOptions(){ + // Reply options are received to View when user click on Record video (Plus icon). + let replyOptions = {}; + if ( this.isVideoTypeReply()) { + replyOptions['replyReceiverUserId'] = this.replyReceiverUserId; + replyOptions['replyReceiverVideoId'] = this.replyReceiverVideoId; + replyOptions['amountToSendWithReply'] = this.amountToSendWithReply; + replyOptions['isChargeble'] = this.isChargeble; + replyOptions['toTokenHolderAddress'] = this.toTokenHolderAddress; + } + return replyOptions; } - goToDetailsScreen() { - this.props.navigation.push('FanVideoDetails'); + getName(){ + return reduxGetters.getUserName(deepGet (reduxGetters.getRecordedVideo(),'reply_obj.replyReceiverUserId')); } + modalRequestClose = () => { if (this.state.recordingScreen) { this.props.navigation.goBack(); @@ -49,6 +145,22 @@ class CaptureVideo extends Component { } }; + getActionSheetText = (videoObject) => { + if (videoObject.video_type ===AppConfig.videoTypes.reply ){ + return 'You have a pre-recorded reply'; + } else if (videoObject.video_type === AppConfig.videoTypes.post ) { + return 'You have already recorded video'; + } + }; + + goToPreviewScreen = (videoUri) => { + this.setState({ + recordingScreen: false, + videoUri + }); + }; + + getCurrentView() { if (this.state.recordingScreen) { return ( @@ -57,11 +169,16 @@ class CaptureVideo extends Component { this.videoRecorder = recorder; }} acceptedCameraTnC={this.state.acceptedCameraTnC} - goToPreviewScreen={(videoUri) => { - this.goToPreviewScreen(videoUri); - }} + proceedWithExistingVideo={this.proceedWithExistingVideo} + saveVideoPrimaryInfo={this.saveVideoPrimaryInfo} + goToPreviewScreen={this.goToPreviewScreen} actionSheetOnRecordVideo={this.state.actionSheetOnRecordVideo} + getActionSheetText={this.getActionSheetText} navigation={this.props.navigation} + isVideoTypeReply={this.isVideoTypeReply()} + showLightBoxOnReply={this.state.showLightBoxOnReply} + videoId={this.replyReceiverVideoId} + /> ); } else { @@ -76,6 +193,7 @@ class CaptureVideo extends Component { goToDetailsScreen={() => { this.goToDetailsScreen(); }} + saveVideoPrimaryInfo={this.saveVideoPrimaryInfo} cachedvideoUrl={this.state.videoUri} navigation={this.props.navigation} /> diff --git a/src/components/CommonComponents/BottomReplyBar/index.js b/src/components/CommonComponents/BottomReplyBar/index.js new file mode 100644 index 00000000..402a2340 --- /dev/null +++ b/src/components/CommonComponents/BottomReplyBar/index.js @@ -0,0 +1,40 @@ +import React, { PureComponent } from 'react'; +import { View , Text , Image } from 'react-native'; +import { withNavigation } from 'react-navigation'; +import inlineStyles from './styles'; +import { TouchableWithoutFeedback } from 'react-native-gesture-handler'; +import cameraIconWhite from '../../../assets/reply-video-white.png'; + +import {navigateToCamera, replyPreValidationAndMessage} from "../../../helpers/cameraHelper"; +import multipleClickHandler from '../../../services/MultipleClickHandler'; + +class BottomReplyBar extends PureComponent { + + constructor(props){ + super(props); + } + + replyVideo =( ) => { + if(!this.props.videoId || !this.props.userId) return; + if( replyPreValidationAndMessage( this.props.videoId , this.props.userId) ) { + navigateToCamera( this.props.videoId , this.props.userId , this.props.navigation ) ; + } + } + + render(){ + return( + + this.replyVideo())} > + + + Add a reply... + + + + ); + + } + +} + +export default withNavigation( BottomReplyBar ) diff --git a/src/components/CommonComponents/BottomReplyBar/styles.js b/src/components/CommonComponents/BottomReplyBar/styles.js new file mode 100644 index 00000000..97d9048f --- /dev/null +++ b/src/components/CommonComponents/BottomReplyBar/styles.js @@ -0,0 +1,26 @@ +import DefaultStyleGenerator from '../../../theme/styles/DefaultStyleGenerator'; +import Colors from '../../../theme/styles/Colors'; +import { CUSTOM_TAB_Height } from "../../../theme/constants"; + +let stylesMap = { + wrapper : { + flex: 1, + height: CUSTOM_TAB_Height, + backgroundColor: Colors.darkShadeOfGray, + paddingHorizontal: 10, + justifyContent: 'center', + borderTopWidth: 0.5, + borderColor: 'rgba(151, 151, 151, 0.6)' + }, + text: { + color: Colors.white, + marginLeft: 10, + fontFamily: 'AvenirNext-Medium' + }, + replyIconSkipFont : { + height:10, + width:15 + } +}; + +export default styles = DefaultStyleGenerator.generate(stylesMap); diff --git a/src/components/CommonComponents/BubbleList/index.js b/src/components/CommonComponents/BubbleList/index.js new file mode 100644 index 00000000..db9a2764 --- /dev/null +++ b/src/components/CommonComponents/BubbleList/index.js @@ -0,0 +1,136 @@ +import React, { PureComponent } from 'react'; +import inlineStyles from './styles'; +import { Text, View, TouchableOpacity} from 'react-native'; +import { withNavigation } from 'react-navigation'; + +import DataContract from '../../../constants/DataContract'; +import deepGet from "lodash/get"; +import reduxGetter from "../../../services/ReduxGetters"; +import multipleClickHandler from '../../../services/MultipleClickHandler'; +import {FetchServices} from "../../../services/FetchServices"; +import SingleBubble from '../SingleBubble'; +import {connect} from "react-redux"; +import Utilities from '../../../services/Utilities'; + +const mapStateToProps = (state, ownProps) => { + return { + replyCount : reduxGetter.getVideoReplyCount(ownProps.videoId) + }; +}; + + +// +const NO_OF_ITEMS_TO_SHOW = 3; + +class BubbleList extends PureComponent { + + static navigationOptions = { + header: null + }; + + constructor(props) { + super(props); + this.state = { + list: [] + }; + this.fetchServices = new FetchServices(this.getFetchUrl()); + this.getDataWhileLoading(); + this.onClickHandler = this.props.onClickHandler || this.defaultClickHandler; + } + + componentDidUpdate(prevProps, prevState ) { + if( this.props.doRender && this.props.doRender !== prevProps.doRender ){ + this.getListData(); + } + if (this.props.replyCount != prevProps.replyCount){ + this.getListData(); + } + } + + + getFetchUrl = () => { + return `/videos/${this.props.videoId}/replies`; + }; + + + + getDataWhileLoading(){ + if (this.props.doRender){ + this.getListData(); + } + }; + + getListData = () => { + if ( this.props.replyCount == 0 ) { + return; + } + + this.getFetchService() + .refresh() + .then((res) => { + this.onRefresh(res); + }) + .catch((error) => { + //this.onRefreshError(error); + console.log(error); + }); + }; + + + onRefresh = (res) => { + let listToBeShownOnUI = this.fetchServices.getAllResults().slice(0,NO_OF_ITEMS_TO_SHOW); + this.replyCount = reduxGetter.getVideoReplyCount(this.props.videoId); + this.setState({ list : listToBeShownOnUI } ); + + }; + + getBubbleListJSX = () => { + let listToRender = this.state.list; + return listToRender.length? listToRender.map((item) => { + let userId = deepGet(item,'payload.user_id'), + replyDetailId=deepGet(item,'payload.reply_detail_id'); + return + }): <> ; + }; + + moreReplyText = () => { + + let list = this.state.list; + if (! this.replyCount || ! list.length){ + return '' + } + if (this.replyCount > list.length){ + return ` + ${this.replyCount - list.length} Replies`; + } + }; + + getFetchService = () => { + return this.fetchServices; + } + + defaultClickHandler= ()=> { + const baseUrl = DataContract.replies.getReplyListApi(this.props.videoId); +    this.props.navigation.push('FullScreenReplyCollection',{ +      "baseUrl": baseUrl, + "fetchServices":this.getFetchService() +        }); + } + + onIconClick = () => { + if(!Utilities.checkActiveUser()) return; + this.onClickHandler(); + } + + render() { + return + {this.onIconClick()})} + style={{flexDirection: 'row-reverse', marginRight: 5}}>{this.getBubbleListJSX()} + + {/*{this.moreReplyText()}*/} + + } +} + + +//make this component available to the app +export default connect(mapStateToProps)(withNavigation(BubbleList)) ; diff --git a/src/components/CommonComponents/BubbleList/styles.js b/src/components/CommonComponents/BubbleList/styles.js new file mode 100644 index 00000000..a5421c81 --- /dev/null +++ b/src/components/CommonComponents/BubbleList/styles.js @@ -0,0 +1,39 @@ +import DefaultStyleGenerator from '../../../theme/styles/DefaultStyleGenerator'; +import Colors from '../../../theme/styles/Colors'; + +let stylesMap = { + + bubbleShadow: { + shadowColor: Colors.wildWatermelon2, + shadowOffset: { width: 0, height: 0 }, + shadowOpacity: 0.8, + shadowRadius: 5, + elevation: 3, + borderColor: Colors.wildWatermelon2, + borderWidth: .5, + borderRadius: 20 + }, + + bubbleSize: { + height: 38, + width: 38, + borderColor: 'white', + borderWidth: 1, + borderRadius: 20 + }, + + bubbleContainer: { + flexDirection:'row', + marginLeft: 30, + alignItems: 'center' + }, + + repliesTxt: { + color: 'white', + fontSize: 16, + fontFamily: 'AvenirNext-DemiBold' + } + +}; + +export default styles = DefaultStyleGenerator.generate(stylesMap); diff --git a/src/components/CommonComponents/DeleteVideo/index.js b/src/components/CommonComponents/DeleteVideo/index.js index 4051062f..ddb61f13 100644 --- a/src/components/CommonComponents/DeleteVideo/index.js +++ b/src/components/CommonComponents/DeleteVideo/index.js @@ -19,14 +19,13 @@ export default class DeleteVideo extends Component { super(props); } - deleteUser = () => { - if(!this.props.videoId) return; - new PepoApi(`/videos/${this.props.videoId}/delete`) + deleteEntity = () => { + if(!this.props.fetchUrl) return; + new PepoApi(this.props.fetchUrl) .post() .then((response) => { - if (response && response.success){ - this.props.removeVideo && this.props.removeVideo(this.props.videoId); + this.props.removeVideo && this.props.removeVideo(); Toast.show({text:'Video deleted successfully!', icon: 'success'}); } else { Toast.show({text:ostErrors.getUIErrorMessage('delete_video_error'), icon: 'error'}); @@ -43,7 +42,7 @@ export default class DeleteVideo extends Component { '', 'Are you sure to delete video?', [ - {text: 'Delete', onPress: () => this.deleteUser() }, + {text: 'Delete', onPress: () => this.deleteEntity() }, { text: 'Cancel', style: 'cancel' diff --git a/src/components/CommonComponents/DeletedVideoInfo/index.js b/src/components/CommonComponents/DeletedVideoInfo/index.js new file mode 100644 index 00000000..3253725b --- /dev/null +++ b/src/components/CommonComponents/DeletedVideoInfo/index.js @@ -0,0 +1,25 @@ +import React, {PureComponent} from 'react'; +import {TouchableOpacity, View, Image, Text} from 'react-native'; +import {withNavigation} from 'react-navigation'; + +import video_not_available from '../../../assets/video-not-available.png'; +import historyBack from "../../../assets/user-video-history-back-icon.png"; +import inlineStyles from './styles'; + +class DeletedVideoInfo extends PureComponent{ + constructor(props){ + super(props); + } + + render(){ + return ( + this.props.navigation.goBack()} style={inlineStyles.historyBackSkipFont}> + + + + Looks like the Video you were looking for isn’t available and might have been deleted by the creator! + ); + } +} + +export default withNavigation(DeletedVideoInfo); \ No newline at end of file diff --git a/src/components/CommonComponents/DeletedVideoInfo/styles.js b/src/components/CommonComponents/DeletedVideoInfo/styles.js new file mode 100644 index 00000000..00b62fe2 --- /dev/null +++ b/src/components/CommonComponents/DeletedVideoInfo/styles.js @@ -0,0 +1,43 @@ +import DefaultStyleGenerator from '../../../theme/styles/DefaultStyleGenerator'; +import {ifIphoneX} from "react-native-iphone-x-helper"; +import Colors from '../../../theme/styles/Colors'; + +let stylesMap = { + + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: Colors.snow + }, + + imgSizeSkipFont: { + width: 68.5, + height: 68.5 + }, + + desc:{ + fontSize: 22, + marginTop: 10, + textAlign: 'center', + paddingHorizontal: 25, + color: Colors.valhalla, + fontFamily: 'AvenirNext-Regular' + }, + + historyBackSkipFont:{ + ...ifIphoneX({ + top: 55, + }, { + top: 25, + }), + width: 29, + height: 34, + position: 'absolute', + left: 10, + alignItems: 'center', + justifyContent: 'center' + } +}; + +export default styles = DefaultStyleGenerator.generate(stylesMap); diff --git a/src/components/CommonComponents/FlotingBackArrow/index.js b/src/components/CommonComponents/FlotingBackArrow/index.js new file mode 100644 index 00000000..3fd1489d --- /dev/null +++ b/src/components/CommonComponents/FlotingBackArrow/index.js @@ -0,0 +1,24 @@ +import React, { PureComponent } from 'react'; +import {Image,TouchableOpacity} from 'react-native'; + +import historyBack from "../../../assets/user-video-history-back-icon.png"; +import inlineStyles from './styles' +import { withNavigation } from 'react-navigation'; + +class FloatingBackArrow extends PureComponent { + + constructor(props){ + super(props); + } + + render(){ + return ( + this.props.navigation.goBack(null)} style={inlineStyles.historyBackSkipFont}> + + + ) + } + +} + +export default withNavigation( FloatingBackArrow ); \ No newline at end of file diff --git a/src/components/CommonComponents/FlotingBackArrow/styles.js b/src/components/CommonComponents/FlotingBackArrow/styles.js new file mode 100644 index 00000000..9a135846 --- /dev/null +++ b/src/components/CommonComponents/FlotingBackArrow/styles.js @@ -0,0 +1,20 @@ +import DefaultStyleGenerator from '../../../theme/styles/DefaultStyleGenerator'; +import {ifIphoneX} from "react-native-iphone-x-helper"; + +let stylesMap = { + historyBackSkipFont:{ + ...ifIphoneX({ + top: 55, + }, { + top: 25, + }), + width: 29, + height: 34, + position: 'absolute', + left: 10, + alignItems: 'center', + justifyContent: 'center' + } +}; + +export default styles = DefaultStyleGenerator.generate(stylesMap); diff --git a/src/components/CommonComponents/InAppBrowser/index.js b/src/components/CommonComponents/InAppBrowser/index.js index 20cca936..21057808 100644 --- a/src/components/CommonComponents/InAppBrowser/index.js +++ b/src/components/CommonComponents/InAppBrowser/index.js @@ -12,6 +12,7 @@ import ForwardActive from '../../../assets/ForwardActive.png'; import BrowserMenu from './BrowserMenu'; import { getHostName } from '../../../helpers/helpers'; import Colors from '../../../theme/styles/Colors'; +import CommonStyle from '../../../theme/styles/Common'; export default class InAppBrowserComponent extends Component { static navigationOptions = (props) => { @@ -92,7 +93,7 @@ export default class InAppBrowserComponent extends Component { render() { return ( - + {this.state.loadingProgress != 1 && ( { + return { + seen: reduxGetters.isReplySeen( deepGet(ownProps.payload,'reply_detail_id')) + }; +}; + +class ReplyThumbnailItem extends Component { + + constructor(props) { + super(props); + this.videoId = this.props.payload.video_id; + this.replyDetailId = this.props.payload.reply_detail_id; + this.userId = reduxGetters.getReplyEntity(this.replyDetailId).creator_user_id; + } + + render() { + return + { this.props.onClickHandler();})} + style={[inlineStyle.wrapperStyle, !this.props.seen && inlineStyle.unseen, this.props.isActive && inlineStyle.active]}> + + + + } + + isActiveOrUnseen = () => { + return !this.props.seen || this.props.isActive; + } + +} + +const outerRingDiameter = AppConfig.thumbnailListConstants.outerRingDiameter; +const outerBorderWidth = AppConfig.thumbnailListConstants.outerBorderWidth; +const transparentGap = AppConfig.thumbnailListConstants.transparentGap; +const iconImageDiameter = AppConfig.thumbnailListConstants.iconImageDiameter(); + +const inlineStyle= { + iconStyle: { height: iconImageDiameter, + width: iconImageDiameter, + borderRadius: iconImageDiameter/ 2, + marginLeft: transparentGap, + marginTop: transparentGap + }, + wrapperStyle: { + width: outerRingDiameter, + height: outerRingDiameter, + borderRadius: outerRingDiameter/ 2, + borderWidth: outerBorderWidth, + borderColor: 'transparent', + }, + active: { + borderColor: '#fff', + }, + unseen: { + borderColor: '#FD8691' + } +}; + +//make this component available to the app +export default connect(mapStateToProps)(ReplyThumbnailItem); diff --git a/src/components/CommonComponents/InvertedReplyThumbnailList/index.js b/src/components/CommonComponents/InvertedReplyThumbnailList/index.js new file mode 100644 index 00000000..0abbfeee --- /dev/null +++ b/src/components/CommonComponents/InvertedReplyThumbnailList/index.js @@ -0,0 +1,335 @@ +import React, { PureComponent } from 'react'; +import { View } from 'react-native'; +import { withNavigation } from 'react-navigation'; +import deepGet from "lodash/get"; + +import Pagination from "../../../services/Pagination"; +import ReplyThumbnailItem from './ReplyThumbnailItem' +import {FlatList} from 'react-native-gesture-handler'; +import ReplyHelper from '../../../helpers/ReplyHelper'; +import AppConfig from "../../../constants/AppConfig"; +import DataContract from '../../../constants/DataContract'; +import ReduxGetters from '../../../services/ReduxGetters'; +import Utilities from '../../../services/Utilities'; + + +const ITEM_HEIGHT = AppConfig.thumbnailListConstants.cellHeight(); +const ITEM_SEPERATOR_HEIGHT = AppConfig.thumbnailListConstants.separatorHeight; + +class InvertedReplyList extends PureComponent { + + static navigationOptions = { + header: null + }; + + constructor(props) { + super(props); + this.paginationService = null; + this.hasInitialData = true; + this.setPagination(); + + this.state = { + list: this.getInitialList(), + refreshing : false, + loadingNext: false + }; + + this.listRef = null; + this.onItemClick = null; + } + + getInitialList = () => { + if (this.props.paginationService){ + return this.paginationService.getResults(); + } + return []; + }; + + setClickHandlers = ()=> { + this.onItemClick = this.props.onChildClickDelegate || this.defaultChildClickHandler; + }; + + getPagination = () => { + return this.paginationService; + }; + + componentDidMount () { + this.bindPaginationEvents(); + this.setClickHandlers(); + } + + bubbleClickHandler = ()=> { + this.props.navigation.goBack(null); + } + + getParentClickHandler =( videoId )=>{ + return () => { + let parentUserId = ReduxGetters.getVideoCreatorUserId(videoId); + this.props.navigation.push('VideoPlayer', { + userId: parentUserId, + videoId: videoId, + bubbleClickHandler: this.bubbleClickHandler + }); + } + } + + defaultChildClickHandler = ( index, item )=> { + const videoId = ReduxGetters.getReplyParentVideoId(deepGet(item , "payload.reply_detail_id")), + baseUrl = DataContract.replies.getReplyListApi(videoId), + clonedInstance = this.getPagination().fetchServices.cloneInstance(); + ReplyHelper.updateEntitySeen( item ); +    this.props.navigation.push('FullScreenReplyCollection',{ +      "fetchServices": clonedInstance, +      "currentIndex":index, +      "baseUrl": baseUrl, + "parentClickHandler": this.getParentClickHandler( videoId ) +    }); +  }; + + setPagination() { + let fetchUrl = this.getFetchUrl(); + this.paginationService = this.props.paginationService; + if( !this.paginationService || !( this.paginationService instanceof Pagination)){ + this.paginationService = new Pagination(fetchUrl); + if( this.props.doRender ){ + this.initPagination(); + }else { + //Load data later + this.hasInitialData = false; + } + } + } + + componentWillUnmount() { + this.removePaginationListeners(); + this.onItemClick = ()=> {}; + } + + componentDidUpdate(prevProps, prevState ) { + if( this.props.doRender && this.props.doRender !== prevProps.doRender && !this.hasInitialData ){ + this.initPagination(); + } + if(this.props.currentIndex != prevProps.currentIndex){ + this.listRef && this.listRef.scrollToIndex({index : this.props.currentIndex, viewOffset: 100, viewPosition: 0.5}); + } + } + + onScrollToIndexFailed =( info) => { + console.log("======onScrollToIndexFailed=====" , info ); + } + + // region - Pagination and Event Handlers + + initPagination(){ + this.getPagination().initPagination(); + } + + bindPaginationEvents(){ + let pagination = this.getPagination(); + if ( !pagination ) { + return; + } + let paginationEvent = pagination.event; + if ( null === paginationEvent ) { + return; + } + + paginationEvent.on("onBeforeRefresh" , this.beforeRefresh.bind(this) ); + paginationEvent.on("onRefresh" , this.onRefresh.bind(this) ); + paginationEvent.on("onRefreshError" , this.onRefreshError.bind(this)); + paginationEvent.on("onBeforeNext" , this.beforeNext.bind(this)); + paginationEvent.on("onNext" , this.onNext.bind(this) ); + paginationEvent.on("onNextError" , this.onNextError.bind(this)); + } + + removePaginationListeners(){ + let pagination = this.getPagination(); + if ( !pagination ) { + return; + } + let paginationEvent = pagination.event; + if ( null === paginationEvent ) { + return; + } + paginationEvent.removeListener('onBeforeRefresh'); + paginationEvent.removeListener('onRefresh'); + paginationEvent.removeListener('onRefreshError'); + paginationEvent.removeListener('onBeforeNext'); + paginationEvent.removeListener('onNext'); + paginationEvent.removeListener('onNextError'); + } + + getFetchUrl = () => { + return DataContract.replies.getReplyListApi(this.props.videoId); + }; + + scrollToTop(){ + this.listRef.scrollToOffset({offset: 0}); + } + + beforeRefresh = ( ) => { + console.log('beforeRefresh') + this.props.beforeRefresh && this.props.beforeRefresh(); + let stateObject = {refreshing : true}; + if (this.state.loadingNext) { + stateObject['loadingNext'] = false; + } + this.setState(stateObject); + }; + + onRefresh = ( res ) => { + let results = this.getPagination().getResults() ; + this.props.onRefresh && this.props.onRefresh( results , res ); + this.setState({ refreshing : false , list : results }); + // this.setState({ refreshing : false , list : [results[0], results[1]] }); + } + + onRefreshError = ( error ) => { + this.setState({ refreshing : false }); + } + + beforeNext =() => { + if (this.state.refreshing) return; + this.setState({ loadingNext : true }); + } + + onNext = ( res ) => { + this.setState({ loadingNext : false , list : this.getPagination().getResults() }); + } + + onNextError = ( error ) => { + this.setState({ loadingNext : false }); + } + + getNext = () => { + this.getPagination().getNext(); + } + + refresh = () => { + this.getPagination().refresh(); + }; + + _keyExtractor = (item, index) => { + return `id_${item.id}`; + }; + + _renderItem = ({item, index}) => { + return + {this.onItemClick(index, item)}} + isActive={this.isActiveEntity( index , item)} + cellIndex={index} + totalCells={this.state.list.length} + /> + ; + }; + + isActiveEntity = ( index , item )=>{ + if(typeof this.props.isActiveEntity == "function" ){ + return this.props.isActiveEntity(this.props.fullVideoReplyId , item , index) ; + } + return this.props.currentIndex == index; + } + + setListRef = (ref) => { + this.listRef = ref; + }; + + getItemSeperatorComponent = ({leadingItem}) => { + let styles = { + backgroundColor: AppConfig.thumbnailListConstants.separatorColor, + height: AppConfig.thumbnailListConstants.separatorHeight, + width: AppConfig.thumbnailListConstants.separatorWidth, + alignSelf:'center' + }; + + const separatorMargin = AppConfig.thumbnailListConstants.separatorMargin(); + + // Leading Item + let leadingItemIndex = this.state.list.indexOf(leadingItem); + let isLeadingSelected = this.isActiveEntity( leadingItemIndex , leadingItem); + let isLeadingSeen = ReduxGetters.isReplySeen( deepGet(leadingItem.payload,'reply_detail_id')); + + //Top margin is needed if and only if the leading cell is seen and is not selected. + if ( isLeadingSeen && !isLeadingSelected ) { + //Leading does not have border. + styles.marginTop = -1 * separatorMargin; + styles.height = styles.height + separatorMargin; + } + + let trailingItemIndex = leadingItemIndex + 1; + let trailingItem = this.state.list[trailingItemIndex]; + let isTrailingSelected = this.isActiveEntity( trailingItemIndex , trailingItem); + let isTrailingSeen = ReduxGetters.isReplySeen( deepGet(trailingItem.payload,'reply_detail_id')); + + //Bottom margin is needed if and only if the trailing cell is seen and is not selected. + if ( isTrailingSeen && !isTrailingSelected ) { + //Trailing cell does not have border. + styles.marginBottom = -1 * separatorMargin; + styles.height = styles.height + separatorMargin; + } + + return + }; + + getAvailableHeight = () => { + let availableHeight = Utilities.getPendantAvailableHeight(); + return availableHeight - this.props.bottomRounding; + } + + getListHeight = () => { + let availableHeight = this.getAvailableHeight(); + let noOfItems = this.state.list.length; + if (noOfItems > 0){ + let heightOfElements = noOfItems * ITEM_HEIGHT, + heightOfSeparator = (noOfItems - 1 ) * AppConfig.thumbnailListConstants.separatorHeight, + heightOfFlatList = heightOfElements + heightOfSeparator , + finalHeight = availableHeight > heightOfFlatList ? heightOfFlatList : availableHeight; + return finalHeight; + } + return 0; + }; + + getItemLayout= (data, index) => { + let itemLength = ITEM_SEPERATOR_HEIGHT + ITEM_HEIGHT; + let itemOffset = index * (itemLength); + return { + length: itemLength, + offset: itemOffset, + index: index + }; + } + + render() { + console.log("InvertedReplyList :: this.props.listKey", this.props.listKey); + return + } +} + +InvertedReplyList.defaultProps = { + paginationService : null, + doRender: true, + currentIndex: 0, + bottomRounding: 10 +}; + +//make this component available to the app +export default withNavigation(InvertedReplyList); diff --git a/src/components/CommonComponents/InvertedReplyThumbnailList/styles.js b/src/components/CommonComponents/InvertedReplyThumbnailList/styles.js new file mode 100644 index 00000000..e69de29b diff --git a/src/components/CommonComponents/NumberInput/index.js b/src/components/CommonComponents/NumberInput/index.js new file mode 100644 index 00000000..201c2b95 --- /dev/null +++ b/src/components/CommonComponents/NumberInput/index.js @@ -0,0 +1,67 @@ +import React,{PureComponent} from 'react'; +import {View,Text,TextInput} from 'react-native'; + +import NumberFormatter from '../../../helpers/NumberFormatter'; +import { ostErrors } from '../../../services/OstErrors'; +import Theme from '../../../theme/styles'; + +export default class NumberInput extends PureComponent{ + constructor( props ){ + super( props ); + this.state={ + value : props.value, + errorMsg : null + }; + this.numberFormatter = new NumberFormatter(); + } + + validateAndSet(val){ + let errMsg = null; + if (!this.numberFormatter.isValidInputProvided(val)) { + errMsg = this.getErrorMessage( val ); + } + this.setState({ + value: val, + valueError: errMsg + }); + } + + getErrorMessage( val ){ + if (val && String(val).indexOf(',') > -1) { + return ostErrors.getUIErrorMessage('bt_amount_decimal_error'); + } + else if (val && String(val).split('.')[1] && String(val).split('.')[1].length > 2) { + return ostErrors.getUIErrorMessage('bt_amount_decimal_allowed_error'); + } else { + return "invalid input"; + } + } + + onChangeText(value) { + let formattedVal = this.numberFormatter.convertToValidFormat(value) + , val = this.numberFormatter.getFullStopValue(formattedVal) + ; + this.validateAndSet(value); + this.props.onChangeText && this.props.onChangeText(val); + } + + render(){ + return( + + + {this.onChangeText(value)}} + value = {String(this.props.value)} + keyboardType = 'decimal-pad' + style={{paddingVertical: 0}} + /> + + { this.props.errorMsg || this.state.valueError } + + + + ) + } +} + + diff --git a/src/components/CommonComponents/ReplyIcon/index.js b/src/components/CommonComponents/ReplyIcon/index.js new file mode 100644 index 00000000..693865bd --- /dev/null +++ b/src/components/CommonComponents/ReplyIcon/index.js @@ -0,0 +1,58 @@ +import React, { PureComponent } from 'react'; +import {TouchableOpacity , Image, View, Text} from "react-native"; +import { connect } from 'react-redux'; +import {withNavigation} from "react-navigation"; +import reduxGetter from '../../../services/ReduxGetters'; +import inlineStyles from './styles'; +import multipleClickHandler from "../../../services/MultipleClickHandler"; +import reply_video from '../../../assets/video-reply.png'; +import Utilities from '../../../services/Utilities'; +import NavigationService from "../../../services/NavigationService"; +import utilities from "../../../services/Utilities"; +import {getVideoReplyObject, replyPreValidationAndMessage} from "../../../helpers/cameraHelper"; +import Pricer from "../../../services/Pricer"; + +const mapStateToProps = (state , ownProps) => { + return { + videoReplyCount : reduxGetter.getVideoReplyCount(ownProps.videoId, state) + } +}; + +class ReplyIcon extends PureComponent { + + constructor(props){ + super(props); + }; + + replyVideo = ()=> { + + if(!Utilities.checkActiveUser()) return; + + if ( this.props.videoReplyCount > 0 ) { + this.props.navigation.push('VideoReplies', + {'videoId': this.props.videoId , + 'userId': this.props.userId + }); + } else if( replyPreValidationAndMessage(this.props.videoId , this.props.userId ) ){ + let activeTab = NavigationService.getActiveTab(); + let params = getVideoReplyObject(this.props.videoId, this.props.userId); + utilities.handleVideoUploadModal(activeTab, this.props.navigation, params); + } + + }; + + render(){ + return ( + + {Pricer.toDisplayAmount(this.props.videoReplyCount)} + this.replyVideo())} > + + + ); + } + +}; + +export default connect(mapStateToProps)(withNavigation(ReplyIcon)); diff --git a/src/components/CommonComponents/ReplyIcon/styles.js b/src/components/CommonComponents/ReplyIcon/styles.js new file mode 100644 index 00000000..70893536 --- /dev/null +++ b/src/components/CommonComponents/ReplyIcon/styles.js @@ -0,0 +1,24 @@ +import DefaultStyleGenerator from '../../../theme/styles/DefaultStyleGenerator'; +import Colors from '../../../theme/styles/Colors'; + +let stylesMap = { + + replyIconWrapper : { + marginBottom: 5, + height: 40, + width: 40, + alignItems: 'center', + justifyContent: 'center' + }, + videoReplyCount : { + fontSize: 18, + color: Colors.white, + alignSelf: 'center', + textShadowColor: 'rgba(0, 0, 0, 0.65)', + textShadowOffset: {width: 1, height: 1}, + textShadowRadius: 1, + fontFamily: 'AvenirNext-DemiBold' + } +}; + +export default styles = DefaultStyleGenerator.generate(stylesMap); diff --git a/src/components/CommonComponents/ReportVideo/index.js b/src/components/CommonComponents/ReportVideo/index.js index 7560c9f8..fe653f19 100644 --- a/src/components/CommonComponents/ReportVideo/index.js +++ b/src/components/CommonComponents/ReportVideo/index.js @@ -32,7 +32,7 @@ class ReportVideo extends PureComponent { reportVideo = () => { new PepoApi('/report') - .post({report_entity_kind: 'video', report_entity_id: this.props.videoId }) + .post({report_entity_kind: this.props.reportKind, report_entity_id: this.props.reportEntityId }) .then((response) => { if (response && response.success){ Toast.show({text:'Video reported successfully!', icon: 'success' }); @@ -87,7 +87,7 @@ class ReportVideo extends PureComponent { render(){ return this.isVisible() && ( this.showActionSheet())} > ) diff --git a/src/components/CommonComponents/ShareIcon/index.js b/src/components/CommonComponents/ShareIcon/Base.js similarity index 58% rename from src/components/CommonComponents/ShareIcon/index.js rename to src/components/CommonComponents/ShareIcon/Base.js index fea0a3d1..510569c0 100644 --- a/src/components/CommonComponents/ShareIcon/index.js +++ b/src/components/CommonComponents/ShareIcon/Base.js @@ -1,44 +1,33 @@ import React, { PureComponent } from 'react'; import {TouchableOpacity , Image} from "react-native"; -import { connect } from 'react-redux'; -import reduxGetter from '../../../services/ReduxGetters'; -import inlineStyles from './styles'; import multipleClickHandler from "../../../services/MultipleClickHandler"; import ShareVideo from "../../../services/shareVideo"; import share_icon from '../../../assets/social-share-icon.png'; import share_icon_disabled from '../../../assets/social-disabled-share-icon.png'; -const mapStateToProps = (state , ownProps) => { - return { - isCreatorApproved : reduxGetter.isCreatorApproved(ownProps.userId) - } -}; -class ShareIcon extends PureComponent { +class Base extends PureComponent { constructor(props){ super(props); }; shareVideo = () => { - let shareVideo = new ShareVideo(this.props.videoId); + let shareVideo = new ShareVideo(this.props.url); shareVideo.perform(); }; - isDisabled = () => { - return this.props.isCreatorApproved != 1; - }; - render(){ return ( this.shareVideo())} > - + ); } }; -export default connect(mapStateToProps)(ShareIcon); + +export default Base; diff --git a/src/components/CommonComponents/ShareIcon/ReplyShare.js b/src/components/CommonComponents/ShareIcon/ReplyShare.js new file mode 100644 index 00000000..463750ca --- /dev/null +++ b/src/components/CommonComponents/ShareIcon/ReplyShare.js @@ -0,0 +1,12 @@ +import reduxGetter from '../../../services/ReduxGetters'; +import { connect } from 'react-redux'; +import Base from "./Base" + +const mapStateToProps = (state , ownProps) => { + return { + canReply : reduxGetter.isReplyShareable(ownProps.entityId , state) + } +}; + +const ReplyShareIcon = connect(mapStateToProps)(Base); +export default ReplyShareIcon; \ No newline at end of file diff --git a/src/components/CommonComponents/ShareIcon/VideoShare.js b/src/components/CommonComponents/ShareIcon/VideoShare.js new file mode 100644 index 00000000..aaf37675 --- /dev/null +++ b/src/components/CommonComponents/ShareIcon/VideoShare.js @@ -0,0 +1,13 @@ +import reduxGetter from '../../../services/ReduxGetters'; +import { connect } from 'react-redux'; +import Base from "./Base" + + +const mapStateToProps = (state , ownProps) => { + return { + canReply : reduxGetter.isVideoShareable(ownProps.entityId , state) + } +}; + +const VideoShareIcon = connect(mapStateToProps)(Base); +export default VideoShareIcon; \ No newline at end of file diff --git a/src/components/CommonComponents/SingleBubble/index.js b/src/components/CommonComponents/SingleBubble/index.js new file mode 100644 index 00000000..47af57b9 --- /dev/null +++ b/src/components/CommonComponents/SingleBubble/index.js @@ -0,0 +1,34 @@ +import React, { Component } from 'react'; +import {View} from 'react-native'; +import { connect } from 'react-redux'; +import ProfilePicture from "../../ProfilePicture"; +import reduxGetters from '../../../services/ReduxGetters'; +import inlineStyles from './styles'; + +const mapStateToProps = (state, ownProps) => { + return { + seen: reduxGetters.isReplySeen(ownProps.replyDetailId) + }; +}; + +class SingleBubble extends Component { + + constructor(props) { + super(props); + } + + render() { + return + + + } + isActiveOrUnseen = () => { + return ! this.props.seen || this.props.isActive(); + } + +} + +//make this component available to the app +export default connect(mapStateToProps)(SingleBubble); diff --git a/src/components/CommonComponents/SingleBubble/styles.js b/src/components/CommonComponents/SingleBubble/styles.js new file mode 100644 index 00000000..0b89cc98 --- /dev/null +++ b/src/components/CommonComponents/SingleBubble/styles.js @@ -0,0 +1,39 @@ +import DefaultStyleGenerator from '../../../theme/styles/DefaultStyleGenerator'; +import Colors from '../../../theme/styles/Colors'; + +let stylesMap = { + + bubbleShadow: { + shadowColor: Colors.wildWatermelon2, + shadowOffset: { width: 0, height: 0 }, + shadowOpacity: 0.8, + shadowRadius: 5, + elevation: 3, + borderColor: Colors.wildWatermelon2, + borderWidth: .5, + borderRadius: 20 + }, + + bubbleSizeSkipFont: { + height: 38, + width: 38, + borderColor: 'white', + borderWidth: 1, + borderRadius: 20 + }, + + bubbleContainer: { + flexDirection:'row', + marginLeft: 30, + alignItems: 'center' + }, + + repliesTxt: { + color: 'white', + fontSize: 16, + fontFamily: 'AvenirNext-DemiBold' + } + +}; + +export default styles = DefaultStyleGenerator.generate(stylesMap); diff --git a/src/components/CommonComponents/SlidingUpPanel/index.js b/src/components/CommonComponents/SlidingUpPanel/index.js new file mode 100644 index 00000000..5dbf5bde --- /dev/null +++ b/src/components/CommonComponents/SlidingUpPanel/index.js @@ -0,0 +1,15 @@ +import SlidingUpPanel from "rn-sliding-up-panel"; + +class Panel extends SlidingUpPanel{ + + constructor(props) { + super(props); + } + + _onBackButtonPress() { + return false; + } + +} + +export default Panel ; diff --git a/src/components/CommonComponents/SlidingUpPanel/styles.js b/src/components/CommonComponents/SlidingUpPanel/styles.js new file mode 100644 index 00000000..e69de29b diff --git a/src/components/CommonComponents/TagsInput/CustomTextInput.js b/src/components/CommonComponents/TagsInput/CustomTextInput.js index 1234830e..bcfdc9f7 100644 --- a/src/components/CommonComponents/TagsInput/CustomTextInput.js +++ b/src/components/CommonComponents/TagsInput/CustomTextInput.js @@ -1,14 +1,17 @@ import React, { PureComponent } from 'react'; -import { View, Text, TextInput } from 'react-native'; +import { View, Text, TextInput, Platform } from 'react-native'; import Theme from '../../../theme/styles'; const MAX_LENGTH = 500; +const IS_ANDROID = Platform.OS === 'android'; class CustomTextInput extends PureComponent { constructor(props) { super(props); this.textInputRef = null; + this.textVal = ""; + this.needsOnChangeText = false; } setTextInputRef = (textInputRef) => { @@ -23,19 +26,40 @@ class CustomTextInput extends PureComponent { this.textInputRef.setNativeProps({ "text": text }); + this.textVal = text; } }; + onChangeText = (text) => { + this.textVal = text; + if ( IS_ANDROID ) { + this.needsOnChangeText = true; + } else { + // Only for iOS + this.triggerOnTextChange(text); + } + } + + onSelectionChange = (...args) => { + this.props.locationGetter(...args); + if ( this.needsOnChangeText ) { + this.triggerOnTextChange(this.textVal); + } + } + + triggerOnTextChange = (text) => { + this.props.onChangeText(text); + this.needsOnChangeText = false; + } + render() { return ( { - this.props.onChangeText(value); - }} + onSelectionChange={this.onSelectionChange} + onChangeText={this.onChangeText} multiline={true} autoFocus={this.props.autoFocus} placeholder={this.props.placeholderText} diff --git a/src/components/CommonComponents/TagsInput/index.js b/src/components/CommonComponents/TagsInput/index.js index b300f45c..6e2e07a2 100644 --- a/src/components/CommonComponents/TagsInput/index.js +++ b/src/components/CommonComponents/TagsInput/index.js @@ -1,14 +1,21 @@ import React, { PureComponent } from 'react'; -import { TouchableOpacity, FlatList } from 'react-native'; +import { View, Dimensions, Text, Animated} from 'react-native'; +import { FlatList, TouchableOpacity, TouchableWithoutFeedback } from 'react-native-gesture-handler'; import PepoApi from '../../../services/PepoApi'; import CustomTextInput from './CustomTextInput'; +import deepGet from 'lodash/get'; +import unescape from 'lodash/unescape'; +import DataContract from "../../../constants/DataContract"; +import inlineStyles from "./styles"; +import ProfilePicture from "../../ProfilePicture"; class TagsInput extends PureComponent { constructor(props) { super(props); this.state = { - data: [], + hashTagsData: [], + mentionsData: [], keyword: '' }; this.value = this.props.initialValue; @@ -24,10 +31,24 @@ class TagsInput extends PureComponent { const reqParam = keyword.substr(1); this.reqTimer = setTimeout(() => { if (!reqParam) return; - new PepoApi('/tags') + new PepoApi(DataContract.tags.userTags) .get('q=' + reqParam) .then((res) => { - this.openSuggestionsPanel(res); + this.openTagsPanel(res); + }) + .catch((error) => {}); + }, 300); + }; + + fetchMentions = (keyword) => { + clearTimeout(this.reqTimer); + const reqParam = keyword.substr(1); + this.reqTimer = setTimeout(() => { + if (!reqParam) return; + new PepoApi(DataContract.mentions.userMentions) + .get('q=' + reqParam) + .then((res) => { + this.openMentionsPanel(res); }) .catch((error) => {}); }, 300); @@ -55,18 +76,21 @@ class TagsInput extends PureComponent { currentIndexChar = val.charAt(location), isValidChar = this.isValidChar(currentIndexChar), wordAtIndex = this.getWordAtIndex(val, location), - isHashTag = this.isHashTag(wordAtIndex); - if (isValidChar && isHashTag) { + isHashTag = this.isHashTag(wordAtIndex), + isMention = this.isMention(wordAtIndex); + if (isValidChar && isHashTag || isMention) { this.wordIndex = location; this.indexWord = wordAtIndex; this.startTracking(); - this.fetchHashTags(wordAtIndex); + isHashTag && this.fetchHashTags(wordAtIndex); + isMention && this.fetchMentions(wordAtIndex); } else { this.closeSuggestionsPanel(); } this.changeValue(val); }; + setCustomInputRef = ( ref )=> { this.customTextInputRef = ref; } @@ -80,10 +104,22 @@ class TagsInput extends PureComponent { }; isHashTag(val) { - const hastagRegex = /(?:\s|^)#[A-Za-z0-9\-\.\_]+(?:\s|$)/g; - return hastagRegex.test(val); + const hastagRegex = /(?:\s|^)#[A-Za-z0-9\-\.\_]+(?:\s|$)/g; + return hastagRegex.test(val); + }; + + inIncludeMentions(){ + return this.props.mentions && this.props.mentions.includes("@"); } + isMention(val){ + if(this.inIncludeMentions()) { + const mentionRegex = /(?:\s|^)@[A-Za-z0-9\-\.\_]+(?:\s|$)/g; + return mentionRegex.test(val); + } + return false; + }; + isValidChar(val) { const spaceRegex = /\s/g; return val && !spaceRegex.test(val); @@ -93,18 +129,56 @@ class TagsInput extends PureComponent { this.location = event && event.nativeEvent && event.nativeEvent.selection && event.nativeEvent.selection.start; }; - openSuggestionsPanel(res) { + openTagsPanel(res) { if (!this.isTrackingStarted) return; - if (res && res.success && res.data ) { - let resultType = res.data.result_type; - resultType && this.setState({ data: res.data[resultType]}); + if ( deepGet(res , "data.meta.search_kind") === "tags" ) { + let resultTypeTags = res.data.result_type; + if ( !resultTypeTags ) { + return; + } + let results = res.data[resultTypeTags]; + this.setState({ mentionsData:[], hashTagsData: results}); + if ( results && results.length ) { + this.triggerOnSuggestionsPanelOpen(); + } else { + this.triggerOnSuggestionsPanelClose(); + } + } + } + + openMentionsPanel(res) { + if (!this.isTrackingStarted) return; + if ( deepGet(res , "data.meta.search_kind") == "users") { + let resultTypeMentions = res.data.result_type; + if ( !resultTypeMentions ) { + return; + } + let results = res.data[resultTypeMentions]; + this.setState({hashTagsData:[], mentionsData: results}); + this.triggerOnSuggestionsPanelOpen(); + if ( results && results.length ) { + this.triggerOnSuggestionsPanelOpen(); + } else { + this.triggerOnSuggestionsPanelClose(); + } } } closeSuggestionsPanel() { this.stopStracking(); - if (this.state.data.length > 0) { - this.setState({ data: [] }); + this.setState({ hashTagsData: [], mentionsData: [] }); + this.triggerOnSuggestionsPanelClose(); + } + + triggerOnSuggestionsPanelOpen() { + if( this.props.onSuggestionsPanelOpen ) { + this.props.onSuggestionsPanelOpen(); + } + } + + triggerOnSuggestionsPanelClose() { + if ( this.props.onSuggestionsPanelClose ) { + this.props.onSuggestionsPanelClose(); } } @@ -118,16 +192,26 @@ class TagsInput extends PureComponent { _keyExtractor = (item, index) => `id_${item.id}`; - _renderItem = ({ item }) => { - const SearchResultRowComponent = this.props.searchResultRowComponent; + _renderHashTagItem = ({ item }) => { + const HashRow = this.props.hashResultRowComponent || HashResultRowComponent ; return ( - this.onSuggestionTap(item)}> - + this.onHashSuggestionTap(item)} style={{paddingLeft: 8}}> + {/* Hashtags do not include special character like '&' today, but unescaped if they start supporting*/} + ); }; - onSuggestionTap(item) { + _renderMentionsItem = ({ item }) => { + const MentionRow = this.props.mentionResultRowComponent || MentionResultRowComponent ; + return ( + this.onMentionSuggestionTap(item)} style={{paddingLeft: 8}}> + + + ); + }; + + onHashSuggestionTap(item ) { this.closeSuggestionsPanel(); const wordToReplace = this.getWordAtIndex(this.value, this.wordIndex), isHashTag = this.isHashTag(wordToReplace); @@ -140,6 +224,19 @@ class TagsInput extends PureComponent { } } + onMentionSuggestionTap(item ) { + this.closeSuggestionsPanel(); + const wordToReplace = this.getWordAtIndex(this.value, this.wordIndex), + isMention = this.isMention(wordToReplace); + if (isMention) { + const startIndex = this.getStartIndex(this.value, this.wordIndex), + endIndex = this.getEndIndex(this.value, this.wordIndex), + replaceString = ` @${item.user_name} `, + newString = this.replaceBetween(startIndex, endIndex, replaceString); + this.changeValue(newString); + } + } + replaceBetween(start, end, replaceString) { return this.value.substring(0, start) + replaceString + this.value.substring(end); } @@ -170,38 +267,81 @@ class TagsInput extends PureComponent { return endIndex; } + isHastagData(){ + return this.state.hashTagsData && this.state.hashTagsData.length > 0 ; + } + + isMentionsData(){ + return this.state.mentionsData && this.state.mentionsData.length > 0 ; + } + render() { return ( - - { - this.props.submitEvent(this.value); - }} - locationGetter={this.locationGetter} - onChangeText={this.onChangeText} - placeholderText={this.props.placeholderText} - autoFocus={this.props.autoFocus} - maxLength={this.props.maxLength} - /> - {this.props.children} - - - } - stickyHeaderIndices={[0]} - /> + + { + this.props.submitEvent(this.value); + }} + locationGetter={this.locationGetter} + onChangeText={this.onChangeText} + placeholderText={this.props.placeholderText} + autoFocus={this.props.autoFocus} + maxLength={this.props.maxLength} + /> + + {this.isHastagData() ? + : this.isMentionsData() && ( + + ) + } + + ); } } + +const HashResultRowComponent = (props) => ( + + {`#${props.val}`} + +); + +const MentionResultRowComponent = (props) => ( + + + + {`${props.name}`} + {`@${props.userName}`} + + +); + export default TagsInput; diff --git a/src/components/CommonComponents/TagsInput/styles.js b/src/components/CommonComponents/TagsInput/styles.js index d329a9ed..5c79c242 100644 --- a/src/components/CommonComponents/TagsInput/styles.js +++ b/src/components/CommonComponents/TagsInput/styles.js @@ -1,6 +1,24 @@ import DefaultStyleGenerator from '../../../theme/styles/DefaultStyleGenerator'; import Colors from '../../../theme/styles/Colors'; -let stylesMap = {}; +let stylesMap = { + suggestionTextWrapper: { + marginVertical: 9 + }, + suggestionText:{ + fontFamily: 'AvenirNext-Regular', + color:Colors.midNightblue, + fontSize:16 + }, + mentionsTitle:{ + fontSize: 15, + color:Colors.midNightblue, + fontFamily: 'AvenirNext-Medium', + }, + mentionSubTitle: { + fontSize: 14, + color: Colors.light + } +}; export default styles = DefaultStyleGenerator.generate(stylesMap); diff --git a/src/components/CommonComponents/UserInfo/index.js b/src/components/CommonComponents/UserInfo/index.js index 1dd789c4..dd552a7e 100644 --- a/src/components/CommonComponents/UserInfo/index.js +++ b/src/components/CommonComponents/UserInfo/index.js @@ -16,7 +16,6 @@ import InAppBrowser from '../../../services/InAppBrowser'; import profileLink from '../../../assets/profile_link.png'; import twitterLink from '../../../assets/twitter_link.png'; import Utilities from '../../../services/Utilities'; -import inlineStyles from '../../Home/styles'; const mapStateToProps = (state, ownProps) => { return { @@ -52,15 +51,12 @@ class UserInfo extends React.PureComponent { } onDidFocus = (payload) => { - let pixelParams = { + PixelCall({ e_entity: 'page', e_action: 'view', - e_data_json: { - profile_user_id: this.props.userId - }, - p_type: 'user_profile' - }; - PixelCall(pixelParams); + p_type: 'user_profile', + p_name: this.props.userId + }); }; goToSupporting = () => { @@ -165,7 +161,7 @@ class UserInfo extends React.PureComponent { if (this.isValidBioTag(this.props.userId, hashTag)) { let tagText = hashTag.replace("#", ""); return ( - + {prevText} + {prevText + hashTag} ) diff --git a/src/components/CommonComponents/UserProfileFlatList/index.js b/src/components/CommonComponents/UserProfileFlatList/index.js index 0923ed5d..16077e1a 100644 --- a/src/components/CommonComponents/UserProfileFlatList/index.js +++ b/src/components/CommonComponents/UserProfileFlatList/index.js @@ -24,7 +24,9 @@ import LinearGradient from "react-native-linear-gradient"; import CurrentUser from "../../../models/CurrentUser"; import DeleteVideo from "../DeleteVideo"; import Colors from '../../../theme/styles/Colors'; -import VideoThumbnailItem from '../VideoThumbnailItem'; +import CommonStyle from "../../../theme/styles/Common" +import VideoThumbnail from '../../CommonComponents/VideoThumbnail/VideoThumbnail'; +import DataContract from '../../../constants/DataContract'; class UserProfileFlatList extends PureComponent { @@ -130,7 +132,7 @@ class UserProfileFlatList extends PureComponent { let array = [...this.state.list]; // make a separate copy of the array array.splice(index, 1); this.setState({list: array}); - this.props.onDelete(array); + this.props.onDelete && this.props.onDelete(array); } } @@ -147,10 +149,11 @@ class UserProfileFlatList extends PureComponent { style={{width: (Dimensions.get('window').width - 6) / 2, margin: 1, position: 'absolute', top: 0, left: 0, zIndex: 1, alignItems: 'flex-end'}} > - {this.removeVideo(videoId , index )}} /> + {this.removeVideo(videoId , index )}} /> } - {this.onVideoClick(item, index)}}/> ); }; @@ -180,7 +183,7 @@ class UserProfileFlatList extends PureComponent { render(){ return( - + {this.listRef = ref } } ListHeaderComponent={this.listHeaderComponent()} diff --git a/src/components/CommonComponents/VideoAmoutStat.js b/src/components/CommonComponents/VideoAmoutStat.js deleted file mode 100644 index 69fc0f1b..00000000 --- a/src/components/CommonComponents/VideoAmoutStat.js +++ /dev/null @@ -1,68 +0,0 @@ -import React, { PureComponent } from 'react'; -import { View, Text } from 'react-native'; -import { connect } from 'react-redux'; - -import reduxGetter from '../../services/ReduxGetters'; - -import pricer from '../../services/Pricer'; - -import inlineStyles from '../Home/styles'; -import { TouchableWithoutFeedback } from 'react-native-gesture-handler'; -import multipleClickHandler from '../../services/MultipleClickHandler'; -import Utilities from "../../services/Utilities"; - - -const mapStateToProps = (state, ownProps) => { - return { - supporters: reduxGetter.getUserSupporters(ownProps.userId, state), - totalBt: reduxGetter.getVideoBt(ownProps.videoId, state) - }; -}; - -class VideoAmountStat extends PureComponent { - constructor(props) { - super(props); - } - - btToFiat(btAmount) { - const priceOracle = pricer.getPriceOracle(); - btAmount = priceOracle.fromDecimal(btAmount); - return (priceOracle && priceOracle.btToFiat(btAmount, 2)) || 0; - } - - onWrapperClick = (e) => { - this.props.onWrapperClick && this.props.onWrapperClick(); - }; - - render() { - return ( - this.onWrapperClick())} pointerEvents={'auto'}> - - {/*{*/} - {/**/} - {/*${`${ pricer.toDisplayAmount(this.btToFiat(this.props.totalBt))}`}{' '}RAISED*/} - {/**/} - {/*}*/} - { - - - {this.props.supporters || 0} - {Utilities.getSingularPluralText(this.props.supporters , "Supporter")} - - - - } - - - ); - } -} - -export default connect(mapStateToProps)(VideoAmountStat); diff --git a/src/components/CommonComponents/VideoPlayer/index.js b/src/components/CommonComponents/VideoPlayer/index.js index b67ab15f..56daa7c1 100644 --- a/src/components/CommonComponents/VideoPlayer/index.js +++ b/src/components/CommonComponents/VideoPlayer/index.js @@ -1,16 +1,16 @@ import React, { Component } from 'react'; -import {View,Image,TouchableOpacity, Text} from 'react-native'; -import VideoRowComponent from "../../UserVideoHistory/UserVideoHistoryRow"; +import UserVideoHistoryRow from "../../UserVideoHistory/UserVideoHistoryRow"; import TopStatus from "../../Home/TopStatus"; import deepGet from "lodash/get"; import PepoApi from "../../../services/PepoApi"; -import inlineStyles from './styles' -import historyBack from "../../../assets/user-video-history-back-icon.png"; -import video_not_available from '../../../assets/video-not-available.png'; import Utilities from '../../../services/Utilities'; -import CurrentUser from '../../../models/CurrentUser'; import reduxGetter from '../../../services/ReduxGetters'; -import Colors from "../../../theme/styles/Colors"; +import DeletedVideoInfo from '../DeletedVideoInfo'; +import CommonStyles from "../../../theme/styles/Common"; +import FlotingBackArrow from "../../CommonComponents/FlotingBackArrow"; +import { SafeAreaView } from "react-navigation"; +import { fetchVideo } from '../../../helpers/helpers'; +import DataContract from '../../../constants/DataContract'; class VideoPlayer extends Component { @@ -24,9 +24,10 @@ class VideoPlayer extends Component { constructor(props){ super(props); this.videoId = this.props.navigation.getParam('videoId'); + this.bubbleClickHandler = this.props.navigation.getParam('bubbleClickHandler'); this.state = { - userId : this.props.navigation.getParam('userId') || null, - isDeleted : reduxGetter.isVideoDeleted(this.videoId) + userId : reduxGetter.getVideoCreatorUserId(this.videoId) || null, + isDeleted : false }; this.refetchVideo(); this.isActiveScreen = true; @@ -54,10 +55,7 @@ class VideoPlayer extends Component { refetchVideo = () => { if (this.state.isDeleted) return; - new PepoApi(`/videos/${this.videoId}`) - .get() - .then((res) => { this.onRefetchVideo(res) }) - .catch((error) => {}); + fetchVideo( this.videoId , this.onRefetchVideo ); }; onRefetchVideo = ( res ) => { @@ -65,45 +63,34 @@ class VideoPlayer extends Component { this.setState({isDeleted: true}); return; } - const users = deepGet(res , "data.users") || {} , - userKeys = Object.keys(users) || [] , - userId = userKeys[0] || null; - if(userId){ - this.setState({ userId : userId}); + const video_details = deepGet(res, `data.${DataContract.videos.videoDetailsKey}`), + item = video_details[this.videoId]; + if( item ){ + this.setState({ + userId: item[DataContract.videos.creatorUserIdKey] + }) } }; - navigateToUserProfile = (e) => { - if (Utilities.checkActiveUser()) { - if (this.state.userId == CurrentUser.getUserId()) { - this.props.navigation.navigate('ProfileScreen'); - } else { - this.isActiveScreen = false; - this.props.navigation.push('UsersProfileScreen', { userId: this.state.userId }); - } - } - }; + getPixelDropData = () => { + return pixelParams = { + p_type: 'single_video', + p_name: this.videoId + }; + } render() { if(this.state.isDeleted){ - return - this.props.navigation.goBack()} style={inlineStyles.historyBackSkipFont}> - - - - Looks like the Video you were looking for isn’t available and might have been deleted by the creator! - + return }else{ return ( - + - - this.props.navigation.goBack()} style={inlineStyles.historyBackSkipFont}> - - - + + + ) } } diff --git a/src/components/CommonComponents/VideoReplyPlayer/index.js b/src/components/CommonComponents/VideoReplyPlayer/index.js new file mode 100644 index 00000000..3baf1618 --- /dev/null +++ b/src/components/CommonComponents/VideoReplyPlayer/index.js @@ -0,0 +1,120 @@ +import React, { PureComponent } from 'react'; +import TopStatus from "../../Home/TopStatus"; +import deepGet from "lodash/get"; +import PepoApi from "../../../services/PepoApi"; +import Utilities from '../../../services/Utilities'; +import reduxGetter from '../../../services/ReduxGetters'; +import DataContract from '../../../constants/DataContract'; +import DeletedVideoInfo from '../DeletedVideoInfo'; +import VideoReplyRow from '../../FullScreenReplyCollection/VideoReplyRow'; +import FlotingBackArrow from "../../CommonComponents/FlotingBackArrow"; +import CommonStyles from "../../../theme/styles/Common"; +import { SafeAreaView } from "react-navigation"; + +class VideoReplyPlayer extends PureComponent { + + static navigationOptions = ({navigation, navigationOptions}) => { + return { + headerBackTitle: null, + header: null + }; + }; + + constructor(props){ + super(props); + this.replyDetailId = this.props.navigation.getParam('replyDetailId'); + this.state = { + userId : reduxGetter.getReplyUserId( this.replyDetailId ) || null, + isLoading: true, + isDeleted : false + }; + this.fetchReply(); + this.isActiveScreen = true; + } + + componentDidMount(){ + this.willFocusSubscription = this.props.navigation.addListener('willFocus', (payload) => { + this.isActiveScreen = true ; + }); + + this.willBlurSubscription = this.props.navigation.addListener('willBlur', (payload) => { + this.isActiveScreen = false ; + }); + } + + componentWillUnmount(){ + this.onReplyFetch = () => {}; + this.willFocusSubscription && this.willFocusSubscription.remove(); + this.willBlurSubscription && this.willBlurSubscription.remove(); + } + + shouldPlay = () => { + return this.isActiveScreen; + }; + + fetchReply = () => { + if (this.state.isDeleted) return; + new PepoApi(DataContract.replies.getSingleVideoReplyApi(this.replyDetailId)) + .get() + .then((res) => { this.onReplyFetch(res) }) + .catch((error) => {}); + }; + + onReplyFetch = ( res ) => { + if(Utilities.isEntityDeleted(res)){ + this.setState({isDeleted: true}); + return; + } + const replyDetails = deepGet(res , `data.${DataContract.replies.replyDetailsKey}`) , + item = replyDetails[this.replyDetailId] + if(item){ + this.setState({ userId : item[DataContract.replies.creatorUserIdKey], isLoading : false}); + } + }; + + getPixelDropData = () => { + return pixelParams = { + e_entity: 'reply', + p_type: 'single_reply', + p_name: this.replyDetailId, + }; + } + + parentClickHandler =()=>{ + const parentVideoId = reduxGetter.getReplyParentVideoId(this.replyDetailId); + this.props.navigation.push('VideoPlayer', { + userId: this.state.userId, + videoId: parentVideoId + }); + } + + isActiveEntity = (fullVideoReplyId , item , index)=> { + let replyId = deepGet(item, `payload.${DataContract.replies.replyDetailIdKey}`) + return fullVideoReplyId == replyId; + } + + render() { + if(this.state.isDeleted){ + return + }else{ + return ( + + + {!this.state.isLoading && ( )} + + + ) + } + } +} + +export default VideoReplyPlayer ; diff --git a/src/components/CommonComponents/VideoReplyPlayer/styles.js b/src/components/CommonComponents/VideoReplyPlayer/styles.js new file mode 100644 index 00000000..00b62fe2 --- /dev/null +++ b/src/components/CommonComponents/VideoReplyPlayer/styles.js @@ -0,0 +1,43 @@ +import DefaultStyleGenerator from '../../../theme/styles/DefaultStyleGenerator'; +import {ifIphoneX} from "react-native-iphone-x-helper"; +import Colors from '../../../theme/styles/Colors'; + +let stylesMap = { + + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: Colors.snow + }, + + imgSizeSkipFont: { + width: 68.5, + height: 68.5 + }, + + desc:{ + fontSize: 22, + marginTop: 10, + textAlign: 'center', + paddingHorizontal: 25, + color: Colors.valhalla, + fontFamily: 'AvenirNext-Regular' + }, + + historyBackSkipFont:{ + ...ifIphoneX({ + top: 55, + }, { + top: 25, + }), + width: 29, + height: 34, + position: 'absolute', + left: 10, + alignItems: 'center', + justifyContent: 'center' + } +}; + +export default styles = DefaultStyleGenerator.generate(stylesMap); diff --git a/src/components/CommonComponents/VideoSupporterStat/Base.js b/src/components/CommonComponents/VideoSupporterStat/Base.js new file mode 100644 index 00000000..c4a566d5 --- /dev/null +++ b/src/components/CommonComponents/VideoSupporterStat/Base.js @@ -0,0 +1,51 @@ +import React, { PureComponent } from 'react'; +import { View, Text } from 'react-native'; + +import pricer from '../../../services/Pricer'; +import inlineStyles from '../../Home/styles'; +import { TouchableWithoutFeedback } from 'react-native-gesture-handler'; +import multipleClickHandler from '../../../services/MultipleClickHandler'; +import Utilities from "../../../services/Utilities"; +import CurrentUser from '../../../models/CurrentUser'; + +class Base extends PureComponent { + constructor(props) { + super(props); + } + + btToFiat(btAmount) { + const priceOracle = pricer.getPriceOracle(); + btAmount = priceOracle.fromDecimal(btAmount); + return (priceOracle && priceOracle.btToFiat(btAmount, 2)) || 0; + } + + navigateToUserProfile = () => { + if (Utilities.checkActiveUser()) { + if (this.props.userId == CurrentUser.getUserId()) { + this.props.navigation.navigate('ProfileScreen'); + } else { + this.props.navigation.push('UsersProfileScreen', { userId: this.props.userId }); + } + } + }; + + render() { + return ( + this.navigateToUserProfile())} pointerEvents={'auto'}> + + { + + + {this.props.supporters || 0} + {Utilities.getSingularPluralText(this.props.supporters , "Supporter")} + + + + } + + + ); + } +} + +export default Base; diff --git a/src/components/CommonComponents/VideoSupporterStat/VideoReplySupporterStat.js b/src/components/CommonComponents/VideoSupporterStat/VideoReplySupporterStat.js new file mode 100644 index 00000000..4a4c6f50 --- /dev/null +++ b/src/components/CommonComponents/VideoSupporterStat/VideoReplySupporterStat.js @@ -0,0 +1,16 @@ +import { connect } from 'react-redux'; + +import reduxGetter from '../../../services/ReduxGetters'; +import Base from './Base'; +import { withNavigation } from 'react-navigation'; + + +const mapStateToProps = (state, ownProps) => { + return { + supporters: reduxGetter.getUserSupporters(ownProps.userId, state), + totalBt: reduxGetter.getReplyBt(ownProps.entityId, state) + }; +}; + + +export default connect(mapStateToProps)( withNavigation( Base )); diff --git a/src/components/CommonComponents/VideoSupporterStat/VideoSupporterStat.js b/src/components/CommonComponents/VideoSupporterStat/VideoSupporterStat.js new file mode 100644 index 00000000..33817844 --- /dev/null +++ b/src/components/CommonComponents/VideoSupporterStat/VideoSupporterStat.js @@ -0,0 +1,16 @@ +import { connect } from 'react-redux'; + +import reduxGetter from '../../../services/ReduxGetters'; +import Base from './Base'; +import { withNavigation } from 'react-navigation'; + + +const mapStateToProps = (state, ownProps) => { + return { + supporters: reduxGetter.getUserSupporters(ownProps.userId, state), + totalBt: reduxGetter.getVideoBt(ownProps.entityId, state) + }; +}; + + +export default connect(mapStateToProps)( withNavigation ( Base) ); diff --git a/src/components/CommonComponents/VideoThumbnailItem/index.js b/src/components/CommonComponents/VideoThumbnail/Base.js similarity index 57% rename from src/components/CommonComponents/VideoThumbnailItem/index.js rename to src/components/CommonComponents/VideoThumbnail/Base.js index c9b39320..0395d6ec 100644 --- a/src/components/CommonComponents/VideoThumbnailItem/index.js +++ b/src/components/CommonComponents/VideoThumbnail/Base.js @@ -1,40 +1,32 @@ import {Dimensions, Image, Text, TouchableWithoutFeedback, View} from "react-native"; -import React from 'react'; +import React, { PureComponent } from 'react'; import FastImage from 'react-native-fast-image'; import Colors from "../../../theme/styles/Colors"; import LinearGradient from "react-native-linear-gradient"; import pepoWhiteIcon from "../../../assets/pepo-white-icon.png"; import inlineStyles from "./style"; -import reduxGetters from "../../../services/ReduxGetters"; -import AppConfig from "../../../constants/AppConfig"; import multipleClickHandler from '../../../services/MultipleClickHandler' -import Pricer from "../../../services/Pricer"; -import deepGet from 'lodash/get'; import ProfilePicture from "../../ProfilePicture"; -let getVideoBtAmount = (videoId) => { - return Pricer.displayAmountWithKFomatter( Pricer.getFromDecimal( reduxGetters.getVideoBt(videoId) ) ) ; -} -export default (props) => { - const videoId = deepGet(props, 'payload.video_id'), - userId = deepGet(props, 'payload.user_id') - userName = reduxGetters.getUserName(userId), - imageUrl = reduxGetters.getVideoImgUrl(videoId,null, AppConfig.userVideos.userScreenCoverImageWidth), - videoDesc =reduxGetters.getVideoDescription(reduxGetters.getVideoDescriptionId(videoId)); +class Base extends PureComponent { + constructor(props){ + super(props); + } - return { props.onVideoClick && props.onVideoClick(videoId, props.index );} )} + render(){ + return { this.props.onVideoClick && this.props.onVideoClick()} )} > { style={{width: (Dimensions.get('window').width - 6) / 2, margin: 1, position: 'absolute', bottom: 0, left: 0}} > - {videoDesc} + {this.props.videoDesc} - - @{userName} + + @{this.props.userName} - {getVideoBtAmount(videoId)} + {this.props.btAmount || 0} @@ -61,4 +53,8 @@ export default (props) => { -} \ No newline at end of file + } + +} + +export default Base; \ No newline at end of file diff --git a/src/components/CommonComponents/VideoThumbnail/ReplyThumbnail.js b/src/components/CommonComponents/VideoThumbnail/ReplyThumbnail.js new file mode 100644 index 00000000..a0b85fa5 --- /dev/null +++ b/src/components/CommonComponents/VideoThumbnail/ReplyThumbnail.js @@ -0,0 +1,30 @@ +import Base from "./Base"; +import React, { PureComponent } from 'react'; +import reduxGetters from '../../../services/ReduxGetters'; +import AppConfig from '../../../constants/AppConfig'; +import deepGet from "lodash/get"; +import Pricer from "../../../services/Pricer"; + +class ReplyThumbnail extends PureComponent { + constructor(props){ + super(props); + } + + getReplyStats(replyId) { + let replyBt = reduxGetters.getReplyBt(replyId); + return Pricer.getFromDecimal(replyBt) || 0; + } + + render(){ + const replyId = deepGet(this.props , "payload.reply_detail_id"), + userId = deepGet(this.props , "payload.user_id") , + videoId = reduxGetters.getReplyEntityId( replyId ), + userName = reduxGetters.getUserName(userId), + imageUrl = reduxGetters.getVideoImgUrl(videoId, null, AppConfig.userVideos.userScreenCoverImageWidth) , + videoDesc = reduxGetters.getVideoDescription(reduxGetters.getReplyDescriptionId(replyId)) , + replyBtAmount = this.getReplyStats(replyId) + ; + return ; + } +} +export default ReplyThumbnail; diff --git a/src/components/CommonComponents/VideoThumbnail/VideoThumbnail.js b/src/components/CommonComponents/VideoThumbnail/VideoThumbnail.js new file mode 100644 index 00000000..63d82e00 --- /dev/null +++ b/src/components/CommonComponents/VideoThumbnail/VideoThumbnail.js @@ -0,0 +1,29 @@ +import Base from "./Base"; +import React, { PureComponent } from 'react'; +import reduxGetters from '../../../services/ReduxGetters'; +import AppConfig from '../../../constants/AppConfig'; +import deepGet from "lodash/get"; +import Pricer from "../../../services/Pricer"; + +class VideoThumbnail extends PureComponent { + constructor(props){ + super(props); + } + + getVideoStats(videoId) { + let videoBt = reduxGetters.getVideoBt(videoId); + return Pricer.getFromDecimal(videoBt) || 0; + } + + render(){ + const videoId = deepGet(this.props , "payload.video_id"), + userId = deepGet(this.props , "payload.user_id") , + userName = reduxGetters.getUserName(userId), + imageUrl = reduxGetters.getVideoImgUrl(videoId, null, AppConfig.userVideos.userScreenCoverImageWidth) , + videoDesc = reduxGetters.getVideoDescription(reduxGetters.getVideoDescriptionId(videoId)) , + videoBtAmount = this.getVideoStats(videoId) + ; + return ; + } +} +export default VideoThumbnail; diff --git a/src/components/CommonComponents/VideoThumbnailItem/style.js b/src/components/CommonComponents/VideoThumbnail/style.js similarity index 100% rename from src/components/CommonComponents/VideoThumbnailItem/style.js rename to src/components/CommonComponents/VideoThumbnail/style.js diff --git a/src/components/CustomDrawerContent/index.js b/src/components/CustomDrawerContent/index.js index cb4b872d..52e41f9a 100644 --- a/src/components/CustomDrawerContent/index.js +++ b/src/components/CustomDrawerContent/index.js @@ -147,7 +147,6 @@ class CustomDrawerContent extends Component { }; initWallet = () => { - //TODO: Navigation should push instead of navigate this.props.navigation.navigate('WalletSettingScreen'); }; diff --git a/src/components/CustomTab/index.js b/src/components/CustomTab/index.js index af1acfa1..4d7cb08d 100644 --- a/src/components/CustomTab/index.js +++ b/src/components/CustomTab/index.js @@ -42,7 +42,7 @@ let refreshTimeOut = 0; function loginInFlow(navigation, tab) { let currentTabIndex = tab.navigationIndex; if (tab.rootStack === 'CaptureVideo') { - utilities.handleVideoUploadModal(previousTabIndex, navigation); + utilities.handleVideoUploadModal(previousTabIndex, navigation, {videoType: appConfig.videoTypes.post }); return; } if (currentTabIndex == undefined || currentTabIndex == null) return; @@ -52,8 +52,8 @@ function loginInFlow(navigation, tab) { } else if (utilities.getLastChildRoutename(navigation.state) !== tab.childStack) { try { navigation.dispatch(StackActions.popToTop()); - } catch { - console.log('Catch error'); + } catch(e) { + console.log('Catch error', e); } } else { clearTimeout(refreshTimeOut); diff --git a/src/components/CustomTab/styles.js b/src/components/CustomTab/styles.js index e818becb..80f9bbbc 100644 --- a/src/components/CustomTab/styles.js +++ b/src/components/CustomTab/styles.js @@ -16,10 +16,6 @@ let stylesMap = { shadowRadius: 1, elevation: 5, alignItems: 'center' - // borderColor: 'rgba(0, 0, 0, 0.25)', - // borderWidth: 1, - // borderBottomWidth: 0, - // elevation: 8 }, tapArea:{ minHeight: 55, @@ -28,17 +24,9 @@ let stylesMap = { tabElementSkipFont: { alignSelf: 'center', marginHorizontal: 20, - // marginVertical: 10, height: 35, width: 35 } - // tabElementFriendsSkipFont: { - // alignSelf: 'center', - // marginHorizontal: 20, - // marginVertical: 10, - // height: 22, - // width: 35 - // } }; export default styles = DefaultStyleGenerator.generate(stylesMap); diff --git a/src/components/Email/styles.js b/src/components/Email/styles.js index 83ca4db7..1e899a1f 100644 --- a/src/components/Email/styles.js +++ b/src/components/Email/styles.js @@ -4,8 +4,9 @@ import DefaultStyleGenerator from '../../theme/styles/DefaultStyleGenerator'; let stylesMap = { container: { flex: 1, - marginVertical: 20, - marginHorizontal: 10 + backgroundColor: Colors.white, + paddingVertical: 20, + paddingHorizontal: 10 }, resend: { color: Colors.valhalla, diff --git a/src/components/FanVideoDetails/VideoDescription.js b/src/components/FanVideoDetails/VideoDescription.js index 8e6f1930..f6403627 100644 --- a/src/components/FanVideoDetails/VideoDescription.js +++ b/src/components/FanVideoDetails/VideoDescription.js @@ -1,5 +1,5 @@ import React, { PureComponent } from 'react'; -import { View, Text } from 'react-native'; +import {View, Text} from 'react-native'; import inlineStyles from './styles'; import TagsInput from '../CommonComponents/TagsInput'; @@ -20,25 +20,22 @@ class VideoDescription extends PureComponent { return ( ); } } -const SearchResultRowComponent = (props) => ( - - {`#${props.val}`} - -); - export default VideoDescription; diff --git a/src/components/FanVideoDetails/VideoLink.js b/src/components/FanVideoDetails/VideoLink.js index 3dcd20f5..81429203 100644 --- a/src/components/FanVideoDetails/VideoLink.js +++ b/src/components/FanVideoDetails/VideoLink.js @@ -1,7 +1,8 @@ import React, { Component } from 'react'; -import { TextInput } from 'react-native'; +import { TextInput, TouchableOpacity, Image } from 'react-native'; import styles from './styles'; +import CloseIcon from '../../assets/modal-cross-icon.png'; class VideoLink extends Component { constructor(props) { @@ -22,6 +23,10 @@ class VideoLink extends Component { ); }; + onLinkCrossIconClick = () =>{ + this.onChangeValue(""); + } + render() { return ( @@ -31,11 +36,14 @@ class VideoLink extends Component { ellipsizeMode={'tail'} returnKeyType="done" returnKeyLabel="Done" - placeholder="Add a link (optional)" + placeholder="Add a link to your video" onChangeText={this.onChangeValue} value={this.state.value} autoCapitalize={'none'} /> + {this.state.value ? + + : } ); } diff --git a/src/components/FanVideoDetails/index.js b/src/components/FanVideoDetails/index.js index 6a2de143..7fd63a39 100644 --- a/src/components/FanVideoDetails/index.js +++ b/src/components/FanVideoDetails/index.js @@ -27,17 +27,29 @@ import { upsertRecordedVideo } from '../../actions'; import multipleClickHandler from '../../services/MultipleClickHandler'; import { getBottomSpace } from 'react-native-iphone-x-helper'; import { StackActions } from 'react-navigation'; +import PepoApi from "../../services/PepoApi"; +import DataContract from "../../constants/DataContract"; +import NumberInput from "../CommonComponents/NumberInput"; +import NumberFormatter from "../../helpers/NumberFormatter"; +import pricer from '../../services/Pricer'; +import PepoPinkIcon from '../../assets/pepo-tx-icon.png'; +import AppConfig from '../../constants/AppConfig' + +//TODO setParams dont use const mapStateToProps = (state, ownProps) => { return { + balance : state.balance, recordedVideo: reduxGetter.getRecordedVideo() }; }; +const DEFAUT_BT_VALUE = AppConfig.default_bt_amt; + class FanVideoDetails extends Component { static navigationOptions = ({ navigation }) => { return { - title: 'Post', + title: 'Details', headerStyle: { backgroundColor: Colors.white, borderBottomWidth: 0, @@ -67,20 +79,39 @@ class FanVideoDetails extends Component { static saveToRedux = (navigation) => { let desc = navigation.state.params.videoDesc, - link = navigation.state.params.videoLink; - Store.dispatch(upsertRecordedVideo({ video_desc: desc, video_link: link })); + link = navigation.state.params.videoLink, + amount = navigation.state.params.replyAmount; + Store.dispatch(upsertRecordedVideo({ video_desc: desc, video_link: link, reply_amount:amount })); }; constructor(props) { super(props); this.videoDesc = this.props.recordedVideo.video_desc; this.videoLink = this.props.recordedVideo.video_link; + this.replyAmount = this.props.recordedVideo.reply_amount ? this.props.recordedVideo.reply_amount : pricer.getToDecimal(DEFAUT_BT_VALUE); + + this.priceOracle = pricer.getPriceOracle(); + this.numberFormatter = new NumberFormatter(); + this.max = props.balance; + this.min = 0; this.state = { viewStyle: { paddingBottom: 10 }, - error: null + error: null, + amountError: null, + linkError: null, + descError : null, + usdVal : this.weiToUSD(this.replyAmount), + replyAmt : null, + buttonText: 'Post', + isSuggestOpen: false }; + + if (! props.recordedVideo.video_type ) { + let videoType = this.props.navigation.getParam('video_type'); + Store.dispatch(upsertRecordedVideo({ video_type: videoType })); + } } _keyboardShown = (e) => { let keyboardHeight = deepGet(e, 'endCoordinates.height') || 350; @@ -110,10 +141,13 @@ class FanVideoDetails extends Component { componentDidMount() { this.props.navigation.setParams({ videoDesc: this.props.recordedVideo.video_desc, - videoLink: this.props.recordedVideo.video_link + videoLink: this.props.recordedVideo.video_link, + replyAmount: this.props.recordedVideo.reply_amount }); + } + componentWillUnmount() { this.keyboardWillShowListener.remove(); this.keyboardWillHideListener.remove(); @@ -122,16 +156,75 @@ class FanVideoDetails extends Component { } enableStartUploadFlag = () => { - // if (!this.validLink()) return; - utilities.saveItem(`${CurrentUser.getUserId()}-accepted-camera-t-n-c`, true); - Store.dispatch( - upsertRecordedVideo({ video_desc: this.videoDesc, video_link: this.videoLink, do_upload: true }) - ); - this.props.navigation.dispatch(StackActions.popToTop()); - this.props.navigation.dispatch(StackActions.popToTop()); - this.props.navigation.navigate('HomeScreen'); + + this.clearErrors(); + this.setState({buttonText: 'Posting...'}); + this.validateData().then((res) => { + utilities.saveItem(`${CurrentUser.getUserId()}-accepted-camera-t-n-c`, true); + Store.dispatch( + upsertRecordedVideo({ video_desc: this.videoDesc, + video_link: this.videoLink, + reply_amount: this.replyAmount, + do_upload: true }) + ); + this.props.navigation.dispatch(StackActions.popToTop()); + this.props.navigation.dispatch(StackActions.popToTop()); + this.setState({buttonText: 'Post'}); + this.props.navigation.navigate('HomeScreen'); + }).catch((err)=>{ + // show error on UI. + this.setState({buttonText: 'Post'}); + this.showError(err); + }); }; + clearErrors = () => { + this.setState({ + linkError: null, + descError: null, + amountError: null, + error: null + }); + }; + + showError = (err) => { + if(!err.error_data) return; + for (let error of err.error_data){ + switch (error.parameter) { + case "link": + this.setState({linkError: error.msg}); + break; + case "video_description": + this.setState({descError: error.msg}); + break; + case "per_reply_amount_in_wei": + this.setState({amountError: error.msg}); + break; + } + } + }; + + validateData = () => { + let params = {}; + params['video_description'] = this.videoDesc; + params['link'] = this.videoLink; + params['per_reply_amount_in_wei'] = this.replyAmount; + return new Promise((resolve, reject) => { + new PepoApi(DataContract.replies.validatePost) + .post(params) + .then((res)=>{ + if (res && res.success){ + return resolve(res); + } else { + return reject(res.err); + } + }).catch((err)=>{ + return reject(err); + }) + }); + }; + + onChangeDesc = (desc) => { this.videoDesc = desc; //Done for the value to be accessible in static navigationOptions @@ -175,14 +268,63 @@ class FanVideoDetails extends Component { return true; }; + + onErrorCallBack = ( errMsg ) =>{ + this.setState({ + amountError : errMsg + }) + } + + replyAmountChange = ( value )=>{ + this.replyAmount = pricer.getToDecimal(value); + if (isNaN(this.replyAmount)) this.replyAmount = ""; + + let formattedUsdVal = this.weiToUSD( pricer.getToDecimal(value) ); + //Done for the value to be accessible in static navigationOptions + this.props.navigation.setParams({ + replyAmount: this.replyAmount + }); + this.setState({ + usdVal : formattedUsdVal, + replyAmt : value + }); + }; + weiToUSD = (value ) =>{ + let weiToBt = pricer.getFromDecimal(value, 2), + usdVal = this.priceOracle.btToFiat(weiToBt), + formattedUsdVal = this.numberFormatter.getFormattedValue( usdVal ); + return formattedUsdVal; + } + + pointerEventForInPageElement() { + if ( this.state.isSuggestOpen ) { + return "none"; + } + return "auto"; + } + + onSuggestionsPanelClose = () => { + this.setState({ + isSuggestOpen: false + }); + } + + onSuggestionsPanelOpen = () => { + this.setState({ + isSuggestOpen: true + }); + } + render() { - let imageUrl = this.props.recordedVideo.cover_image; + let imageUrl = this.props.recordedVideo.cover_image, + value = pricer.getFromDecimal(this.replyAmount, 2) ; return ( @@ -197,13 +339,38 @@ class FanVideoDetails extends Component { - + - + {this.state.descError } + + Link + {this.state.linkError } + + Set Price for replies + + + + + ${this.state.usdVal} + + + + {this.state.amountError } - + {this.state.error} { this.enableStartUploadFlag(); })} diff --git a/src/components/FanVideoDetails/styles.js b/src/components/FanVideoDetails/styles.js index d7a9a900..58e17fb2 100644 --- a/src/components/FanVideoDetails/styles.js +++ b/src/components/FanVideoDetails/styles.js @@ -31,6 +31,26 @@ let stylesMap = { paddingHorizontal: 15, paddingVertical: 20 }, + videoLinkItem: { + flexDirection: 'row', + borderBottomWidth: 1, + borderColor: '#ccd3cd', + paddingBottom: 16, + alignItems: 'center', + paddingHorizontal: 12, + zIndex: -1 + }, + videoAmountItem: { + flexDirection: 'row', + borderBottomWidth: 1, + borderColor: '#ccd3cd', + + alignItems: 'center', + paddingBottom: 16, + paddingHorizontal: 12, + zIndex: -1 + + }, videoDescription: { color: 'rgba(42, 41, 59, 0.8)', // flex: 1, @@ -43,18 +63,31 @@ let stylesMap = { marginLeft: 10, marginTop: 0, padding: 0, + paddingTop: 0, + position: 'relative', paddingLeft: 0, height: 100 }, - suggestionText: { - fontWeight: 'bold', - color: Colors.midNightblue, - fontSize: 18 + dropDownStyle: { + marginLeft: -90, + width: Dimensions.get('window').width, + shadowColor: '#000', + shadowOffset: { width: -1, height: 1 }, + shadowOpacity: 0.2, + elevation: 1 }, - suggestionTextWrapper: { - marginTop: 20 + linkText: { color: Colors.softBlue, flex: 3, paddingVertical: 0}, + replyAmtWrapper : { + flex:1, + flexDirection:"row", + justifyContent:'space-between', + alignItems:'center' }, - linkText: { color: Colors.softBlue, flex: 1 } + errorStyle: { + width: 0, + flex: 0, + height: 0 + } }; export default styles = DefaultStyleGenerator.generate(stylesMap); diff --git a/src/components/FanVideoReplyDetails/TouchableButton.js b/src/components/FanVideoReplyDetails/TouchableButton.js new file mode 100644 index 00000000..2460e629 --- /dev/null +++ b/src/components/FanVideoReplyDetails/TouchableButton.js @@ -0,0 +1,23 @@ +import React from 'react'; +import { Image, Text, TouchableOpacity } from 'react-native'; +import Theme from '../../theme/styles'; +import source from '../../assets/reply-with-pepo.png' + +const TouchableButton = ({ TouchableStyles, TextStyles, textBeforeImage, textAfterImage, onPress, imgDimension, buttonText, disabled = false }) => ( + + {buttonText ? + {buttonText}: + + {textBeforeImage} + + {textAfterImage} + + } + +); + +export default TouchableButton; diff --git a/src/components/FanVideoReplyDetails/index.js b/src/components/FanVideoReplyDetails/index.js new file mode 100644 index 00000000..9fa80f81 --- /dev/null +++ b/src/components/FanVideoReplyDetails/index.js @@ -0,0 +1,400 @@ +import React, { Component } from 'react'; +import { + Image, + ImageBackground, + TouchableOpacity, + View, + Text, + Keyboard, + ScrollView +} from 'react-native'; +import deepGet from 'lodash/get'; +import CurrentUser from '../../models/CurrentUser'; +import styles from './styles'; +import VideoDescription from '../FanVideoDetails/VideoDescription'; +import BackArrow from '../CommonComponents/BackArrow'; +import Colors from '../../theme/styles/Colors'; +import playIcon from '../../assets/play_icon.png'; +import TouchableButton from './TouchableButton'; +import Theme from '../../theme/styles'; +import LinearGradient from 'react-native-linear-gradient'; +import VideoLink from '../FanVideoDetails/VideoLink'; +import reduxGetter from '../../services/ReduxGetters'; +import { connect } from 'react-redux'; +import Store from '../../store'; +import { upsertRecordedVideo } from '../../actions'; +import multipleClickHandler from '../../services/MultipleClickHandler'; +import { getBottomSpace } from 'react-native-iphone-x-helper'; +import { StackActions } from 'react-navigation'; +import PepoApi from "../../services/PepoApi"; +import DataContract from "../../constants/DataContract"; +import pricer from "../../services/Pricer"; +import {ensureDeivceAndSession, ON_USER_CANCLLED_ERROR_MSG} from "../../helpers/TransactionHelper"; +import Toast from "../../theme/components/NotificationToast"; +import { WORKFLOW_CANCELLED_MSG } from '../../services/OstSdkErrors'; +import AppConfig from '../../constants/AppConfig'; + +//TODO setParams dont use + +const PROCESSING_TEXT = 'Processing...'; + +const mapStateToProps = (state, ownProps) => { + return { + recordedVideo: reduxGetter.getRecordedVideo() + }; +}; + +class FanVideoReplyDetails extends Component { + static navigationOptions = ({ navigation }) => { + return { + title: 'Post', + headerBackTitle: null, + headerStyle: { + backgroundColor: Colors.white, + borderBottomWidth: 0, + shadowColor: '#000', + shadowOffset: { + width: 0, + height: 1 + }, + shadowOpacity: 0.1, + shadowRadius: 3 + }, + headerTitleStyle: { + fontFamily: 'AvenirNext-Medium' + }, + headerLeft: ( + { + FanVideoReplyDetails.saveToRedux(navigation); + navigation.goBack(); + })} + > + + + ) + }; + }; + + static saveToRedux = (navigation) => { + let desc = navigation.state.params.videoDesc, + link = navigation.state.params.videoLink; + Store.dispatch(upsertRecordedVideo({ video_desc: desc, video_link: link })); + }; + + constructor(props) { + super(props); + this.videoDesc = props.recordedVideo.video_desc || ''; + this.videoLink = props.recordedVideo.video_link || ''; + if (! props.recordedVideo.video_type ){ + // It means + this.replyObject = this.props.navigation.getParam('reply_obj'); + let videoType = this.props.navigation.getParam('video_type'); + Store.dispatch(upsertRecordedVideo({ reply_obj: this.replyObject, video_type: videoType })); + } else { + this.replyObject = props.recordedVideo.reply_obj; + } + + this.state = { + viewStyle: { + paddingBottom: 10 + }, + error: null, + linkError: null, + descError : null, + buttonText: null, + isSuggestOpen: false + }; + } + _keyboardShown = (e) => { + let keyboardHeight = deepGet(e, 'endCoordinates.height') || 350; + this.setState({ + viewStyle: { + paddingBottom: keyboardHeight - (15 + getBottomSpace([true])) + } + }); + }; + + _keyboardHidden = () => { + this.setState({ + viewStyle: { + paddingBottom: 10 + } + }); + }; + + componentWillMount() { + this.keyboardWillShowListener = Keyboard.addListener('keyboardWillShow', this._keyboardShown.bind(this)); + this.keyboardWillHideListener = Keyboard.addListener('keyboardWillHide', this._keyboardHidden.bind(this)); + + this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardShown.bind(this)); + this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardHidden.bind(this)); + } + + componentDidMount() { + this.props.navigation.setParams({ + videoDesc: this.props.recordedVideo.video_desc, + videoLink: this.props.recordedVideo.video_link + }); + this.setState({buttonText: this.setButtonText()}); + } + + setButtonText = () => { + if (! this.checkIfReplyChargeable()){ + return 'Reply'; + } + return null; + }; + + + componentWillUnmount() { + this.keyboardWillShowListener.remove(); + this.keyboardWillHideListener.remove(); + this.keyboardDidShowListener.remove(); + this.keyboardDidHideListener.remove(); + } + + checkIfReplyChargeable = () => { + let btAmount = this.getAmountToSend(); + return this.replyObject.isChargeble && btAmount != '0'; + }; + + + ensureSession = () => { + return new Promise((resolve, reject)=> { + let btAmount = this.getAmountToSend(); + const btInDecimal = pricer.getToDecimal(btAmount); + if (! this.checkIfReplyChargeable()) { + return resolve(); + } + ensureDeivceAndSession(CurrentUser.getOstUserId(), btInDecimal, (device) => { + this._deviceUnauthorizedCallback(device); + reject(); + }, (errorMessage, success) => { + if ( success ) { + return resolve(); + } else { + if (ON_USER_CANCLLED_ERROR_MSG !== errorMessage && WORKFLOW_CANCELLED_MSG !== errorMessage) { + Toast.show({ + text: errorMessage, + icon: 'error' + }); + } + return reject() + } + }); + }); + + // check If session active + }; + + + _deviceUnauthorizedCallback = (device) => { + this.props.navigation.push('AuthDeviceDrawer', { device }); + }; + + + giveUploadConsent = () => { + Store.dispatch( + upsertRecordedVideo({ video_desc: this.videoDesc, video_link: this.videoLink, do_upload: true }) + ); + const popAction = StackActions.pop(1); + this.props.navigation.dispatch(popAction); + this.props.navigation.dispatch(popAction); + }; + + + + + enableStartUploadFlag = () => { + this.clearErrors(); + //todo @mayur button text change to validating or similar + let buttonText = this.state.buttonText; + this.setState({buttonText: PROCESSING_TEXT}); + this.validateData().then((res) => { + let videoOwnerId = this.replyObject.replyReceiverUserId; + if (videoOwnerId === CurrentUser.getUserId()){ + this.giveUploadConsent(); + return; + } + this.ensureSession().then(() => { + this.giveUploadConsent(); + }).catch(()=> { + }); + this.setState({buttonText: buttonText}); + }).catch((err)=>{ + // show error on UI. + this.setState({buttonText: buttonText}); + this.showError(err); + }) ; + }; + + + showError = (err) => { + if(!err.error_data) return; + for (let error of err.error_data){ + switch (error.parameter) { + case "link": + this.setState({linkError: error.msg}); + break; + case "video_description": + this.setState({descError: error.msg}); + break; + } + } + const status = deepGet(err , "code") || ""; + if(status.toLowerCase() == AppConfig.beKnownErrorCodeMaps.validateUploadError){ + this.setState({error: err.msg}); + } + }; + + + clearErrors = () => { + this.setState({ + linkError: null, + descError: null, + error: null + }); + }; + + + validateData = () => { + let params = {}; + //todo : @mayur put strings in data contract file + params['video_description'] = this.videoDesc; + params['link'] = this.videoLink; + + params['parent_kind'] = 'video'; + params['parent_id'] = this.replyObject.replyReceiverVideoId; + + return new Promise((resolve, reject) => { + new PepoApi(DataContract.replies.validateReply) + .post(params) + .then((res)=>{ + if (res && res.success){ + return resolve(res); + } else { + return reject(res.err); + } + }) + .catch((err)=>{ + return reject(err); + }) + }); + }; + + onChangeDesc = (desc) => { + this.videoDesc = desc; + //Done for the value to be accessible in static navigationOptions + this.props.navigation.setParams({ + videoDesc: desc + }); + }; + + onChangeLink = (link) => { + this.videoLink = link; + //Done for the value to be accessible in static navigationOptions + this.props.navigation.setParams({ + videoLink: link + }); + }; + + setError = (error) => { + this.setState({ + error + }); + }; + + + getAmountToSend = () => { + let amount = this.replyObject.amountToSendWithReply ; + return pricer.getToBT(pricer.getFromDecimal(amount), 2); + }; + + + pointerEventForInPageElement() { + if ( this.state.isSuggestOpen ) { + return "none"; + } + return "auto"; + }; + + onSuggestionsPanelClose = () => { + this.setState({ + isSuggestOpen: false + }); + }; + + onSuggestionsPanelOpen = () => { + this.setState({ + isSuggestOpen: true + }); + }; + + render() { + let imageUrl = this.props.recordedVideo.cover_image; + return ( + + + + { + FanVideoReplyDetails.saveToRedux(this.props.navigation); + this.props.navigation.goBack(); + })} + style={{ height: 100 }} + > + + + + + + + + {this.state.descError } + + Link + + + {this.state.linkError } + + + {this.state.error} + + { + this.enableStartUploadFlag(); + })} + /> + + + + ); + } +} + +export default connect(mapStateToProps)(FanVideoReplyDetails); diff --git a/src/components/FanVideoReplyDetails/styles.js b/src/components/FanVideoReplyDetails/styles.js new file mode 100644 index 00000000..e792ac12 --- /dev/null +++ b/src/components/FanVideoReplyDetails/styles.js @@ -0,0 +1,79 @@ +import DefaultStyleGenerator from '../../theme/styles/DefaultStyleGenerator'; +import Colors from '../../theme/styles/Colors'; +import { Dimensions } from 'react-native'; +import { Header } from 'react-navigation-stack'; + +import { getStatusBarHeight, getBottomSpace, isIphoneX } from 'react-native-iphone-x-helper'; + +const safeAreaHeight = getStatusBarHeight() + getBottomSpace([true]); + +let stylesMap = { + container: { + backgroundColor: '#fff', + flex: 1, + justifyContent: 'space-between', + height: Dimensions.get('window').height - safeAreaHeight - Header.HEIGHT - 55 + }, + posterImageSkipFont: { + aspectRatio: 3 / 4, + height: 100, + justifyContent: 'center' + }, + playIconSkipFont: { + height: 14, + width: 14, + alignSelf: 'center' + }, + videoDescriptionItem: { + flexDirection: 'row', + borderBottomWidth: 1, + borderColor: '#ccd3cd', + paddingHorizontal: 15, + paddingVertical: 20 + }, + videoDescription: { + color: 'rgba(42, 41, 59, 0.8)', + // flex: 1, + flexWrap: 'wrap', + fontFamily: 'AvenirNext-Regular', + borderColor: 'transparent', + borderWidth: 0, + borderRadius: 0, + fontWeight: '300', + marginLeft: 10, + marginTop: 0, + padding: 0, + paddingTop: 0, + position: 'relative', + paddingLeft: 0, + height: 100 + }, + videoLinkItem: { + flexDirection: 'row', + borderBottomWidth: 1, + borderColor: '#ccd3cd', + paddingBottom: 16, + alignItems: 'center', + paddingHorizontal: 12, + zIndex: -1 + }, + dropDownStyle: { + marginLeft: -90, + width: Dimensions.get('window').width, + shadowColor: '#000', + shadowOffset: { width: -1, height: 1 }, + shadowOpacity: 0.2, + elevation: 1 + }, + suggestionText: { + color: Colors.midNightblue, + fontSize: 16, + fontFamily: 'AvenirNext-Regular' + }, + suggestionTextWrapper: { + marginVertical: 9 + }, + linkText: { color: Colors.softBlue, flex: 3, paddingVertical: 0} +}; + +export default styles = DefaultStyleGenerator.generate(stylesMap); diff --git a/src/components/FullScreenReplyCollection/BaseVideoReplyRow.js b/src/components/FullScreenReplyCollection/BaseVideoReplyRow.js new file mode 100644 index 00000000..13f31018 --- /dev/null +++ b/src/components/FullScreenReplyCollection/BaseVideoReplyRow.js @@ -0,0 +1,191 @@ +import React, { PureComponent } from 'react'; +import { View, TouchableOpacity} from 'react-native'; +import FanVideo from "../VideoWrapper/FanVideo"; +import ReportVideo from "../CommonComponents/ReportVideo"; +import BottomReplyBar from "../CommonComponents/BottomReplyBar"; +import PepoApi from '../../services/PepoApi'; +import deepGet from 'lodash/get'; + +import inlineStyles from './styles'; + +import ReplyPepoTxBtn from '../PepoTransactionButton/ReplyPepoTxBtn'; +import VideoReplySupporterStat from '../CommonComponents/VideoSupporterStat/VideoReplySupporterStat'; + +import ReplyVideoBottomStatus from '../BottomStatus/ReplyVideoBottomStatus'; +import DataContract from '../../constants/DataContract'; +import ReduxGetters from '../../services/ReduxGetters'; +import CommonStyle from "../../theme/styles/Common"; +import assignIn from 'lodash/assignIn'; +import InvertedReplyList from "../CommonComponents/InvertedReplyThumbnailList"; + +import AppConfig from "../../constants/AppConfig"; +import ProfilePicture from "../ProfilePicture"; +import multipleClickHandler from '../../services/MultipleClickHandler'; +import { fetchVideo } from '../../helpers/helpers'; +import ReplyShareIcon from '../CommonComponents/ShareIcon/ReplyShare'; + +class BaseVideoReplyRow extends PureComponent { + constructor(props) { + super(props); + this.state = { + parentVideoId : ReduxGetters.getReplyParentVideoId( props.replyDetailId ), + parentUserId : ReduxGetters.getReplyParentUserId( props.replyDetailId ) + } + this.onParentClickDelegate = this.props.parentClickHandler || this.defaultParentClickHandler; + } + + componentDidMount(){ + if(this.props.doRender && this.state.parentVideoId && !this.state.parentUserId ){ + this.fetchParentVideo = fetchVideo(this.state.parentVideoId, this.onParentVideoFetch , null , this.onParentVideoFetchComplete); + } + } + + componentDidUpdate(prevProps){ + if(!this.fetchParentVideo && this.props.doRender && this.props.doRender !== prevProps.doRender && !this.state.parentUserId ){ + fetchVideo(this.state.parentVideoId, this.onParentVideoFetch , null , this.onParentVideoFetchComplete); + } + } + + componentWillUnmount(){ + this.onParentVideoFetch = () => {}; + this.onParentVideoFetchComplete = () => {}; + } + + onParentProfileIconClick() { + if(this.state.parentVideoId){ + this.onParentClickDelegate(); + } + } + + onParentVideoFetchComplete() { + this.fetchParentVideo = null; + } + + onParentVideoFetch = (res) => { + const video_details = deepGet(res, `data.${DataContract.videos.videoDetailsKey}`), + item = video_details[this.state.parentVideoId]; + if( item ){ + this.setState({ + parentUserId: item[DataContract.videos.creatorUserIdKey] + }) + } + } + + refetchVideoReply = () => { + new PepoApi(`/replies/${this.props.replyDetailId}`) + .get() + .then((res) => {}) + .catch((error) => {}); + }; + + getPixelDropData = () => { + const parentData = this.props.getPixelDropData(); + const pixelParams = { + e_entity: 'reply', + parent_video_id : this.state.parentVideoId, + p_name: this.state.parentVideoId, + reply_detail_id :this.props.replyDetailId + }; + return assignIn({}, pixelParams, parentData); + } + + defaultParentClickHandler(){ + this.props.navigation.goBack(null); + } + + _renderInvertedFlatList = () => { + if( this.state.parentVideoId && this.props.isActive ){ + return ( + + + + ) + } + return null; + } + + render() { + const videoId = ReduxGetters.getReplyEntityId(this.props.replyDetailId); + return ( + + + + + + + {!!videoId && !!this.props.userId && ( + + + + + {this._renderInvertedFlatList()} + + + + + {this.onParentProfileIconClick()})}> + + + + + + + + + + + + + + )} + + + + + + ); + } +} + +BaseVideoReplyRow.defaultProps = { + getPixelDropData: function(){ + console.warn("getPixelDropData props is mandatory for Video component"); + return {}; + }, + paginationService : null, + currentIndex: 0 + }; + +export default BaseVideoReplyRow diff --git a/src/components/FullScreenReplyCollection/NoPendantsVideoReplyRow.js b/src/components/FullScreenReplyCollection/NoPendantsVideoReplyRow.js new file mode 100644 index 00000000..a2325106 --- /dev/null +++ b/src/components/FullScreenReplyCollection/NoPendantsVideoReplyRow.js @@ -0,0 +1,16 @@ +import BaseVideoReplyRow from "./BaseVideoReplyRow"; +import { withNavigation } from "react-navigation"; + +class NoPendantsVideoReplyRow extends BaseVideoReplyRow { + constructor(props){ + super(props); + } + + _renderInvertedFlatList =() => { + return null; + } +} + +NoPendantsVideoReplyRow.defaultProps = BaseVideoReplyRow.defaultProps; + +export default withNavigation( NoPendantsVideoReplyRow ); diff --git a/src/components/FullScreenReplyCollection/VideoReplyRow.js b/src/components/FullScreenReplyCollection/VideoReplyRow.js new file mode 100644 index 00000000..5a606b04 --- /dev/null +++ b/src/components/FullScreenReplyCollection/VideoReplyRow.js @@ -0,0 +1,14 @@ + + +import BaseVideoReplyRow from "./BaseVideoReplyRow"; +import { withNavigation } from "react-navigation"; + +class VideoReplyRow extends BaseVideoReplyRow { + constructor(props) { + super(props); + } +} + +VideoReplyRow.defaultProps = BaseVideoReplyRow.defaultProps; + +export default withNavigation( VideoReplyRow ) diff --git a/src/components/FullScreenReplyCollection/index.js b/src/components/FullScreenReplyCollection/index.js new file mode 100644 index 00000000..98563d5e --- /dev/null +++ b/src/components/FullScreenReplyCollection/index.js @@ -0,0 +1,281 @@ +import React , {PureComponent} from "react"; +import {FlatList ,View } from "react-native"; +import FloatingBackArrow from "../CommonComponents/FlotingBackArrow"; +import deepGet from "lodash/get"; + +import Pagination from "../../services/Pagination"; +import VideoReplyRow from "./VideoReplyRow"; +import entityHelper from "../../helpers/EntityHelper"; +import DataContract from "../../constants/DataContract"; +import CommonStyle from "../../theme/styles/Common"; +import { SafeAreaView } from "react-navigation"; +import ReplyHelper from "../../helpers/ReplyHelper"; +import TopStatus from "../Home/TopStatus"; +import InvertedReplyList from "../CommonComponents/InvertedReplyThumbnailList"; +import NoPendantsVideoReplyRow from "./NoPendantsVideoReplyRow"; +import Utilities from "../../services/Utilities"; + +const maxVideosThreshold = 3; + +class FullScreenReplyCollection extends PureComponent{ + + static navigationOptions = (props) => { + return { + headerBackTitle: null, + header: null + }; + }; + + constructor(props){ + super(props); + this.setVideoPagination(); + this.paginationEvent = this.getVideoPagination().event; + this.currentIndex = this.props.navigation.getParam("currentIndex") || 0; + this.parentClickHandler = this.props.navigation.getParam("parentClickHandler"); + this.isScrolled = false ; + this.willFocusSubscription = null ; + this.flatlistRef = null; + + this.state = { + list : this.getVideoPagination().getResults(), + activeIndex: this.currentIndex, + refreshing : false, + loadingNext: false + }; + this.isActiveScreen = true; + } + + getBaseUrl(){ + return this.props.navigation.getParam("baseUrl"); + } + + getPassedFetchServices(){ + return this.props.navigation.getParam("fetchServices") + } + + setVideoPagination(){ + let fetchService = this.getPassedFetchServices(); + fetchService = fetchService.cloneWithData(); + this.fullPagePagination = new Pagination( this.getBaseUrl(), null , fetchService); + } + + getVideoPagination(){ + return this.fullPagePagination; + } + + componentDidMount(){ + this.paginationEvent.on("onBeforeRefresh" , this.beforeRefresh.bind(this) ); + this.paginationEvent.on("onRefresh" , this.onRefresh.bind(this) ); + this.paginationEvent.on("onRefreshError" , this.onRefreshError.bind(this) ); + this.paginationEvent.on("onBeforeNext" , this.beforeNext.bind(this) ); + this.paginationEvent.on("onNext" , this.onNext.bind(this) ); + this.paginationEvent.on("onNextError" , this.onNextError.bind(this) ); + + //This is an hack for reset scroll for flatlist. Need to debug a bit more. + this.willFocusSubscription = this.props.navigation.addListener('willFocus', (payload) => { + const offset = this.state.activeIndex > 0 ? CommonStyle.fullScreen.height * this.state.activeIndex : 0 ; + this.flatlistRef && this.flatlistRef.scrollToOffset({offset: offset , animated: false}); + this.isActiveScreen = true ; + }); + + this.willBlurSubscription = this.props.navigation.addListener('willBlur', (payload) => { + this.isActiveScreen = false ; + }); + + //If there is no getPassedFetchServices passed that means its a fresh view. + //So load data + if(!this.getPassedFetchServices()){ + this.refresh(); + } + } + + componentWillUnmount(){ + this.paginationEvent.removeListener('onBeforeRefresh'); + this.paginationEvent.removeListener('onRefresh'); + this.paginationEvent.removeListener('onRefreshError'); + this.paginationEvent.removeListener('onBeforeNext'); + this.paginationEvent.removeListener('onNext'); + this.paginationEvent.removeListener('onNextError'); + this.willFocusSubscription && this.willFocusSubscription.remove(); + this.willBlurSubscription && this.willBlurSubscription.remove(); + } + + shouldPlay = () => { + return this.isActiveScreen; + }; + + beforeRefresh = ( ) => { + this.setState({ refreshing : true }); + } + + onRefresh = ( res ) => { + let paginationService = this.getVideoPagination(); + let resultList = paginationService.getResults(); + this.setState({ refreshing : false , list : resultList }); + } + + onRefreshError = ( error ) => { + this.setState({ refreshing : false }); + } + + beforeNext =() => { + this.isScrolled = false; + this.setState({ loadingNext : true }); + } + + onNext = ( res ) => { + let paginationService = this.getVideoPagination(); + let resultList = paginationService.getResults(); + this.setState({ loadingNext : false , list : resultList }); + } + + onNextError = ( error ) => { + this.setState({ loadingNext : false }); + } + + getNext = () => { + // if(!this.isScrolled) return; + this.getVideoPagination().getNext(); + } + + refresh = () => { + this.getVideoPagination().refresh(); + } + + _keyExtractor = (item, index) => { + return `id_${item.id}`; + } + + _renderItem = ({ item, index }) => { + if(entityHelper.isVideoReplyEntity( item )){ + if(entityHelper.isReplyVideoTypeEntity(item)){ + return this._renderVideoReplyRow( item, index ); + } + } + }; + + getPixelDropData = ( replyDetailId ) => { + return () => { + return { + e_entity: 'reply', + p_type: 'video_reply' + }; + } + } + + _renderVideoReplyRow(item, index){ + let userId = deepGet(item,'payload.user_id'), + replyDetailId = deepGet(item,`payload.${DataContract.replies.replyDetailIdKey}`); + return ; + } + + childClickHandler = ( index, item )=> { + ReplyHelper.updateEntitySeen( item ); + this.scrollToIndex( index ); + } + + scrollToIndex = ( index )=>{ + this.setActiveIndex( index, () => { + this.flatlistRef.scrollToIndex({index: index}); + }); + } + + onViewableItemsChanged = (data) => { + let item = deepGet(data, 'viewableItems[0].item'); + item && ReplyHelper.updateEntitySeen( item ); + let currentIndex = deepGet(data, 'viewableItems[0].index'); + if ( "number" === typeof currentIndex ) { + this.currentIndex = currentIndex; + } + } + + setActiveIndex( index, callback ) { + if( typeof index === "number"){ + this.currentIndex = index; + } + this.setState({ activeIndex: this.currentIndex }, callback); + } + + onMomentumScrollEndCallback = () => { + this.setActiveIndex(); + }; + + onMomentumScrollBeginCallback = () => { + this.isScrolled = true; + } + + onScrollToIndexFailed =( info) => { + console.log("======onScrollToIndexFailed=====" , info ); + } + + getItemLayout= (data, index) => { + return {length: CommonStyle.fullScreen.height, offset: CommonStyle.fullScreen.height * index, index} ; + } + + closeVideo = () => { + this.navigateBack(); + }; + + navigateBack() { + this.props.navigation.goBack(); + } + + onScrollToTop = () => { + this.setActiveIndex(); + } + + render() { + return ( + + + + + + + + {this.flatlistRef = ref }} + onEndReachedThreshold={7} + onViewableItemsChanged={this.onViewableItemsChanged} + onMomentumScrollEnd={this.onMomentumScrollEndCallback} + onMomentumScrollBegin={this.onMomentumScrollBeginCallback} + renderItem={this._renderItem} + style={[CommonStyle.fullScreen , {backgroundColor: "#000"}]} + showsVerticalScrollIndicator={false} + onScrollToTop={this.onScrollToTop} + initialScrollIndex={this.state.activeIndex} + getItemLayout={this.getItemLayout} + onScrollToIndexFailed={this.onScrollToIndexFailed} + /> + + + ); + } + +} + +export default FullScreenReplyCollection ; diff --git a/src/components/FullScreenReplyCollection/styles.js b/src/components/FullScreenReplyCollection/styles.js new file mode 100644 index 00000000..555f80e3 --- /dev/null +++ b/src/components/FullScreenReplyCollection/styles.js @@ -0,0 +1,132 @@ +import { Dimensions, StatusBar, NativeModules } from 'react-native'; +import DefaultStyleGenerator from '../../theme/styles/DefaultStyleGenerator'; +import Colors from '../../theme/styles/Colors'; +import { ifIphoneX, getBottomSpace , getStatusBarHeight} from 'react-native-iphone-x-helper'; + +import NotchHelper from "../../helpers/NotchHelper"; +import { HEADER_HEIGHT } from '../../theme/constants'; +const {width, height} = Dimensions.get('window'); +const statusBarHeight = StatusBar.currentHeight || 20; + + +let RNDeviceInfo = NativeModules.RNDeviceInfo; +let modalDeviceName = RNDeviceInfo.model === "Redmi Note 7 Pro" && RNDeviceInfo.brand === "xiaomi"; +let btmSpace = modalDeviceName ? 5 : 0; + +let stylesMap = { + fullScreen: { + width: width, + ...ifIphoneX( + { + height: height - HEADER_HEIGHT - getStatusBarHeight(true) + }, + { + height: + NotchHelper.hasNotch() + ? height + statusBarHeight - btmSpace - HEADER_HEIGHT + : height - statusBarHeight - HEADER_HEIGHT + } + ) + }, + touchablesBtns: { + alignItems: 'flex-end', + marginBottom: -10, + zIndex: 1, + flexDirection: 'row' + }, + invertedList: { + marginRight: 'auto', + minWidth: '20%', + marginBottom: 40 + }, + pepoTxCount: { + fontSize: 18, + color: Colors.white, + alignSelf: 'center', + marginTop: 3, + marginBottom: 5 + }, + txElem: { + marginBottom: 20 + }, + bottomContainer: { + width: width, + position: 'absolute', + bottom: 0 + }, + bottomBg: { + backgroundColor: 'rgba(0, 0, 0, 0.6)', + borderTopLeftRadius: 20, + minHeight: height * 0.05, + paddingHorizontal: 12 + }, + handle: { + fontSize: 15, + paddingBottom: 3, + color: Colors.white, + fontFamily: 'AvenirNext-DemiBold', + fontWeight: '700' + }, + bottomBgTxt: { + color: Colors.white + }, + raisedSupported: { + backgroundColor: Colors.wildWatermelon2, + borderTopLeftRadius: 25, + borderBottomRightRadius: 25, + paddingHorizontal: 8, + minWidth: 120, + height: 40, + alignItems: 'center', + justifyContent: 'center', + marginRight: 10 + }, + raisedSupportedTxt: { + color: Colors.white, + fontSize: 14, + fontFamily: 'AvenirNext-DemiBold' + }, + btnText: { + color: Colors.white + }, + iconWrapper: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + height: 60, + width: 60 + }, + iconSkipFont: { + height: 20, + width: 20 + }, + headerStyles: { + backgroundColor: Colors.white, + borderBottomWidth: 0, + shadowColor: '#000', + shadowOffset: { + width: 0, + height: 1 + }, + shadowOpacity: 0.1, + shadowRadius: 3, + height: HEADER_HEIGHT + }, + headerText:{ + fontWeight: '600' + }, + headerSubText:{ + fontSize: 12 + }, + listContainer: { + // width: width, + position: 'absolute', + bottom: height * 0.05 + 50, + left: 10, + zIndex: 9, + alignSelf: 'flex-start', + height: height - 200 + } +}; + +export default styles = DefaultStyleGenerator.generate(stylesMap); diff --git a/src/components/FullScreenVideoCollection/FullScreeVideoRow.js b/src/components/FullScreenVideoCollection/FullScreeVideoRow.js deleted file mode 100644 index 0dbf318a..00000000 --- a/src/components/FullScreenVideoCollection/FullScreeVideoRow.js +++ /dev/null @@ -1,114 +0,0 @@ -import React, { PureComponent } from 'react'; -import { View, TouchableOpacity, Image } from 'react-native'; -import { withNavigation } from 'react-navigation'; -import VideoWrapper from '../Home/VideoWrapper'; -import ShareIcon from "../CommonComponents/ShareIcon"; -import ReportVideo from "../CommonComponents/ReportVideo"; -import PepoApi from '../../services/PepoApi'; -import TransactionPepoButton from '../Home/TransactionPepoButton'; -import deepGet from 'lodash/get'; -import CurrentUser from '../../models/CurrentUser'; - -import BottomStatus from '../Home/BottomStatus'; -import VideoAmountStat from '../CommonComponents/VideoAmoutStat'; -import ShareVideo from '../../services/shareVideo'; -import inlineStyles from './styles'; - -import utilities from '../../services/Utilities'; -import OptionsIcon from '../../assets/options_self_video.png'; - - -class FullScreeVideoRow extends PureComponent { - constructor(props) { - super(props); - console.log("this.props", this.props); - this.userId = deepGet(this.props.payload, 'user_id'); - this.videoId = deepGet(this.props.payload, 'video_id'); - - - - } - - refetchVideo = () => { - new PepoApi(`/videos/${this.videoId}`) - .get() - .then((res) => {}) - .catch((error) => {}); - }; - - - - shareVideo = () => { - let shareVideo = new ShareVideo(this.videoId); - shareVideo.perform(); - }; - - navigateToUserProfile = (e) => { - if (utilities.checkActiveUser()) { - if (this.userId == CurrentUser.getUserId()) { - this.props.navigation.navigate('ProfileScreen'); - } else { - this.props.navigation.push('UsersProfileScreen', { userId: this.userId }); - } - } - }; - - onDescriptionClick = ( tapEntity , tapText ) => { - if (!tapEntity) { - return; - } - - if( tapEntity.kind === 'tags'){ - this.props.navigation.push('VideoTags', { - "tagId": tapEntity.id - }); - } - - } - - render() { - return ( - - - - {!!this.videoId && !!this.userId && ( - - - - - - - - - - - - - - - )} - - ); - } -} - -export default withNavigation(FullScreeVideoRow); diff --git a/src/components/FullScreenVideoCollection/FullScreenVideoRow.js b/src/components/FullScreenVideoCollection/FullScreenVideoRow.js new file mode 100644 index 00000000..bbd181b1 --- /dev/null +++ b/src/components/FullScreenVideoCollection/FullScreenVideoRow.js @@ -0,0 +1,110 @@ +import React, { PureComponent } from 'react'; +import { View , Dimensions} from 'react-native'; +import { withNavigation } from 'react-navigation'; +import FanVideo from '../VideoWrapper/FanVideo'; +import ReportVideo from "../CommonComponents/ReportVideo"; +import PepoApi from '../../services/PepoApi'; +import deepGet from 'lodash/get'; + +import VideoBottomStatus from '../BottomStatus/VideoBottomStatus'; +import inlineStyles from './styles'; + +import ReplyIcon from '../CommonComponents/ReplyIcon'; +import PepoTxBtn from '../PepoTransactionButton/PepoTxBtn'; +import VideoSupporterStat from '../CommonComponents/VideoSupporterStat/VideoSupporterStat'; +import DataContract from '../../constants/DataContract'; +import BottomReplyBar from '../CommonComponents/BottomReplyBar'; +import assignIn from 'lodash/assignIn'; + + +import AppConfig from "../../constants/AppConfig"; +import CommonStyle from "../../theme/styles/Common"; +import BubbleList from "../CommonComponents/BubbleList"; +import VideoShareIcon from '../CommonComponents/ShareIcon/VideoShare'; + + +const AREA = AppConfig.MaxDescriptionArea; +const height = AREA / Dimensions.get('window').width + 20; + +class FullScreeVideoRow extends PureComponent { + constructor(props) { + super(props); + this.userId = deepGet(this.props.payload, 'user_id'); + this.videoId = deepGet(this.props.payload, 'video_id'); + } + + refetchVideo = () => { + new PepoApi(`/videos/${this.videoId}`) + .get() + .then((res) => {}) + .catch((error) => {}); + }; + + getPixelDropData = () => { + const parentData = this.props.getPixelDropData(); + const pixelParams = { e_entity: 'video' , video_id : this.videoId}; + return assignIn({}, pixelParams, parentData); + } + + render() { + return ( + + + + + + + {!!this.videoId && !!this.userId && ( + + + + + + + + + + + + + + + + {p_type: 'feed'}} + /> + + )} + + + + ); + } +} + +FullScreeVideoRow.defaultProps = { + getPixelDropData: function(){ + console.warn("getPixelDropData props is mandatory for Video component"); + return {}; + } + }; + + +export default withNavigation(FullScreeVideoRow); diff --git a/src/components/FullScreenVideoCollection/index.js b/src/components/FullScreenVideoCollection/index.js index 4028438f..e8297b13 100644 --- a/src/components/FullScreenVideoCollection/index.js +++ b/src/components/FullScreenVideoCollection/index.js @@ -1,12 +1,17 @@ import React , {PureComponent} from "react"; -import {FlatList , View , TouchableOpacity, Image} from "react-native"; +import {FlatList} from "react-native"; import deepGet from "lodash/get"; + import reduxGetters from "../../services/ReduxGetters"; import Pagination from "../../services/Pagination"; -import FullScreeVideoRow from "./FullScreeVideoRow"; -import inlineStyles from "./styles"; -import historyBack from '../../assets/user-video-history-back-icon.png'; +import FullScreenVideoRow from "./FullScreenVideoRow"; +import FloatingBackArrow from "../CommonComponents/FlotingBackArrow"; import TopStatus from "../Home/TopStatus"; +import CommonStyle from "../../theme/styles/Common"; +import entityHelper from '../../helpers/EntityHelper'; +import DataContract from "../../constants/DataContract"; +import VideoReplyRow from "../FullScreenReplyCollection/VideoReplyRow"; +import { SafeAreaView } from "react-navigation"; const maxVideosThreshold = 3; @@ -24,12 +29,13 @@ class FullScreenVideoCollection extends PureComponent{ this.setVideoPagination(); this.paginationEvent = this.getVideoPagination().event; this.currentIndex = this.props.navigation.getParam("currentIndex"); + this.tagId = this.props.navigation.getParam("tagId"); this.isScrolled = false ; this.willFocusSubscription = null ; this.flatlistRef = null; this.state = { - list : this.getVideoPagination().getList(), + list : this.getVideoPagination().getResults(), activeIndex: this.currentIndex, refreshing : false, loadingNext: false @@ -63,7 +69,7 @@ class FullScreenVideoCollection extends PureComponent{ //This is an hack for reset scroll for flatlist. Need to debug a bit more. this.willFocusSubscription = this.props.navigation.addListener('willFocus', (payload) => { - const offset = this.state.activeIndex > 0 ? inlineStyles.fullScreen.height * this.state.activeIndex : 0 ; + const offset = this.state.activeIndex > 0 ? CommonStyle.fullScreen.height * this.state.activeIndex : 0 ; this.flatlistRef && this.flatlistRef.scrollToOffset({offset: offset , animated: false}); this.isActiveScreen = true ; }); @@ -93,7 +99,7 @@ class FullScreenVideoCollection extends PureComponent{ } onRefresh = ( res ) => { - this.setState({ refreshing : false , list : this.getVideoPagination().getList() }); + this.setState({ refreshing : false , list : this.getVideoPagination().getResults() }); } onRefreshError = ( error ) => { @@ -106,7 +112,7 @@ class FullScreenVideoCollection extends PureComponent{ } onNext = ( res ) => { - this.setState({ loadingNext : false , list : this.getVideoPagination().getList() }); + this.setState({ loadingNext : false , list : this.getVideoPagination().getResults() }); } onNextError = ( error ) => { @@ -124,21 +130,86 @@ class FullScreenVideoCollection extends PureComponent{ } _keyExtractor = (item, index) => { - return `id_${item}`; + let keyStr = `id_${item.id}`; + return keyStr; }; _renderItem = ({ item, index }) => { const payload = reduxGetters.getTagsVideoPayload(item); console.log("payload", payload); - return ; + if(entityHelper.isVideoReplyEntity( item )){ + if(entityHelper.isReplyVideoTypeEntity(item)){ + return this._renderVideoReplyRow( item, index ); + } + } else if( entityHelper.isVideoEntity( item )) { + return this._renderVideoRow( item, index); + } + }; + getPixelDropData = () => { + return { + e_entity: 'video', + p_type: 'tag', + p_name: this.tagId, + }; + } + + getReplyPixelDrop = () => { + return { + e_entity: 'reply', + p_type: 'tag', + p_name: this.tagId, + }; + } + + parentClickHandler =(replyDetailId)=>{ + const parentVideoId = reduxGetters.getReplyParentVideoId(replyDetailId), + parentUserId = reduxGetters.getReplyParentUserId(replyDetailId ); + this.props.navigation.push('VideoPlayer', { + userId: parentUserId, + videoId: parentVideoId + }); + } + + isActiveEntity = (fullVideoReplyId , item , index)=> { + let replyId = deepGet(item, `payload.${DataContract.replies.replyDetailIdKey}`) + return fullVideoReplyId == replyId; + } + + _renderVideoReplyRow(item, index){ + let userId = deepGet(item,'payload.user_id'), + replyDetailId = deepGet(item,`payload.${DataContract.replies.replyDetailIdKey}`), + rowKey = this._keyExtractor(item, index) + ; + return {this.parentClickHandler(replyDetailId)}} + isActiveEntity={this.isActiveEntity} + /> ; + } + + _renderVideoRow( item, index ){ + let rowKey = this._keyExtractor(item, index); + return ; + } + onViewableItemsChanged = (data) => { - this.currentIndex = deepGet(data, 'viewableItems[0].index') || 0; + const currentIndex = deepGet(data, 'viewableItems[0].index'); + if("number" === typeof currentIndex ){ + this.currentIndex = currentIndex; + } } setActiveIndex() { @@ -158,7 +229,7 @@ class FullScreenVideoCollection extends PureComponent{ } getItemLayout= (data, index) => { - return {length: inlineStyles.fullScreen.height, offset: inlineStyles.fullScreen.height * index, index} ; + return {length: CommonStyle.fullScreen.height, offset: CommonStyle.fullScreen.height * index, index} ; } closeVideo = () => { @@ -176,9 +247,10 @@ class FullScreenVideoCollection extends PureComponent{ render() { return ( - - {this.props.navigation.getParam("showBalanceFlier") && } + + {this.props.navigation.getParam("showBalanceFlyer") && } - - - - + + ); } diff --git a/src/components/FullScreenVideoCollection/styles.js b/src/components/FullScreenVideoCollection/styles.js index b59db44e..23f8a34f 100644 --- a/src/components/FullScreenVideoCollection/styles.js +++ b/src/components/FullScreenVideoCollection/styles.js @@ -33,25 +33,26 @@ let stylesMap = { height: "100%" }, touchablesBtns: { - alignSelf: 'flex-end', - marginBottom: -15, - zIndex: 1 + alignItems: 'flex-end', + marginBottom: -10, + zIndex: 1, + flexDirection: 'row' + }, + invertedList: { + marginRight: 'auto', + minWidth: '20%', + marginBottom: 40 }, pepoTxCount: { fontSize: 18, color: Colors.white, alignSelf: 'center', marginTop: 3, - marginBottom: 15 + marginBottom: 5 }, txElem: { marginBottom: 20 }, - bottomContainer: { - width: width, - position: 'absolute', - bottom: 0 - }, bottomBg: { backgroundColor: 'rgba(0, 0, 0, 0.6)', borderTopLeftRadius: 20, @@ -118,7 +119,21 @@ let stylesMap = { height :10, width:23, zIndex: 1, - } + }, + bottomContainer: { + width: width, + position: 'absolute', + bottom: 0 + }, + listContainer: { + // width: width, + position: 'absolute', + bottom: height * 0.05 + 50, + left: 10, + zIndex: 9, + alignSelf: 'flex-start', + height: height - 200 + } }; export default styles = DefaultStyleGenerator.generate(stylesMap); diff --git a/src/components/Home/AuthDeviceDrawer.js b/src/components/Home/AuthDeviceDrawer.js index cee77513..b0122d27 100644 --- a/src/components/Home/AuthDeviceDrawer.js +++ b/src/components/Home/AuthDeviceDrawer.js @@ -10,6 +10,7 @@ import infoIcon from '../../assets/toast_error.png'; import Theme from "../../theme/styles"; import modalCross from '../../assets/modal-cross-icon.png'; import LinearGradient from 'react-native-linear-gradient'; +import CommonStyle from '../../theme/styles/Common'; @@ -128,7 +129,7 @@ export default class AuthDeviceDrawer extends PureComponent { let config = this._getViewConfig(); return( - + diff --git a/src/components/Home/BottomStatus.js b/src/components/Home/BottomStatus.js deleted file mode 100644 index 307a4928..00000000 --- a/src/components/Home/BottomStatus.js +++ /dev/null @@ -1,133 +0,0 @@ -import React, { PureComponent } from 'react'; -import { View, Text } from 'react-native'; -import { connect } from 'react-redux'; -import { withNavigation } from 'react-navigation'; - -import inlineStyles from './styles'; -import { TouchableWithoutFeedback, TouchableOpacity } from 'react-native-gesture-handler'; -import reduxGetter from '../../services/ReduxGetters'; - -import multipleClickHandler from '../../services/MultipleClickHandler'; -import InAppBrowser from '../../services/InAppBrowser'; - -const mapStateToProps = (state, ownProps) => { - return { - userName: reduxGetter.getUserName(ownProps.userId, state), - name: reduxGetter.getName(ownProps.userId, state), - description: reduxGetter.getVideoDescription(reduxGetter.getVideoDescriptionId(ownProps.videoId, state), state), - link: reduxGetter.getVideoLink(reduxGetter.getVideoLinkId(ownProps.videoId, state), state), - supporters: reduxGetter.getVideoSupporters(ownProps.videoId), - totalBt: reduxGetter.getVideoBt(ownProps.videoId, state) - }; -}; - -class BottomStatus extends PureComponent { - constructor(props) { - super(props); - this.videoDescriptionId = reduxGetter.getVideoDescriptionId(this.props.videoId); - } - - onWrapperClick = (e) => { - this.props.onWrapperClick && this.props.onWrapperClick(); - }; - - onLinkClick = () => { - InAppBrowser.openBrowser(this.props.link); - }; - - onTagPressed = (tag) => { - let entity = reduxGetter.getTappedIncludesEntity(this.videoDescriptionId, tag); - this.props.onDescriptionClick && this.props.onDescriptionClick(entity, tag); - }; - - isValidTag(videoId, tappedText) { - let entity = reduxGetter.getTappedIncludesEntity(videoId, tappedText); - return !!entity - } - - render() { - - let processingString = this.props.description; - let hasTagArray = processingString.match(/#\w+/g) || []; - - return ( - - - - this.onWrapperClick())} pointerEvents={'auto'}> - - {`@${this.props.userName}`} - - - {this.props.description ? ( - - - {(hasTagArray.map((hashTag) => { - - let tagLocation = processingString.search(hashTag); - let prevText = processingString.slice(0, tagLocation); - - processingString = processingString.slice(tagLocation + hashTag.length); - - if (this.isValidTag(this.videoDescriptionId, hashTag)) { - let tagText = hashTag.replace("#", ""); - return ( - - {prevText} - { - this.onTagPressed(hashTag) - }}> - # - {tagText} - - - - ); - } else { - return ( - - {prevText + hashTag} - - ) - } - })) - } - {processingString} - - - ) : ( - - ) - } - - - {this.props.link ? ( - { - this.onLinkClick(); - })} - pointerEvents={'auto'} - > - - {this.props.link.replace(/^(?:https?:\/\/)?(?:www\.)?/i, '')} - - - ) : ( - - )} - - ); - }; -} - -export default connect(mapStateToProps)(withNavigation(BottomStatus)); diff --git a/src/components/Home/HomeFeedRow.js b/src/components/Home/HomeFeedRow.js index 7814e81a..07e1f7c6 100644 --- a/src/components/Home/HomeFeedRow.js +++ b/src/components/Home/HomeFeedRow.js @@ -1,20 +1,21 @@ import React, { PureComponent } from 'react'; -import { View, TouchableOpacity, Image, TouchableWithoutFeedback } from 'react-native'; +import {View, Dimensions} from 'react-native'; import { withNavigation } from 'react-navigation'; -import VideoWrapper from './VideoWrapper'; -import ShareIcon from "../CommonComponents/ShareIcon"; +import FanVideo from '../VideoWrapper/FanVideo'; import PepoApi from '../../services/PepoApi'; import reduxGetter from '../../services/ReduxGetters'; -import TransactionPepoButton from './TransactionPepoButton'; -import CurrentUser from '../../models/CurrentUser'; +import assignIn from 'lodash/assignIn'; -import BottomStatus from './BottomStatus'; -import VideoAmountStat from '../CommonComponents/VideoAmoutStat'; +import VideoBottomStatus from '../BottomStatus/VideoBottomStatus'; import inlineStyles from './styles'; -import utilities from '../../services/Utilities'; import ReportVideo from "../CommonComponents/ReportVideo"; +import ReplyIcon from '../CommonComponents/ReplyIcon'; +import PepoTxBtn from '../PepoTransactionButton/PepoTxBtn'; +import VideoSupporterStat from '../CommonComponents/VideoSupporterStat/VideoSupporterStat'; +import DataContract from '../../constants/DataContract'; +import VideoShareIcon from '../CommonComponents/ShareIcon/VideoShare'; class HomeFeedRow extends PureComponent { constructor(props) { @@ -36,80 +37,61 @@ class HomeFeedRow extends PureComponent { .catch((error) => {}); }; - navigateToTransactionScreen = (e) => { - if (utilities.checkActiveUser() && CurrentUser.isUserActivated(true)) { - this.props.navigation.push('TransactionScreen', { - toUserId: this.userId, - videoId: reduxGetter.getHomeFeedVideoId(this.props.feedId), - requestAcknowledgeDelegate: this.refetchFeed - }); - } - }; - - navigateToUserProfile = (e) => { - if (utilities.checkActiveUser()) { - if (this.userId == CurrentUser.getUserId()) { - this.props.navigation.navigate('ProfileScreen'); - } else { - this.props.navigation.push('UsersProfileScreen', { userId: this.userId }); - } - } - }; - - onDescriptionClick = ( tapEntity , tapText ) => { - if (utilities.checkActiveUser()) { - if (!tapEntity) { - return; - } - - if( tapEntity.kind === 'tags'){ - this.props.navigation.push('VideoTags', { - "tagId": tapEntity.id - }); - } - - } - }; + getPixelDropData = () => { + const parentData = this.props.getPixelDropData && this.props.getPixelDropData() || {}; + const pixelParams = { + e_entity: 'video', + p_type: 'feed', + video_id: this.videoId, + }; + return assignIn({}, pixelParams, parentData); + } render() { return ( - - + + - - - - + + + + - - + + + - + + + - + ); } } + export default withNavigation(HomeFeedRow); diff --git a/src/components/Home/HomeScreen.js b/src/components/Home/HomeScreen.js index fde383cd..cb02c59c 100644 --- a/src/components/Home/HomeScreen.js +++ b/src/components/Home/HomeScreen.js @@ -16,6 +16,7 @@ import {navigateTo} from "../../helpers/navigateTo"; import { LoadingModal } from '../../theme/components/LoadingModalCover'; import Colors from "../../theme/styles/Colors"; import utilities from "../../services/Utilities"; +import reduxGetter from '../../services/ReduxGetters'; const mapStateToProps = (state) => { return { @@ -165,6 +166,16 @@ class HomeScreen extends Component { updateFlatList && updateFlatList([]); } + + getUploadingText = () => { + let videoType = reduxGetter.getRecordedVideoType(); + if (videoType === 'post'){ + return "Uploading Video"; + } else if (videoType === 'reply'){ + return "Posting reply"; + } + }; + beforeRefresh = () => { Pricer.getBalance(); }; @@ -190,7 +201,7 @@ class HomeScreen extends Component { ), left: 10 }} - displayText="Uploading Video" + displayText={this.getUploadingText()} extendDirection="right" extend={true} id={2} diff --git a/src/components/Home/VideoList.js b/src/components/Home/VideoList.js index b57e6fa3..e8d044bb 100644 --- a/src/components/Home/VideoList.js +++ b/src/components/Home/VideoList.js @@ -84,6 +84,7 @@ class VideoList extends PureComponent { onScrollToTop={this.onScrollToTop} onScrollToIndexFailed={this.onScrollToIndexFailed} ref={(ref) => (this.flatlistRef = ref)} + /> ); } diff --git a/src/components/Home/styles.js b/src/components/Home/styles.js index d46318af..bdabc21f 100644 --- a/src/components/Home/styles.js +++ b/src/components/Home/styles.js @@ -33,15 +33,21 @@ let stylesMap = { height: "100%" }, touchablesBtns: { - alignSelf: 'flex-end', - marginBottom: -15, - zIndex: 1 + alignItems: 'flex-end', + marginBottom: -10, + zIndex: 1, + flexDirection: 'row' + }, + invertedList: { + marginRight: 'auto', + minWidth: '20%', + marginBottom: 40 }, pepoTxCount: { fontSize: 18, color: Colors.white, alignSelf: 'center', - marginBottom: 8, + marginBottom: 5, textShadowColor: 'rgba(0, 0, 0, 0.65)', textShadowOffset: {width: 1, height: 1}, textShadowRadius: 1, @@ -55,6 +61,15 @@ let stylesMap = { position: 'absolute', bottom: 0 }, + listContainer: { + // width: width, + position: 'absolute', + bottom: height * 0.05 + 50, + left: 10, + zIndex: 9, + // alignSelf: 'flex-start', + //height: height - 200 + }, bottomBg: { backgroundColor: 'rgba(0, 0, 0, 0.6)', borderTopLeftRadius: 20, diff --git a/src/components/InviteCode/index.js b/src/components/InviteCode/index.js index 05cc1c56..4758879e 100644 --- a/src/components/InviteCode/index.js +++ b/src/components/InviteCode/index.js @@ -18,6 +18,7 @@ import Utilities from '../../services/Utilities'; import AppConfig from '../../constants/AppConfig'; import { upsertInviteCode } from '../../actions'; import Store from '../../store'; +import CommonStyle from '../../theme/styles/Common'; const mapStateToProps = ({ invite_code }) => ({ invite_code }); @@ -183,7 +184,7 @@ class InviteCodeScreen extends React.Component { render() { return ( - + diff --git a/src/components/Invites/index.js b/src/components/Invites/index.js index 29759ae8..b2867f0c 100644 --- a/src/components/Invites/index.js +++ b/src/components/Invites/index.js @@ -1,11 +1,10 @@ import React, { Component } from 'react'; import { Text, SafeAreaView } from 'react-native'; -import styles from './styles'; import Colors from '../../theme/styles/Colors'; import BackArrow from '../CommonComponents/BackArrow'; import InvitesList from './InvitesList'; -import CurrentUser from '../../models/CurrentUser'; +import CommonStyle from "../../theme/styles/Common"; class Invites extends Component { static navigationOptions = (options) => { @@ -46,7 +45,7 @@ class Invites extends Component { render() { return ( - + { TwitterAuthService = imports.default; }); -const mapStateToProps = ({ login_popover }) => ({ - show: login_popover.show -}); +const mapStateToProps = ({ login_popover }) => { + return { + show: login_popover.show, + isTwitterConnecting: login_popover.isTwitterConnecting + } +}; const btnPreText = 'Connect with Twitter'; const btnPostText = 'Connecting...'; @@ -32,39 +36,43 @@ class loginPopover extends React.Component { constructor(props) { super(props); this.state = { - disableLoginBtn: false, - btnText: btnPreText + disableLoginBtn: false }; - this.isTwitterConnecting = false; } componentWillUnmount() { this.state.disableLoginBtn = false; - this.state.btnText = btnPreText; - this.isTwitterConnecting = false; + } componentDidUpdate(prevProps) { - if (this.props.show && this.props.show !== prevProps.show) { - this.setState({ disableLoginBtn: false, btnText: btnPreText }); - this.isTwitterConnecting = false; + if ( this.props.isTwitterConnecting && this.props.isTwitterConnecting !== prevProps.isTwitterConnecting ) { + this.setState({ disableLoginBtn: true }); + } else if (this.props.show && this.props.show !== prevProps.show) { + this.setState({ disableLoginBtn: false }); } } onSignUp = () => { - this.setState({ disableLoginBtn: true, btnText: btnPostText }); - this.isTwitterConnecting = true; + this.setState({ disableLoginBtn: true }); TwitterAuthService.signUp(); }; //Use this function if needed to handle hardware back handling for android. closeModal = () => { - if (!this.isTwitterConnecting) { + if (!this.props.isTwitterConnecting) { Store.dispatch(hideLoginPopover()); } return true; }; + getConnectBtnText = () => { + if ( this.props.isTwitterConnecting || this.state.disableLoginBtn) { + return btnPostText; + } + return btnPreText; + } + render() { return ( @@ -75,7 +83,6 @@ class loginPopover extends React.Component { visible={this.props.show} coverScreen={false} hasBackdrop={true} - onRequestClose={() => console.log('onRequestClose')} > @@ -132,7 +139,7 @@ class loginPopover extends React.Component { this.state.disableLoginBtn ? Theme.Button.disabled : null ]} TextStyles={[Theme.Button.btnPinkText, { fontSize: 18 }]} - text={this.state.btnText} + text={this.getConnectBtnText()} onPress={this.onSignUp} source={twitterBird} imgDimension={{ width: 28, height: 22.5, marginRight: 8 }} @@ -140,15 +147,14 @@ class loginPopover extends React.Component { /> - By signing up, you confirm that you agree to - our + By signing up you confirm that you agree to our { this.closeModal(); InAppBrowser.openBrowser( `${WEB_ROOT}/terms` ); })}>Terms of use - and have read and agree to our + and { this.closeModal(); InAppBrowser.openBrowser( @@ -170,11 +176,21 @@ class loginPopover extends React.Component { export const LoginPopover = connect(mapStateToProps)(loginPopover); export const LoginPopoverActions = { + _track: () => { + let analyticsAction = AppConfig.routesAnalyticsMap.TwitterLogin; + firebase.analytics().setCurrentScreen(analyticsAction, analyticsAction); + }, show: () => { Store.dispatch(showLoginPopover()); - let analyticsAction = AppConfig.routesAnalyticsMap.TwitterLogin; - console.log('firebase.analytics().setCurrentScreen() ::', analyticsAction); - firebase.analytics().setCurrentScreen(analyticsAction, analyticsAction); + LoginPopoverActions._track(); + }, + showConnecting: () => { + let loginPopoverProps = ReduxGetter.getLoginPopOverProps(); + if ( !loginPopoverProps || !loginPopoverProps.show ) { + //Track. + LoginPopoverActions._track(); + } + Store.dispatch(showConnectingLoginPopover()); }, hide: () => { Store.dispatch(hideLoginPopover()); diff --git a/src/components/Notification/NotificationItem.js b/src/components/Notification/NotificationItem.js index 8a1bef4e..abeba325 100644 --- a/src/components/Notification/NotificationItem.js +++ b/src/components/Notification/NotificationItem.js @@ -125,7 +125,7 @@ class NotificationItem extends Component { }; notificationInfo = () => { - if (this.props.kind == AppConfig.notificationConstants.videoAddKind) { + if (this.shouldShowVideo()) { return this.showVideoComponent(); } }; @@ -183,8 +183,10 @@ class NotificationItem extends Component { let includesObj = this.props.heading && this.props.heading["includes"] || {} , userObj ; for(let key in includesObj ){ - userObj = includesObj[key] || null; - break ; + if ( includesObj[key] && includesObj[key].kind === 'users'){ + userObj = includesObj[key] || null; + break; + } } if(userObj){ return this.includesTextNavigate(userObj))}> @@ -195,7 +197,7 @@ class NotificationItem extends Component { }else { return( - ; + ) @@ -204,10 +206,23 @@ class NotificationItem extends Component { } } + shouldShowVideo = () => { + let videoKinds = [ + AppConfig.notificationConstants.videoAddKind, + AppConfig.notificationConstants.userMention, + AppConfig.notificationConstants.replyUserMention, + AppConfig.notificationConstants.replySenderWithAmount, + AppConfig.notificationConstants.replySenderWithoutAmount, + AppConfig.notificationConstants.replyReceiverWithAmount, + AppConfig.notificationConstants.replyReceiverWithoutAmount + ] + return videoKinds.includes(this.props.kind); + } + render() { let headerWidth = '92%', notificationInfoWidth = '0%'; - if (this.props.kind === AppConfig.notificationConstants.videoAddKind) { + if (this.shouldShowVideo()) { headerWidth = '72%'; notificationInfoWidth = '20%'; } @@ -220,7 +235,7 @@ class NotificationItem extends Component { {this.getActivityIcon()} - + {this.getHeading()} @@ -233,7 +248,7 @@ class NotificationItem extends Component { {this.showIfFailed()} - {this.props.kind === AppConfig.notificationConstants.videoAddKind ? + { this.shouldShowVideo() ? {this.notificationInfo()} diff --git a/src/components/Notification/index.js b/src/components/Notification/index.js index e6d8a27c..0d9f7c47 100644 --- a/src/components/Notification/index.js +++ b/src/components/Notification/index.js @@ -18,6 +18,9 @@ import {PushNotificationMethods} from '../../services/PushNotificationManager' import AndroidOpenSettings from "react-native-android-open-settings"; import LinearGradient from "react-native-linear-gradient"; import Theme from "../../theme/styles"; + +import CommonStyle from "../../theme/styles/Common"; + const mapStateToProps = (state) => { return { userId: CurrentUser.getUserId(), @@ -197,7 +200,7 @@ class NotificationScreen extends Component { render() { return ( this.props.userId && ( - + { this.listRef = ref; diff --git a/src/components/Home/TransactionPepoButton.js b/src/components/PepoTransactionButton/Base.js similarity index 76% rename from src/components/Home/TransactionPepoButton.js rename to src/components/PepoTransactionButton/Base.js index c7107575..8df4cff6 100644 --- a/src/components/Home/TransactionPepoButton.js +++ b/src/components/PepoTransactionButton/Base.js @@ -3,9 +3,7 @@ import { TouchableWithoutFeedback, View } from 'react-native'; import { OstWalletSdk } from '@ostdotcom/ost-wallet-sdk-react-native'; import Toast from '../../theme/components/NotificationToast'; import deepGet from 'lodash/get'; -import clone from 'lodash/clone'; -import { connect } from 'react-redux'; -import { withNavigation } from 'react-navigation'; +import assignIn from "lodash/assignIn"; import CurrentUser from '../../models/CurrentUser'; import PepoButton from './PepoButton'; @@ -13,7 +11,7 @@ import appConfig from '../../constants/AppConfig'; import PepoApi from '../../services/PepoApi'; import pricer from '../../services/Pricer'; import Store from '../../store'; -import { updateExecuteTransactionStatus, updateBalance, upsertVideoStatEntities } from '../../actions'; +import { updateExecuteTransactionStatus, updateBalance } from '../../actions'; import ExecuteTransactionWorkflow from '../../services/OstWalletCallbacks/ExecuteTransactionWorkFlow'; import reduxGetter from '../../services/ReduxGetters'; import { ostErrors } from '../../services/OstErrors'; @@ -24,17 +22,8 @@ import PixelCall from '../../services/PixelCall'; import {ON_USER_CANCLLED_ERROR_MSG, ensureDeivceAndSession} from '../../helpers/TransactionHelper'; import {VideoPlayPauseEmitter} from "../../helpers/Emitters"; -const mapStateToProps = (state, ownProps) => ({ - balance: state.balance, - disabled: state.executeTransactionDisabledStatus, - isVideoUserActivated: utilities.isUserActivated(reduxGetter.getUserActivationStatus(ownProps.userId)), - supporters: reduxGetter.getVideoSupporters(ownProps.videoId), - isSupporting: reduxGetter.isVideoSupported(ownProps.videoId), - totalBt: reduxGetter.getVideoBt(ownProps.videoId, state), - isCurrentUserActivated: CurrentUser.isUserActivated() -}); - -class TransactionPepoButton extends PureComponent { + +class Base extends PureComponent { constructor(props) { super(props); this.localSupported = this.props.isSupporting; @@ -42,7 +31,7 @@ class TransactionPepoButton extends PureComponent { isDisabled = () => { return ( - this.props.userId == CurrentUser.getUserId() || !this.isBalance() || !this.props.isCurrentUserActivated || this.props.disabled || !this.props.isVideoUserActivated + this.props.userId == CurrentUser.getUserId() || !this.isBalance() || !this.props.isCurrentUserActivated || this.props.disabled || !this.props.isEntityUserActivated ); }; @@ -119,12 +108,7 @@ class TransactionPepoButton extends PureComponent { } getSdkMetaProperties() { - const metaProperties = clone(appConfig.metaProperties); - if (this.props.videoId) { - metaProperties['name'] = 'video'; - metaProperties['details'] = `vi_${this.props.videoId}`; - } - return metaProperties; + throw "Overwrite"; } onRequestAcknowledge(ostWorkflowContext, ostWorkflowEntity) { @@ -132,18 +116,14 @@ class TransactionPepoButton extends PureComponent { this.dropPixel(); } + getDropPixel(){ + console.log("getDropPixel is mandatory to be implemented in Transaction button"); + return {}; + } + dropPixel() { - let pixelParams = { - e_entity: 'video', - e_action: 'contribution', - e_data_json: { - video_id: this.props.videoId, - profile_user_id: this.props.userId, - amount: this.btAmount - }, - p_type: 'feed' - }; - PixelCall(pixelParams); + + PixelCall(this.getDropPixel()); } onFlowInterrupt(ostWorkflowContext, error) { @@ -178,10 +158,7 @@ class TransactionPepoButton extends PureComponent { getSendTransactionPlatformData(ostWorkflowEntity) { return { ost_transaction: deepGet(ostWorkflowEntity, 'entity'), - ost_transaction_uuid: deepGet(ostWorkflowEntity, 'entity.id'), - meta: { - vi: this.props.videoId - } + ost_transaction_uuid: deepGet(ostWorkflowEntity, 'entity.id') }; } @@ -199,21 +176,11 @@ class TransactionPepoButton extends PureComponent { Store.dispatch(updateBalance(balance)); } - let videoStats = reduxGetter.getVideoStats(this.props.videoId), - updateVideoStats = false; - if (totalBt && totalBt > 0) { - videoStats['total_amount_raised_in_wei'] = Pricer.getToDecimal(totalBt); - updateVideoStats = true; - } - - if (supporters && !this.props.isSupporting) { - videoStats['total_contributed_by'] = supporters; - updateVideoStats = true; - } + this.reduxEntityUpdate( totalBt, supporters ); + } - if (updateVideoStats) { - Store.dispatch(upsertVideoStatEntities(utilities._getEntityFromObj(videoStats))); - } + reduxEntityUpdate( totalBt, supporters ){ + throw "OverWrite"; } onPressOut = (btAmount, totalBt) => { @@ -265,7 +232,7 @@ class TransactionPepoButton extends PureComponent { - {/*+ {this.props.count}*/} ); @@ -73,7 +69,6 @@ export default class ClapBubble extends React.Component { const styles = StyleSheet.create({ clapbubble: { elevation: 4, - // backgroundColor:Colors.primary , height: 40, width: 40, borderRadius: 20, diff --git a/src/components/Home/PepoButton/ClapButton.js b/src/components/PepoTransactionButton/PepoButton/ClapButton.js similarity index 98% rename from src/components/Home/PepoButton/ClapButton.js rename to src/components/PepoTransactionButton/PepoButton/ClapButton.js index 2c0b7710..b90c263c 100644 --- a/src/components/Home/PepoButton/ClapButton.js +++ b/src/components/PepoTransactionButton/PepoButton/ClapButton.js @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Animated, Easing, View, Vibration } from 'react-native'; +import { Animated, Easing, View } from 'react-native'; import ReactNativeHapticFeedback from 'react-native-haptic-feedback'; import pepo_tx_img from '../../../assets/pepo_anim_btn.png'; diff --git a/src/components/Home/PepoButton/index.js b/src/components/PepoTransactionButton/PepoButton/index.js similarity index 98% rename from src/components/Home/PepoButton/index.js rename to src/components/PepoTransactionButton/PepoButton/index.js index 5c76758a..a16ce52d 100644 --- a/src/components/Home/PepoButton/index.js +++ b/src/components/PepoTransactionButton/PepoButton/index.js @@ -6,7 +6,7 @@ import { TouchableWithoutFeedback } from "react-native"; import ClapBubble from "./ClapBubble"; -import inlineStyles from '../styles' +import inlineStyles from '../styles'; import ClapButton from "./ClapButton"; import appConfig from "../../../constants/AppConfig"; import Pricer from "../../../services/Pricer"; diff --git a/src/components/PepoTransactionButton/PepoTxBtn.js b/src/components/PepoTransactionButton/PepoTxBtn.js new file mode 100644 index 00000000..47cf7bcf --- /dev/null +++ b/src/components/PepoTransactionButton/PepoTxBtn.js @@ -0,0 +1,70 @@ +import { connect } from 'react-redux'; +import clone from 'lodash/clone'; +import assignIn from 'lodash/assignIn'; + +import CurrentUser from '../../models/CurrentUser'; +import appConfig from '../../constants/AppConfig'; +import Store from '../../store'; +import { upsertVideoStatEntities } from '../../actions'; +import reduxGetter from '../../services/ReduxGetters'; +import Pricer from '../../services/Pricer'; +import utilities from '../../services/Utilities'; +import { withNavigation } from 'react-navigation'; + +import Base from "./Base"; + +const mapStateToProps = (state, ownProps) => ({ + balance: state.balance, + disabled: state.executeTransactionDisabledStatus, + isEntityUserActivated: utilities.isUserActivated(reduxGetter.getUserActivationStatus(ownProps.userId)), + supporters: reduxGetter.getVideoSupporters(ownProps.entityId), + isSupporting: reduxGetter.isVideoSupported(ownProps.entityId), + totalBt: reduxGetter.getVideoBt(ownProps.entityId, state), + isCurrentUserActivated: CurrentUser.isUserActivated() + }); + +class PepoTxBtn extends Base { + + constructor(props){ + super(props); + } + + getSdkMetaProperties() { + const metaProperties = clone(appConfig.metaProperties); + metaProperties['name'] = 'video'; + metaProperties['details'] = `vi_${this.props.entityId}`; + return metaProperties; + } + + getDropPixel(){ + let specificData = this.props.getPixelDropData(), + defaultData = { + e_entity: 'video', + e_action: 'contribution', + video_id: this.props.entityId, + amount: this.btAmount + }; + return assignIn({}, specificData, defaultData); + } + + reduxEntityUpdate( totalBt, supporters ){ + let videoStats = reduxGetter.getVideoStats(this.props.entityId), + updateVideoStats = false; + if (totalBt && totalBt > 0) { + videoStats['total_amount_raised_in_wei'] = Pricer.getToDecimal(totalBt); + updateVideoStats = true; + } + + if (supporters && !this.props.isSupporting) { + videoStats['total_contributed_by'] = supporters; + updateVideoStats = true; + } + + if (updateVideoStats) { + Store.dispatch(upsertVideoStatEntities(utilities._getEntityFromObj(videoStats))); + } + } + +} + +export default connect(mapStateToProps)(withNavigation(PepoTxBtn)); diff --git a/src/components/PepoTransactionButton/ReplyPepoTxBtn.js b/src/components/PepoTransactionButton/ReplyPepoTxBtn.js new file mode 100644 index 00000000..ba5087c3 --- /dev/null +++ b/src/components/PepoTransactionButton/ReplyPepoTxBtn.js @@ -0,0 +1,70 @@ +import { connect } from 'react-redux'; +import clone from 'lodash/clone'; +import CurrentUser from '../../models/CurrentUser'; +import appConfig from '../../constants/AppConfig'; +import Store from '../../store'; +import { upsertReplyDetailEntities } from '../../actions'; +import reduxGetter from '../../services/ReduxGetters'; +import Pricer from '../../services/Pricer'; +import utilities from '../../services/Utilities'; +import { withNavigation } from 'react-navigation'; + +import Base from "./Base"; +import assignIn from "lodash/assignIn"; + +const mapStateToProps = (state, ownProps) => ({ + balance: state.balance, + disabled: state.executeTransactionDisabledStatus, + isEntityUserActivated: utilities.isUserActivated(reduxGetter.getUserActivationStatus(ownProps.userId)), + isCurrentUserActivated: CurrentUser.isUserActivated() , + supporters: reduxGetter.getReplySupporters(ownProps.entityId), + isSupporting: reduxGetter.isReplySupported(ownProps.entityId) , + totalBt: reduxGetter.getReplyBt(ownProps.entityId), + }); + +class ReplyPepoTxBtn extends Base { + + constructor(props){ + super(props); + } + + getSdkMetaProperties() { + const metaProperties = clone(appConfig.metaProperties); + metaProperties['name'] = 'pepo_on_reply'; + metaProperties['details'] = `rdi_${this.props.entityId}`; + return metaProperties; + } + + getDropPixel(){ + let specificData = this.props.getPixelDropData(), + defaultData = { + e_entity: 'reply', + e_action: 'contribution', + reply_detail_id: this.props.entityId, + amount: this.btAmount + }; + return assignIn({}, specificData, defaultData); + } + + reduxEntityUpdate( totalBt, supporters ){ + let replyDetails = reduxGetter.getReplyEntity(this.props.entityId), + updateEntity = false + ; + if (totalBt && totalBt > 0) { + replyDetails['total_amount_raised_in_wei'] = Pricer.getToDecimal(totalBt); + updateEntity = true; + } + + if (supporters && !this.props.isSupporting) { + replyDetails['total_contributed_by'] = supporters; + updateEntity = true; + } + + if (updateEntity) { + Store.dispatch(upsertReplyDetailEntities(utilities._getEntityFromObj(replyDetails))); + } + } + +} + +export default connect(mapStateToProps)( withNavigation( ReplyPepoTxBtn )); diff --git a/src/components/PepoTransactionButton/styles.js b/src/components/PepoTransactionButton/styles.js new file mode 100644 index 00000000..b9f7185b --- /dev/null +++ b/src/components/PepoTransactionButton/styles.js @@ -0,0 +1,19 @@ +import DefaultStyleGenerator from '../../theme/styles/DefaultStyleGenerator'; +import Colors from '../../theme/styles/Colors'; + +let stylesMap = { + pepoTxCount: { + fontSize: 18, + color: Colors.white, + alignSelf: 'center', + marginBottom: 5, + textShadowColor: 'rgba(0, 0, 0, 0.65)', + textShadowOffset: {width: 1, height: 1}, + textShadowRadius: 1, + fontFamily: 'AvenirNext-DemiBold' + } +} + +; + +export default styles = DefaultStyleGenerator.generate(stylesMap); diff --git a/src/components/PreviewRecordedVideo/index.js b/src/components/PreviewRecordedVideo/index.js index cfed2368..3fedfaac 100644 --- a/src/components/PreviewRecordedVideo/index.js +++ b/src/components/PreviewRecordedVideo/index.js @@ -99,10 +99,10 @@ class PreviewRecordedVideo extends Component { do_discard: true }) ); + this.props.saveVideoPrimaryInfo(); this.props.goToRecordScreen(); } else if (buttonIndex == ACTION_SHEET_DESCTRUCTIVE_INDEX) { this.props.navigation.goBack(null); - // videoUploaderComponent.emit('hide'); Store.dispatch( upsertRecordedVideo({ do_discard: true diff --git a/src/components/Profile/ProfileEditScreen.js b/src/components/Profile/ProfileEditScreen.js index 410e6979..e5e9fe5d 100644 --- a/src/components/Profile/ProfileEditScreen.js +++ b/src/components/Profile/ProfileEditScreen.js @@ -353,204 +353,208 @@ class ProfileEdit extends React.PureComponent { render() { return ( - - - {this.getImageSrc()} - - - - - - - - Name - this.setState({ name, error: null, name_error: null })} - fieldName="name" - textContentType="none" - style={[Theme.TextInput.textInputStyle]} - placeholder="Name" - returnKeyType="next" - returnKeyLabel="Next" - placeholderTextColor="#ababab" - blurOnSubmit={false} - isFocus={this.state.current_formField == this.tabIndex.name} - onFocus={() => { - this.setState({ - current_formField: this.tabIndex.name - }); + + { - this.onSubmitEditing(this.tabIndex.name); - }} - value={this.state.name} - errorMsg={this.state.name_error} - serverErrors={this.state.server_errors} - /> - - Username - this.setState({ user_name, error: null, user_name_error: null })} - fieldName="user_name" - textContentType="none" - style={[Theme.TextInput.textInputStyle]} - placeholder="User Name" - returnKeyType="next" - returnKeyLabel="Next" - placeholderTextColor="#ababab" - blurOnSubmit={false} - onSubmitEditing={() => { - Keyboard.dismiss(); - this.onSubmitEditing(this.tabIndex.username); - }} - isFocus={this.state.current_formField == this.tabIndex.username} - onFocus={() => { - this.setState({ - current_formField: this.tabIndex.username - }); - }} - value={this.state.user_name} - errorMsg={this.state.user_name_error} - serverErrors={this.state.server_errors} - /> - - Email Address - - this.onEmailFocus())} - style={[inlineStyles.clickWrapper,{height : EMAIL_HEIGHT}]} - > - - + showsVerticalScrollIndicator={false} + > + + {this.getImageSrc()} + + + + + + + + Name this.setState({ name, error: null, name_error: null })} + fieldName="name" textContentType="none" - style={[ - Theme.TextInput.textInputStyle, - !this.state.isVerifiedEmail - ? { borderColor: Colors.wildWatermelon2 } - : { backgroundColor: Colors.whiteSmoke } - ]} - placeholder="Email" + style={[Theme.TextInput.textInputStyle]} + placeholder="Name" returnKeyType="next" returnKeyLabel="Next" placeholderTextColor="#ababab" blurOnSubmit={false} - value={this.state.emailAddress} - serverErrors={this.state.server_errors} - autoCapitalize={'none'} - isFocus={false} - /> - { + this.setState({ + current_formField: this.tabIndex.name + }); }} + onSubmitEditing={() => { + this.onSubmitEditing(this.tabIndex.name); + }} + value={this.state.name} + errorMsg={this.state.name_error} + serverErrors={this.state.server_errors} /> - - Bio - - this.onBioFocus())}> - + Username this.setState({ user_name, error: null, user_name_error: null })} + fieldName="user_name" textContentType="none" - style={[Theme.TextInput.textInputStyle, { height: BIO_HEIGHT, paddingVertical: 15 }]} - placeholder="Bio" + style={[Theme.TextInput.textInputStyle]} + placeholder="User Name" returnKeyType="next" returnKeyLabel="Next" placeholderTextColor="#ababab" blurOnSubmit={false} - maxLength={100} - value={this.state.bio} + onSubmitEditing={() => { + Keyboard.dismiss(); + this.onSubmitEditing(this.tabIndex.username); + }} + isFocus={this.state.current_formField == this.tabIndex.username} + onFocus={() => { + this.setState({ + current_formField: this.tabIndex.username + }); + }} + value={this.state.user_name} + errorMsg={this.state.user_name_error} serverErrors={this.state.server_errors} - isFocus={false} - //onFocus={multipleClickHandler(() => this.onBioFocus())} /> - - - - Link - this.setState({ link, error: null, link_error: null })} - fieldName="link" - textContentType="none" - style={[Theme.TextInput.textInputStyle]} - placeholder="Link" - returnKeyType="done" - returnKeyLabel="Done" - placeholderTextColor="#ababab" - blurOnSubmit={false} - onSubmitEditing={() => { - Keyboard.dismiss(); - }} - value={this.state.link} - serverErrors={this.state.server_errors} - /> - - - {this.state.btnText} - - - - {this.state.general_error} - { - this.setState({ - showGalleryAccessModal: false - }); - }} - modalVisibility={this.state.showGalleryAccessModal} - headerText="Library" - accessText="Enable Library Access" - accessTextDesc="Please allow access to photo library to select your profile picture" - imageSrc={GalleryIcon} - imageSrcStyle={{ height: 40, width: 40 }} - /> - { - this.setState({ - showCameraAccessModal: false - }); - }} - modalVisibility={this.state.showCameraAccessModal} - headerText="Camera" - accessText="Enable Camera Access" - accessTextDesc="Allow access to your camera and microphone to take video " - imageSrc={CameraIcon} - /> - + Email Address + + this.onEmailFocus())} + style={[inlineStyles.clickWrapper,{height : EMAIL_HEIGHT}]} + > + + + + + + + Bio + + this.onBioFocus())}> + + this.onBioFocus())} + /> + + + + Link + this.setState({ link, error: null, link_error: null })} + fieldName="link" + textContentType="none" + style={[Theme.TextInput.textInputStyle]} + placeholder="Link" + returnKeyType="done" + returnKeyLabel="Done" + placeholderTextColor="#ababab" + blurOnSubmit={false} + onSubmitEditing={() => { + Keyboard.dismiss(); + }} + value={this.state.link} + serverErrors={this.state.server_errors} + /> + + + + {this.state.btnText} + + + + {this.state.general_error} + { + this.setState({ + showGalleryAccessModal: false + }); + }} + modalVisibility={this.state.showGalleryAccessModal} + headerText="Library" + accessText="Enable Library Access" + accessTextDesc="Please allow access to photo library to select your profile picture" + imageSrc={GalleryIcon} + imageSrcStyle={{ height: 40, width: 40 }} + /> + { + this.setState({ + showCameraAccessModal: false + }); + }} + modalVisibility={this.state.showCameraAccessModal} + headerText="Camera" + accessText="Enable Camera Access" + accessTextDesc="Allow access to your camera and microphone to take video " + imageSrc={CameraIcon} + /> + + ); } } diff --git a/src/components/Redemption/index.js b/src/components/Redemption/index.js index 1bc9e6f4..8c9bb259 100644 --- a/src/components/Redemption/index.js +++ b/src/components/Redemption/index.js @@ -50,6 +50,7 @@ import clone from 'lodash/clone'; import { ostSdkErrors, WORKFLOW_CANCELLED_MSG } from '../../services/OstSdkErrors'; import Colors from '../../theme/styles/Colors'; import InAppBrowser from '../../services/InAppBrowser'; +import CommonStyle from '../../theme/styles/Common'; const bottomSpace = getBottomSpace([true]), extraPadding = 10, @@ -658,7 +659,7 @@ class Redemption extends PureComponent{ render(){ return ( - + diff --git a/src/components/ReferAndEarn/index.js b/src/components/ReferAndEarn/index.js index f4e4a10e..182b57de 100644 --- a/src/components/ReferAndEarn/index.js +++ b/src/components/ReferAndEarn/index.js @@ -14,6 +14,7 @@ import BackArrow from '../CommonComponents/BackArrow'; import { ScrollView } from 'react-native-gesture-handler'; import PepoApi from '../../services/PepoApi'; import { WEB_ROOT } from '../../constants/index'; +import CommonStyle from "../../theme/styles/Common"; class ReferAndEarn extends Component { static navigationOptions = (options) => { @@ -116,7 +117,7 @@ class ReferAndEarn extends Component { render() { return ( - + {this.state.pendingInvites != 0 ? ( @@ -126,7 +127,7 @@ class ReferAndEarn extends Component { style={[styles.heading, { textAlign: 'center' }]} >{`You have ${this.state.inviteText} invites remaining`} - Pepo is currently invite only. Invite your friends and you will get 5% commission on their earnings. + Invite your friends and you will get 5% commission on their earnings. diff --git a/src/components/ReplyCollection/index.js b/src/components/ReplyCollection/index.js new file mode 100644 index 00000000..59ed5e00 --- /dev/null +++ b/src/components/ReplyCollection/index.js @@ -0,0 +1,262 @@ +import React, { PureComponent } from 'react'; +import { + FlatList, + ActivityIndicator, + Dimensions, + View +} from "react-native"; +import { withNavigation} from "react-navigation"; +import deepGet from "lodash/get"; +import LinearGradient from "react-native-linear-gradient"; + +import Pagination from "../../services/Pagination"; +import DeleteVideo from "../CommonComponents/DeleteVideo"; +import inlineStyles from './styles'; +import CurrentUser from '../../models/CurrentUser'; +import DataContract from '../../constants/DataContract'; +import { VideoReplyEmitter } from '../../helpers/Emitters'; +import PepoApi from '../../services/PepoApi'; +import ReplyThumbnail from '../CommonComponents/VideoThumbnail/ReplyThumbnail'; +import ReduxGetters from '../../services/ReduxGetters'; +import entityHelper from '../../helpers/EntityHelper'; +import { fetchVideo } from '../../helpers/helpers'; + + +class ReplyCollection extends PureComponent { + + constructor(props){ + super(props); + this.state = { + list : [], + refreshing : true, + loadingNext: false + }; + this.listRef = null; + this.numColumns = 2; + } + + componentDidMount(){ + this.initPagination(); + this.refresh(); + this.bindEvents(); + } + + bindEvents(){ + VideoReplyEmitter.on('videoUploaded', ( payload )=>{ + this.fetchVideoReply( payload.videoId ); + }) + } + + fetchVideoReply = (id)=> { + new PepoApi(DataContract.replies.getSingleVideoReplyApi(id)) + .get() + .then((res)=> { + let newVideoReply = res.data['result_type']; + this.addVideo( newVideoReply ); + }) + } + + addVideo = ( item ) => { + this.videoPagination.addItems( item ); + let array = [...this.state.list]; // make a separate copy of the array + array.unshift( item ); + this.setState({list: array}); + } + + componentWillUnmount() { + this.removePaginationListeners(); + } + + getPagination = () => { + return this.videoPagination; + }; + + + initPagination() { + // First, take care of existing Pagination if exists. + this.removePaginationListeners(); + + // Now, create a new one. + let fetchUrl = this.props.fetchUrl; + this.videoPagination = new Pagination(fetchUrl); + this.bindPaginationEvents(); + } + + bindPaginationEvents(){ + let pagination = this.videoPagination; + if ( !pagination ) { + return; + } + let paginationEvent = pagination.event; + if ( null === paginationEvent ) { + return; + } + + paginationEvent.on("onBeforeRefresh" , this.beforeRefresh.bind(this) ); + paginationEvent.on("onRefresh" , this.onRefresh.bind(this) ); + paginationEvent.on("onRefreshError" , this.onRefreshError.bind(this)); + paginationEvent.on("onBeforeNext" , this.beforeNext.bind(this)); + paginationEvent.on("onNext" , this.onNext.bind(this) ); + paginationEvent.on("onNextError" , this.onNextError.bind(this)); + } + + removePaginationListeners(){ + let pagination = this.videoPagination; + if ( !pagination ) { + return; + } + let paginationEvent = pagination.event; + if ( null === paginationEvent ) { + return; + } + paginationEvent.removeListener('onBeforeRefresh'); + paginationEvent.removeListener('onRefresh'); + paginationEvent.removeListener('onRefreshError'); + paginationEvent.removeListener('onBeforeNext'); + paginationEvent.removeListener('onNext'); + paginationEvent.removeListener('onNextError'); + } + + beforeRefresh = ( ) => { + this.setState({ refreshing : true }); + }; + + onRefresh = ( res ) => { + const list = this.getResultList(); + this.props.onRefresh && this.props.onRefresh( list , res ); + this.setState({ + refreshing : false, + list : list + }); + }; + + onRefreshError = ( error ) => { + this.setState({ refreshing : false }); + }; + + beforeNext =() => { + this.setState({ loadingNext : true }); + }; + + onNext = ( res ) => { + this.setState({ + loadingNext : false, + list : this.getResultList() + }); + } + + onNextError = ( error ) => { + this.setState({ loadingNext : false }); + }; + + getNext = () => { + this.videoPagination.getNext(); + }; + + refresh = () => { + this.videoPagination.refresh(); + }; + + _keyExtractor = (item, index) => { + return `id_${item.id}`; + } + + _renderItem = ({ item, index }) => { + // Render Video cell + return this._renderVideoCell({item,index}); + }; + + _renderVideoCell = ({ item, index }) => { + return this.renderThumbnailItem(item , index) + }; + + renderThumbnailItem ( item , index ){ + if(entityHelper.isVideoReplyEntity( item )){ + if(entityHelper.isReplyVideoTypeEntity(item)){ + return this._renderVideoReplyThumbnail( item, index ); + } + } + } + + _renderVideoReplyThumbnail( item, index ) { + const reply_detail_id = deepGet(item,`payload.${DataContract.replies.replyDetailIdKey}`); + return ( + {ReduxGetters.getCanDeleteReply( reply_detail_id ) && + + {this.removeVideo(reply_detail_id , index )}} /> + + } + {this.onVideoClick(index , item)}}/> + ); + } + + isCurrentUser = ( userId ) => { + return userId === CurrentUser.getUserId(); + } + + removeVideo = (id, index) => { + const videoId = ReduxGetters.getReplyEntityId(id); + videoId && fetchVideo(videoId); + if (index > -1) { + this.videoPagination.deleteItem(id , "payload.reply_detail_id"); + let array = [...this.state.list]; // make a separate copy of the array + array.splice(index, 1); + this.setState({list: array}); + } + } + + onVideoClick = ( index , item) => { + const clonedInstance = this.videoPagination.fetchServices.cloneInstance(); + this.props.navigation.push("FullScreenReplyCollection", { + "fetchServices" : clonedInstance, + "currentIndex": index, + "baseUrl": this.props.fetchUrl + }); + } + + renderFooter = () => { + if (!this.state.loadingNext) return null; + return ; + }; + + getResultList(){ + return this.videoPagination.getResults(); + } + + scrollToTop(){ + this.listRef.scrollToOffset({offset: 0}); + } + + setListRef = (listRef) => { + this.listRef = listRef; + }; + + render(){ + return ( + + ); + } + +} + +export default withNavigation( ReplyCollection ); diff --git a/src/components/ReplyCollection/styles.js b/src/components/ReplyCollection/styles.js new file mode 100644 index 00000000..9c094b87 --- /dev/null +++ b/src/components/ReplyCollection/styles.js @@ -0,0 +1,12 @@ +import DefaultStyleGenerator from '../../theme/styles/DefaultStyleGenerator'; + +let stylesMap = { + deleteButton: { + height: 24, + width: 40, + justifyContent: 'center', + alignItems: 'center' + } +}; + +export default styles = DefaultStyleGenerator.generate(stylesMap); diff --git a/src/components/SayThanks/index.js b/src/components/SayThanks/index.js index fa63803a..4bcf31ec 100644 --- a/src/components/SayThanks/index.js +++ b/src/components/SayThanks/index.js @@ -5,8 +5,7 @@ import { Keyboard, BackHandler, ActivityIndicator, - TouchableWithoutFeedback, - Switch + TouchableWithoutFeedback } from 'react-native'; import { connect } from 'react-redux'; import { getBottomSpace, isIphoneX } from 'react-native-iphone-x-helper'; @@ -20,6 +19,7 @@ import Theme from '../../theme/styles'; import Colors from '../../theme/styles/Colors'; import TwitterAuth from '../../services/ExternalLogin/TwitterAuth'; import Toast from '../../theme/components/NotificationToast'; +import CommonStyle from '../../theme/styles/Common'; const bottomSpace = getBottomSpace([true]), extraPadding = 10, @@ -156,7 +156,6 @@ class SayThanks extends Component { thanksMessage: this.tweeterHandle ? `@${this.tweeterHandle} ${this.state.thanksMessage}`: this.state.thanksMessage }); } else { - //TODO: show error if (resp.err.msg) { Toast.show({ text: resp.err.msg, icon: 'error' }); } @@ -197,7 +196,7 @@ class SayThanks extends Component { } }} > - + {this.state.gettingTweetInfo && ( diff --git a/src/components/Search/index.js b/src/components/Search/index.js index 525b7a7a..cd4c25a9 100644 --- a/src/components/Search/index.js +++ b/src/components/Search/index.js @@ -49,7 +49,7 @@ const TabMap = { } }, extraParams: { - showBalanceFlier: true + showBalanceFlyer: true }, supported: true }, @@ -97,7 +97,7 @@ const TabMap = { "isEmpty": true }, extraParams: { - showBalanceFlier: true + showBalanceFlyer: true }, renderNoResults : (noResultsData) => { const oThis = TabMap.videos; @@ -136,10 +136,9 @@ class SearchScreen extends PureComponent { return prevProps.userId !== this.props.userId; } - componentDidUpdate(prevProps){ if(this.isUserUpdated(prevProps)) { - this.initReferences(); + //this.initReferences(); } } @@ -164,8 +163,9 @@ class SearchScreen extends PureComponent { let tabData = TabsArray[ tabIndx]; let newTabUrl = this.getUrlForTab( tabData ); let tabFlatList = this.getTabFlatList( tabIndx ); + if(!tabFlatList) return; tabFlatList.forcedRefresh(newTabUrl); - tabFlatList.scrollToTop(); + tabFlatList && tabFlatList.scrollToTop(); }; initReferences = () =>{ @@ -201,6 +201,7 @@ class SearchScreen extends PureComponent { if ( !currentTabUrl || currentTabUrl !== newTabUrl ) { // Force refresh let tabFlatList = this.getTabFlatList( tabIndx ); + if(!tabFlatList) return; tabFlatList.scrollToTop(); tabFlatList.forcedRefresh(newTabUrl); } else { @@ -319,12 +320,13 @@ class SearchScreen extends PureComponent { if (this.isUserLoggedIn()){ return this.renderLoggedInView(); } else { + this.initReferences(); return this.renderLoggedOutView() } } renderLoggedOutView = () => { - return + return }; renderTabBar = () => { diff --git a/src/components/Search/styles.js b/src/components/Search/styles.js index 8b3670ee..27f3c949 100644 --- a/src/components/Search/styles.js +++ b/src/components/Search/styles.js @@ -5,6 +5,7 @@ import { Platform } from 'react-native'; let stylesMap = { container: { paddingBottom: 0, + backgroundColor: Colors.white, paddingTop: Platform.OS === 'ios' ? 30 : 20, flex: 1 }, diff --git a/src/components/StoreProducts/index.js b/src/components/StoreProducts/index.js index 536a60e4..e1e3bdb1 100644 --- a/src/components/StoreProducts/index.js +++ b/src/components/StoreProducts/index.js @@ -26,6 +26,7 @@ import toastError from '../../assets/toast_error.png'; import pepoIcon from '../../assets/self-amount-pepo-icon.png'; import mailIcon from '../../assets/mail-filled.png'; import { ostErrors } from '../../services/OstErrors'; +import CommonStyle from '../../theme/styles/Common'; class StoreProductsScreen extends PureComponent{ @@ -279,7 +280,7 @@ class StoreProductsScreen extends PureComponent{ render(){ return ( - + {/**/} diff --git a/src/components/SupportersList/SupportersListComponent.js b/src/components/SupportersList/SupportersListComponent.js index d083fa5f..9dd6d46c 100644 --- a/src/components/SupportersList/SupportersListComponent.js +++ b/src/components/SupportersList/SupportersListComponent.js @@ -5,6 +5,7 @@ import User from '../Users/User'; import Pricer from '../../services/Pricer'; import reduxGetters from '../../services/ReduxGetters'; import EmptyList from '../EmptyFriendsList/EmptyList'; +import CommonStyle from "../../theme/styles/Common"; class SupportersList extends PureComponent { constructor(props) { @@ -27,7 +28,7 @@ class SupportersList extends PureComponent { render() { return ( - + + - + diff --git a/src/components/UserVideoHistory/UserVideoHistoryRow.js b/src/components/UserVideoHistory/UserVideoHistoryRow.js index 9a3b88a1..153a41ec 100644 --- a/src/components/UserVideoHistory/UserVideoHistoryRow.js +++ b/src/components/UserVideoHistory/UserVideoHistoryRow.js @@ -1,111 +1,124 @@ import React, { PureComponent } from 'react'; -import { View, TouchableOpacity, Image } from 'react-native'; +import { View, Dimensions } from 'react-native'; import { withNavigation } from 'react-navigation'; -import VideoWrapper from '../Home/VideoWrapper'; -import ShareIcon from "../CommonComponents/ShareIcon"; +import FanVideo from '../VideoWrapper/FanVideo'; import ReportVideo from "../CommonComponents/ReportVideo"; import PepoApi from '../../services/PepoApi'; -import TransactionPepoButton from '../Home/TransactionPepoButton'; -import CurrentUser from '../../models/CurrentUser'; - -import BottomStatus from '../Home/BottomStatus'; -import VideoAmountStat from '../CommonComponents/VideoAmoutStat'; -import ShareVideo from '../../services/shareVideo'; +import VideoBottomStatus from '../BottomStatus/VideoBottomStatus'; import inlineStyles from './styles'; -import utilities from '../../services/Utilities'; -import OptionsIcon from '../../assets/options_self_video.png'; +import Utilities from '../../services/Utilities'; +import ReplyIcon from '../CommonComponents/ReplyIcon'; +import PepoTxBtn from '../PepoTransactionButton/PepoTxBtn'; +import VideoSupporterStat from '../CommonComponents/VideoSupporterStat/VideoSupporterStat'; +import DataContract from '../../constants/DataContract'; +import BottomReplyBar from '../CommonComponents/BottomReplyBar'; +import CommonStyle from "../../theme/styles/Common"; +import assignIn from 'lodash/assignIn'; +import InvertedReplyList from "../CommonComponents/InvertedReplyThumbnailList"; +import AppConfig from "../../constants/AppConfig"; +import BubbleList from '../CommonComponents/BubbleList'; +import VideoShareIcon from '../CommonComponents/ShareIcon/VideoShare'; + +const AREA = AppConfig.MaxDescriptionArea; +const height = AREA / Dimensions.get('window').width + 20; class UserVideoHistoryRow extends PureComponent { constructor(props) { super(props); + this.isUserNavigate = false; + this.pageType = "user_profile"; + this.pageInit(); } - refetchVideo = () => { - new PepoApi(`/videos/${this.props.videoId}`) - .get() - .then((res) => {}) - .catch((error) => {}); - }; - - navigateToTransactionScreen = (e) => { - if (this.isCurrentUser()) return; - if (utilities.checkActiveUser() && CurrentUser.isUserActivated(true)) { - this.props.navigation.push('TransactionScreen', { - toUserId: this.props.userId, - videoId: this.props.videoId, - requestAcknowledgeDelegate: this.refetchVideo - }); + pageInit = () => { + if( Utilities.getLastChildRoutename(this.props.navigation.state) === 'VideoPlayer'){ + this.isUserNavigate = true; + return this.pageType = 'video_player'; } - }; - - isCurrentUser() { - return this.props.userId == CurrentUser.getUserId(); } - shareVideo = () => { - let shareVideo = new ShareVideo(this.props.videoId); - shareVideo.perform(); - }; - - onDescriptionClick = ( tapEntity , tapText ) => { + getPixelDropData = () => { + const parentData = this.props.getPixelDropData() ; + const pixelParams = { + e_entity: 'video', + video_id: this.props.videoId, + }; + return assignIn({}, pixelParams, parentData); + } - if (!tapEntity) { - return; - } - if(tapEntity.kind === 'tags'){ - this.props.navigation.push('VideoTags', { - "tagId": tapEntity.id - }); - } + refetchVideo = () => { + new PepoApi(`/videos/${this.props.videoId}`) + .get() + .then((res) => {}) + .catch((error) => {}); }; render() { return ( - - - - {!!this.props.videoId && !!this.props.userId && ( - - - - - - - - - - - - - - - )} + + + + + + + {!!this.props.videoId && !!this.props.userId && ( + + + + + + + + + + + + + + + + + + + + + )} + + + ); } } +UserVideoHistoryRow.defaultProps = { + getPixelDropData: function(){ + console.warn("getPixelDropData props is mandatory for UserVideoHistoryRow component"); + return {}; + } +}; + export default withNavigation(UserVideoHistoryRow); diff --git a/src/components/UserVideoHistory/index.js b/src/components/UserVideoHistory/index.js index 85808e50..8446e1b2 100644 --- a/src/components/UserVideoHistory/index.js +++ b/src/components/UserVideoHistory/index.js @@ -1,16 +1,16 @@ import React , {PureComponent} from "react"; -import {FlatList , View , TouchableOpacity, Image} from "react-native"; +import {FlatList} from "react-native"; import deepGet from "lodash/get"; import reduxGetters from "../../services/ReduxGetters"; import Pagination from "../../services/Pagination"; import UserVideoHistoryRow from "./UserVideoHistoryRow"; import TopStatus from "../../components/Home/TopStatus"; - -import inlineStyles from "./styles"; import CurrentUser from "../../models/CurrentUser"; +import CommonStyle from "../../theme/styles/Common"; -import historyBack from '../../assets/user-video-history-back-icon.png'; +import FlotingBackArrow from "../CommonComponents/FlotingBackArrow"; +import { SafeAreaView } from "react-navigation"; const maxVideosThreshold = 3; @@ -56,7 +56,7 @@ class UserVideoHistoryScreen extends PureComponent{ //This is an hack for reset scroll for flatlist. Need to debug a bit more. this.willFocusSubscription = this.props.navigation.addListener('willFocus', (payload) => { - const offset = this.state.activeIndex > 0 ? inlineStyles.fullScreen.height * this.state.activeIndex : 0 ; + const offset = this.state.activeIndex > 0 ? CommonStyle.fullScreen.height * this.state.activeIndex : 0 ; this.flatlistRef && this.flatlistRef.scrollToOffset({offset: offset , animated: false}); this.isActiveScreen = true ; }); @@ -119,9 +119,17 @@ class UserVideoHistoryScreen extends PureComponent{ return `id_${item}`; }; + getPixelDropData = () => { + return pixelParams = { + p_type: 'user_profile', + p_name: this.userId + }; + } + _renderItem = ({ item, index }) => { const videoId = reduxGetters.getUserVideoId(item) ; return ; @@ -148,7 +156,7 @@ class UserVideoHistoryScreen extends PureComponent{ } getItemLayout= (data, index) => { - return {length: inlineStyles.fullScreen.height, offset: inlineStyles.fullScreen.height * index, index} ; + return {length: CommonStyle.fullScreen.height, offset: CommonStyle.fullScreen.height * index, index} ; } isCurrentUser(){ @@ -170,7 +178,7 @@ class UserVideoHistoryScreen extends PureComponent{ render() { return( - + {!this.isCurrentUser() && } - - - - + + ); } diff --git a/src/components/UserVideoHistory/styles.js b/src/components/UserVideoHistory/styles.js index c4eb3865..8ceb8980 100644 --- a/src/components/UserVideoHistory/styles.js +++ b/src/components/UserVideoHistory/styles.js @@ -33,16 +33,22 @@ let stylesMap = { height: "100%" }, touchablesBtns: { - alignSelf: 'flex-end', - marginBottom: -15, - zIndex: 1 + alignItems: 'flex-end', + marginBottom: -10, + zIndex: 1, + flexDirection: 'row' + }, + invertedList: { + marginRight: 'auto', + minWidth: '20%', + marginBottom: 40 }, pepoTxCount: { fontSize: 18, color: Colors.white, alignSelf: 'center', marginTop: 3, - marginBottom: 15 + marginBottom: 5 }, txElem: { marginBottom: 20 @@ -118,7 +124,16 @@ let stylesMap = { height :10, width:23, zIndex: 1, - } + }, +listContainer: { + // width: width, + position: 'absolute', + bottom: height * 0.05 + 50, + left: 10, + zIndex: 9, + alignSelf: 'flex-start', + height: height - 200 +} }; export default styles = DefaultStyleGenerator.generate(stylesMap); diff --git a/src/components/UsersProfile/index.js b/src/components/UsersProfile/index.js index e094eb56..3bb9acb2 100644 --- a/src/components/UsersProfile/index.js +++ b/src/components/UsersProfile/index.js @@ -16,14 +16,18 @@ import { fetchUser } from '../../helpers/helpers'; import utilities from '../../services/Utilities'; import inlineStyles from './styles'; import UserProfileActionSheet from './userProfileActionSheet'; +import CommonStyle from "../../theme/styles/Common"; +import deepGet from "lodash/get"; +import unescape from'lodash/unescape'; import EventEmitter from "eventemitter3"; const userActionEvents = new EventEmitter(); export default class UsersProfile extends Component { static navigationOptions = ({ navigation }) => { + const name = navigation.getParam('headerTitle') || reduxGetter.getName(navigation.getParam('userId')); return { - title: reduxGetter.getName(navigation.getParam('userId')), + title: name, headerBackTitle: null, headerStyle: { backgroundColor: '#ffffff', @@ -80,7 +84,10 @@ export default class UsersProfile extends Component { onUserResponse = ( res ) => { if(utilities.isEntityDeleted(res)){ this.setState({isDeleted: true}); + return } + let userName = deepGet(res, `data.users.${this.userId}.name` , ""); + this.props.navigation.setParams({ headerTitle: unescape(userName)}); } _headerComponent() { @@ -97,7 +104,7 @@ export default class UsersProfile extends Component { ) }else{ return ( - + - + ); } } diff --git a/src/components/UsersProfile/userProfileActionSheet.js b/src/components/UsersProfile/userProfileActionSheet.js index fee0026b..9dc9c371 100644 --- a/src/components/UsersProfile/userProfileActionSheet.js +++ b/src/components/UsersProfile/userProfileActionSheet.js @@ -18,7 +18,7 @@ const ACTION_SHEET_BLOCK_UNBLOCK_INDEX = 1; const mapStateToProps = (state, ownProps) => { return { - canBlockUser: ReduxGetters.canBlockUser(ownProps.userId , state) + canBlockUser: ReduxGetters.canBlockUser(ownProps.userId , state) }; }; @@ -34,15 +34,17 @@ class UserProfileActionSheet extends PureComponent { .then((response) => { if (response && response.success){ Toast.show({text:'User reported successfully!', icon: 'success' }); + } else { + Toast.show({text: `Unable to report user right now.`, icon: 'error' }); } }) .catch((err) => { - console.log('Report user failed', err); + Toast.show({text: `Unable to report user right now.`, icon: 'error' }); }); } blockUser = () => { - const oThis =this; + const oThis = this; new PepoApi(`/users/${this.props.userId}/block`) .post() .then((response) => { @@ -50,11 +52,11 @@ class UserProfileActionSheet extends PureComponent { oThis.onBlockUnblockSuccessToast(true); oThis.onBlockUnBlockSuccess(true); }else{ - oThis.onBlockUnblockErrorToast(true); + oThis.onBlockUnblockErrorToast(true); } }) .catch((err) => { - oThis.onBlockUnblockErrorToast(true); + oThis.onBlockUnblockErrorToast(true); }); } @@ -67,11 +69,11 @@ class UserProfileActionSheet extends PureComponent { oThis.onBlockUnblockSuccessToast(); oThis.onBlockUnBlockSuccess(); }else{ - oThis.onBlockUnblockErrorToast(); + oThis.onBlockUnblockErrorToast(); } }) .catch((err) => { - oThis.onBlockUnblockErrorToast(); + oThis.onBlockUnblockErrorToast(); }); } @@ -116,7 +118,7 @@ class UserProfileActionSheet extends PureComponent { } ], {cancelable: true}, - ); + ); } showActionSheet = () => { @@ -124,13 +126,14 @@ class UserProfileActionSheet extends PureComponent { blockAction = this.showBlockAlert; if(!this.props.canBlockUser){ actionOptions = ['Report', 'Unblock' ,'Cancel'] ; //Mutate via code later. - blockAction = this.unBlockUser; + blockAction = this.unBlockUser; } ActionSheet.show( { options: actionOptions, cancelButtonIndex: ACTION_SHEET_CANCEL_INDEX, - destructiveButtonIndex: ACTION_SHEET_DESCTRUCTIVE_INDEX + destructiveButtonIndex: ACTION_SHEET_DESCTRUCTIVE_INDEX, + title: "Select user action" }, (buttonIndex) => { if (buttonIndex == ACTION_SHEET_REPORT_INDEX) { @@ -153,4 +156,4 @@ class UserProfileActionSheet extends PureComponent { } -export default connect(mapStateToProps)(UserProfileActionSheet); \ No newline at end of file +export default connect(mapStateToProps)(UserProfileActionSheet); diff --git a/src/components/VideoCollections/index.js b/src/components/VideoCollections/index.js index 319099c9..2781811f 100644 --- a/src/components/VideoCollections/index.js +++ b/src/components/VideoCollections/index.js @@ -2,7 +2,11 @@ import React, { PureComponent } from 'react'; import { ActivityIndicator , FlatList} from "react-native"; import {SafeAreaView} from "react-navigation"; import Pagination from "../../services/Pagination"; -import VideoThumbnailItem from '../../components/CommonComponents/VideoThumbnailItem'; + +import CommonStyle from "../../theme/styles/Common"; +import VideoThumbnail from '../CommonComponents/VideoThumbnail/VideoThumbnail'; +import ReplyThumbnail from '../CommonComponents/VideoThumbnail/ReplyThumbnail'; +import entityHelper from '../../helpers/EntityHelper'; class VideoCollections extends PureComponent { constructor(props){ @@ -92,8 +96,7 @@ class VideoCollections extends PureComponent { } beforeRefresh = ( ) => { - //this.props.beforeRefresh && this.props.beforeRefresh(); - //this.onPullToRefresh(); + this.props.beforeRefresh && this.props.beforeRefresh(); this.setState({ refreshing : true }); }; @@ -149,25 +152,41 @@ class VideoCollections extends PureComponent { }; _renderVideoCell = ({ item, index }) => { - return ( {this.onVideoClick(item.payload, index)}} - isEmpty={item.isEmpty} - emptyRenderFunction={this.props.getNoResultsCell}/>); + if(entityHelper.isVideoReplyEntity( item )){ + if(entityHelper.isReplyVideoTypeEntity(item)){ + return this._renderVideoReplyThumbnail( item, index ); + } + } else if( entityHelper.isVideoEntity( item )) { + return this._renderVideoThumbnail( item, index); + } }; + _renderVideoReplyThumbnail( item, index ) { + return ( {this.onVideoClick(item.payload,index)}}/>); + } + + _renderVideoThumbnail( item, index){ + return ( {this.onVideoClick(item.payload, index)}} + isEmpty={item.isEmpty} + emptyRenderFunction={this.props.getNoResultsCell}/>); + } + onVideoClick = (payload, index) => { const clonedInstance = this.videoPagination.fetchServices.cloneInstance(); this.props.navigation.push("FullScreenVideoCollection", { fetchServices : clonedInstance, + tagId: this.props.tagId, currentIndex: index, payload, baseUrl: this.props.getFetchUrl(), - showBalanceFlier: this.props.extraParams && this.props.extraParams.showBalanceFlier + showBalanceFlyer: this.props.extraParams && this.props.extraParams.showBalanceFlyer }); } + renderFooter = () => { if (!this.state.loadingNext) return null; return ; @@ -199,15 +218,13 @@ class VideoCollections extends PureComponent { this.listRef.scrollToOffset({offset: 0}); } - - setListRef = (listRef) => { this.listRef = listRef; }; render(){ return ( - + { nextAppState === 'background' && this.cancleVideoHandling(); }; - async componentDidMount() { - if (this.props.acceptedCameraTnC === null) { - utilities.getItem(`${CurrentUser.getUserId()}-accepted-camera-t-n-c`).then((terms) => { - this.setState({ acceptedCameraTnC: terms }); - }); + componentWillReceiveProps( nextProps ){ + if( nextProps.acceptedCameraTnC != this.state.acceptedCameraTnC ){ + this.setState({acceptedCameraTnC: nextProps.acceptedCameraTnC }) + } + + if ( nextProps.showLightBoxOnReply != this.state.showLightBoxOnReply ) { + this.setState({showLightBoxOnReply: nextProps.showLightBoxOnReply }) } + } + + + + isStaleReduxObjectPresent(){ + let acceptableKeys = ['reply_obj', 'video_type']; + for (let key in this.recordedVideoObj) { + if (! acceptableKeys.includes(key)){ + return true; + } + } + return false; + } + + async componentDidMount() { + BackHandler.addEventListener('hardwareBackPress', this._handleBackPress); AppState.addEventListener('change', this._handleAppStateChange); if (this.props.actionSheetOnRecordVideo) { - let recordedVideoObj = reduxGetters.getRecordedVideo(); - this.recordedVideo = recordedVideoObj.raw_video; - let isFileExists = false; + let isFileExists = await this.ifLocalVideoPresent(); const oThis = this; - - // this.showActionSheet(); - if (this.recordedVideo) { - isFileExists = await RNFS.exists(this.recordedVideo); - } if (isFileExists) { + this.setState({ isLocalVideoPresent: true }); setTimeout(function() { oThis.showActionSheet(); }, 100); - } else if (Object.keys(recordedVideoObj).length > 0) { + } else if (this.isStaleReduxObjectPresent()) { Store.dispatch( upsertRecordedVideo({ do_discard: true }) ); + } else { + this.props.saveVideoPrimaryInfo(); } } } @@ -85,7 +104,7 @@ class VideoRecorder extends Component { ActionSheet.show( { options: ACTION_SHEET_BUTTONS, - title: 'You have already recorded video' + title: this.props.getActionSheetText(this.recordedVideoObj) }, (buttonIndex) => { if (buttonIndex == ACTION_SHEET_RESHOOT_INDEX) { @@ -96,9 +115,10 @@ class VideoRecorder extends Component { do_discard: true }) ); + this.props.saveVideoPrimaryInfo(); } else if (buttonIndex == ACTION_SHEET_CONTINUE_INDEX) { //navigate to previous page - this.props.goToPreviewScreen(this.recordedVideo); + this.props.proceedWithExistingVideo(this.recordedVideoObj); } } ); @@ -125,10 +145,132 @@ class VideoRecorder extends Component { }); }; + replyToVideo = () => { + this.setState({ + showLightBoxOnReply: false + }); + }; + flipCamera = () => { this.setState({ cameraFrontMode: !this.state.cameraFrontMode }); }; + + ifLocalVideoPresent = async () => { + let recordedVideo = this.recordedVideoObj.raw_video; + let isFileExists = false; + if (recordedVideo) { + isFileExists = await RNFS.exists(recordedVideo); + } + return isFileExists; + }; + + getPepoAmount = () => { + let amount = reduxGetters.getBtAmountForReply(this.props.videoId); + return Pricer.getToBT(Pricer.getFromDecimal(amount), 2); + }; + + getUserFullName = () => { + let userId = reduxGetters.getVideoCreatorUserId(this.props.videoId); + return reduxGetters.getName(userId) + }; + + + showCoachForPosting = () => { + // If already recorded video present in local, do not show coach. + if (this.state.isLocalVideoPresent) return; + + if (this.props.isVideoTypeReply) { + // TODO: return coach for posting + if (this.state.showLightBoxOnReply){ + // Show video + return + + + + + + + Post a reply + + + + + Reply to {this.getUserFullName()}'s video for {this.getPepoAmount()} Pepo Coins + + + + { + this.replyToVideo(); + })} + /> + + + + + } + // if (no video reply present) { return Coach } + } else { + if (this.state.acceptedCameraTnC !== 'true'){ + console.log('=========----============-----------'); + + return + + Submit your first video + + + Create a 30 second video update. Share what you're working on, what excites you, or anything on your + mind. + + + + + Approval process + + + The Pepo team will review your first video before it is shared publicly. We'll get in touch with you + ASAP! + + + + + + Get Started + + + + + + + + } + } + }; + cameraView() { return ( @@ -154,56 +296,33 @@ class VideoRecorder extends Component { defaultVideoQuality={RNCamera.Constants.VideoQuality[AppConfig.cameraConstants.VIDEO_QUALITY]} defaultMuted={false} > - {this.state.acceptedCameraTnC != 'true' && ( - - - Submit your first video - - - Create a 30 second video update. Share what you're working on, what excites you, or anything on your - mind. - - - - - Approval process - - - The Pepo team will review your first video before it is shared publicly. We'll get in touch with you - ASAP! - - - - - - Get Started - - - - - - - )} + {this.showCoachForPosting()} {this.showCameraActions()} ); } + + shouldShowActionButtons = () => { + + if (this.state.isLocalVideoPresent){ + // If local video present, coach screen will not be seen. So we need to show action buttons. + return true; + } + + if (this.props.isVideoTypeReply){ + // If video type is reply and has video replies then we will not show coach and we need to show action buttons + return ! this.state.showLightBoxOnReply; + } else { + // If video type is post and terms and conditions are accepted then we will not show coach and we need to show action buttons + return this.state.acceptedCameraTnC === 'true'; + } + + }; + showCameraActions = () => { - if (this.state.acceptedCameraTnC == 'true') { + if (this.shouldShowActionButtons()) { return ( height; +const topPadding = getInset('top', landScape); +const bottomPadding = getInset('bottom', landScape); +const bottomReplyViewHeight = isIphoneX() ? 89 : 54; +const listBottomPadding = height - (height/1.5)+bottomReplyViewHeight ; + +const bottomSpace = getBottomSpace([true]); + +class VideoRepliesScreen extends PureComponent { + + static navigationOptions = ({ navigation, navigationOptions }) => { + return { + header: null, + headerBackTitle: null, + gesturesEnabled: false + }; + }; + + constructor(props){ + super(props); + this.userId = props.navigation.getParam('userId'); + this.videoId = props.navigation.getParam('videoId'); + this.fetchUrl = DataContract.replies.getReplyListApi(this.videoId); + this.initialHeight = height/1.5; + this.animatedValue = new Animated.Value(this.initialHeight) ; + this.listener = null; + this.panelAnimateTimeOut = 0 ; + + + this.state = { + showBackdrop : false, + addRepliesVisible : true, + videoUploaderVisible: false, + currentHeight : this.initialHeight, + videoReplyCount : ReduxGetters.getVideoReplyCount(this.videoId) + } + } + + componentDidMount(){ + this.listener = this.animatedValue.addListener(this.onAnimatedValueChange); + setTimeout(()=> { + this.setState({ + showBackdrop: true, + videoUploaderVisible: ReduxGetters.getVideoProcessingStatus() + }); + + }, 300) + videoUploaderComponent.on('show', this.showVideoUploader); + videoUploaderComponent.on('hide', this.hideVideoUploader); + } + + componentWillUnmount() { + this.onAnimatedValueChange= () => {}; + this.animatedValue.removeListener(this.listener); + videoUploaderComponent.removeListener('show' , this.showVideoUploader , this); + videoUploaderComponent.removeListener('hide' , this.hideVideoUploader , this); + } + + showVideoUploader = () => { + this.setState({ + videoUploaderVisible: true + }); + }; + + hideVideoUploader = ( otherStates, callback ) => { + otherStates = otherStates || {}; + this.setState({ + ...otherStates, + videoUploaderVisible: false + }, callback); + }; + + onAnimatedValueChange = ({ value }) => { + clearTimeout(this.panelAnimateTimeOut); + this.panelAnimateTimeOut = setTimeout(()=> { + if( value < 10){ + this.hideVideoUploader({ + addRepliesVisible: false + },() => { + this.props.navigation.goBack(); + }); + } + } , 10) + }; + + onCrossIconClick = () => { + this._panel.hide(); + }; + + getUploadingText = () => { + let videoType = ReduxGetters.getRecordedVideoType(); + if (videoType === 'post'){ + return "Uploading Video"; + } else if (videoType === 'reply'){ + return "Posting reply"; + } + } + ; + + openCamera = () => { + if( replyPreValidationAndMessage( this.videoId , this.userId ) ){ + let activeTab = NavigationService.getActiveTab(); + let params = getVideoReplyObject ( this.videoId , this.userId) ; + utilities.handleVideoUploadModal(activeTab, this.props.navigation, params); + } + } + + onData = ( data ) => { + this.dataLoaded = true; + } + + getWrapViewStyles = () => { + let wrapStyles = {}; + if ( !this.state.addRepliesVisible ) { + wrapStyles.display = "none"; + } + return wrapStyles; + }; + + onRefresh = ()=> { + this.setState({ + videoReplyCount : ReduxGetters.getVideoReplyCount(this.videoId) + }) + }; + + render(){ + return ( + + {this.userId && this.state.videoUploaderVisible && ( + + )} + (this._panel = c)} + containerStyle={{zIndex: 99}} + animatedValue={this.animatedValue} + onMomentumDragEnd={(value) => { + this.setState({ + currentHeight : value + }) + + }} + ref={c => (this._panel = c)} + draggableRange={{ + top: height - topPadding, + bottom: 0 + }} + showBackdrop={this.state.showBackdrop} + snappingPoints={[0, this.initialHeight, height]}> + {dragHandler => ( + + + + + + + + + + {this.state.videoReplyCount} Repl{this.state.videoReplyCount > 1 ? 'ies' : 'y'} + + {/*Send a reply with{' '}*/} + {/**/} + {/*{ Pricer.getToBT(Pricer.getFromDecimal(ReduxGetters.getBtAmountForReply(this.videoId )), 2)}*/} + + + this.initialHeight? topPadding+bottomPadding+bottomReplyViewHeight : listBottomPadding} + onRefresh={this.onRefresh} + /> + + + + )} + + {this.state.addRepliesVisible && ( + + + + + + Add a reply... + + + + + )} + + ); + } + +} + + +export default VideoRepliesScreen; + + diff --git a/src/components/VideoReplies/styles.js b/src/components/VideoReplies/styles.js new file mode 100644 index 00000000..1fd35680 --- /dev/null +++ b/src/components/VideoReplies/styles.js @@ -0,0 +1,99 @@ +import DefaultStyleGenerator from '../../theme/styles/DefaultStyleGenerator'; +import Colors from '../../theme/styles/Colors'; +import {ifIphoneX} from "react-native-iphone-x-helper"; + +let stylesMap = { + container: { + flex: 1, + zIndex: 1, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: Colors.white, + borderTopLeftRadius: 30, + borderTopRightRadius: 30, + overflow: 'hidden' + }, + dragHandler: { + height: 65, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: Colors.white, + flexDirection: 'row', + zIndex: 9, + width: '100%', + borderBottomWidth: 0.4, + borderColor: 'rgba(0, 0, 0, 0.3)' + }, + iconWrapper: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + height: 50, + width: 50 + }, + iconSkipFont: { + height: 16, + width: 16 + }, + repliesTxt: { + flex: 4, + alignItems: 'center', + marginRight:50 //equal to crossIcon width in order to maintain text in center + }, + headerStyles: { + backgroundColor: Colors.white, + borderBottomWidth: 0, + shadowColor: '#000', + shadowOffset: { + width: 0, + height: 1 + }, + shadowOpacity: 0.1, + shadowRadius: 3 + }, + headerText:{ + fontFamily: 'AvenirNext-Medium', + color: Colors.valhalla, + fontSize: 18 + }, + headerSubText:{ + fontSize: 12, + color: 'rgba(42, 41, 59, 0.7)' + }, + addReplyView : { + width:'100%', + ...ifIphoneX( + { + height: 89 + }, + { + height: 54 + }), + shadowColor:'#000', + elevation: 8, + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.8,shadowRadius: 1, + position:'absolute', + bottom:0, + zIndex:9999, + backgroundColor:Colors.white + }, + addReplyInnerView:{ + height: 54, + width: '100%', + flexDirection:'row', + alignItems: 'center', + paddingHorizontal:12, + backgroundColor:Colors.white, + }, + addReplyText : { + color:Colors.black, + marginLeft:10 + }, + addReplyImageDimensionSkipFont : { + height:10, + width:15 + } +}; + +export default styles = DefaultStyleGenerator.generate(stylesMap); diff --git a/src/components/VideoTags/index.js b/src/components/VideoTags/index.js index d61b9514..a30d7010 100644 --- a/src/components/VideoTags/index.js +++ b/src/components/VideoTags/index.js @@ -1,5 +1,7 @@ import React, { PureComponent } from 'react'; import { View } from 'react-native'; +import qs from 'qs'; + import reduxGetter from '../../services/ReduxGetters'; import VideoCollections from '../VideoCollections'; import Colors from '../../theme/styles/Colors'; @@ -79,16 +81,17 @@ class VideoTags extends PureComponent { this.didFocus && this.didFocus.remove && this.didFocus.remove(); } - - _headerComponent() { // do nothing } - getFetchUrl = () => { - return `/tags/${this.getTagId()}/videos` - - } + getFetchUrl = () => { + let baseUrl = `/tags/${this.getTagId()}/allvideos`; + let params = { + supported_entities: ['videos', 'replies'] + }; + return baseUrl + "?" + qs.stringify(params); + }; getTagId = () => { return this.props.navigation.getParam("tagId") @@ -101,7 +104,7 @@ class VideoTags extends PureComponent { getExtraParams = () => { return { - showBalanceFlier: true + showBalanceFlyer: true }; }; @@ -115,6 +118,7 @@ class VideoTags extends PureComponent { navigation={this.props.navigation} noResultsData={this.noResultsData} getNoResultsCell={this.renderNoResults} + tagId={this.getTagId()} extraParams={this.getExtraParams()} /> } else { diff --git a/src/components/Home/VideoWrapper.js b/src/components/VideoWrapper/Base.js similarity index 85% rename from src/components/Home/VideoWrapper.js rename to src/components/VideoWrapper/Base.js index 76b27141..ad79a296 100644 --- a/src/components/Home/VideoWrapper.js +++ b/src/components/VideoWrapper/Base.js @@ -1,29 +1,21 @@ import React, { PureComponent } from 'react'; import { TouchableWithoutFeedback, AppState, View, Image, ActivityIndicator } from 'react-native'; -import { connect } from 'react-redux'; import { withNavigation } from 'react-navigation'; import Video from 'react-native-video'; import inlineStyles from './styles'; -import reduxGetter from '../../services/ReduxGetters'; import playIcon from '../../assets/play_icon.png'; import PixelCall from '../../services/PixelCall'; import {VideoPlayPauseEmitter} from '../../helpers/Emitters'; import AppConfig from '../../constants/AppConfig'; import socketPixelCall from './../../services/SocketPixelCall' import CurrentUser from "../../models/CurrentUser"; +import assignIn from 'lodash/assignIn'; -const mapStateToProps = (state, ownProps) => { - return { - videoImgUrl: reduxGetter.getVideoImgUrl(ownProps.videoId, state), - videoUrl: reduxGetter.getVideoUrl(ownProps.videoId, state), - loginPopover: ownProps.isActive && state.login_popover.show - }; -}; const VIDEO_PLAY_START_EVENT_NAME = "video_play_start"; const VIDEO_PLAY_END_EVENT_NAME = "video_play_end"; -class VideoWrapper extends PureComponent { +class Base extends PureComponent { constructor(props) { super(props); this.state = { @@ -115,7 +107,7 @@ class VideoWrapper extends PureComponent { if (this.props.isActive && this.state.paused) { this.setState({ paused: false }); return; - } + } if ( this.isPaused() != this.currentPauseStatus ) { //Force render. this.forceUpdate(); @@ -167,19 +159,11 @@ class VideoWrapper extends PureComponent { fireEvent(params) { if (this.videoContext.isEventCalledOnView(CurrentUser.getUserId(), this.props.video_id)) return; if (params.currentTime >= this.minTimeConsideredForView) { - let pixelParams = { - e_entity: 'video', - e_action: 'view', - e_data_json: { - video_id: this.props.videoId, - profile_user_id: this.props.userId - }, - p_type: this.props.navigation.state.routeName === 'HomeScreen' ? 'feed' : 'user_profile' - }; + const parentData = this.props.getPixelDropData() ; + let pixelParams = { e_action: 'view' }; + pixelParams = assignIn({}, pixelParams, parentData); PixelCall(pixelParams); - this.sendFeedVideoEvent(VIDEO_PLAY_START_EVENT_NAME); - this.videoContext.eventFired(CurrentUser.getUserId(), this.props.video_id); } } @@ -199,17 +183,13 @@ class VideoWrapper extends PureComponent { onEnd = (params) => { if (this.isPixelCalledOnEnd) return; - let pixelParams = { - e_entity: 'video', - e_action: 'full_viewed', - e_data_json: { - video_id: this.props.videoId, - profile_user_id: this.props.userId - }, - p_type: this.props.navigation.state.routeName === 'HomeScreen' ? 'feed' : 'user_profile' - }; + const parentData = this.props.getPixelDropData() ; + let pixelParams = { e_action: 'full_viewed', }; + pixelParams = assignIn({}, pixelParams, parentData); PixelCall(pixelParams); this.isPixelCalledOnEnd = true; + + this.sendFeedVideoEvent(VIDEO_PLAY_END_EVENT_NAME); }; getIsVideoPausedStatus = () => { @@ -248,12 +228,16 @@ class VideoWrapper extends PureComponent { } } -VideoWrapper.defaultProps = { +Base.defaultProps = { shouldPlay: function(){ return true; }, + getPixelDropData: function(){ + console.warn("getPixelDropData props is mandatory for Video component"); + return {}; + }, doRender: true , isActive: true }; -export default connect(mapStateToProps)(withNavigation(VideoWrapper)); +export default withNavigation(Base); diff --git a/src/components/VideoWrapper/FanVideo.js b/src/components/VideoWrapper/FanVideo.js new file mode 100644 index 00000000..9b53894e --- /dev/null +++ b/src/components/VideoWrapper/FanVideo.js @@ -0,0 +1,15 @@ + +import reduxGetter from "../../services/ReduxGetters"; +import { connect } from 'react-redux'; +import Base from "./Base"; + +const mapStateToProps = (state, ownProps) => { + return { + videoImgUrl: reduxGetter.getVideoImgUrl(ownProps.videoId, state), + videoUrl: reduxGetter.getVideoUrl(ownProps.videoId, state), + loginPopover: ownProps.isActive && state.login_popover.show + }; +}; + +const FanVideo = connect(mapStateToProps)(Base); +export default FanVideo; \ No newline at end of file diff --git a/src/components/VideoWrapper/styles.js b/src/components/VideoWrapper/styles.js new file mode 100644 index 00000000..2af13b42 --- /dev/null +++ b/src/components/VideoWrapper/styles.js @@ -0,0 +1,21 @@ +import { Dimensions} from 'react-native'; +import DefaultStyleGenerator from '../../theme/styles/DefaultStyleGenerator'; + +const {width, height} = Dimensions.get('window'); + +let stylesMap = { + + fullHeightWidthSkipFont: { + width: "100%", + height: "100%" + }, + playIconSkipFont: { + position: 'absolute', + height: 25, + width: 25, + top: height * 0.5 - 12, + left: width * 0.5 - 12 + } +}; + +export default styles = DefaultStyleGenerator.generate(stylesMap); diff --git a/src/components/WalletSetting/WalletSettingController.js b/src/components/WalletSetting/WalletSettingController.js index 4dffe4b9..781d50cc 100644 --- a/src/components/WalletSetting/WalletSettingController.js +++ b/src/components/WalletSetting/WalletSettingController.js @@ -44,15 +44,15 @@ class WalletSettingController { } _initializeOptions() { - this._createOptionsData(optionIds.addSession, "Add Session", "Add Session to do transaction"); - this._createOptionsData(optionIds.walletDetails, "Wallet Details", "View your wallet details"); - this._createOptionsData(optionIds.recoverDevice, "Recover Device", "Recover your device"); - this._createOptionsData(optionIds.abortRecovery, "Abort Device Recovery", "Abort Device Recovery"); - this._createOptionsData(optionIds.resetPin, "Reset Pin", "Reset your wallet pin"); + this._createOptionsData(optionIds.addSession, "Add Session", "Add Session to do transaction."); + this._createOptionsData(optionIds.walletDetails, "Wallet Details", "View your wallet details."); + this._createOptionsData(optionIds.recoverDevice, "Recover Device", "Recover your device."); + this._createOptionsData(optionIds.abortRecovery, "Abort Device Recovery", "Abort Device Recovery."); + this._createOptionsData(optionIds.resetPin, "Reset Pin", "Reset your wallet pin."); this._createOptionsData(optionIds.viewMnemonics, "Show Mnemonics", "While not required, writing down your 12 word mnemonic phrase provides an additional backup in case you forget your PIN."); - this._createOptionsData(optionIds.authorizeWithMnemonics, "Authorize Device with Mnemonics", "Authorize current device by using mnemonics"); + this._createOptionsData(optionIds.authorizeWithMnemonics, "Authorize Device with Mnemonics", "Authorize current device by using mnemonics."); this._createOptionsData(optionIds.authorizeWithQR, "Add Another Device", "Scan QR Code to add another device."); - this._createOptionsData(optionIds.showQR, "Show Device QR Code", "Scan QR Code from the device authorized device to authorize this device"); + this._createOptionsData(optionIds.showQR, "Show Device QR Code", "Scan QR Code from the device authorized device to authorize this device."); this._createOptionsData(optionIds.updateBiometricPreference, "Enable Biometric", "Use biometrics to authorize new Sessions and to confirm high value transactions."); } diff --git a/src/components/WalletSetting/styles.js b/src/components/WalletSetting/styles.js index 5e03fa29..d57ac17c 100644 --- a/src/components/WalletSetting/styles.js +++ b/src/components/WalletSetting/styles.js @@ -5,7 +5,8 @@ let stylesMap = { list: { flex: 1, - marginTop: 15 + backgroundColor: Colors.white, + paddingTop: 15 }, title: { color: Colors.valhalla, diff --git a/src/components/WalletSetting/walletDetailsStyles.js b/src/components/WalletSetting/walletDetailsStyles.js index 1ca75d48..61227ec5 100644 --- a/src/components/WalletSetting/walletDetailsStyles.js +++ b/src/components/WalletSetting/walletDetailsStyles.js @@ -5,6 +5,7 @@ let stylesMap = { list: { flex: 1, + backgroundColor: Colors.white }, title: { color: Colors.grey, diff --git a/src/constants/AppConfig.js b/src/constants/AppConfig.js index 430e6147..e633a791 100644 --- a/src/constants/AppConfig.js +++ b/src/constants/AppConfig.js @@ -3,12 +3,28 @@ import balance_header_pepo_icon from '../assets/balance_header_pepo_icon.png'; import twitterDisconnectIcon from '../assets/drawer-twitter-icon.png'; import defaultLinkIcon from '../assets/default_link_icon.png'; import feedLinkIcon from '../assets/Link.png'; +import {getBottomSpace, ifIphoneX} from "react-native-iphone-x-helper"; +import {CUSTOM_TAB_Height} from "../theme/constants"; +import NotchHelper from "../helpers/NotchHelper"; +import {Dimensions, NativeModules, StatusBar} from "react-native"; + +const statusBarHeight = StatusBar.currentHeight; +const {height} = Dimensions.get('window'); +let RNDeviceInfo = NativeModules.RNDeviceInfo; +let modalDeviceName = RNDeviceInfo.model === "Redmi Note 7 Pro" && RNDeviceInfo.brand === "xiaomi"; +let btmSpace = modalDeviceName ? 5 : 0; const PROFILE_TX_SEND_SUCCESS = 'PROFILE_TX_SEND_SUCCESS', PROFILE_TX_RECEIVE_SUCCESS = 'PROFILE_TX_RECEIVE_SUCCESS', VIDEO_TX_SEND_SUCCESS = 'VIDEO_TX_SEND_SUCCESS', VIDEO_TX_RECEIVE_SUCCESS = 'VIDEO_TX_RECEIVE_SUCCESS', VIDEO_ADD = 'VIDEO_ADD', + USER_MENTION = 'USER_MENTION', + REPLY_USER_MENTION = 'REPLY_USER_MENTION', + REPLY_SENDER_WITH_AMOUNT = 'REPLY_SENDER_WITH_AMOUNT', + REPLY_SENDER_WITHOUT_AMOUNT = 'REPLY_SENDER_WITHOUT_AMOUNT', + REPLY_RECEIVER_WITH_AMOUNT = 'REPLY_RECEIVER_WITH_AMOUNT', + REPLY_RECEIVER_WITHOUT_AMOUNT = 'REPLY_RECEIVER_WITHOUT_AMOUNT', CONTRIBUTION_THANKS = 'CONTRIBUTION_THANKS', SYSTEM_NOTIFICATION = 'SYSTEM_NOTIFICATION', PROFILE_TX_SEND_FAILURE = 'PROFILE_TX_SEND_FAILURE', @@ -22,7 +38,8 @@ export default { logoutTimeOut : 2000, beKnownErrorCodeMaps : { - entityDeleted: "not_found" + entityDeleted: "not_found", + validateUploadError: "precondition_failed" }, userStatusMap: { @@ -35,6 +52,10 @@ export default { deleted: 'deleted' }, + replyStatusMap:{ + deleted: 'deleted' + }, + deviceStatusMap: { registered: 'registered', authorizing: 'authorizing', @@ -54,6 +75,11 @@ export default { name: 'profile' }, + replyMetaProperties: { + type: 'user_to_user', + name: 'reply_on_video' + }, + redemptionMetaProperties : { type: 'user_to_company', name: 'redemption' @@ -141,6 +167,12 @@ export default { videoTxSendKind: VIDEO_TX_SEND_SUCCESS, videoTxReceiveKind: VIDEO_TX_RECEIVE_SUCCESS, videoAddKind: VIDEO_ADD, + userMention: USER_MENTION, + replyUserMention: REPLY_USER_MENTION, + replySenderWithAmount: REPLY_SENDER_WITH_AMOUNT, + replySenderWithoutAmount: REPLY_SENDER_WITHOUT_AMOUNT, + replyReceiverWithAmount: REPLY_RECEIVER_WITH_AMOUNT, + replyReceiverWithoutAmount: REPLY_RECEIVER_WITHOUT_AMOUNT, AppreciationKind: CONTRIBUTION_THANKS, systemNotification: SYSTEM_NOTIFICATION, airDropNotification: AIRDROP_DONE, @@ -154,8 +186,9 @@ export default { PROFILE_TX_SEND_FAILURE, VIDEO_TX_SEND_FAILURE, AIRDROP_DONE, - TOPUP_DONE - + TOPUP_DONE, + REPLY_SENDER_WITH_AMOUNT, + REPLY_RECEIVER_WITH_AMOUNT ], whitelistedNotificationKinds: [ PROFILE_TX_SEND_SUCCESS, @@ -291,7 +324,71 @@ export default { RedemptionSuccess: 'Redemption/Success', CouchMarks: 'CouchMarks', AuthDeviceDrawer: 'DeviceUnauthorized', - TwitterLogin: 'TwitterLogin' + TwitterLogin: 'TwitterLogin', + FullScreenReplyCollection: 'FullScreenReplyCollection', + VideoReplyPlayer: 'VideoReplyPlayer', + VideoReplies: 'VideoReplies' + }, + default_bt_amt : 10, + + videoTypes: { + post : 'post', + reply: 'reply' + }, + + MaxDescriptionArea: 35250, + thumbnailListConstants: { + + + // NOTE: Outer Circle Configs. + // ----------------------------------------------------------------------- + // 1. outerRingDiameter - Replaces old iconHeight/iconWidth. + // + // 2. borderWidth - The border width is applied outside the + // this iconHeight (Diameter). + // + // 3. transparentGap - Gap between the inner edge of Outer-Circle + // and icon itself. + // + // 4. iconImageDiameter - Diameter of icon Image is computed + // using outerRingDiameter, outerBorderWidth + // and transparentGap. + // ----------------------------------------------------------------------- + outerRingDiameter: 48, + outerBorderWidth: 2, + transparentGap: 2, + iconImageDiameter: function () { + return this.outerRingDiameter - (2 * (this.outerBorderWidth + this.transparentGap )); + }, + + // cellHeight - Gives actual height of cell. + cellHeight: function() { + return this.outerRingDiameter + (2 * this.outerBorderWidth); + }, + + separatorHeight: 25, + separatorWidth: 1, + separatorColor: 'white', + //separatorMargin - Margin to draw over the transparent outer ring (to be applied in negative); + separatorMargin: function () { + return this.outerBorderWidth + this.transparentGap; + }, + + parentIconHeight: 46, + parentIconWidth: 46 + }, + VideoScreenObject : { + ...ifIphoneX( + { + height: height - CUSTOM_TAB_Height - getBottomSpace([true]) + }, + { + height: + NotchHelper.hasNotch() + ? height + statusBarHeight - CUSTOM_TAB_Height - btmSpace + : height - CUSTOM_TAB_Height + } + ) } }; diff --git a/src/constants/DataContract.js b/src/constants/DataContract.js index a0e5f2c0..ca562d9a 100644 --- a/src/constants/DataContract.js +++ b/src/constants/DataContract.js @@ -37,8 +37,59 @@ export default { infoApi: "/support/info" }, + replies: { + validateReply: "/replies/validate-upload", + getReplyListApi : (id) => { + return `/videos/${id}/replies` + }, + getDeleteVideoReplyApi : (id) => { + return `/replies/${id}/delete`; + }, + validatePost: "/videos/validate-upload", + getSingleVideoReplyApi : (id) => { + return `/replies/${id}`; + }, + videoReplyKind: { + video: "VIDEO" + }, + replyDetailIdKey: 'reply_detail_id', + parentVideoIdKey: 'parent_id', + creatorUserIdKey: 'creator_user_id', + replyDetailsKey: 'reply_details' + }, + common: { resultType : "data.result_type" + }, + + mentions: { + userMentions: "/search/users-mention" + }, + + tags: { + userTags: "/tags" + }, + + videos: { + getDeleteVideoApi : (id) => { + return `/videos/${id}/delete`; + }, + videoKind: { + reply : "VIDEO_REPLY", + video:"FAN_UPDATE" + }, + kindKey: "kind", + creatorUserIdKey: 'creator_user_id', + videoDetailsKey: 'video_details' + }, + + share: { + getVideoShareApi: ( id ) => { + return `/videos/${id}/share`; + }, + getVideoReplyShareApi: ( id ) => { + return `/replies/${id}/share`; + } } } diff --git a/src/helpers/Emitters.js b/src/helpers/Emitters.js index c63dce6a..13a77023 100644 --- a/src/helpers/Emitters.js +++ b/src/helpers/Emitters.js @@ -1,5 +1,6 @@ import EventEmitter from 'eventemitter3'; let VideoPlayPauseEmitter = new EventEmitter(); let DrawerEmitter = new EventEmitter(); +let VideoReplyEmitter = new EventEmitter(); -export { VideoPlayPauseEmitter, DrawerEmitter }; +export { VideoPlayPauseEmitter, DrawerEmitter, VideoReplyEmitter }; diff --git a/src/helpers/EntityHelper.js b/src/helpers/EntityHelper.js new file mode 100644 index 00000000..b8ff4ca8 --- /dev/null +++ b/src/helpers/EntityHelper.js @@ -0,0 +1,35 @@ +import deepGet from 'lodash/get'; +import DataContract from '../constants/DataContract'; +import ReduxGetters from '../services/ReduxGetters'; + +class EntityHelper { + + getEntityType( entity ){ + const type = deepGet(entity,`${DataContract.videos.kindKey}`); + return type; + } + + getReplyKind( entity ){ + const replyDetailId = deepGet(entity,`payload.${DataContract.replies.replyDetailIdKey}`); + const replyKind = ReduxGetters.getReplyKind(replyDetailId); + return replyKind; + } + + isVideoEntity( entity ){ + const type = this.getEntityType(entity); + return type == DataContract.videos.videoKind.video; + } + + isVideoReplyEntity( entity ){ + const type = this.getEntityType(entity); + return type == DataContract.videos.videoKind.reply; + } + + isReplyVideoTypeEntity( entity ){ + const replyKind = this.getReplyKind( entity ); + return replyKind == DataContract.replies.videoReplyKind.video; + } + +} + +export default new EntityHelper(); \ No newline at end of file diff --git a/src/helpers/NavigationAnimation.js b/src/helpers/NavigationAnimation.js new file mode 100644 index 00000000..0e9b2e13 --- /dev/null +++ b/src/helpers/NavigationAnimation.js @@ -0,0 +1,102 @@ + +import { Easing, Animated,Platform } from 'react-native'; + + +export default { + zoomIn: ( duration=300 ) =>{ + return { + transitionSpec: { + duration, + easing: Easing.out(Easing.poly(4)), + timing: Animated.timing, + useNativeDriver: true, + }, + screenInterpolator: ({ position, scene }) => { + const { index } = scene; + let start = 0; + if (Platform.OS !== 'ios') { + start = 0.005 + } + + const scale = position.interpolate({ + inputRange: [index - 1, index , index + 1], + outputRange: [start, 1 , 1], + }); + + return { transform: [{ scale }] }; + }, + } + }, + zoomOut : ( duration=300 ) => { + return { + transitionSpec: { + duration, + easing: Easing.out(Easing.poly(4)), + timing: Animated.timing, + useNativeDriver: true, + }, + screenInterpolator: ({ position, scene }) => { + const { index } = scene; + + const scale = position.interpolate({ + inputRange: [index - 1, index], + outputRange: [10, 1], + }); + + return { transform: [{ scale }] }; + }, + } + }, + fromBottom : (duration = 300) => { + return { + transitionSpec: { + duration, + easing: Easing.out(Easing.poly(4)), + timing: Animated.timing, + useNativeDriver: true, + }, + screenInterpolator: ({ layout, position, scene }) => { + const { index } = scene; + const { initHeight } = layout; + + const translateY = position.interpolate({ + inputRange: [index - 1, index, index + 1], + outputRange: [initHeight, 0, 0], + }); + + const opacity = position.interpolate({ + inputRange: [index - 1, index - 0.99, index], + outputRange: [0, 1, 1], + }); + + return { opacity, transform: [{ translateY }] }; + }, + }; + }, + defaultTransition: ( duration=300 ) => { + return { + transitionSpec: { + duration: 300, + easing: Easing.out(Easing.poly(4)), + timing: Animated.timing + }, + screenInterpolator: (sceneProps) => { + const { layout, position, scene } = sceneProps; + const { index } = scene; + + const height = layout.initHeight; + const translateY = position.interpolate({ + inputRange: [index - 1, index, index + 1], + outputRange: [height, 0, 0] + }); + + const opacity = position.interpolate({ + inputRange: [index - 1, index - 0.99, index], + outputRange: [0, 1, 1] + }); + + return { opacity, transform: [{ translateY }] }; + } + } + } +} \ No newline at end of file diff --git a/src/helpers/NotchHelper.js b/src/helpers/NotchHelper.js index 09340d93..005da3bd 100644 --- a/src/helpers/NotchHelper.js +++ b/src/helpers/NotchHelper.js @@ -1,6 +1,4 @@ -import {NativeModules} from "react-native"; -const RNDeviceInfo = NativeModules.RNDeviceInfo; -import PepoApi from "../services/PepoApi"; +import DeviceInfo from 'react-native-device-info'; let devicesWithNotch = [ { @@ -123,6 +121,10 @@ let devicesWithNotch = [ "brand": "HONOR", "model": "COR-AL00" }, + { + "brand": "HONOR", + "model": "JSN-L42" + }, { "brand": "OnePlus", "model": "ONEPLUS A6010" @@ -132,21 +134,36 @@ let devicesWithNotch = [ class NotchHelper { syncList(){ - let listUrl = "https://d3attjoi5jlede.cloudfront.net/pepo-app/devices-list/notch-devices.json"; - new PepoApi(listUrl).get().then((res)=> { - const devices = res && res.devices; - if( devices && devices instanceof Array && devices.length < 1 ) { - devicesWithNotch = devices; + fetch(`https://d3attjoi5jlede.cloudfront.net/pepo-app/devices-list/notch-devices.json?${Date.now()}`) + .then((response) => response.json()) + .then((responseJson) => { + const devices = responseJson && responseJson.devices; + if( devices && devices instanceof Array && devices.length > 0 ) { + devicesWithNotch = devices; + } + }) + .catch((error) => { + console.log("Notch helper fetch error ignored"); + }); + } + + hasNotchRemote(){ + if(devicesWithNotch.length > 0){ + return ( + devicesWithNotch.findIndex( + item => + item.brand.toLowerCase() === DeviceInfo.getBrand().toLowerCase() && + (item.model.toLowerCase() === DeviceInfo.getDeviceName().toLowerCase() || item.model.toLowerCase() === DeviceInfo.getModel().toLowerCase()) + ) !== -1 + ); } - }).catch((error)=> { - console.log("ignore") - }) + return false; } hasNotch() { - return devicesWithNotch.findIndex(item => item.brand === RNDeviceInfo.brand && ( item.model === RNDeviceInfo.deviceName || item.model === RNDeviceInfo.model )) !== -1; + return DeviceInfo.hasNotch() || this.hasNotchRemote(); } } -export default new NotchHelper() \ No newline at end of file +export default new NotchHelper() diff --git a/src/helpers/ReplyHelper.js b/src/helpers/ReplyHelper.js new file mode 100644 index 00000000..8c3d9015 --- /dev/null +++ b/src/helpers/ReplyHelper.js @@ -0,0 +1,19 @@ +import deepGet from 'lodash/get'; + +import store from '../store'; +import ReduxGetters from "../services/ReduxGetters"; +import { upsertCurrentUserReplyDetailRelationEntities } from '../reducers'; +import Utilities from '../services/Utilities'; +import DataContract from '../constants/DataContract'; + + +function updateEntitySeen (item) { + const replyDetailId = deepGet(item,`payload.${DataContract.replies.replyDetailIdKey}`); + let currentUserReplyDetailRelationEntity = ReduxGetters.getReplyDetailRelationEntity( replyDetailId ); + if(currentUserReplyDetailRelationEntity){ + currentUserReplyDetailRelationEntity['has_seen'] = true; + store.dispatch(upsertCurrentUserReplyDetailRelationEntities(Utilities._getEntityFromObj(currentUserReplyDetailRelationEntity))); + } + } + +export default { updateEntitySeen } \ No newline at end of file diff --git a/src/helpers/TransactionHelper.js b/src/helpers/TransactionHelper.js index 5fa262b3..473d872d 100644 --- a/src/helpers/TransactionHelper.js +++ b/src/helpers/TransactionHelper.js @@ -1,12 +1,10 @@ import BigNumber from 'bignumber.js'; -import {OstWalletSdk, OstWalletSdkUI, OstJsonApi} from '@ostdotcom/ost-wallet-sdk-react-native'; +import {OstWalletSdk, OstWalletSdkUI} from '@ostdotcom/ost-wallet-sdk-react-native'; import { IS_PRODUCTION, DEFAULT_SESSION_KEY_EXPIRY_TIME, DEFAULT_SPENDING_LIMIT, MAX_SPENDING_LIMIT, HIGH_SPEND_SESSION_KEY_EXPIRY_TIME, MEDIUM_SPEND_SESSION_KEY_EXPIRY_TIME } from '../constants'; import CurrentUser from "../models/CurrentUser"; -import pricer from '../services/Pricer'; -import Toast from '../theme/components/NotificationToast'; import {LoadingModal} from '../theme/components/LoadingModalCover'; import {ostSdkErrors, WORKFLOW_CANCELLED_MSG} from '../services/OstSdkErrors'; import {ostErrors} from "../services/OstErrors"; @@ -14,7 +12,7 @@ import {VideoPlayPauseEmitter} from './Emitters' const ON_USER_CANCLLED_ERROR_MSG = WORKFLOW_CANCELLED_MSG; -const DEVICE_UNAUTHORIZED_ERROR_MSG = "Your device is not authorized to perform transactions";; +const DEVICE_UNAUTHORIZED_ERROR_MSG = "Your device is not authorized to perform transactions"; const bnDefaultSpendingLimit = new BigNumber(DEFAULT_SPENDING_LIMIT); const bnMaxSpendingLimit = new BigNumber(MAX_SPENDING_LIMIT); const bnOne = new BigNumber(1); diff --git a/src/helpers/cameraHelper.js b/src/helpers/cameraHelper.js new file mode 100644 index 00000000..8399de6e --- /dev/null +++ b/src/helpers/cameraHelper.js @@ -0,0 +1,68 @@ +import AppConfig from '../constants/AppConfig' +import NavigationService from "../services/NavigationService"; +import Utilities from "../services/Utilities"; +import CurrentUser from "../models/CurrentUser"; +import { ostErrors } from "../services/OstErrors"; +import Pricer from "../services/Pricer"; +import ReduxGetters from "../services/ReduxGetters"; +import Toast from '../theme/components/NotificationToast'; + + +// creatorUserId is mandatory as without it token holder address can not be accessed. +const getVideoReplyObject = (videoId, creatorUserId) => { + return { + videoType : AppConfig.videoTypes.reply, + videoId: videoId, + userId: creatorUserId, + amount: ReduxGetters.getBtAmountForReply(videoId), + videoReplyCount: ReduxGetters.getVideoReplyCount(videoId), + isChargeble: ReduxGetters.isVideoIsChargeable(videoId), + toTokenHolderAddress: ReduxGetters.getUser( creatorUserId ).ost_token_holder_address + }; +}; + + +const navigateToCamera = (videoId , userId , navigation) => { + let activeTab = NavigationService.getActiveTab(); + let params = getVideoReplyObject ( videoId , userId); + Utilities.handleVideoUploadModal(activeTab, navigation, params); +}; + +const replyPreValidationAndMessage = (videoId , userId) => { + + if(!videoId || !userId){ + console.warn("replyPreValidationAndMessage missing videoId - " + videoId + "or userId - " + userId ); + return false; + } + + if(!CurrentUser.isUserActivated( true )){ + return false; + } + + const isReplyAllowed = ReduxGetters.isReplyAllowed(videoId) , + isVideoUserActivated = Utilities.isUserActivated(ReduxGetters.getUserActivationStatus(userId)) + ; + + if( !isReplyAllowed || !isVideoUserActivated ){ + Toast.show({ + text: ostErrors.getUIErrorMessage("video_reply_not_allowed"), + icon: 'error' + }); + return false; + } + + const requiredPepo = ReduxGetters.getBtAmountForReply(videoId); + + if(Pricer.getWeiToNumber(ReduxGetters.getBalance()) < Pricer.getWeiToNumber(requiredPepo)){ + Toast.show({ + text: ostErrors.getUIErrorMessage("video_reply_not_allowed_low_bal"), + icon: 'error' + }); + return false ; + } + + return true; + +} + +export { getVideoReplyObject , navigateToCamera , replyPreValidationAndMessage} diff --git a/src/helpers/helpers.js b/src/helpers/helpers.js index e0e6d203..91b51a60 100644 --- a/src/helpers/helpers.js +++ b/src/helpers/helpers.js @@ -33,6 +33,20 @@ function fetchUser(userId, onResponse, errorCallback, finallyCallback) { }); } +function fetchVideo( videoId , onResponse, onError, onComplete ){ + return new PepoApi(`/videos/${videoId}`) + .get() + .then((res) => { + onResponse && onResponse(res); + }) + .catch((error) => { + onError && onError(error); + }) + .finally(() => { + onComplete && onComplete(); + }); +} + function getSocialIcon(url, screen) { let hostName = url && url.match(/^(?:https?:\/\/)?(?:[^@\/\n]+@)?([^:\/?\n]+)/im); if ( !hostName || hostName.length < 2) { @@ -53,4 +67,4 @@ function getHostName( url ){ return url.match(/^(?:https?:\/\/)?(?:[^@\/\n]+@)?(?:www\.)?([^:\/?\n]+)/im)[1]; } -export { fetchUser, getHostName }; +export { fetchUser, getHostName , fetchVideo}; diff --git a/src/helpers/navigateTo.js b/src/helpers/navigateTo.js index 3d6c0206..c2f54e01 100644 --- a/src/helpers/navigateTo.js +++ b/src/helpers/navigateTo.js @@ -41,6 +41,8 @@ class NavigateTo { this.goToSupporters(goToObject.v.puid, payload); } else if (goToObject && goToObject.pn === 'v') { this.goToVideo(goToObject.v.vid, payload); + }else if (goToObject && goToObject.pn === 'rd') { + this.goToVideoReply(goToObject.v.rdi, payload); } else if (goToObject && goToObject.pn === 'f') { this.__navigate('Home', payload); } else if (goToObject && goToObject.pn === 'nc') { @@ -122,6 +124,21 @@ class NavigateTo { }, timeOut) }; + goToVideoReply = (rdId, payload) => { + let timeOut = 0 ; + payload = payload || {}; + payload['replyDetailId'] = rdId; + if(NavigationService.getActiveTab() != "Notification"){ + timeOut = 100; + this.__navigate('NotificationScreen', payload); + } + //Once migrated to react-navigation version 4 remove the settimeout code + //as version 3 dosent provides navigation chaining. + setTimeout(()=> { + this.__push('VideoReplyPlayer', payload); + }, timeOut) + }; + goToSupportings = (profileId, payload) => { payload = payload || {}; payload['userId'] = profileId; diff --git a/src/reducers/index.js b/src/reducers/index.js index fc0be1b0..4743b9e0 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -10,6 +10,7 @@ export const { hideModalCover, showLoginPopover, hideLoginPopover, + showConnectingLoginPopover, showToast, hideToast, upsertUserEntities, @@ -25,6 +26,9 @@ export const { upsertUserStatEntities, upsertVideoEntities, upsertVideoStatEntities, + upsertReplyDetailEntities, + upsertCurrentUserVideoRelationEntities, + upsertCurrentUserReplyDetailRelationEntities, upsertImageEntities, upsertHomeFeedEntities, updateBalance, @@ -34,6 +38,7 @@ export const { updatePepocorn, updateExecuteTransactionStatus, upsertVideoContributionEntities, + upsertReplyContributionEntities, upsertUserContributionEntities, upsertRecordedVideo, clearRecordedVideo, @@ -68,6 +73,9 @@ const defaultState = { link_entities: {}, video_entities: {}, video_stat_entities: {}, + reply_detail_entities: {}, + current_user_video_relation_entities: {}, + current_user_reply_detail_relation_entities: {}, video_description_entities: {}, image_entities: {}, home_feed_entities: {}, @@ -99,6 +107,9 @@ const logoutDefault = { link_entities: {}, video_entities: {}, video_stat_entities: {}, + reply_detail_entities: {}, + current_user_video_relation_entities: {}, + current_user_reply_detail_relation_entities: {}, video_description_entities: {}, image_entities: {}, home_feed_entities: {}, @@ -119,10 +130,14 @@ export const reducer = handleActions( { [showModal]: (state, action) => ({ ...state, modal: action.payload.modal }), [showModalCover]: (state, action) => ({ ...state, modal_cover: action.payload.modal_cover }), + [showLoginPopover]: (state, action) => ({ ...state, login_popover: action.payload.login_popover }), + [showConnectingLoginPopover]: (state, action) => ({ ...state, login_popover: action.payload.login_popover }), + [hideLoginPopover]: (state, action) => ({ ...state, login_popover: action.payload.login_popover }), + [hideModal]: (state, action) => ({ ...state, modal: action.payload.modal }), [hideModalCover]: (state, action) => ({ ...state, modal_cover: action.payload.modal_cover }), - [hideLoginPopover]: (state, action) => ({ ...state, login_popover: action.payload.login_popover }), + [showToast]: (state, action) => ({ ...state, toast: action.payload.toast }), [hideToast]: (state, action) => ({ ...state, toast: action.payload.toast }), [upsertUserEntities]: (state, action) => ({ @@ -173,6 +188,18 @@ export const reducer = handleActions( ...state, video_stat_entities: assignIn({}, state.video_stat_entities, action.payload.video_stat_entities) }), + [upsertReplyDetailEntities]: (state, action) => ({ + ...state, + reply_detail_entities: assignIn({}, state.reply_detail_entities, action.payload.reply_detail_entities) + }), + [upsertCurrentUserVideoRelationEntities]: (state, action) => ({ + ...state, + current_user_video_relation_entities: assignIn({}, state.current_user_video_relation_entities, action.payload.current_user_video_relation_entities) + }), + [upsertCurrentUserReplyDetailRelationEntities]: (state, action) => ({ + ...state, + current_user_reply_detail_relation_entities: assignIn({}, state.current_user_reply_detail_relation_entities, action.payload.current_user_reply_detail_relation_entities) + }), [upsertVideoDescriptionEntities]: (state, action) => ({ ...state, video_description_entities: assignIn( @@ -205,6 +232,14 @@ export const reducer = handleActions( action.payload.video_contribution_entities ) }), + [upsertReplyContributionEntities]: (state, action) => ({ + ...state, + reply_contribution_entities: assignIn( + {}, + state.reply_contribution_entities, + action.payload.reply_contribution_entities + ) + }), [upsertUserContributionEntities]: (state, action) => ({ ...state, user_contribution_entities: assignIn( @@ -221,10 +256,22 @@ export const reducer = handleActions( ...state, balance: action.payload.isPurchase }), - [updatePricePoints]: (state, action) => ({ - ...state, - price_points: action.payload.price_points - }), + [updatePricePoints]: (state, action) => { + //Make sure price_points is not null; + if (!action.payload.price_points) { + return {...state}; + } + + // Make sure response has keys; + if ( !Object.keys(action.payload.price_points).length ) { + return {...state}; + } + + return { + ...state, + price_points: action.payload.price_points + }; + }, [updateToken]: (state, action) => ({ ...state, token: action.payload.token diff --git a/src/services/CameraWorker.js b/src/services/CameraWorker.js index a262e788..af2e645c 100644 --- a/src/services/CameraWorker.js +++ b/src/services/CameraWorker.js @@ -2,6 +2,9 @@ import React, { PureComponent } from 'react'; import { connect } from 'react-redux'; import RNFS from 'react-native-fs'; import Store from '../store'; +import deepGet from 'lodash/get'; +import clone from 'lodash/clone'; + import { upsertRecordedVideo, @@ -20,7 +23,11 @@ import videoUploaderComponent from './CameraWorkerEventEmitter'; import createObjectForRedux from '../helpers/createObjectForRedux'; import Toast from '../theme/components/NotificationToast'; import CurrentUser from '../models/CurrentUser'; - +import DataContract from "../constants/DataContract"; +import {TransactionExecutor} from './TransactionExecutor'; +import { ostSdkErrors } from '../services/OstSdkErrors'; +import AppConfig from '../constants/AppConfig'; +import { fetchVideo } from '../helpers/helpers'; const recordedVideoStates = [ 'raw_video', 'compressed_video', @@ -28,7 +35,10 @@ const recordedVideoStates = [ 'cover_image', 's3_cover_image', 'video_desc', - 'video_link' + 'video_link', + 'reply_amount', + 'video_type', + 'reply_obj' ]; const processingStatuses = [ @@ -87,6 +97,20 @@ class CameraWorker extends PureComponent { } } + VideoUploadStatusToProcessing = () => { + if (this.isCleanUpCalled || this.props.recorded_video.do_discard || ! this.props.recorded_video.do_upload) { + console.log('VideoUploadStatusToProcessing:: return condition'); + return; + } + Store.dispatch(videoInProcessing(true)); + videoUploaderComponent.emit('show'); + }; + + VideoUploadStatusToNotProcessing = () => { + Store.dispatch(videoInProcessing(false)); + videoUploaderComponent.emit('hide'); + }; + async processVideo() { // Early exit if ( ! this.props.currentUserId || Object.keys(this.props.recorded_video).length === 0) { @@ -113,28 +137,320 @@ class CameraWorker extends PureComponent { } if (this.props.recorded_video.do_upload) { - !ReduxGetters.getVideoProcessingStatus() && Store.dispatch(videoInProcessing(true)); + if (!ReduxGetters.getVideoProcessingStatus()) { + console.log('processVideo :: VideoUploadStatusToProcessing'); + this.VideoUploadStatusToProcessing(); + } console.log( 'processVideo :: Got upload consent. Uploading video and cover image to s3 and attempting post Video with Cover Image...' ); - this.updateProfileViewRawVideo(); - await this.uploadVideo(); - await this.uploadCoverImage(); - await this.postVideoWithCoverImage(); + await this.processVideoBasedOnType (); + } } - updateProfileViewRawVideo() { - if (this.props.recorded_video.cover_image && !this.props.recorded_video.video_s3_upload_processing) { - // this.updateProfileViewVideo(this.props.recorded_video.cover_image, this.props.recorded_video.raw_video); + + processVideoBasedOnType = async () => { + let videoType = this.props.recorded_video.video_type; + if (! videoType){ + return; + } + if (videoType === 'post'){ + await this.processPostVideo(); + } else if (videoType === 'reply'){ + await this.processReplyVideo(); } + }; + + + processPostVideo = async () => { + await this.uploadVideo(); + await this.uploadCoverImage(); + await this.postVideoToPepoApi(); + }; + + + processReplyVideo = async () => { + console.log( + '-----processReplyVideo--------' + ); + + // let sessionCreated = await this.ensureSession(); + // + // if (! sessionCreated) { + // return ; + // } + await this.uploadReplyVideo(); + await this.executeTransaction() + + + }; + + videoUploadedSuccessCallback = ( ostWorkflowContext, ostWorkflowEntity ) => { + console.log('CameraWorker.videoUploadedSuccessCallback'); + Toast.show({ + text: 'Your video uploaded successfully.', + icon: 'success', + imageUri: this.props.recorded_video.cover_image + }); + this.executeTx = false; + Store.dispatch( + upsertRecordedVideo({ + do_discard: true, + pepo_api_posting: false, + }) + ); + this.fetchParentVideo(); + }; + + fetchParentVideo = () => { + const videoId = deepGet(this.props.recorded_video , 'reply_obj.replyReceiverVideoId'); + fetchVideo( videoId ); + }; + + onFlowInterrupt = (ostWorkflowContext, error) => { + console.log('CameraWorker.onFlowInterrupt', ostWorkflowContext, error); + console.log('onFlowInterrupt::VideoUploadStatusToNotProcessing'); + this.executionFailed(); + Toast.show({ + text: ostSdkErrors.getErrorMessage(ostWorkflowContext, error), + icon: 'error' + }); + }; + + + executionFailed = () => { + Store.dispatch( + upsertRecordedVideo({ + do_upload: false, + go_for_tx: false + }) + ); + this.executeTx = false; + this.VideoUploadStatusToNotProcessing(); + this.executingTx = false; } + + getSdkMetaProperties = () => { + const metaProperties = clone(AppConfig.replyMetaProperties); + let parentVideoId = deepGet(this.props.recorded_video , 'reply_obj.replyReceiverVideoId'), + replyDetailId = deepGet(this.props.recorded_video , 'reply_obj.replyDetailId') + ; + + if (! parentVideoId || ! replyDetailId ){ + return ; + } + + let details = `vi_${parentVideoId} `; + details += `rdi_${replyDetailId}`; + metaProperties['details'] = details; + return metaProperties; + }; + + isReplyChargeable = () => { + let isChargeable = deepGet (this.props.recorded_video, 'reply_obj.isChargeble'), + amountToSendWithReply = deepGet(this.props.recorded_video, 'reply_obj.amountToSendWithReply'); + return isChargeable && amountToSendWithReply != '0'; + }; + + onPlatfromAcknowledgeComplete = (videoId) => { + new PepoApi(`/videos/${videoId}`) + .get() + .then((res) => {}) + .catch((error) => {}); + }; + + + + + executeTransaction = () => { + + if (this.checkIfDiscardedAndClean()){ + return; + } + + let goForTx = this.props.recorded_video.go_for_tx, + doDiscard = this.props.recorded_video.do_discard, + receiverUserId = deepGet (this.props.recorded_video, 'reply_obj.replyReceiverUserId'), + amountToSendWithReply = deepGet(this.props.recorded_video, 'reply_obj.amountToSendWithReply'), + toTokenHolderAddress = deepGet(this.props.recorded_video,'reply_obj.toTokenHolderAddress' ), + parentVideoId = deepGet(this.props.recorded_video , 'reply_obj.replyReceiverVideoId'); + if (! goForTx || ! receiverUserId || doDiscard || this.executeTx ){ + return; + } + this.executeTx = true; + console.log('executeTransaction::VideoUploadStatusToProcessing'); + this.VideoUploadStatusToProcessing(); + console.log('CameraWorker.executeTransaction'); + + if (! this.isReplyChargeable()) { + this.videoUploadedSuccessCallback(); + return; + } + + upsertRecordedVideo({ + executing_tx: true + }); + + + let callbacks = { + onRequestAcknowledge: this.videoUploadedSuccessCallback, + onFlowInterrupt: this.onFlowInterrupt, + onPlatfromAcknowledgeComplete: () => {this.onPlatfromAcknowledgeComplete(parentVideoId)} + }; + let config = {metaProperties: this.getSdkMetaProperties()}; + let txExecutor = new TransactionExecutor(config, callbacks); + txExecutor.sendTransactionToSdk( amountToSendWithReply, toTokenHolderAddress, false); + // todo : Execute transaction code. call clean up after that. + + }; + + checkIfDiscardedAndClean = () => { + if (this.props.recorded_video.do_discard) { + console.log( + 'processVideo :: Discarding video... Cleaning up Async, Stop Compression, remove files from cache, cleanup Redux' + ); + this.cleanUp(); + return true; + } + }; + + + uploadReplyVideo = async () => { + console.log('CameraWorker.uploadReplyVideo'); + await this.uploadVideo(); + await this.uploadCoverImage(); + await this.postReplyVideoToPepoApi() ; + }; + + + postReplyVideoToPepoApi = async () => { + + if (this.checkIfDiscardedAndClean()){ + return; + } + + let readyForTx = deepGet(this.props.recorded_video , 'go_for_tx' ), + parentVideoId = deepGet(this.props.recorded_video , 'reply_obj.replyReceiverVideoId'); + + if ( readyForTx || !parentVideoId ) { + // we have reply OR we dont have video Id + return true + } + + if ( + this.props.recorded_video.s3_video && + !this.props.recorded_video.pepo_api_posting && + !this.postToPepoApi + ) { + this.postToPepoApi = true; + let videoInfo = await RNFS.stat(this.props.recorded_video.compressed_video); + let videoSize = videoInfo.size; + let imageInfo, imageSize; + if(this.props.recorded_video.cover_image !== INVALID){ + imageInfo = await RNFS.stat(this.props.recorded_video.cover_image); + imageSize = imageInfo.size; + } + console.log('CameraWorker.postReplyVideoToPepoApi'); + Store.dispatch( + upsertRecordedVideo({ + pepo_api_posting: true + }) + ); + + let payloadWithoutImage = { + video_url: this.props.recorded_video.s3_video, + video_description: this.props.recorded_video.video_desc, + link: this.props.recorded_video.video_link, + video_width: appConfig.cameraConstants.VIDEO_WIDTH, + video_height: appConfig.cameraConstants.VIDEO_HEIGHT, + image_width: appConfig.cameraConstants.VIDEO_WIDTH, + image_height: appConfig.cameraConstants.VIDEO_HEIGHT, + video_size: videoSize, + parent_kind: 'video', + parent_id: parentVideoId + }; + + let payload = payloadWithoutImage; + + if(this.props.recorded_video.cover_image !== INVALID){ + payload = { + ...payload, + poster_image_url: this.props.recorded_video.s3_cover_image, + image_size: imageSize + }; + } + + let replyDetailId = deepGet(this.props.recorded_video , 'reply_obj.replyDetailId' ); + if (replyDetailId) { + payload['reply_detail_id'] = replyDetailId; + } + + new PepoApi(`/replies`) + .post(payload) + .then((responseData) => { + Store.dispatch( + upsertRecordedVideo({ + pepo_api_posting: false + }) + ); + + if (responseData.success && responseData.data) { + console.log('reply sent Successfully to Pepo Api'); + let resultType = deepGet(responseData, DataContract.common.resultType), + reply = deepGet(responseData, `data.${resultType}` ); + + if (reply && reply.length > 0 ){ + let replyObj = deepGet(this.props.recorded_video , 'reply_obj' ); + replyObj['replyDetailId'] = reply[0].payload.reply_detail_id; + Store.dispatch( + upsertRecordedVideo({ + reply_obj: replyObj, + go_for_tx: true + }) + ); + } + } else { + Toast.show({ + text: responseData.err.msg, + icon: 'error' + }); + console.log('/replies:::VideoUploadStatusToNotProcessing'); + this.VideoUploadStatusToNotProcessing(); + Store.dispatch( + upsertRecordedVideo({ + do_upload: false + }) + ); + this.executeTx = false; + } + this.postToPepoApi = false; + }) + .catch(() => { + this.postToPepoApi = false; + Store.dispatch( + upsertRecordedVideo({ + pepo_api_posting: false + }) + ); + Toast.show({ + text: 'Video upload failed - Try Again', + icon: 'error' + }); + }); + } + }; + async cleanUp() { // stop ffmpge processing - videoUploaderComponent.emit('hide'); - Store.dispatch(videoInProcessing(false)); + if (this.isCleanUpCalled){ + return; + } + this.isCleanUpCalled = true; + console.log('cleanUp:::VideoUploadStatusToNotProcessing'); + this.VideoUploadStatusToNotProcessing(); FfmpegProcesser.cancel(); // remove files from cache, await this.removeFile(this.props.recorded_video.raw_video); @@ -144,6 +460,7 @@ class CameraWorker extends PureComponent { utilities.removeItem(this.getCurrentUserRecordedVideoKey()); // cleanup Redux Store.dispatch(clearRecordedVideo()); + this.isCleanUpCalled = false; } async removeFile(file) { @@ -209,7 +526,8 @@ class CameraWorker extends PureComponent { compression_processing: true }) ); - videoUploaderComponent.emit('show'); + console.log('compressVideo:::VideoUploadStatusToProcessing'); + this.VideoUploadStatusToProcessing(); FfmpegProcesser.init(this.props.recorded_video.raw_video); FfmpegProcesser.compress() @@ -261,6 +579,10 @@ class CameraWorker extends PureComponent { } async uploadVideo() { + if (this.checkIfDiscardedAndClean()){ + return; + } + !this.props.recorded_video.compressed_video && (await this.compressVideo()); if ( @@ -273,8 +595,9 @@ class CameraWorker extends PureComponent { video_s3_upload_processing: true }) ); - videoUploaderComponent.emit('show'); - this.uploadToS3(this.props.recorded_video.compressed_video, 'video') + console.log('uploadVideo:::VideoUploadStatusToProcessing'); + this.VideoUploadStatusToProcessing(); + return this.uploadToS3(this.props.recorded_video.compressed_video, 'video') .then((s3Video) => { console.log('uploadVideo success :: s3Video', s3Video); Store.dispatch( @@ -296,6 +619,11 @@ class CameraWorker extends PureComponent { } async uploadCoverImage() { + + if (this.checkIfDiscardedAndClean()){ + return; + } + !this.props.recorded_video.cover_image && (await this.createThumbnail()); if ( this.props.recorded_video.cover_image && @@ -309,7 +637,7 @@ class CameraWorker extends PureComponent { }) ); - this.uploadToS3(this.props.recorded_video.cover_image, 'image') + return this.uploadToS3(this.props.recorded_video.cover_image, 'image') .then((s3CoverImage) => { console.log('uploadCoverImage success :: s3CoverImage', s3CoverImage); Store.dispatch( @@ -335,7 +663,12 @@ class CameraWorker extends PureComponent { return uploadToS3.perform(); } - async postVideoWithCoverImage() { + async postVideoToPepoApi() { + + if (this.checkIfDiscardedAndClean()){ + return; + } + if ( this.props.recorded_video.s3_video && !this.props.recorded_video.pepo_api_posting && @@ -364,7 +697,8 @@ class CameraWorker extends PureComponent { video_height: appConfig.cameraConstants.VIDEO_HEIGHT, image_width: appConfig.cameraConstants.VIDEO_WIDTH, image_height: appConfig.cameraConstants.VIDEO_HEIGHT, - video_size: videoSize + video_size: videoSize, + per_reply_amount_in_wei: this.props.recorded_video.reply_amount }; let payload = payloadWithoutImage; @@ -377,7 +711,7 @@ class CameraWorker extends PureComponent { }; } - new PepoApi(`/users/${this.props.currentUserId}/fan-video`) + return new PepoApi(`/users/${this.props.currentUserId}/fan-video`) .post(payload) .then((responseData) => { if (responseData.success && responseData.data) { @@ -393,6 +727,9 @@ class CameraWorker extends PureComponent { pepo_api_posting: false }) ); + } else { + console.log('/fanvideo:::VideoUploadStatusToNotProcessing'); + this.VideoUploadStatusToNotProcessing(); } this.postToPepoApi = false; }) @@ -436,6 +773,6 @@ class CameraWorker extends PureComponent { } } -const mapStateToProps = ({ recorded_video }) => ({ recorded_video, currentUserId: CurrentUser.getUserId() }); +const mapStateToProps = ({ recorded_video }) => ({ recorded_video, currentUserId: CurrentUser.getUserId(), currentOstUserId: CurrentUser.getOstUserId() }); export default connect(mapStateToProps)(CameraWorker); diff --git a/src/services/FetchServices.js b/src/services/FetchServices.js index e5f8c32a..ab4ecbfd 100644 --- a/src/services/FetchServices.js +++ b/src/services/FetchServices.js @@ -23,7 +23,7 @@ class FetchServices { } initVals() { - + //NOTE: If adding removing vals from here, update cloneWithData method. this.isFetching = false; this.hasNextPage = true; this.nextPagePayload = null; @@ -160,6 +160,20 @@ class FetchServices { return new Constructor(this.url, this.extraParams, this.id, this.options); } + cloneWithData() { + let Constructor = this.constructor; + let newInstance = new Constructor(this.url, this.extraParams, this.id); + const seedData = this.result; + + newInstance.processResults( this.results || []); + newInstance.isFetching = false; + newInstance.hasNextPage = this.hasNextPage; + newInstance.nextPagePayload = this.nextPagePayload; + newInstance.meta = Object.assign({}, this.meta); + console.log("newInstance.results.length", newInstance.results.length); + return newInstance; + } + cloneInstance(){ return Object.assign( Object.create( Object.getPrototypeOf(this)), this); } diff --git a/src/services/NavigationService.js b/src/services/NavigationService.js index 354dcc9c..fc166760 100644 --- a/src/services/NavigationService.js +++ b/src/services/NavigationService.js @@ -43,6 +43,26 @@ const findCurrentRoute = (navState) => { } } +const getStackNumber = (routes , index , num , whiteListedStacks) => { + if (typeof index !== "number" || !whiteListedStacks.length) { + return num; + } else { + const currentRoute = routes[index] || {}; + const innerRoutes = currentRoute["routes"] || [] + for(let cnt = 0 ; cnt < innerRoutes.length ; cnt++){ + const route = innerRoutes[cnt]; + const routeName = route["routeName"]; + const indexOfItem = whiteListedStacks.indexOf(routeName); + if(indexOfItem > -1){ + num ++; + whiteListedStacks.splice(indexOfItem, 1); + } + num = getStackNumber( route["routes"], route["index"], num , whiteListedStacks); + } + return num; + } +} + function reset(navigation) { if(!navigation) return; navigation.dispatch(StackActions.popToTop("Notification")); @@ -56,5 +76,6 @@ export default { getTopLevelNavigator, findCurrentRoute, getActiveTab, + getStackNumber, reset }; diff --git a/src/services/NavigationStateHandler.js b/src/services/NavigationStateHandler.js index beaf2809..359c6e9f 100644 --- a/src/services/NavigationStateHandler.js +++ b/src/services/NavigationStateHandler.js @@ -5,7 +5,7 @@ import Colors from "../theme/styles/Colors"; import NavigationService from './NavigationService'; import AppConfig from '../constants/AppConfig'; -const routesWithoutStatusBar = ['Home', 'HomeScreen', 'VideoPlayer', 'CaptureVideo', 'CaptureImageScreen', 'ImageGalleryScreen', 'UserVideoHistory', 'FullScreenVideoCollection']; +const routesWithoutStatusBar = ['Home', 'HomeScreen', 'VideoPlayer', 'CaptureVideo', 'CaptureImageScreen', 'ImageGalleryScreen', 'UserVideoHistory', 'VideoReplies', 'FullScreenVideoCollection', 'FullScreenReplyCollection', 'VideoReplyPlayer']; const typesToIgnore = ['Navigation/COMPLETE_TRANSITION', 'Navigation/MARK_DRAWER_SETTLING', 'Navigation/MARK_DRAWER_IDLE', 'Navigation/DRAWER_CLOSED']; const routesAnalyticsMap = AppConfig.routesAnalyticsMap; @@ -16,7 +16,7 @@ const StatusBarShow = () => { StatusBar.setTranslucent(false); } - if(Platform.OS == "ios"){ + if(Platform.OS === "ios"){ StatusBar.setBarStyle("dark-content") } diff --git a/src/services/OstErrors.js b/src/services/OstErrors.js index 7d837bea..f7093352 100644 --- a/src/services/OstErrors.js +++ b/src/services/OstErrors.js @@ -5,7 +5,8 @@ const sdkErrors = { USER_NOT_ACTIVATED: 'User is not activated.', SESSION_NOT_FOUND: 'Please check your spending limit.', DEVICE_UNAUTHORIZED: 'Device is unauthorized.', - GENERAL_ERROR: 'Something went wrong please try again later.' + GENERAL_ERROR: 'Something went wrong please try again later.', + WORKFLOW_CANCELLED: 'Workflow Cancelled' }; const UIErros = { @@ -35,12 +36,14 @@ const UIErros = { email_error: 'Enter a valid email.', device_unathorized: "Your device is not authorized. Please authorized the device.", top_not_available: "Topup not available at this time, we are looking into it. Please check back later.", - delete_video_error:"Unable to delete Video at this moment.", + delete_video_error:"Unable to delete Video at this moment.", redemption_error : "Failed to redeem, please try again later", max_pepocorns: `Sorry, you don't have enough Pepo Coins to buy this many ${Utilities.getPepoCornsName()}`, price_point_validation_failed: `Sorry, you don't have enough Pepo Coins to buy this many ${Utilities.getPepoCornsName()}`, min_pepocorns: `Minimum amount allowed is 1 ${Utilities.getPepoCornsName()}.`, - bt_exceeds_bal_amount_error : `Sorry, you don't have enough Pepo Coins for this transactions.` + bt_exceeds_bal_amount_error : `Sorry, you don't have enough Pepo Coins for this transactions.`, + video_reply_not_allowed: 'Reply is not allowed for this video.', + video_reply_not_allowed_low_bal: "Insufficient balance for reply" }; const UIWhitelistedErrorCode = { diff --git a/src/services/Pagination.js b/src/services/Pagination.js index 9329cb97..89510490 100644 --- a/src/services/Pagination.js +++ b/src/services/Pagination.js @@ -111,6 +111,10 @@ class Pagination { console.log("this.fetchServices.results" ,this.fetchServices.results ); } + addItems( items ) { + this.getResults().unshift( items ); + } + }; diff --git a/src/services/PepoApi.js b/src/services/PepoApi.js index 68a0a7eb..3f7b57dc 100644 --- a/src/services/PepoApi.js +++ b/src/services/PepoApi.js @@ -25,6 +25,7 @@ export default class PepoApi { 'User-Agent': DeviceInfo.getUserAgent(), 'X-PEPO-DEVICE-OS': Platform.OS, 'X-PEPO-DEVICE-OS-VERSION': DeviceInfo.getSystemVersion(), + 'X-PEPO-DEVICE-ID': DeviceInfo.getUniqueID(), 'X-PEPO-BUILD-NUMBER': DeviceInfo.getBuildNumber(), 'X-PEPO-APP-VERSION': DeviceInfo.getVersion() } diff --git a/src/services/PepoSocket.js b/src/services/PepoSocket.js index 2f811571..1efc22a5 100644 --- a/src/services/PepoSocket.js +++ b/src/services/PepoSocket.js @@ -1,7 +1,9 @@ import io from 'socket.io-client'; -import PepoApi from '../services/PepoApi'; +import { YellowBox } from 'react-native'; import deepGet from 'lodash/get'; +import PepoApi from '../services/PepoApi'; + import { upsertNotificationUnread } from '../actions'; import Store from '../store'; @@ -23,6 +25,11 @@ export default class PepoSocket { this.socket = null; this.isConnecting = false; this.attempt = 0; + // Supress un-actionable warnings + YellowBox.ignoreWarnings(['Setting a timer']); + YellowBox.ignoreWarnings([ + 'Unrecognized WebSocket connection option(s) `agent`, `perMessageDeflate`, `pfx`, `key`, `passphrase`, `cert`, `ca`, `ciphers`, `rejectUnauthorized`. Did you mean to put these under `headers`?' + ]) } setConnectionParams(response) { diff --git a/src/services/PixelCall.js b/src/services/PixelCall.js index 60bfedf9..3ab02af7 100644 --- a/src/services/PixelCall.js +++ b/src/services/PixelCall.js @@ -2,8 +2,10 @@ import {Platform, Dimensions} from 'react-native'; import DeviceInfo from 'react-native-device-info'; import qs from 'qs'; import assignIn from 'lodash/assignIn'; -import Store from '../store'; +import momentTimezone from 'moment-timezone'; + import { TRACKER_ENDPOINT } from '../constants/index'; +import CurrentUser from "../models/CurrentUser"; const keyAliasMap = { t_version: 'v', @@ -12,7 +14,6 @@ const keyAliasMap = { u_service_id: 'serid', u_session_id: 'sesid', u_timezone: 'tz', - e_timestamp: 'ts', e_entity: 'ee', e_action: 'ea', p_type: 'pt', @@ -32,12 +33,11 @@ const keyAliasMap = { }; const staticData = { - t_version: 1.0, - t_gid: 'placeholder_t_gid', + t_version: 2, + t_gid: DeviceInfo.getUniqueID(), u_service_id: 1, u_session_id: 'placeholder_u_session_id', - u_timezone: DeviceInfo.getTimezone(), - e_timestamp: Math.round((new Date).getTime()/1000), + u_timezone: momentTimezone.tz(DeviceInfo.getTimezone()).utcOffset(), device_id: DeviceInfo.getUniqueID(), device_model: DeviceInfo.getModel(), device_platform: DeviceInfo.getSystemVersion(), @@ -45,19 +45,19 @@ const staticData = { device_language: DeviceInfo.getDeviceLocale(), device_width: Dimensions.get('window').width, device_height: Dimensions.get('window').height, - user_agent: DeviceInfo.getUserAgent() + device_type: 'mobile_app', + user_agent: DeviceInfo.getUserAgent(), + mobile_app_version: DeviceInfo.getVersion() }; -if(Store.getState().current_user.id){ - staticData.u_id = Store.getState().current_user.id; - staticData.e_data_json.user_id = Store.getState().current_user.id; -} +const mandatoryKeys = ['e_entity', 'e_action', 'p_type']; const makeCompactData = params => { let compactData = {}; for(var key in params){ if (params.hasOwnProperty(key)) { - compactData[keyAliasMap[key]] = typeof params[key] === 'object' ? JSON.stringify(params[key]) : params[key]; + let keyName = keyAliasMap[key] ? keyAliasMap[key] : key; + compactData[keyName] = typeof params[key] === 'object' ? JSON.stringify(params[key]) : params[key]; } } return compactData; @@ -68,15 +68,33 @@ export default (data) => { // Extend outer data with staticData let pixelData = assignIn({}, staticData, data); + // Add user context (if any) else bail out + let currentUserId = CurrentUser.getUserId(); + if(!currentUserId) return; + pixelData.u_id = currentUserId; + + // Validate and bail out on fail + for(var mi = 0; mi < mandatoryKeys.length; mi++){ + if(!pixelData[mandatoryKeys[mi]]){ + console.log(`PixelCall validation failed. Mandatory key ${mandatoryKeys[mi]} missing.`); + return; + } + } + for(var key in pixelData){ + if (pixelData.hasOwnProperty(key) && !pixelData[key]) { + console.log(`PixelCall validation failed. Invalid value of ${mandatoryKeys[mi]}.`); + return; + } + } + // Compact data let compactData = makeCompactData(pixelData); - // Log - let ts = (new Date).getTime(); - console.log(`PixelCall (${ts}) URL: ${TRACKER_ENDPOINT}, data: `, compactData); - // Fire the fetch call - fetch(`${TRACKER_ENDPOINT}?${qs.stringify(compactData)}`) - .then((response) => console.log(`PixelCall (${ts}) fetch request complete!`)) - .catch((error) => console.log(`PixelCall (${ts}) fetch error: `, error)); + fetch(`${TRACKER_ENDPOINT}?${qs.stringify(compactData)}`, { + headers: { + 'User-Agent': DeviceInfo.getUserAgent() + } + }).then((response) => console.log(`PixelCall to URL: ${TRACKER_ENDPOINT} completed with data: `, compactData)) + .catch((error) => console.log(`PixelCall fetch error: `, error)); } diff --git a/src/services/PriceOracle.js b/src/services/PriceOracle.js index d2e4e25e..42a8f210 100644 --- a/src/services/PriceOracle.js +++ b/src/services/PriceOracle.js @@ -5,11 +5,21 @@ const btPrecession = 2; export default class PriceOracle { constructor(token, pricePoints) { - if (!token || !token.conversion_factor || !pricePoints) { - return null; + if ( !token ) { + //Assign token to an empty object so that fallback values apply. + token = {}; } - this.conversionFactor = token.conversion_factor; + if ( !pricePoints ) { + //Assign pricePoints to an empty object so that fallback values apply. + pricePoints = {}; + } + + ///NOTE: If you are wondering why fall back is 1, Ask Ashutosh. + this.conversionFactor = token.conversion_factor || 1; + + ///NOTE: If you are wondering why fall back is 18, Ask Ashutosh. this.decimals = token.decimal || 18; + this.usdPricePoint = pricePoints['USD']; } diff --git a/src/services/Pricer.js b/src/services/Pricer.js index dfbcfd0d..0c653d47 100644 --- a/src/services/Pricer.js +++ b/src/services/Pricer.js @@ -6,14 +6,16 @@ import PriceOracle from './PriceOracle'; import ReduxGetter from "./ReduxGetters"; import numeral from "numeral"; import OstWalletSdkHelper from '../helpers/OstWalletSdkHelper'; -import PepoApi from './PepoApi'; import DataContract from '../constants/DataContract'; import BigNumber from 'bignumber.js'; -let CurrentUser; +let CurrentUser, PepoApi; import('../models/CurrentUser').then((imports) => { CurrentUser = imports.default; }); +import('./PepoApi').then((imports) => { + PepoApi = imports.default; +}); let ostErrors; import('./OstErrors').then((imports) => { @@ -94,6 +96,10 @@ class Pricer { toDisplayAmount(amount) { if(isNaN(amount)) return amount; + if(amount <= 0.0000001 ){ + amount = parseInt(amount); + amount = amount.toFixed(2); + } return numeral(amount).format('0[.]00a') || 0; } @@ -106,10 +112,14 @@ class Pricer { } } + getWeiToNumber = ( val ) => { + return val && Math.floor(Number(this.getFromDecimal(val))) || 0; + } ; + getBtFromPepoCornsInWei( pepoCorns , step , pepoInWeiPerStep){ if(!pepoCorns || !step || !pepoInWeiPerStep ) return "0"; let pepoInEthPerStep = this.getFromDecimal(pepoInWeiPerStep); //Normalize to ETH - let pepoInEthPerStepBN = BigNumber( pepoInEthPerStep ); //Convert to BN + let pepoInEthPerStepBN = BigNumber( pepoInEthPerStep ); //Convert to BN let amountBn = BigNumber(pepoCorns).dividedBy(step) ; //Get amount in BN amountBn = pepoInEthPerStepBN.multipliedBy(amountBn).toString(10); //Get pepo in ETH amountBn = this.getToDecimal( amountBn ); // Convert to WEI diff --git a/src/services/ReduxGetters.js b/src/services/ReduxGetters.js index 26581708..82d97f9b 100644 --- a/src/services/ReduxGetters.js +++ b/src/services/ReduxGetters.js @@ -42,6 +42,10 @@ class ReduxGetters { state = state || Store.getState(); size = size || appConfig.videoConstant.videoImageWidth; let posterImageId = deepGet(state, `video_entities.id_${id}.poster_image_id`); + return this.getImageUrl(posterImageId, state, size); + } + + getImageUrl( posterImageId, state, size) { return ( deepGet(state, `image_entities.id_${posterImageId}.resolutions.${size}.url`) || deepGet(state, `image_entities.id_${posterImageId}.resolutions.original.url`) @@ -50,13 +54,14 @@ class ReduxGetters { getUserName(id, state) { state = state || Store.getState(); - return deepGet(state, `user_entities.id_${id}.user_name`); + return deepGet(state, `user_entities.id_${id}.user_name`, ""); } getUserTwitterHandle(id, state) { state = state || Store.getState(); return deepGet(state, `twitter_entities.id_${id}.handle`); } + getUserTwitterHandleLink(id, state) { state = state || Store.getState(); return deepGet(state, `twitter_entities.id_${id}.link`); @@ -123,10 +128,30 @@ class ReduxGetters { return deepGet(state, `video_stat_entities.id_${id}.description_id`); } + getVideoReplyCount(id, state){ + state = state || Store.getState(); + return deepGet(state, `video_stat_entities.id_${id}.total_video_replies`, 0); + } + + getBtAmountForReply(id, state){ + state = state || Store.getState(); + return deepGet(state, `video_stat_entities.id_${id}.per_reply_amount_in_wei`, 0); + } + + isReplyAllowed(id, state){ + state = state || Store.getState(); + return deepGet(state, `video_stat_entities.id_${id}.is_reply_allowed`, true); + } + + getVideoCreatorUserId(id, state){ + state = state || Store.getState(); + return deepGet(state, `video_stat_entities.id_${id}.creator_user_id`, true); + } + getTappedIncludesEntity(videoId, tappedText) { let lowercasedText = tappedText.toLowerCase(); let state = Store.getState(); - return deepGet(state, `video_description_entities.id_${videoId}.includes.${lowercasedText}`); + return deepGet(state, `video_description_entities.id_${videoId}.includes.${lowercasedText}`) || deepGet(state, `video_description_entities.id_${videoId}.includes.${tappedText}`); } getVideoLink(id, state) { @@ -170,6 +195,12 @@ class ReduxGetters { return deepGet(Store.getState(), `recorded_video`); } + getRecordedVideoType() { + return deepGet(Store.getState(), `recorded_video.video_type`); + } + + + getRecordedVideoCurrentProcess() { let processing = []; if (deepGet(Store.getState(), `recorded_video.cover_capture_processing`)) { @@ -187,6 +218,7 @@ class ReduxGetters { return processing.join(', '); } + getUserCoverVideoId(id, state) { state = state || Store.getState(); if (deepGet(state, `user_profile_entities.id_${id}.cover_video_id`)) { @@ -402,11 +434,6 @@ class ReduxGetters { return deepGet(state, `user_entities.id_${id}.status`, '').toLowerCase() == appConfig.userStatusMap.inActive; } - isVideoDeleted(id, state) { - state = state || Store.getState(); - return deepGet(state, `video_entities.id_${id}.status`, '').toLowerCase() == appConfig.videoStatusMap.deleted; - } - getUSDPrice(state){ state = state || Store.getState(); return deepGet(state, "price_points.OST.USD"); @@ -422,6 +449,110 @@ class ReduxGetters { return deepGet(state, `tag_entities.id_${id}`); } + getReplyEntity(id, state) { + state = state || Store.getState(); + return deepGet(state, `reply_detail_entities.id_${id}`); + } + + getReplyBt(id, state) { + state = state || Store.getState(); + return deepGet(state, `reply_detail_entities.id_${id}.total_amount_raised_in_wei`); + } + + getReplyKind(id, state){ + state = state || Store.getState(); + return deepGet(state, `reply_detail_entities.id_${id}.entity_kind`); + } + + getReplyEntityId(id, state){ + state = state || Store.getState(); + return deepGet(state, `reply_detail_entities.id_${id}.entity_id`); + } + + getVideoRelationEntity(id, state){ + state = state || Store.getState(); + return deepGet(state, `current_user_video_relation_entities.id_${id}`); + } + + getReplyDetailRelationEntity(id, state){ + state = state || Store.getState(); + return deepGet(state, `current_user_reply_detail_relation_entities.id_${id}`); + } + + isReplySeen(id, state){ + state = state || Store.getState(); + return deepGet(state, `current_user_reply_detail_relation_entities.id_${id}.has_seen`); + } + + getCanDeleteReply(id, state){ + state = state || Store.getState(); + return !!Number( deepGet(state, `current_user_reply_detail_relation_entities.id_${id}.can_delete` , 0) ); + } + + isVideoIsChargeable(id, state){ + state = state || Store.getState(); + return !!Number( deepGet(state, `current_user_video_relation_entities.id_${id}.is_reply_chargeable` , 0) ); + + } + + isReplyShareable(id , state){ + state = state || Store.getState(); + return !!Number( deepGet(state, `current_user_reply_detail_relation_entities.id_${id}.is_shareable` , 0)); + } + + isVideoShareable(id ,state){ + state = state || Store.getState(); + return !!Number( deepGet(state, `current_user_video_relation_entities.id_${id}.is_shareable` , 0) ); + } + + getReplyDescriptionId(id, state) { + state = state || Store.getState(); + return deepGet(state, `reply_detail_entities.id_${id}.description_id`); + } + + getReplyBt(id, state) { + state = state || Store.getState(); + return deepGet(state, `reply_detail_entities.id_${id}.total_amount_raised_in_wei`); + } + + getReplySupporters(id , state){ + state = state || Store.getState(); + return deepGet(state, `reply_detail_entities.id_${id}.total_contributed_by`); + } + + getReplyLinkId(id, state) { + state = state || Store.getState(); + return deepGet(state, `reply_detail_entities.id_${id}.link_ids[0]`); + } + + isReplySupported(id, state) { + state = state || Store.getState(); + let val = deepGet(state, `reply_contribution_entities.id_${id}`); + val = val && Number(val); + return !!val; + } + + getReplyParentVideoId(id , state){ + state = state || Store.getState(); + return deepGet(state, `reply_detail_entities.id_${id}.parent_id`); + } + + getReplyUserId(id , state){ + state = state || Store.getState(); + return deepGet(state, `reply_detail_entities.id_${id}.creator_user_id`); + } + + getReplyParentUserId(id , state){ + state = state || Store.getState(); + let parentId = deepGet(state, `reply_detail_entities.id_${id}.parent_id`); + return deepGet(state, `video_stat_entities.id_${parentId}.creator_user_id`); + } + + getLoginPopOverProps(state) { + state = state || Store.getState(); + return deepGet(state, `login_popover`); + } + } export default new ReduxGetters(); diff --git a/src/services/ReduxSetters.js b/src/services/ReduxSetters.js index 56005376..b13c16cb 100644 --- a/src/services/ReduxSetters.js +++ b/src/services/ReduxSetters.js @@ -6,14 +6,20 @@ const knownEntitiesDispatcherMap = { gifs: 'upsertGiffyEntities', tags: 'upsertTagEntities', tag_search_results:'upsertTagEntities', + user_search_results:'upsertUserEntities', user_profiles: 'upsertUserProfileEntities', user_stats: 'upsertUserStatEntities', links: 'upsertLinkEntities', video_descriptions: 'upsertVideoDescriptionEntities', videos: 'upsertVideoEntities', video_details: 'upsertVideoStatEntities', + video_replies: 'upsertVideoReplyEntities', + reply_details: 'upsertReplyDetailEntities', + current_user_video_relations: 'upsertCurrentUserVideoRelationEntities', + current_user_reply_detail_relations: 'upsertCurrentUserReplyDetailRelationEntities', images: 'upsertImageEntities', current_user_video_contributions: 'upsertVideoContributionEntities', + current_user_reply_detail_contributions: 'upsertReplyContributionEntities', current_user_user_contributions: 'upsertUserContributionEntities', price_points: 'updatePricePoints', token: 'updateToken', diff --git a/src/services/TransactionExecutor.js b/src/services/TransactionExecutor.js new file mode 100644 index 00000000..c33d5328 --- /dev/null +++ b/src/services/TransactionExecutor.js @@ -0,0 +1,121 @@ +import { OstWalletSdk } from '@ostdotcom/ost-wallet-sdk-react-native'; +import {ensureDeivceAndSession} from '../helpers/TransactionHelper'; +import ExecuteTransactionWorkflow from '../services/OstWalletCallbacks/ExecuteTransactionWorkFlow'; +import Pricer from "../services/Pricer"; +import deepGet from 'lodash/get'; +import PepoApi from '../services/PepoApi'; +import CurrentUser from "../models/CurrentUser"; +import AppConfig from '../constants/AppConfig'; +import ReduxGetters from './ReduxGetters'; + + +/*** + * Mandatory config + * - apiEndpoint + * - metaProperties + * + * Optional config + * - ruleName + * + * CallBack + * onDeviceUnauthorized + * onEnsureDeivceAndSessionSuccess + * onEnsureDeivceAndSessionError + * onRequestAcknowledge + * onFlowInterrupt + * onPlatfromAcknowledgeSuccess + * onPlatfromAcknowledgeError + * onPlatfromAcknowledgeComplete + */ + +class TransactionExecutor { + + constructor( config , callbacks ){ + this.config = config || {}; + this.callbacks = callbacks || {}; + } + + sendTransactionToSdk(btInDecimal, toTokenHolderAddress , validateSession=true ) { + if(!btInDecimal) return ; + this.toTokenHolderAddress = toTokenHolderAddress; + + if( !validateSession ){ + return this._executeTransaction(btInDecimal); + } + + ensureDeivceAndSession(CurrentUser.getOstUserId(), btInDecimal, (device) => { + this._deviceUnauthorizedCallback(device); + }, (errorMessage, success) => { + this._ensureDeivceAndSessionCallback(btInDecimal,errorMessage, success); + }); + } + + _deviceUnauthorizedCallback(device){ + this.callbacks.onDeviceUnauthorized && this.callbacks.onDeviceUnauthorized( device ); + } + + _ensureDeivceAndSessionCallback(btInDecimal,errorMessage, success){ + if( success ){ + this.callbacks.onEnsureDeivceAndSessionSuccess && this.callbacks.onEnsureDeivceAndSessionSuccess( btInDecimal,errorMessage, success ); + return this._executeTransaction(btInDecimal); + } + + if( errorMessage ){ + this.callbacks.onEnsureDeivceAndSessionError && this.callbacks.onEnsureDeivceAndSessionError( btInDecimal,errorMessage, success ); + } + } + + _executeTransaction(btInDecimal) { + this.workflow = new ExecuteTransactionWorkflow(this); + OstWalletSdk.executeTransaction( + CurrentUser.getOstUserId(), + [this.toTokenHolderAddress], + [btInDecimal], + this.config.ruleName || AppConfig.ruleTypeMap.directTransfer, + this.getSdkMetaProperties(), + this.workflow + ); + } + + getSdkMetaProperties() { + return this.config.metaProperties || {}; + } + + onRequestAcknowledge(ostWorkflowContext, ostWorkflowEntity) { + Pricer.getBalance(); + this.sendTransactionToPlatform(ostWorkflowEntity); + this.callbacks.onRequestAcknowledge && this.callbacks.onRequestAcknowledge( ostWorkflowContext, ostWorkflowEntity ); + } + + onFlowInterrupt(ostWorkflowContext, error) { + this.callbacks.onFlowInterrupt && this.callbacks.onFlowInterrupt( ostWorkflowContext, error ); + } + + sendTransactionToPlatform(ostWorkflowEntity) { + const params = this.getSendTransactionPlatformData(ostWorkflowEntity); + const apiEndpoint = this.config.apiEndpoint || '/ost-transactions'; + new PepoApi(apiEndpoint) + .post(params) + .then((res) => { + this.callbacks.onPlatfromAcknowledgeSuccess && this.callbacks.onPlatfromAcknowledgeSuccess(res); + }) + .catch((error) => { + this.callbacks.onPlatfromAcknowledgeError && this.callbacks.onPlatfromAcknowledgeError(error); + }) + .finally(() => { + this.callbacks.onPlatfromAcknowledgeComplete && this.callbacks.onPlatfromAcknowledgeComplete(); + }); + } + + getSendTransactionPlatformData(ostWorkflowEntity) { + const params = { + ost_transaction: deepGet(ostWorkflowEntity, 'entity'), + ost_transaction_uuid: deepGet(ostWorkflowEntity, 'entity.id') + }; + return params; + } + + +} + +export { TransactionExecutor }; diff --git a/src/services/TwitterAuthService.js b/src/services/TwitterAuthService.js index dde3dd2f..1cb1a96f 100644 --- a/src/services/TwitterAuthService.js +++ b/src/services/TwitterAuthService.js @@ -1,5 +1,5 @@ import deepGet from 'lodash/get'; - +import ReduxGetter from "./ReduxGetters"; import Toast from '../theme/components/NotificationToast'; import { upsertInviteCode } from '../actions'; import Store from '../store'; @@ -29,6 +29,9 @@ class TwitterAuthService { signUp() { TwitterAuth.signIn() .then(async (params) => { + // Disable the Pop-Up. + LoginPopoverActions.showConnecting(); + if (params) { let inviteCode = await Utilities.getItem(AppConfig.appInstallInviteCodeASKey); if (inviteCode) { @@ -50,7 +53,6 @@ class TwitterAuthService { }); } else { LoginPopoverActions.hide(); - console.log('No user data!'); } }) .catch((error) => { diff --git a/src/services/Utilities.js b/src/services/Utilities.js index 9d81505b..3d9bc648 100644 --- a/src/services/Utilities.js +++ b/src/services/Utilities.js @@ -1,37 +1,39 @@ import AsyncStorage from '@react-native-community/async-storage'; -import { Alert, Platform, Linking } from 'react-native'; +import { Alert, Platform, Linking , Dimensions } from 'react-native'; import deepGet from 'lodash/get'; import pricer from './Pricer'; import reduxGetters from './ReduxGetters'; -import appConfig from '../constants/AppConfig'; import { FlyerEventEmitter } from '../components/CommonComponents/FlyerHOC'; import { LoginPopoverActions } from '../components/LoginPopover'; import Toast from '../theme/components/NotificationToast'; import CameraPermissionsApi from '../services/CameraPermissionsApi'; import { allowAcessModalEventEmitter } from '../components/AllowAccessModalScreen'; -import AppConfig from '../constants/AppConfig'; -import PepoApi from './PepoApi'; +import AppConfig from '../constants/AppConfig';; import DataContract from '../constants/DataContract'; +import { isIphoneX } from 'react-native-iphone-x-helper'; -let CurrentUser; +let CurrentUser, PepoApi; import('../models/CurrentUser').then((imports) => { CurrentUser = imports.default; }); +import('./PepoApi').then((imports) => { + PepoApi = imports.default; +}); let os = Platform.OS || ""; os = os.toLowerCase(); let recursiveMaxCount = 0; -let checkVideoPermission = function(navigation) { +let checkVideoPermission = function(navigation, params ) { CameraPermissionsApi.requestPermission('camera').then((cameraResult) => { CameraPermissionsApi.requestPermission('microphone').then((microphoneResult) => { if (cameraResult == 'authorized' && microphoneResult == 'authorized') { console.log('checkVideoPermission:cameraResult', cameraResult); console.log('checkVideoPermission:microphoneResult', microphoneResult); - navigation.navigate('CaptureVideo'); + navigation.navigate('CaptureVideo', params); } else if ( (Platform.OS == 'ios' && (cameraResult == 'denied' || microphoneResult == 'denied')) || cameraResult == 'restricted' || @@ -84,7 +86,7 @@ export default { getTokenSymbolImageConfig() { let symbol = pricer.getTokenSymbol(); - return appConfig['tokenSymbols'][symbol]; + return AppConfig['tokenSymbols'][symbol]; }, _getIDList(resultData, key = 'id') { @@ -120,7 +122,7 @@ export default { isUserActivated(status) { status = status || ''; - return status.toLowerCase() == appConfig.userStatusMap.activated; + return status.toLowerCase() == AppConfig.userStatusMap.activated; }, getLastChildRoutename(state) { @@ -135,15 +137,23 @@ export default { return this.getLastChildRoutename(routes[index]); }, - handleVideoUploadModal(previousTabIndex, navigation) { - if (reduxGetters.getVideoProcessingStatus() == true && previousTabIndex == 0) { + getActiveTab( navigation ){ + if( !navigation ) return ; + let activeIndex = deepGet(navigation , 'state.index'), + route = deepGet(navigation , `state.routes[${activeIndex}]`); + return route && route["routeName"]; + }, + + handleVideoUploadModal(previousTabIndex, navigation, params = {}) { + //todo: @mayur toast and flyer logic change. + if (reduxGetters.getVideoProcessingStatus() && previousTabIndex == 0) { FlyerEventEmitter.emit('onShowProfileFlyer', { id: 2 }); - } else if (reduxGetters.getVideoProcessingStatus() == true) { + } else if (reduxGetters.getVideoProcessingStatus()) { Toast.show({ text: 'Video uploading in progress.' }); } else { - checkVideoPermission(navigation); + checkVideoPermission(navigation, params); } }, @@ -206,6 +216,21 @@ export default { return pepocornsName.substring(0, length - 1); } return pepocornsName; + }, + + getPendantAvailableHeight(){ + const area = AppConfig.MaxDescriptionArea; + let height = ( area / Dimensions.get('window').width ) + 20; + //70 is height of top section + return AppConfig.VideoScreenObject.height - height - (isIphoneX ? 78 : Platform.OS === 'ios' ? 28 : 80) ; + } , + + getPendantTop(){ + if( isIphoneX ){ + return 60 + 45; + }else{ + return 30+45; + } } }; diff --git a/src/services/shareVideo.js b/src/services/shareVideo.js index 8c3dc8c3..226a6cbd 100644 --- a/src/services/shareVideo.js +++ b/src/services/shareVideo.js @@ -1,16 +1,15 @@ import PepoApi from './PepoApi'; - -import { Share, Platform } from 'react-native'; +import { Share } from 'react-native'; class ShareVideo { - constructor(videoId) { - this.videoId = videoId; + constructor(url , entityId) { + this.url = url; + this.entityId = entityId; } perform() { return new Promise((resolve, reject) => { - new PepoApi(`/videos/${this.videoId}/share`).get().then((res) => { - console.log(res, 'videos/:video_id/share in then'); + new PepoApi(this.url).get().then((res) => { this.shareTray(res); }); }); diff --git a/src/store/index.js b/src/store/index.js index 14a81640..b386dca5 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -3,6 +3,6 @@ import logger from 'redux-logger'; import { reducer } from '../reducers'; // export const store = __DEV__ === true ? createStore(reducer, applyMiddleware(logger)) : createStore(reducer); -export const store = createStore(reducer); + export const store = createStore(reducer); export default store; diff --git a/src/theme/constants/index.js b/src/theme/constants/index.js index dc0daa99..5ac76747 100644 --- a/src/theme/constants/index.js +++ b/src/theme/constants/index.js @@ -1 +1,2 @@ -export const CUSTOM_TAB_Height = 55; \ No newline at end of file +export const CUSTOM_TAB_Height = 55; +export const HEADER_HEIGHT = 56; \ No newline at end of file diff --git a/src/theme/ostsdk/ost-sdk-content-config.js b/src/theme/ostsdk/ost-sdk-content-config.js index 4d2d0d70..ec563d36 100644 --- a/src/theme/ostsdk/ost-sdk-content-config.js +++ b/src/theme/ostsdk/ost-sdk-content-config.js @@ -56,7 +56,7 @@ export default { "text": "Authorize New Session" }, "lead_label": { - "text": "Add a 6-digit PIN to secure your wallet" + "text": "Enter your 6-digit PIN to authorize yourself" }, "info_label":{ "text": "" diff --git a/src/theme/ostsdk/ost-sdk-theme-config.js b/src/theme/ostsdk/ost-sdk-theme-config.js index 9c660b55..5e1934ef 100644 --- a/src/theme/ostsdk/ost-sdk-theme-config.js +++ b/src/theme/ostsdk/ost-sdk-theme-config.js @@ -91,7 +91,7 @@ export default { "tint_color": colors.paynesGrey }, "back":{ - "tint_color": "#438bad" + "tint_color": colors.paynesGrey } }, diff --git a/src/theme/styles/Colors.js b/src/theme/styles/Colors.js index 8a036a1d..ffc788ce 100644 --- a/src/theme/styles/Colors.js +++ b/src/theme/styles/Colors.js @@ -25,5 +25,8 @@ export default { whisper: '#ECECEC', rhino: "#34445b", snow: '#FBFBFB', - green: '#0F9D58' + green: '#0F9D58', + nobel: '#979797', + gray:"#333333", + darkShadeOfGray: '#111111' }; diff --git a/src/theme/styles/Common.js b/src/theme/styles/Common.js new file mode 100644 index 00000000..7e0ab5db --- /dev/null +++ b/src/theme/styles/Common.js @@ -0,0 +1,62 @@ +import { ifIphoneX , getBottomSpace} from 'react-native-iphone-x-helper'; +import { Dimensions, StatusBar , NativeModules } from 'react-native'; +import NotchHelper from "../../helpers/NotchHelper"; +import DefaultStyleGenerator from './DefaultStyleGenerator'; +import Colors from './Colors'; + + +let RNDeviceInfo = NativeModules.RNDeviceInfo; +let modalDeviceName = RNDeviceInfo.model === "Redmi Note 7 Pro" && RNDeviceInfo.brand === "xiaomi"; +let btmSpace = modalDeviceName ? 5 : 0; +import { CUSTOM_TAB_Height } from '../../theme/constants'; +const statusBarHeight = StatusBar.currentHeight; +const {width, height} = Dimensions.get('window'); + +const styles = { + viewContainer: { + flex:1, backgroundColor: Colors.white + }, + modalViewContainer: { + flex:1, + backgroundColor: 'rgba(0,0,0,0.5)' + }, + fullScreenVideoSafeAreaContainer: { + flex:1, backgroundColor: Colors.darkShadeOfGray, + }, + fullScreen: { + width: width, + ...ifIphoneX( + { + height: height - getBottomSpace([true]) + }, + { + height: + NotchHelper.hasNotch() + ? height + statusBarHeight - btmSpace + : height + } + ) + }, + + videoWrapperfullScreen: { + width: width, + ...ifIphoneX( + { + height: height - CUSTOM_TAB_Height - getBottomSpace([true]) + }, + { + height: + NotchHelper.hasNotch() + ? height + statusBarHeight - CUSTOM_TAB_Height - btmSpace + : height - CUSTOM_TAB_Height + } + ) + }, + + fullHeightWidth: { + width: "100%", + height: "100%" + }, +}; + +export default CommonStyle = DefaultStyleGenerator.generate(styles); diff --git a/src/theme/styles/index.js b/src/theme/styles/index.js index 5e272451..6cc375bb 100644 --- a/src/theme/styles/index.js +++ b/src/theme/styles/index.js @@ -2,10 +2,12 @@ import Button from './Button'; import TextInput from './TextInput'; import Errors from './Errors'; import Colors from './Colors'; +import CommonStyle from './Common'; export default { Button, TextInput, Errors, - Colors + Colors, + CommonStyle };