wip on spin game

This commit is contained in:
ue 2019-09-26 00:31:30 +03:00
commit 4c25525a2a
279 changed files with 11405 additions and 6920 deletions

View File

@ -33,6 +33,7 @@
"react/prop-types": 0,
"import/no-named-default": "off",
"no-param-reassign": "off",
"no-case-declarations": "off"
"no-case-declarations": "off",
"no-cycle": "off"
}
}

1
.gitignore vendored
View File

@ -57,6 +57,7 @@ buck-out/
config.js
!src/screens/steem-connect/config.js
google-services.json
keystore/
.env
package-lock.json

6
App.js
View File

@ -1,7 +1,5 @@
import { Client } from 'bugsnag-react-native';
import Config from 'react-native-config';
// eslint-disable-next-line
import bugsnag from './src/config/bugsnag';
import App from './src/index';
const bugsnag = new Client(Config.BUGSNAG_API_KEY);
export default App;

View File

@ -114,7 +114,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode versionMajor * 10000 + versionMinor * 100 + versionPatch
versionName "2.2.0"
versionName "2.2.2"
vectorDrawables.useSupportLibrary = true
resValue "string", "build_config_package", "app.esteem.mobile.android"
}

Binary file not shown.

View File

@ -3,10 +3,10 @@
platform :ios, '9.0'
target 'eSteem' do
pod 'AppCenter/Push', '~> 2.2.0'
pod 'AppCenter/Crashes', '~> 2.2.0'
pod 'AppCenter/Analytics', '~> 2.2.0'
pod 'AppCenterReactNativeShared', '~> 2.2.0'
pod 'AppCenter/Push', '~> 2.3.0'
pod 'AppCenter/Crashes', '~> 2.3.0'
pod 'AppCenter/Analytics', '~> 2.3.0'
pod 'AppCenterReactNativeShared', '~> 2.3.0'
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!

View File

@ -1,18 +1,18 @@
PODS:
- AppCenter/Analytics (2.2.0):
- AppCenter/Analytics (2.3.0):
- AppCenter/Core
- AppCenter/Core (2.2.0)
- AppCenter/Crashes (2.2.0):
- AppCenter/Core (2.3.0)
- AppCenter/Crashes (2.3.0):
- AppCenter/Core
- AppCenter/Push (2.2.0):
- AppCenter/Push (2.3.0):
- AppCenter/Core
- AppCenterReactNativeShared (2.2.0):
- AppCenter/Core (= 2.2.0)
- AppCenterReactNativeShared (2.3.0):
- AppCenter/Core (= 2.3.0)
- boost-for-react-native (1.63.0)
- BugsnagReactNative (2.21.0):
- BugsnagReactNative/Core (= 2.21.0)
- BugsnagReactNative (2.23.0):
- BugsnagReactNative/Core (= 2.23.0)
- React
- BugsnagReactNative/Core (2.21.0):
- BugsnagReactNative/Core (2.23.0):
- React
- CodePush (1000.0.0):
- CodePush/Base64 (= 1000.0.0)
@ -43,7 +43,7 @@ PODS:
- React
- SDWebImage/Core
- SDWebImage/GIF
- react-native-netinfo (4.1.3):
- react-native-netinfo (4.1.5):
- React
- react-native-version-number (0.3.6):
- React
@ -95,7 +95,7 @@ PODS:
- React/Core
- React/fishhook
- React/RCTBlob
- RNIap (3.3.0):
- RNIap (3.4.1):
- React
- RNImageCropPicker (0.24.1):
- QBImagePickerController
@ -109,10 +109,10 @@ PODS:
- yoga (0.59.8.React)
DEPENDENCIES:
- AppCenter/Analytics (~> 2.2.0)
- AppCenter/Crashes (~> 2.2.0)
- AppCenter/Push (~> 2.2.0)
- AppCenterReactNativeShared (~> 2.2.0)
- AppCenter/Analytics (~> 2.3.0)
- AppCenter/Crashes (~> 2.3.0)
- AppCenter/Push (~> 2.3.0)
- AppCenterReactNativeShared (~> 2.3.0)
- BugsnagReactNative (from `../node_modules/bugsnag-react-native`)
- CodePush (from `../node_modules/react-native-code-push`)
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
@ -172,10 +172,10 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/yoga"
SPEC CHECKSUMS:
AppCenter: c809e69d93218b9861b4f8e54ded8dcf4d2131e7
AppCenterReactNativeShared: 5751a607a8aa06ebf19b48bf9d92bf3a75dab04e
AppCenter: 9784d2fc998c9bd0b8fbaf4fb9ed69526d12ce1a
AppCenterReactNativeShared: 7ac481cba7f926848a7be76dca4dcb2692df3b06
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
BugsnagReactNative: 9f273f292378a016977f578e4467cf84b7182b07
BugsnagReactNative: a469858d1040621a04247bb4ec15ca18dceabfd6
CodePush: a503ca0caee269e68d8faaafe4414990ec282520
DoubleConversion: bb338842f62ab1d708ceb63ec3d999f0f3d98ecd
FLAnimatedImage: 4a0b56255d9b05f18b6dd7ee06871be5d3b89e31
@ -184,14 +184,14 @@ SPEC CHECKSUMS:
QBImagePickerController: d54cf93db6decf26baf6ed3472f336ef35cae022
React: 76e6aa2b87d05eb6cccb6926d72685c9a07df152
react-native-fast-image: 6d50167ad4d68b59640ceead8c2bc4e58d91d8bd
react-native-netinfo: e2c8a843bb18ab6c3f1e6cbead694491b36e3ffd
react-native-netinfo: 0e563248a4b9a99c33ec29bd03c2d50767db22a6
react-native-version-number: b415bbec6a13f2df62bf978e85bc0d699462f37f
RNIap: ee89b7442f32161016fe213a3434ee1fc19c1a80
RNIap: 8104f5fb44555f163531ca9c6200b7b405a44f1c
RNImageCropPicker: 6134b66a3d5bc13e2895a97c630a4254006902b4
RSKImageCropper: 98296ad26b41753f796b6898d015509598f13d97
SDWebImage: 3f3f0c02f09798048c47a5ed0a13f17b063572d8
yoga: 92b2102c3d373d1a790db4ab761d2b0ffc634f64
PODFILE CHECKSUM: afb8c6a227837e2a901eaed832d5bb66c6fd9cec
PODFILE CHECKSUM: 0e644f6a56d015441b78d265a6c615ed194b94f0
COCOAPODS: 1.7.5

View File

@ -55,7 +55,7 @@
/*
* The offset in minutes from UTC for the device time zone, including daylight savings time.
*/
@property(nonatomic, readonly) NSNumber *timeZoneOffset;
@property(nonatomic, readonly, strong) NSNumber *timeZoneOffset;
/*
* Screen size of the device in pixels (example: 640x480).

View File

@ -15,7 +15,7 @@
/**
* Log timestamp.
*/
@property(nonatomic) NSDate *timestamp;
@property(nonatomic, strong) NSDate *timestamp;
/**
* A session identifier is used to correlate logs together. A session is an abstract concept in the API and is not necessarily an analytics
@ -36,13 +36,13 @@
/**
* Device properties associated to this log.
*/
@property(nonatomic) MSDevice *device;
@property(nonatomic, strong) MSDevice *device;
/**
* Transient object tag. For example, a log can be tagged with a transmission target. We do this currently to prevent properties being
* applied retroactively to previous logs by comparing their tags.
*/
@property(nonatomic) NSObject *tag;
@property(nonatomic, strong) NSObject *tag;
/**
* Checks if the object's values are valid.

View File

@ -10,6 +10,6 @@
/**
* Additional key/value pair parameters. [optional]
*/
@property(nonatomic) NSDictionary<NSString *, NSString *> *properties;
@property(nonatomic, strong) NSDictionary<NSString *, NSString *> *properties;
@end

View File

@ -16,7 +16,7 @@
/**
* The flag indicates whether the service is started from application or not.
*/
@property(nonatomic) BOOL startedFromApplication;
@property(nonatomic, assign) BOOL startedFromApplication;
/**
* Start this service with a channel group. Also sets the flag that indicates that a service has been started.

View File

@ -28,7 +28,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
* The type.
*/
@property(nonatomic, readonly) MSAnalyticsAuthenticationType type;
@property(nonatomic, readonly, assign) MSAnalyticsAuthenticationType type;
/**
* The ticket key for this authentication provider.

View File

@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
* Property configurator.
*/
@property(nonatomic, readonly) MSPropertyConfigurator *propertyConfigurator;
@property(nonatomic, readonly, strong) MSPropertyConfigurator *propertyConfigurator;
+ (void)addAuthenticationProvider:(MSAnalyticsAuthenticationProvider *)authenticationProvider
NS_SWIFT_NAME(addAuthenticationProvider(authenticationProvider:));

View File

@ -16,6 +16,6 @@
/**
* Event properties.
*/
@property(nonatomic) MSEventProperties *typedProperties;
@property(nonatomic, strong) MSEventProperties *typedProperties;
@end

View File

@ -10,6 +10,6 @@
/**
* Additional key/value pair parameters. [optional]
*/
@property(nonatomic) NSDictionary<NSString *, NSString *> *properties;
@property(nonatomic, strong) NSDictionary<NSString *, NSString *> *properties;
@end

View File

@ -16,7 +16,7 @@
/**
* The flag indicates whether the service is started from application or not.
*/
@property(nonatomic) BOOL startedFromApplication;
@property(nonatomic, assign) BOOL startedFromApplication;
/**
* Start this service with a channel group. Also sets the flag that indicates that a service has been started.

View File

@ -89,6 +89,13 @@ typedef NS_ENUM(NSUInteger, MSUserConfirmation) {
*/
+ (BOOL)hasCrashedInLastSession;
/**
* Check if the app received memory warning in the last session.
*
* @return Returns YES is the app received memory warning in the last session.
*/
+ (BOOL)hasReceivedMemoryWarningInLastSession;
/**
* Provides details about the crash that occurred in the last app session
*/

View File

@ -35,17 +35,17 @@
/**
* Date and time the app started, nil if unknown.
*/
@property(nonatomic, readonly) NSDate *appStartTime;
@property(nonatomic, readonly, strong) NSDate *appStartTime;
/**
* Date and time the error occurred, nil if unknown
*/
@property(nonatomic, readonly) NSDate *appErrorTime;
@property(nonatomic, readonly, strong) NSDate *appErrorTime;
/**
* Device information of the app when it crashed.
*/
@property(nonatomic, readonly) MSDevice *device;
@property(nonatomic, readonly, strong) MSDevice *device;
/**
* Identifier of the app process that crashed.

View File

@ -16,7 +16,7 @@
/**
* The flag indicates whether the service is started from application or not.
*/
@property(nonatomic) BOOL startedFromApplication;
@property(nonatomic, assign) BOOL startedFromApplication;
/**
* Start this service with a channel group. Also sets the flag that indicates that a service has been started.

View File

@ -16,7 +16,7 @@
/**
* The flag indicates whether the service is started from application or not.
*/
@property(nonatomic) BOOL startedFromApplication;
@property(nonatomic, assign) BOOL startedFromApplication;
/**
* Start this service with a channel group. Also sets the flag that indicates that a service has been started.

View File

@ -0,0 +1 @@
../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashIdentifier.h

View File

@ -1 +0,0 @@
../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_Deadlock.h

View File

@ -1 +0,0 @@
../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSSafeCollections.h

View File

@ -1 +0,0 @@
../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSZombie.h

View File

@ -1 +0,0 @@
../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/NSDictionary+BSG_Merge.h

View File

@ -0,0 +1 @@
../../../../../node_modules/bugsnag-react-native/cocoa/vendor/bugsnag-cocoa/Source/Private.h

View File

@ -1,6 +1,6 @@
{
"name": "BugsnagReactNative",
"version": "2.21.0",
"version": "2.23.0",
"license": "MIT",
"summary": "Bugsnag crash and error reporting for React Native apps",
"authors": {
@ -9,7 +9,7 @@
"homepage": "https://docs.bugsnag.com/platforms/react-native",
"source": {
"git": "https://github.com/bugsnag/bugsnag-react-native.git",
"tag": "v2.21.0"
"tag": "v2.23.0"
},
"platforms": {
"ios": "8.0"

View File

@ -1,19 +1,18 @@
{
"name": "RNIap",
"version": "3.3.0",
"summary": "RNIap",
"description": "React Native In App Purchase Module.",
"version": "3.4.1",
"summary": "React Native In App Purchase Module.",
"homepage": "https://github.com/dooboolab/react-native-iap",
"license": "MIT",
"authors": "dooboolab",
"platforms": {
"ios": "7.0"
"ios": "9.0"
},
"source": {
"git": "https://github.com/dooboolab/react-native-iap",
"tag": "v3.3.0"
"git": "https://github.com/dooboolab/react-native-iap.git",
"tag": "3.4.1"
},
"source_files": "ios/**/*.{h,m}",
"source_files": "ios/*.{h,m}",
"requires_arc": true,
"dependencies": {
"React": [

View File

@ -1,6 +1,6 @@
{
"name": "react-native-netinfo",
"version": "4.1.3",
"version": "4.1.5",
"summary": "React Native Network Info API for iOS & Android",
"license": "MIT",
"authors": "Matt Oakes <hello@mattoakes.net>",
@ -11,7 +11,7 @@
},
"source": {
"git": "https://github.com/react-native-community/react-native-netinfo.git",
"tag": "4.1.3"
"tag": "v4.1.5"
},
"source_files": "ios/**/*.{h,m}",
"dependencies": {

42
ios/Pods/Manifest.lock generated
View File

@ -1,18 +1,18 @@
PODS:
- AppCenter/Analytics (2.2.0):
- AppCenter/Analytics (2.3.0):
- AppCenter/Core
- AppCenter/Core (2.2.0)
- AppCenter/Crashes (2.2.0):
- AppCenter/Core (2.3.0)
- AppCenter/Crashes (2.3.0):
- AppCenter/Core
- AppCenter/Push (2.2.0):
- AppCenter/Push (2.3.0):
- AppCenter/Core
- AppCenterReactNativeShared (2.2.0):
- AppCenter/Core (= 2.2.0)
- AppCenterReactNativeShared (2.3.0):
- AppCenter/Core (= 2.3.0)
- boost-for-react-native (1.63.0)
- BugsnagReactNative (2.21.0):
- BugsnagReactNative/Core (= 2.21.0)
- BugsnagReactNative (2.23.0):
- BugsnagReactNative/Core (= 2.23.0)
- React
- BugsnagReactNative/Core (2.21.0):
- BugsnagReactNative/Core (2.23.0):
- React
- CodePush (1000.0.0):
- CodePush/Base64 (= 1000.0.0)
@ -43,7 +43,7 @@ PODS:
- React
- SDWebImage/Core
- SDWebImage/GIF
- react-native-netinfo (4.1.3):
- react-native-netinfo (4.1.5):
- React
- react-native-version-number (0.3.6):
- React
@ -95,7 +95,7 @@ PODS:
- React/Core
- React/fishhook
- React/RCTBlob
- RNIap (3.3.0):
- RNIap (3.4.1):
- React
- RNImageCropPicker (0.24.1):
- QBImagePickerController
@ -109,10 +109,10 @@ PODS:
- yoga (0.59.8.React)
DEPENDENCIES:
- AppCenter/Analytics (~> 2.2.0)
- AppCenter/Crashes (~> 2.2.0)
- AppCenter/Push (~> 2.2.0)
- AppCenterReactNativeShared (~> 2.2.0)
- AppCenter/Analytics (~> 2.3.0)
- AppCenter/Crashes (~> 2.3.0)
- AppCenter/Push (~> 2.3.0)
- AppCenterReactNativeShared (~> 2.3.0)
- BugsnagReactNative (from `../node_modules/bugsnag-react-native`)
- CodePush (from `../node_modules/react-native-code-push`)
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
@ -172,10 +172,10 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/yoga"
SPEC CHECKSUMS:
AppCenter: c809e69d93218b9861b4f8e54ded8dcf4d2131e7
AppCenterReactNativeShared: 5751a607a8aa06ebf19b48bf9d92bf3a75dab04e
AppCenter: 9784d2fc998c9bd0b8fbaf4fb9ed69526d12ce1a
AppCenterReactNativeShared: 7ac481cba7f926848a7be76dca4dcb2692df3b06
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
BugsnagReactNative: 9f273f292378a016977f578e4467cf84b7182b07
BugsnagReactNative: a469858d1040621a04247bb4ec15ca18dceabfd6
CodePush: a503ca0caee269e68d8faaafe4414990ec282520
DoubleConversion: bb338842f62ab1d708ceb63ec3d999f0f3d98ecd
FLAnimatedImage: 4a0b56255d9b05f18b6dd7ee06871be5d3b89e31
@ -184,14 +184,14 @@ SPEC CHECKSUMS:
QBImagePickerController: d54cf93db6decf26baf6ed3472f336ef35cae022
React: 76e6aa2b87d05eb6cccb6926d72685c9a07df152
react-native-fast-image: 6d50167ad4d68b59640ceead8c2bc4e58d91d8bd
react-native-netinfo: e2c8a843bb18ab6c3f1e6cbead694491b36e3ffd
react-native-netinfo: 0e563248a4b9a99c33ec29bd03c2d50767db22a6
react-native-version-number: b415bbec6a13f2df62bf978e85bc0d699462f37f
RNIap: ee89b7442f32161016fe213a3434ee1fc19c1a80
RNIap: 8104f5fb44555f163531ca9c6200b7b405a44f1c
RNImageCropPicker: 6134b66a3d5bc13e2895a97c630a4254006902b4
RSKImageCropper: 98296ad26b41753f796b6898d015509598f13d97
SDWebImage: 3f3f0c02f09798048c47a5ed0a13f17b063572d8
yoga: 92b2102c3d373d1a790db4ab761d2b0ffc634f64
PODFILE CHECKSUM: afb8c6a227837e2a901eaed832d5bb66c6fd9cec
PODFILE CHECKSUM: 0e644f6a56d015441b78d265a6c615ed194b94f0
COCOAPODS: 1.7.5

File diff suppressed because it is too large Load Diff

View File

@ -9,154 +9,154 @@
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>7</integer>
<integer>3</integer>
</dict>
<key>AppCenterReactNativeShared.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>8</integer>
<integer>4</integer>
</dict>
<key>BugsnagReactNative.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>10</integer>
<integer>6</integer>
</dict>
<key>CodePush.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>11</integer>
<integer>7</integer>
</dict>
<key>DoubleConversion.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>12</integer>
<integer>8</integer>
</dict>
<key>FLAnimatedImage.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>13</integer>
<integer>9</integer>
</dict>
<key>Folly.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>14</integer>
<integer>10</integer>
</dict>
<key>Pods-eSteem-tvOS.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>17</integer>
<integer>13</integer>
</dict>
<key>Pods-eSteem-tvOSTests.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>18</integer>
<integer>14</integer>
</dict>
<key>Pods-eSteem.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>16</integer>
<integer>12</integer>
</dict>
<key>Pods-eSteemTests.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>19</integer>
<integer>15</integer>
</dict>
<key>QBImagePickerController-QBImagePicker.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>21</integer>
<integer>17</integer>
</dict>
<key>QBImagePickerController.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>20</integer>
<integer>16</integer>
</dict>
<key>RNIap.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>25</integer>
<integer>21</integer>
</dict>
<key>RNImageCropPicker.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>26</integer>
<integer>22</integer>
</dict>
<key>RSKImageCropper.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>27</integer>
<integer>23</integer>
</dict>
<key>SDWebImage.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>28</integer>
<integer>24</integer>
</dict>
<key>boost-for-react-native.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>9</integer>
<integer>5</integer>
</dict>
<key>glog.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>15</integer>
<integer>11</integer>
</dict>
<key>react-native-fast-image.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>22</integer>
<integer>18</integer>
</dict>
<key>react-native-netinfo.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>23</integer>
<integer>19</integer>
</dict>
<key>react-native-version-number.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>24</integer>
<integer>20</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>

View File

@ -15,11 +15,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2.2.0</string>
<string>2.2.2</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>3</string>
<string>7</string>
<key>LSRequiresIPhoneOS</key>
<true />
<key>UILaunchStoryboardName</key>

View File

@ -15,10 +15,10 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>2.2.0</string>
<string>2.2.2</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>3</string>
<string>7</string>
</dict>
</plist>

View File

@ -82,6 +82,7 @@
EDFF927300154818B0BF6EA4 /* libAppCenterReactNativePush.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0E465F10CD4424E9B80CFD3 /* libAppCenterReactNativePush.a */; };
F02CA133DF337C8C8FF1753C /* libPods-eSteem-tvOSTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 38204CFB05512D7FC94CF03D /* libPods-eSteem-tvOSTests.a */; };
FA64A93BC95743E58F77F404 /* FontAwesome5_Solid.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2DEB48DE97FD4A7585118CB4 /* FontAwesome5_Solid.ttf */; };
06625C4421A949818C7F02C9 /* Fontisto.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C2DEBF9FC196418CA61B9CA9 /* Fontisto.ttf */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -563,6 +564,7 @@
F202583F54E7475B0E1F9020 /* Pods-eSteem.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-eSteem.debug.xcconfig"; path = "Target Support Files/Pods-eSteem/Pods-eSteem.debug.xcconfig"; sourceTree = "<group>"; };
F3D23ACB36194C40BFB26B7A /* Roboto-medium.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-medium.ttf"; path = "../src/assets/Fonts/Roboto-medium.ttf"; sourceTree = "<group>"; };
F81DF68DDD5447119FDA7F5F /* EvilIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = EvilIcons.ttf; path = ../src/assets/Fonts/EvilIcons.ttf; sourceTree = "<group>"; };
C2DEBF9FC196418CA61B9CA9 /* Fontisto.ttf */ = {isa = PBXFileReference; name = "Fontisto.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -990,6 +992,7 @@
9A6F7F46844C4CAF9A9ABBDD /* Roboto.ttf */,
434B140115EF4DE49D4CD6BA /* rubicon-icon-font.ttf */,
F3D23ACB36194C40BFB26B7A /* Roboto-medium.ttf */,
C2DEBF9FC196418CA61B9CA9 /* Fontisto.ttf */,
);
name = Resources;
sourceTree = "<group>";
@ -1661,6 +1664,7 @@
C872C215BA9C4FFF97A12F81 /* Roboto.ttf in Resources */,
CF34742E37F4471D813F00E4 /* rubicon-icon-font.ttf in Resources */,
5FA7E43947714B96866CAE94 /* Roboto-medium.ttf in Resources */,
06625C4421A949818C7F02C9 /* Fontisto.ttf in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1961,7 +1965,7 @@
CODE_SIGN_ENTITLEMENTS = eSteem/eSteem.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 3;
CURRENT_PROJECT_VERSION = 7;
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = 75B6RXTKGT;
GCC_NO_COMMON_BLOCKS = NO;
@ -2004,7 +2008,7 @@
CODE_SIGN_ENTITLEMENTS = eSteem/eSteem.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 3;
CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = 75B6RXTKGT;
GCC_NO_COMMON_BLOCKS = NO;
HEADER_SEARCH_PATHS = (

View File

@ -12,7 +12,7 @@
<key>eSteem.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>29</integer>
<integer>30</integer>
</dict>
</dict>
</dict>

View File

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2.2.0</string>
<string>2.2.2</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
@ -31,26 +31,26 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>1666</string>
<string>1674</string>
<key>CodePushDeploymentKey</key>
<string>13ThFZsgwk6UZp6mIe95IDbnfw8iHy1jfsn-E</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<true />
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<true />
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<true />
</dict>
<key>steemconnect.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<true />
</dict>
</dict>
</dict>
@ -91,6 +91,7 @@
<string>FontAwesome5_Regular.ttf</string>
<string>FontAwesome5_Solid.ttf</string>
<string>Roboto-medium.ttf</string>
<string>Fontisto.ttf</string>
</array>
<key>UIBackgroundModes</key>
<array>
@ -103,12 +104,12 @@
<string>armv7</string>
</array>
<key>UIRequiresFullScreen</key>
<true/>
<true />
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<false />
</dict>
</plist>

View File

@ -15,10 +15,10 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>2.2.0</string>
<string>2.2.2</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>3</string>
<string>7</string>
</dict>
</plist>

View File

@ -1,6 +1,6 @@
{
"name": "eSteem",
"version": "2.2.0",
"version": "2.2.2",
"private": true,
"rnpm": {
"assets": [
@ -22,7 +22,7 @@
"clear": "watchman watch-del-all && rm -rf $TMPDIR/react-native-packager-cache-* && rm -rf $TMPDIR/metro-bundler-cache-* && rm -rf node_modules/ && yarn install && yarn start -- --reset-cache"
},
"dependencies": {
"@babel/runtime": "^7.1.2",
"@babel/runtime": "^7.5.5",
"@esteemapp/esteem-render-helpers": "^1.1.0",
"@ptomasroos/react-native-multi-slider": "^1.0.0",
"@react-native-community/netinfo": "^4.1.3",
@ -37,13 +37,13 @@
"crypto-js": "^3.1.9-1",
"currency-symbol-map": "^4.0.4",
"diff-match-patch": "^1.0.4",
"dsteem": "^0.10.1",
"dsteem": "https://github.com/esteemapp/dsteem#expire",
"intl": "^1.2.5",
"jsc-android": "^236355.1.1",
"lodash": "^4.17.13",
"moment": "^2.22.2",
"react": "16.8.3",
"react-intl": "^2.7.2",
"react-intl": "^3.1.6",
"react-native": "0.59.8",
"react-native-actionsheet": "^2.4.2",
"react-native-autocomplete-input": "^4.1.0",
@ -52,7 +52,7 @@
"react-native-datepicker": "^1.7.2",
"react-native-extended-stylesheet": "^0.10.0",
"react-native-fast-image": "^4.0.14",
"react-native-iap": "^3.3.0",
"react-native-iap": "^3.3.8",
"react-native-image-crop-picker": "^0.24.1",
"react-native-keyboard-aware-scroll-view": "^0.8.0",
"react-native-linear-gradient": "^2.4.2",
@ -78,11 +78,11 @@
"rn-placeholder": "^1.3.2",
"speakingurl": "^14.0.1",
"stacktrace-parser": "0.1.4",
"steemconnect": "^2.0.1"
"steemconnect": "^3.0.4"
},
"devDependencies": {
"@babel/core": "^7.4.4",
"@babel/runtime": "^7.4.4",
"@babel/core": "^7.5.5",
"@babel/runtime": "^7.5.5",
"babel-eslint": "^10.0.1",
"babel-jest": "^24.8.0",
"babel-preset-react-native": "~5.0.2",

View File

@ -0,0 +1,46 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
headerContainer: {
height: 100,
flexDirection: 'row',
padding: 21,
},
backIcon: {
color: '$white',
},
wrapper: {
marginLeft: 16,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
textWrapper: {
marginLeft: 16,
},
name: {
color: '$white',
fontSize: 14,
fontWeight: 'bold',
},
username: {
color: '$white',
fontSize: 12,
marginTop: 4,
},
addIcon: {
color: '$white',
textAlign: 'center',
},
addButton: {
backgroundColor: '$iconColor',
width: 20,
height: 20,
borderRadius: 20 / 2,
borderColor: '$white',
borderWidth: 1,
position: 'absolute',
bottom: 0,
left: 45,
},
});

View File

@ -0,0 +1,61 @@
import React from 'react';
import { withNavigation } from 'react-navigation';
import { View, Text, SafeAreaView } from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
import { UserAvatar } from '../userAvatar';
import { IconButton } from '../iconButton';
// Styles
import styles from './avatarHeaderStyles';
const AvatarHeader = ({
username,
name,
reputation,
navigation,
avatarUrl,
showImageUploadActions,
}) => (
<LinearGradient
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
colors={['#357ce6', '#2d5aa0']}
style={styles.headerView}
>
<SafeAreaView>
<View style={styles.headerContainer}>
<IconButton
iconStyle={styles.backIcon}
iconType="MaterialIcons"
name="arrow-back"
onPress={navigation.goBack}
size={25}
/>
<View style={styles.wrapper}>
<UserAvatar
key={avatarUrl || username}
noAction
size="xl"
username={username}
avatarUrl={avatarUrl}
/>
<IconButton
iconStyle={styles.addIcon}
style={styles.addButton}
iconType="MaterialCommunityIcons"
name="plus"
onPress={showImageUploadActions}
size={15}
/>
<View style={styles.textWrapper}>
{!!name && <Text style={styles.name}>{name}</Text>}
<Text style={styles.username}>{`@${username} (${reputation})`}</Text>
</View>
</View>
</View>
</SafeAreaView>
</LinearGradient>
);
export default withNavigation(AvatarHeader);

View File

@ -0,0 +1,3 @@
import AvatarHeader from './avatarHeaderView';
export { AvatarHeader };

View File

@ -67,6 +67,7 @@ class BasicHeaderView extends Component {
render() {
const {
disabled,
dropdownComponent,
handleOnPressBackButton,
handleOnPressClose,
@ -83,10 +84,10 @@ class BasicHeaderView extends Component {
isLoading,
isLoggedIn,
isModalHeader,
rightButtonText,
isPreviewActive,
isReply,
quickTitle,
rightButtonText,
rightIconName,
title,
} = this.props;
@ -99,8 +100,9 @@ class BasicHeaderView extends Component {
<IconButton
iconStyle={[styles.backIcon, isModalHeader && styles.closeIcon]}
iconType="MaterialIcons"
name={isModalHeader ? 'arrow-back' : 'arrow-back'}
name="arrow-back"
onPress={() => (isModalHeader ? handleOnPressClose() : handleOnPressBackButton())}
disabled={disabled}
/>
{isHasIcons && !isReply && (
<View>
@ -148,7 +150,6 @@ class BasicHeaderView extends Component {
{rightIconName && !isHasSearch && (
<IconButton
style={styles.rightIcon}
size={25}
onPress={() => handleRightIconPress()}
iconStyle={styles.rightIcon}
@ -171,7 +172,6 @@ class BasicHeaderView extends Component {
{isHasSearch && (
<IconButton
style={styles.rightIcon}
size={22}
onPress={() => this._handleSearchButtonPress()}
iconStyle={styles.rightIcon}

View File

@ -9,9 +9,10 @@ export default EStyleSheet.create({
justifyContent: 'space-between',
width: '$deviceWidth',
height: 50,
shadowOpacity: 0.2,
shadowOpacity: 0.1,
shadowOffset: {
height: 1.5,
width: 0,
height: -3,
},
elevation: 3,
},

View File

@ -1,9 +1,11 @@
import React from 'react';
import { View } from 'react-native';
import { View, SafeAreaView } from 'react-native';
import styles from './stickyBarStyles';
const StickyBar = ({ children, isFixedFooter }) => (
<View style={[styles.container, isFixedFooter && styles.fixedFooter]}>{children}</View>
<SafeAreaView>
<View style={[styles.container, isFixedFooter && styles.fixedFooter]}>{children}</View>
</SafeAreaView>
);
export default StickyBar;

View File

@ -1,17 +1,15 @@
import React, { Fragment } from 'react';
import React from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import styles from './tagStyles';
const Tag = ({ onPress, isPin, value, isPostCardTag }) => (
<Fragment>
<TouchableOpacity onPress={() => onPress && onPress(value)}>
<View
style={[styles.textWrapper, isPin && styles.isPin, isPostCardTag && styles.isPostCardTag]}
>
<Text style={[styles.text]}>{value}</Text>
</View>
</TouchableOpacity>
</Fragment>
<TouchableOpacity onPress={() => onPress && onPress(value)}>
<View
style={[styles.textWrapper, isPin && styles.isPin, isPostCardTag && styles.isPostCardTag]}
>
<Text style={[styles.text]}>{value}</Text>
</View>
</TouchableOpacity>
);
export default Tag;

View File

@ -18,19 +18,34 @@ const UserListItem = ({
handleOnLongPress,
isClickable,
text,
middleText,
rightTextStyle,
}) => (
<TouchableOpacity
onLongPress={() => handleOnLongPress && handleOnLongPress()}
disabled={!isClickable}
onPress={() => handleOnPress && handleOnPress()}
>
<View style={[styles.voteItemWrapper, index % 2 !== 0 && styles.voteItemWrapperGray]}>
<View style={[styles.voteItemWrapper, index % 2 === 0 && styles.voteItemWrapperGray]}>
{itemIndex && <Text style={styles.itemIndex}>{itemIndex}</Text>}
<UserAvatar noAction={userCanPress} style={styles.avatar} username={username} />
<View style={styles.userDescription}>
<Text style={styles.name}>{text || username}</Text>
{description && <Text style={styles.date}>{description}</Text>}
</View>
{middleText && (
<View style={styles.middleWrapper}>
<Text
style={[
styles.value,
isRightColor && styles.valueGray,
isBlackRightColor && styles.valueBlack,
]}
>
{middleText}
</Text>
</View>
)}
{isHasRightItem && (
<View style={styles.rightWrapper}>
<Text
@ -38,6 +53,7 @@ const UserListItem = ({
styles.value,
isRightColor && styles.valueGray,
isBlackRightColor && styles.valueBlack,
rightTextStyle,
]}
>
{rightText}

View File

@ -47,6 +47,7 @@ export default EStyleSheet.create({
rightWrapper: {
textAlign: 'center',
alignItems: 'center',
width: 80,
},
text: {
color: '$iconColor',
@ -68,4 +69,7 @@ export default EStyleSheet.create({
fontSize: 10,
marginRight: 17,
},
middleWrapper: {
marginRight: 30,
},
});

View File

@ -27,7 +27,6 @@ const WalletLineItem = ({
isHasdropdown,
dropdownOptions,
onDropdownSelect,
dropdownStyle,
}) => (
<GrayWrapper isGray={index && index % 2 !== 0}>
<View style={[styles.container, fitContent && styles.fitContent, style]}>

View File

@ -1 +1,3 @@
export { default } from './view/checkboxView';
import CheckBox from './view/checkboxView';
export { CheckBox };

View File

@ -41,7 +41,7 @@ export default EStyleSheet.create({
alignSelf: 'center',
justifyContent: 'center',
alignItems: 'center',
fontSize: 10,
fontSize: 12,
color: '$iconColor',
},
voteCountWrapper: {

View File

@ -1,7 +1,8 @@
import React, { PureComponent, Fragment } from 'react';
import { View, Text, TouchableWithoutFeedback } from 'react-native';
import { View, TouchableWithoutFeedback } from 'react-native';
import ActionSheet from 'react-native-actionsheet';
import { injectIntl } from 'react-intl';
import get from 'lodash/get';
import { getTimeFromNow } from '../../../utils/time';
// Constants
@ -51,6 +52,7 @@ class CommentView extends PureComponent {
handleOnLongPress,
handleOnReplyPress,
handleOnUserPress,
handleOnVotersPress,
isLoggedIn,
isShowComments,
isShowMoreButton,
@ -84,15 +86,23 @@ class CommentView extends PureComponent {
{isLoggedIn && (
<Fragment>
<Upvote isShowPayoutValue content={comment} />
<IconButton
size={18}
iconStyle={styles.leftIcon}
<TextWithIcon
iconName="people"
iconSize={20}
wrapperStyle={styles.leftButton}
iconType="MaterialIcons"
name="people"
isClickable
onPress={() =>
handleOnVotersPress &&
voteCount > 0 &&
handleOnVotersPress(get(comment, 'active_votes'))
}
text={voteCount}
textMarginLeft={20}
textStyle={styles.voteCountText}
/>
<Text style={styles.voteCountText}>{voteCount}</Text>
<IconButton
size={18}
size={20}
iconStyle={styles.leftIcon}
style={styles.leftButton}
name="reply"
@ -102,7 +112,7 @@ class CommentView extends PureComponent {
{currentAccountUsername === comment.author && (
<Fragment>
<IconButton
size={18}
size={20}
iconStyle={styles.leftIcon}
style={styles.leftButton}
name="create"
@ -112,7 +122,7 @@ class CommentView extends PureComponent {
{!comment.children && !voteCount && (
<Fragment>
<IconButton
size={18}
size={20}
iconStyle={styles.leftIcon}
style={styles.leftButton}
name="delete-forever"

View File

@ -51,9 +51,11 @@ class CommentsContainer extends Component {
// Component Functions
_shortComments = sortOrder => {
_shortComments = (sortOrder, comments) => {
const { comments: parent } = this.state;
const sortedComments = comments || parent;
const allPayout = c =>
parseFloat(get(c, 'pending_payout_value').split(' ')[0]) +
parseFloat(get(c, 'total_payout_value').split(' ')[0]) +
@ -116,25 +118,39 @@ class CommentsContainer extends Component {
},
};
parent.sort(sortOrders[sortOrder]);
sortedComments.sort(sortOrders[sortOrder]);
return parent;
return sortedComments;
};
_getComments = async () => {
const {
author,
permlink,
selectedFilter,
currentAccount: { name },
isOwnProfile,
fetchPost,
} = this.props;
await getComments(author, permlink, name)
.then(comments => {
this.setState({
comments,
});
})
.catch(() => {});
if (isOwnProfile) {
fetchPost();
} else {
await getComments(author, permlink, name)
.then(comments => {
if (selectedFilter && selectedFilter !== 'TRENDING') {
const sortComments = this._shortComments(selectedFilter, comments);
this.setState({
comments: sortComments,
});
} else {
this.setState({
comments,
});
}
})
.catch(() => {});
}
};
_handleOnReplyPress = item => {
@ -165,31 +181,43 @@ class CommentsContainer extends Component {
};
_handleDeleteComment = permlink => {
const { currentAccount, pinCode } = this.props;
const { currentAccount, pinCode, comments } = this.props;
const { comments: _comments } = this.state;
let filteredComments;
deleteComment(currentAccount, pinCode, permlink).then(() => {
this._getComments();
if (_comments.length > 0) {
filteredComments = _comments.filter(item => item.permlink !== permlink);
} else {
filteredComments = comments.filter(item => item.permlink !== permlink);
}
this.setState({ comments: filteredComments });
});
};
_handleCommentCopyAction = (index, selectedComment) => {
const { dispatch, intl } = this.props;
_handleOnPressCommentMenu = (index, selectedComment) => {
const { dispatch, intl, navigation, isOwnProfile } = this.props;
switch (index) {
case 0:
writeToClipboard(`https://steemit.com${get(selectedComment, 'url')}`).then(() => {
dispatch(
toastNotification(
intl.formatMessage({
id: 'alert.copied',
}),
),
);
});
break;
default:
break;
if (index === 0) {
writeToClipboard(`https://esteem.app${get(selectedComment, 'url')}`).then(() => {
dispatch(
toastNotification(
intl.formatMessage({
id: 'alert.copied',
}),
),
);
});
} else if (index === 1 && isOwnProfile) {
navigation.navigate({
routeName: ROUTES.SCREENS.POST,
key: get(selectedComment, 'permlink'),
params: {
author: get(selectedComment, 'author'),
permlink: get(selectedComment, 'permlink'),
isHasParentPost: get(selectedComment, 'parent_permlink'),
},
});
}
};
@ -207,6 +235,7 @@ class CommentsContainer extends Component {
selectedFilter,
mainAuthor,
selectedPermlink: _selectedPermlink,
isOwnProfile,
} = this.props;
return (
@ -219,15 +248,15 @@ class CommentsContainer extends Component {
isShowMoreButton={isShowMoreButton}
commentNumber={commentNumber || 1}
commentCount={commentCount}
comments={_comments || comments}
comments={_comments.length > 0 ? _comments : comments}
currentAccountUsername={currentAccount.name}
handleOnEditPress={this._handleOnEditPress}
handleOnReplyPress={this._handleOnReplyPress}
isLoggedIn={isLoggedIn}
fetchPost={fetchPost}
handleDeleteComment={this._handleDeleteComment}
handleCommentCopyAction={this._handleCommentCopyAction}
{...this.props}
handleOnPressCommentMenu={this._handleOnPressCommentMenu}
isOwnProfile={isOwnProfile}
/>
);
}

View File

@ -1,8 +1,7 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
text: {
color: '$primaryBlack',
fontSize: 10,
list: {
marginBottom: 20,
},
});

View File

@ -1,4 +1,4 @@
import React, { PureComponent, Fragment } from 'react';
import React, { Component, Fragment } from 'react';
import { FlatList } from 'react-native';
import ActionSheet from 'react-native-actionsheet';
import get from 'lodash/get';
@ -9,9 +9,9 @@ import { injectIntl } from 'react-intl';
import { Comment } from '../../comment';
// Styles
// import styles from './commentStyles';
import styles from './commentStyles';
class CommentsView extends PureComponent {
class CommentsView extends Component {
/* Props
* ------------------------------------------------
* @prop { type } name - Description....
@ -52,14 +52,25 @@ class CommentsView extends PureComponent {
isShowSubComments,
marginLeft,
handleDeleteComment,
handleCommentCopyAction,
handleOnPressCommentMenu,
handleOnVotersPress,
intl,
isOwnProfile,
} = this.props;
const { selectedComment } = this.state;
const menuItems = isOwnProfile
? [
intl.formatMessage({ id: 'post.copy_link' }),
intl.formatMessage({ id: 'post.open_thread' }),
intl.formatMessage({ id: 'alert.cancel' }),
]
: [intl.formatMessage({ id: 'post.copy_link' }), intl.formatMessage({ id: 'alert.cancel' })];
return (
<Fragment>
<FlatList
style={styles.list}
data={comments}
keyExtractor={this._keyExtractor}
renderItem={({ item }) => (
@ -75,6 +86,7 @@ class CommentsView extends PureComponent {
handleOnEditPress={handleOnEditPress}
handleOnReplyPress={handleOnReplyPress}
handleOnUserPress={handleOnUserPress}
handleOnVotersPress={handleOnVotersPress}
isLoggedIn={isLoggedIn}
isShowMoreButton={commentNumber === 1 && get(item, 'children') > 0}
voteCount={get(item, 'vote_count')}
@ -87,17 +99,10 @@ class CommentsView extends PureComponent {
/>
<ActionSheet
ref={this.commentMenu}
options={[
intl.formatMessage({
id: 'post.copy_link',
}),
intl.formatMessage({
id: 'alert.cancel',
}),
]}
options={menuItems}
title={get(selectedComment, 'summary')}
cancelButtonIndex={1}
onPress={index => handleCommentCopyAction(index, selectedComment)}
cancelButtonIndex={isOwnProfile ? 2 : 1}
onPress={index => handleOnPressCommentMenu(index, selectedComment)}
/>
</Fragment>
);

View File

@ -31,7 +31,15 @@ class CommentsDisplayView extends PureComponent {
};
render() {
const { author, commentCount, fetchPost, intl, permlink, mainAuthor } = this.props;
const {
author,
commentCount,
fetchPost,
intl,
permlink,
mainAuthor,
handleOnVotersPress,
} = this.props;
const { selectedFilter } = this.state;
return (
@ -56,6 +64,7 @@ class CommentsDisplayView extends PureComponent {
author={author}
permlink={permlink}
mainAuthor={mainAuthor}
handleOnVotersPress={handleOnVotersPress}
/>
</View>
</Fragment>

View File

@ -40,7 +40,7 @@ class DateTimePickerView extends PureComponent {
}
if (date && _value) {
const formatedDateTime = new Date(`${date} ${_value}`).toISOString();
const formatedDateTime = moment(`${date} ${_value}`, 'YYYY-MM-DD HH:mm').toISOString();
onSubmit(formatedDateTime);
this._initState();
}
@ -55,11 +55,11 @@ class DateTimePickerView extends PureComponent {
if (type === 'date-time') {
_type = date ? 'time' : 'date';
_format = date ? 'HH:MM' : 'YYYY-MM-DD';
_format = date ? 'HH:mm' : 'YYYY-MM-DD';
_minDate = date ? null : moment().format('YYYY-MM-DD');
} else {
_type = type;
_format = type === 'date' ? 'YYYY-MM-DD' : 'HH:MM';
_format = type === 'date' ? 'YYYY-MM-DD' : 'HH:mm';
_minDate = type === 'date' ? moment().format('YYYY-MM-DD') : null;
}

View File

@ -19,7 +19,7 @@ export default class TagAreaView extends Component {
this.state = {
currentText: '',
chips: props.draftChips || [' '],
chipsCount: props.chipsCount || 5,
chipsCount: props.chipsCount || 7,
activeChip: 0,
};
}
@ -102,7 +102,7 @@ export default class TagAreaView extends Component {
<ScrollView horizontal style={styles.tagWrapper}>
{chips.map(
(chip, i) =>
i < 5 && (
i < 7 && (
<Chip
key={i}
refs={input => {

View File

@ -4,8 +4,7 @@ export default EStyleSheet.create({
wrapper: {
borderTopLeftRadius: 8,
borderTopRightRadius: 8,
marginHorizontal: 30,
marginVertical: 10,
marginTop: 16,
flexDirection: 'row',
backgroundColor: '$primaryGray',
height: 60,
@ -21,7 +20,6 @@ export default EStyleSheet.create({
textInput: {
flex: 0.7,
flexDirection: 'row',
justifyContent: 'center',
},
icon: {
flex: 0.15,

View File

@ -1,12 +1,14 @@
import React, { Component } from 'react';
import { View } from 'react-native';
import FastImage from 'react-native-fast-image';
import { Icon } from '../../icon';
// Constants
// Components
import { TextInput } from '../../textInput';
import { Icon } from '../../icon';
// Utils
import { getResizedAvatar } from '../../../utils/image';
// Styles
import styles from './formInputStyles';
@ -19,28 +21,18 @@ class FormInputView extends Component {
* @prop { boolean } isEditable - Can permission edit.
* @prop { boolean } isValid - This delegate input valit or not.
* @prop { boolean } secureTextEntry - For hiding password value.
*
*
*
*/
constructor(props) {
super(props);
this.state = {
value: '',
inputBorderColor: '#c1c5c7',
value: props.value || '',
inputBorderColor: '#e7e7e7',
isValid: true,
formInputWidth: '99%',
};
}
// Component Life Cycles
componentWillMount() {
setTimeout(() => {
this.setState({ formInputWidth: '100%' });
}, 100);
}
componentWillReceiveProps(nextProps) {
const { isValid } = this.props;
@ -54,18 +46,15 @@ class FormInputView extends Component {
const { onChange } = this.props;
this.setState({ value });
onChange && onChange(value);
if (onChange) onChange(value);
};
_handleOnFocus = () => {
const { inputBorderColor } = this.state;
if (inputBorderColor !== '#357ce6') {
this.setState({ inputBorderColor: '#357ce6' });
}
this.setState({ inputBorderColor: '#357ce6' });
};
render() {
const { inputBorderColor, isValid, value, formInputWidth } = this.state;
const { inputBorderColor, isValid, value } = this.state;
const {
placeholder,
type,
@ -75,6 +64,9 @@ class FormInputView extends Component {
rightIconName,
secureTextEntry,
iconType,
wrapperStyle,
height,
inputStyle,
} = this.props;
return (
<View
@ -83,6 +75,7 @@ class FormInputView extends Component {
{
borderBottomColor: isValid ? inputBorderColor : 'red',
},
wrapperStyle,
]}
>
{isFirstImage && value && value.length > 2 ? (
@ -90,26 +83,30 @@ class FormInputView extends Component {
<FastImage
style={styles.firstImage}
source={{
uri: `https://steemitimages.com/u/${value}/avatar/small`,
uri: getResizedAvatar(value),
priority: FastImage.priority.high,
}}
resizeMode={FastImage.resizeMode.cover}
/>
</View>
) : (
<Icon iconType={iconType || 'MaterialIcons'} name={rightIconName} style={styles.icon} />
rightIconName && (
<Icon iconType={iconType || 'MaterialIcons'} name={rightIconName} style={styles.icon} />
)
)}
<View style={styles.textInput}>
<TextInput
onFocus={() => this._handleOnFocus()}
style={inputStyle}
onFocus={() => this.setState({ inputBorderColor: '#357ce6' })}
onBlur={() => this.setState({ inputBorderColor: '#e7e7e7' })}
autoCapitalize="none"
secureTextEntry={secureTextEntry}
height={height}
placeholder={placeholder}
editable={isEditable || true}
textContentType={type}
onChangeText={val => this._handleOnChange(val)}
onChangeText={this._handleOnChange}
value={value}
style={{ width: formInputWidth }}
/>
</View>

View File

@ -1,12 +1,7 @@
import React, { PureComponent } from 'react';
import { withNavigation } from 'react-navigation';
import { connect } from 'react-redux';
// Services and Actions
// Middleware
// Constants
import { get, has } from 'lodash';
// Component
import HeaderView from '../view/headerView';
@ -30,7 +25,7 @@ class HeaderContainer extends PureComponent {
_handleOpenDrawer = () => {
const { navigation } = this.props;
if (navigation && navigation.openDrawer && typeof navigation.openDrawer === 'function') {
if (has(navigation, 'openDrawer') && typeof get(navigation, 'openDrawer') === 'function') {
navigation.openDrawer();
}
};
@ -52,19 +47,11 @@ class HeaderContainer extends PureComponent {
isLoginDone,
isDarkTheme,
} = this.props;
let displayName;
let username;
let reputation;
const _user = isReverse && selectedUser ? selectedUser : currentAccount;
if (isReverse && selectedUser) {
displayName = selectedUser.display_name;
username = selectedUser.name;
reputation = selectedUser.reputation;
} else if (!isReverse) {
displayName = currentAccount.display_name;
username = currentAccount.name;
reputation = currentAccount.reputation;
}
const displayName = get(_user, 'display_name');
const username = get(_user, 'name');
const reputation = get(_user, 'reputation');
return (
<HeaderView

View File

@ -34,19 +34,20 @@ class HeaderView extends Component {
render() {
const {
avatarUrl,
displayName,
handleOnPressBackButton,
handleOpenDrawer,
intl,
isDarkTheme,
isLoggedIn,
isLoginDone,
isReverse,
reputation,
username,
isDarkTheme,
} = this.props;
const { isSearchModalOpen } = this.state;
let gredientColor = isDarkTheme ? ['#081c36', '#43638e'] : ['#2d5aa0', '#357ce6'];
let gredientColor;
if (isReverse) {
gredientColor = isDarkTheme ? ['#43638e', '#081c36'] : ['#357ce6', '#2d5aa0'];

View File

@ -1,5 +1,5 @@
import React, { Fragment } from 'react';
import { TouchableHighlight } from 'react-native';
import { TouchableHighlight, ActivityIndicator } from 'react-native';
import { Icon } from '../../icon';
import styles from './iconButtonStyles';
@ -22,28 +22,33 @@ const IconButton = ({
onPress,
size,
style,
isLoading,
}) => (
<Fragment>
<TouchableHighlight
style={[styles.iconButton, style]}
onPress={() => onPress && onPress()}
onPress={() => !isLoading && onPress && onPress()}
underlayColor={backgroundColor || 'white'}
disabled={disabled}
>
<Icon
style={[
color && { color },
backgroundColor && { backgroundColor },
styles.icon,
iconStyle && iconStyle,
]}
badgeTextStyle={badgeTextStyle}
name={name}
badgeStyle={badgeStyle}
size={size}
iconType={iconType}
badgeCount={badgeCount}
/>
{!isLoading ? (
<Icon
style={[
color && { color },
backgroundColor && { backgroundColor },
styles.icon,
iconStyle && iconStyle,
]}
badgeTextStyle={badgeTextStyle}
name={name}
badgeStyle={badgeStyle}
size={size}
iconType={iconType}
badgeCount={badgeCount}
/>
) : (
<ActivityIndicator color="white" style={styles.activityIndicator} />
)}
</TouchableHighlight>
</Fragment>
);

View File

@ -1,31 +1,184 @@
import { CircularButton, TextButton, IconButton } from './buttons';
import { AvatarHeader } from './avatarHeader';
import { BasicHeader } from './basicHeader';
import { BottomTabBar } from './bottomTabBar';
import { CheckBox } from './checkbox';
import { CircularButton, TextButton, SquareButton } from './buttons';
import { CollapsibleCard } from './collapsibleCard';
import { ContainerHeader } from './containerHeader';
import { DateTimePicker } from './dateTimePicker';
import { DropdownButton } from './dropdownButton';
import { FilterBar } from './filterBar';
import { FormatedCurrency } from './formatedElements';
import { FormInput } from './formInput';
import { Header } from './header';
import { Icon } from './icon';
import { IconButton } from './iconButton';
import { InformationArea } from './informationArea';
import { InformationBox } from './informationBox';
import { LoginHeader } from './loginHeader';
import { MainButton } from './mainButton';
import { MarkdownEditor } from './markdownEditor';
import { Modal } from './modal';
import { NotificationLine } from './notificationLine';
import { NumericKeyboard } from './numericKeyboard';
import { ParentPost } from './parentPost';
import { PercentBar } from './percentBar';
import { PinAnimatedInput } from './pinAnimatedInput';
import { PostCard } from './postCard';
import { PostDisplay } from './postView';
import { PostDropdown } from './postDropdown';
import { PostForm } from './postForm';
import { PostHeaderDescription, PostBody, Tags } from './postElements';
import { PostListItem } from './postListItem';
import { ProfileSummary } from './profileSummary';
import { PulseAnimation } from './animations';
import { SearchInput } from './searchInput';
import { SearchModal } from './searchModal';
import { SettingsItem } from './settingsItem';
import { SideMenu } from './sideMenu';
import Icon from './icon';
import Logo from './logo/logo';
import Modal from './modal';
import { SummaryArea, TagArea, TextArea, TitleArea } from './editorElements';
import { TabBar } from './tabBar';
import { TextInput } from './textInput';
import ScaleSlider from './scaleSlider/scaleSliderView';
import UserListItem from './basicUIElements/view/userListItem/userListItem';
import { ToastNotification } from './toastNotification';
import { ToggleSwitch } from './toggleSwitch';
import { TransferFormItem } from './transferFormItem';
import { Upvote } from './upvote';
import { UserAvatar } from './userAvatar';
import Logo from './logo/logo';
import PostButton from './postButton/postButtonView';
import SpinGame from './spinGame/view/spinGameView';
import ProfileEditForm from './profileEditForm/profileEditFormView';
import ScaleSlider from './scaleSlider/scaleSliderView';
// View
import { Comment } from './comment';
import { Comments } from './comments';
import { CommentsDisplay } from './commentsDisplay';
import { LeaderBoard } from './leaderboard';
import { Notification } from './notification';
import { Points } from './points';
import { Posts } from './posts';
import { Transaction } from './transaction';
import { VotersDisplay } from './votersDisplay';
import { Wallet } from './wallet';
import { WalletDetails } from './walletDetails';
import PostBoost from './postBoost/postBoostView';
import Profile from './profile/profileView';
import Promote from './promote/promoteView';
// Basic UI Elements
import {
BoostPlaceHolder,
Card,
Chip,
GrayWrapper,
LineBreak,
ListItemPlaceHolder,
ListPlaceHolder,
NoInternetConnection,
NoPost,
PostCardPlaceHolder,
PostPlaceHolder,
ProfileSummaryPlaceHolder,
StickyBar,
Tag,
TextWithIcon,
UserListItem,
WalletDetailsPlaceHolder,
WalletLineItem,
WalletUnclaimedPlaceHolder,
} from './basicUIElements';
export {
Card,
Chip,
GrayWrapper,
LineBreak,
ListItemPlaceHolder,
ListPlaceHolder,
BoostPlaceHolder,
NoInternetConnection,
NoPost,
PostCardPlaceHolder,
PostPlaceHolder,
ProfileSummaryPlaceHolder,
StickyBar,
Tag,
TextWithIcon,
UserListItem,
WalletDetailsPlaceHolder,
WalletLineItem,
WalletUnclaimedPlaceHolder,
AvatarHeader,
BasicHeader,
InformationBox,
BottomTabBar,
CheckBox,
CircularButton,
CollapsibleCard,
Comment,
Comments,
CommentsDisplay,
ContainerHeader,
DateTimePicker,
DropdownButton,
FilterBar,
FormatedCurrency,
FormInput,
Header,
Icon,
IconButton,
InformationArea,
LeaderBoard,
LoginHeader,
Logo,
SpinGame,
MainButton,
MarkdownEditor,
Modal,
Notification,
NotificationLine,
NumericKeyboard,
ParentPost,
PercentBar,
PinAnimatedInput,
Points,
PostBody,
PostBoost,
PostButton,
PostCard,
PostDisplay,
PostDropdown,
PostForm,
PostHeaderDescription,
PostListItem,
Posts,
Profile,
ProfileEditForm,
ProfileSummary,
Promote,
PulseAnimation,
ScaleSlider,
SearchInput,
SearchModal,
SettingsItem,
SideMenu,
SquareButton,
SummaryArea,
TabBar,
TagArea,
Tags,
TextArea,
TextButton,
TextInput,
UserListItem,
PostButton,
TitleArea,
ToastNotification,
ToggleSwitch,
Transaction,
TransferFormItem,
Upvote,
UserAvatar,
VotersDisplay,
Wallet,
WalletDetails,
};

View File

@ -1 +1,3 @@
export { default } from './view/informationBox';
import InformationBox from './view/informationBoxView';
export { InformationBox };

View File

@ -48,7 +48,7 @@ class LeaderboardContainer extends PureComponent {
});
};
_fetchLeaderBoard = async () => {
_fetchLeaderBoard = async selectedFilter => {
const { intl, isConnected } = this.props;
let users;
@ -57,7 +57,7 @@ class LeaderboardContainer extends PureComponent {
this.setState({ refreshing: true });
try {
users = await getLeaderboard();
users = await getLeaderboard(selectedFilter);
} catch (error) {
Alert.alert(
intl.formatMessage({ id: 'alert.error' }),

View File

@ -5,12 +5,29 @@ export default EStyleSheet.create({
flex: 1,
padding: 8,
},
text: {
title: {
color: '$primaryDarkGray',
fontSize: 14,
fontWeight: 'bold',
marginLeft: 20,
fontFamily: '$primaryFont',
paddingBottom: 10,
flexGrow: 1,
},
rewardText: {
color: '$primaryBlue',
},
columnTitleWrapper: {
flexDirection: 'row',
marginTop: 10,
justifyContent: 'flex-end',
},
columnTitle: {
color: '$primaryDarkGray',
fontSize: 12,
fontWeight: 'bold',
marginRight: 40,
marginBottom: 10,
fontFamily: '$primaryFont',
},
});

View File

@ -1,11 +1,12 @@
import React, { PureComponent } from 'react';
import React, { PureComponent, Fragment } from 'react';
import { View, FlatList, Text } from 'react-native';
import { injectIntl } from 'react-intl';
import get from 'lodash/get';
// Components
import { UserListItem, ListPlaceHolder } from '../../basicUIElements';
import { FilterBar } from '../../filterBar';
import FILTER_OPTIONS, { VALUE } from '../../../constants/options/leaderboard';
// Styles
import styles from './leaderboardStyles';
@ -28,9 +29,11 @@ class LeaderboardView extends PureComponent {
isHasRightItem
isClickable
isBlackRightColor
rightText={get(item, 'count')}
rightText={get(item, 'points')}
middleText={get(item, 'count')}
itemIndex={index + 1}
handleOnPress={() => handleOnUserPress(get(item, '_id'))}
rightTextStyle={styles.rewardText}
/>
);
};
@ -39,25 +42,37 @@ class LeaderboardView extends PureComponent {
const { users, intl, fetchLeaderBoard, refreshing } = this.props;
return (
<View style={styles.container}>
<Text style={styles.text}>
{intl.formatMessage({
id: 'notification.leaderboard_title',
})}
</Text>
{!users ? (
<ListPlaceHolder />
) : (
<Fragment>
<FilterBar
dropdownIconName="arrow-drop-down"
options={VALUE.map(val => intl.formatMessage({ id: `leaderboard.${val}` }))}
selectedOptionIndex={0}
defaultText={intl.formatMessage({ id: `leaderboard.${VALUE[0]}` })}
onDropdownSelect={selectedIndex => fetchLeaderBoard(FILTER_OPTIONS[selectedIndex])}
/>
<View style={styles.container}>
<View style={styles.columnTitleWrapper}>
<Text style={styles.title}>
{intl.formatMessage({
id: 'notification.leaderboard_title',
})}
</Text>
<Text style={[styles.columnTitle]}>Activities</Text>
<Text style={[styles.columnTitle]}>Reward</Text>
</View>
<FlatList
data={users}
refreshing={refreshing}
keyExtractor={item => get(item, '_id', Math.random()).toString()}
removeClippedSubviews={false}
ListEmptyComponent={<ListPlaceHolder />}
onRefresh={() => fetchLeaderBoard()}
renderItem={({ item, index }) => this._renderItem(item, index)}
/>
)}
</View>
</View>
</Fragment>
);
}
}

View File

@ -63,9 +63,9 @@ export default class MarkdownEditorView extends Component {
componentDidUpdate(prevProps, prevState) {
const { text } = this.state;
const { isFormValid, handleIsFormValid } = this.props;
const { handleIsFormValid } = this.props;
if (prevState.text !== text && !isFormValid) {
if (prevState.text !== text) {
const nextText = text.replace(prevState.text, '');
if (nextText && nextText.length > 0) {
@ -81,14 +81,6 @@ export default class MarkdownEditorView extends Component {
// Component functions
_changeText = input => {
const { onChange, handleOnTextChange, handleIsValid, componentID } = this.props;
const { textUpdated } = this.state;
if (textUpdated) {
this.setState({
textUpdated: false,
});
return;
}
this.setState({ text: input });
@ -199,7 +191,7 @@ export default class MarkdownEditorView extends Component {
};
render() {
const { handleOpenImagePicker, intl, isPreviewActive, isReply } = this.props;
const { handleOpenImagePicker, intl, isPreviewActive, isReply, isLoading } = this.props;
const { text, selection } = this.state;
return (
@ -223,6 +215,7 @@ export default class MarkdownEditorView extends Component {
underlineColorAndroid="transparent"
value={text}
innerRef={this.inputRef}
editable={!isLoading}
/>
) : (
this._renderPreview()

View File

@ -1,3 +1,4 @@
/* eslint-disable react/jsx-wrap-multilines */
import React, { PureComponent, Fragment } from 'react';
import { View, FlatList, ActivityIndicator, RefreshControl } from 'react-native';
import { injectIntl } from 'react-intl';
@ -7,7 +8,7 @@ import { injectIntl } from 'react-intl';
// Components
import { ContainerHeader } from '../../containerHeader';
import { FilterBar } from '../../filterBar';
import NotificationLine from '../../notificationLine';
import { NotificationLine } from '../..';
import { ListPlaceHolder } from '../../basicUIElements';
// Utils
@ -168,38 +169,35 @@ class NotificationView extends PureComponent {
rightIconType="MaterialIcons"
onRightIconPress={readAllNotification}
/>
{_notifications.length === 0 ? (
<ListPlaceHolder />
) : (
<FlatList
data={_notifications}
refreshing={isNotificationRefreshing}
onRefresh={() => getActivities()}
keyExtractor={item => item.title}
onEndReached={() => getActivities(null, selectedFilter, true)}
ListFooterComponent={this._renderFooterLoading}
refreshControl={
<RefreshControl
refreshing={isNotificationRefreshing}
progressBackgroundColor="#357CE6"
tintColor={!isDarkTheme ? '#357ce6' : '#96c0ff'}
titleColor="#fff"
colors={['#fff']}
<FlatList
data={_notifications}
refreshing={isNotificationRefreshing}
onRefresh={() => getActivities()}
keyExtractor={item => item.title}
onEndReached={() => getActivities(null, selectedFilter, true)}
ListFooterComponent={this._renderFooterLoading}
ListEmptyComponent={<ListPlaceHolder />}
refreshControl={
<RefreshControl
refreshing={isNotificationRefreshing}
progressBackgroundColor="#357CE6"
tintColor={!isDarkTheme ? '#357ce6' : '#96c0ff'}
titleColor="#fff"
colors={['#fff']}
/>
}
renderItem={({ item, index }) => (
<Fragment>
<ContainerHeader
hasSeperator={index !== 0}
isBoldTitle
title={item.title}
key={item.title}
/>
}
renderItem={({ item, index }) => (
<Fragment>
<ContainerHeader
hasSeperator={index !== 0}
isBoldTitle
title={item.title}
key={item.title}
/>
{this._renderList(item.notifications)}
</Fragment>
)}
/>
)}
{this._renderList(item.notifications)}
</Fragment>
)}
/>
</View>
);
}

View File

@ -1,3 +1,3 @@
import NotificationLineView from './view/notificationLineView';
import NotificationLine from './view/notificationLineView';
export default NotificationLineView;
export { NotificationLine };

View File

@ -1,7 +1,8 @@
import React from 'react';
import { View } from 'react-native';
import times from 'lodash/times';
import { CircularButton, IconButton } from '../../buttons';
import { CircularButton } from '../../buttons';
import { IconButton } from '../../iconButton';
import styles from './numericKeyboardStyles';
@ -26,11 +27,11 @@ const NumericKeyboard = ({ onPress }) => (
onPress={value => onPress && onPress(value)}
/>
<IconButton
handleOnPress={() => onPress && onPress('clear')}
onPress={() => onPress && onPress('clear')}
isCircle
buttonStyle={styles.buttonWithoutBorder}
style={styles.iconButton}
name="backspace"
name="ios-backspace"
/>
</View>
</View>

View File

@ -1,3 +1,4 @@
/* eslint-disable react/jsx-wrap-multilines */
import React, { Component, Fragment } from 'react';
import { Text, View, FlatList, ScrollView, RefreshControl, TouchableOpacity } from 'react-native';
import { injectIntl } from 'react-intl';
@ -11,6 +12,7 @@ import { IconButton } from '../../iconButton';
import { Icon } from '../../icon';
import { MainButton } from '../../mainButton';
import { DropdownButton } from '../../dropdownButton';
import { CollapsibleCard } from '../../collapsibleCard';
// Utils
import { getTimeFromNow } from '../../../utils/time';
@ -140,6 +142,7 @@ class PointsView extends Component {
<FlatList
style={styles.iconsList}
data={POINTS_KEYS}
keyExtractor={item => get(item, 'type', Math.random()).toString()}
horizontal
renderItem={({ item }) => (
<PopoverController key={get(item, 'type')}>
@ -192,27 +195,45 @@ class PointsView extends Component {
</View>
<View style={styles.listWrapper}>
{!userActivities ? (
this._renderLoading()
) : (
<FlatList
data={userActivities}
keyExtractor={item => item.id.toString()}
renderItem={({ item, index }) => (
<WalletLineItem
index={index + 1}
text={this._getTranslation(get(item, 'textKey'))}
description={getTimeFromNow(get(item, 'created'))}
isCircleIcon
isThin
isBlackText
iconName={get(item, 'icon')}
iconType={get(item, 'iconType')}
rightText={`${get(item, 'amount')} ESTM`}
/>
)}
/>
)}
<FlatList
data={userActivities}
keyExtractor={item => item.id.toString()}
ListEmptyComponent={this._renderLoading()}
renderItem={({ item, index }) => (
<CollapsibleCard
noBorder
noContainer
key={item.id.toString()}
titleComponent={
<WalletLineItem
index={index + 1}
text={this._getTranslation(get(item, 'textKey'))}
description={getTimeFromNow(get(item, 'created'))}
isCircleIcon
isThin
isBlackText
iconName={get(item, 'icon')}
iconType={get(item, 'iconType')}
rightText={`${get(item, 'amount')} ESTM`}
/>
}
>
{(get(item, 'memo') || get(item, 'sender')) && (
<WalletLineItem
isBlackText
isThin
text={
get(item, 'sender')
? `${intl.formatMessage({ id: 'points.from' })} @${get(item, 'sender')}`
: get(item, 'receiver') &&
`${intl.formatMessage({ id: 'points.to' })} @${get(item, 'receiver')}`
}
description={get(item, 'memo')}
/>
)}
</CollapsibleCard>
)}
/>
</View>
</ScrollView>
</Fragment>

View File

@ -37,7 +37,7 @@ export default EStyleSheet.create({
color: '$primaryBlack',
width: 172,
},
autocomplate: {
autocomplete: {
borderWidth: 0,
borderColor: '$borderColor',
borderRadius: 8,
@ -46,33 +46,34 @@ export default EStyleSheet.create({
width: 172,
marginRight: 33,
},
autocomplateLineContainer: {
autocompleteLineContainer: {
flexDirection: 'row',
paddingHorizontal: 20,
zIndex: 999,
},
autocomplateLabelText: {
autocompleteLabelText: {
color: '$primaryBlack',
fontWeight: '600',
},
autocomplateListContainer: {
autocompleteListContainer: {
backgroundColor: '$primaryWhiteLightBackground',
width: 172,
zIndex: 999,
},
autocomplateItemText: {
autocompleteItemText: {
color: '$primaryBlack',
padding: 3,
},
autocomplateList: {
autocompleteList: {
zIndex: 999,
backgroundColor: '$primaryWhiteLightBackground',
},
autocomplateLabelContainer: {
autocompleteLabelContainer: {
flex: 1,
padding: 10,
justifyContent: 'center',
color: '$primaryBlack',
maxWidth: '$deviceWidth / 2.9',
},
textarea: {
borderWidth: 1,

View File

@ -0,0 +1,272 @@
import React, { PureComponent, Fragment } from 'react';
import { injectIntl } from 'react-intl';
import { Text, View, WebView, ScrollView, TouchableOpacity, Alert } from 'react-native';
import get from 'lodash/get';
import ActionSheet from 'react-native-actionsheet';
import Autocomplete from 'react-native-autocomplete-input';
import { Icon, TextInput } from '..';
import { steemConnectOptions } from '../../constants/steemConnectOptions';
// Services and Actions
import { getUser } from '../../providers/esteem/ePoint';
import { searchPath } from '../../providers/esteem/esteem';
// Components
import { BasicHeader } from '../basicHeader';
import { TransferFormItem } from '../transferFormItem';
import { MainButton } from '../mainButton';
import { DropdownButton } from '../dropdownButton';
import { Modal } from '../modal';
// Styles
import styles from './postBoostStyles';
class BoostPostScreen extends PureComponent {
/* Props
* ------------------------------------------------
* @prop { type } name - Description....
*/
constructor(props) {
super(props);
this.state = {
permlink: '',
selectedUser: '',
balance: '',
factor: 0,
permlinkSuggestions: [],
isValid: false,
};
this.startActionSheet = React.createRef();
}
// Component Life Cycles
// Component Functions
_handleOnPermlinkChange = async text => {
this.setState({ permlink: text, isValid: false });
if (this.timer) {
clearTimeout(this.timer);
}
if (text.trim().length < 3) {
this.setState({ permlinkSuggestions: [] });
return;
}
if (text && text.length > 0) {
this.timer = setTimeout(
() =>
searchPath(text).then(res => {
this.setState({ permlinkSuggestions: res && res.length > 10 ? res.slice(0, 7) : res });
}),
500,
);
} else {
await this.setState({ permlinkSuggestions: [], isValid: false });
}
};
_renderDescription = text => <Text style={styles.description}>{text}</Text>;
_renderDropdown = (accounts, currentAccountName) => (
<DropdownButton
dropdownButtonStyle={styles.dropdownButtonStyle}
rowTextStyle={styles.rowTextStyle}
style={styles.dropdown}
dropdownStyle={styles.dropdownStyle}
textStyle={styles.dropdownText}
options={accounts.map(item => item.username)}
defaultText={currentAccountName}
selectedOptionIndex={accounts.findIndex(item => item.username === currentAccountName)}
onSelect={(index, value) => {
this.setState({ selectedUser: value }, () => {
this._getUserBalance(value);
});
}}
/>
);
_getUserBalance = async username => {
await getUser(username)
.then(userPoints => {
const balance = Math.round(get(userPoints, 'points') * 1000) / 1000;
this.setState({ balance });
})
.catch(err => {
Alert.alert(err.message || err.toString());
});
};
_handleOnSubmit = async () => {
const { handleOnSubmit, redeemType, navigationParams } = this.props;
const { permlink, selectedUser, factor } = this.state;
const fullPermlink = permlink || get(navigationParams, 'permlink');
const amount = 150 + 50 * factor;
handleOnSubmit(redeemType, amount, fullPermlink, selectedUser);
};
render() {
const { intl } = this.props;
const { selectedUser, balance, factor, permlinkSuggestions, permlink, isValid } = this.state;
const {
isLoading,
accounts,
currentAccountName,
balance: _balance,
navigationParams,
SCPath,
isSCModalOpen,
handleOnSCModalClose,
getESTMPrice,
} = this.props;
const calculatedESTM = 150 + 50 * factor;
return (
<Fragment>
<BasicHeader title={intl.formatMessage({ id: 'boostPost.title' })} />
<View style={styles.container}>
<ScrollView>
<View style={styles.middleContent}>
<TransferFormItem
label={intl.formatMessage({ id: 'promote.user' })}
rightComponent={() =>
this._renderDropdown(accounts, selectedUser || currentAccountName)
}
/>
<Text style={styles.balanceText}>{`${balance || _balance} ESTM`}</Text>
<View style={styles.autocompleteLineContainer}>
<View style={styles.autocompleteLabelContainer}>
{
<Text style={styles.autocompleteLabelText}>
{intl.formatMessage({ id: 'promote.permlink' })}
</Text>
}
</View>
<Autocomplete
autoCapitalize="none"
autoCorrect={false}
inputContainerStyle={styles.autocomplete}
data={permlinkSuggestions}
listContainerStyle={styles.autocompleteListContainer}
listStyle={styles.autocompleteList}
onChangeText={this._handleOnPermlinkChange}
renderTextInput={() => (
<TextInput
style={styles.input}
onChangeText={text => this._handleOnPermlinkChange(text)}
value={permlink || get(navigationParams, 'permlink', '')}
placeholder={intl.formatMessage({ id: 'promote.permlinkPlaceholder' })}
placeholderTextColor="#c1c5c7"
autoCapitalize="none"
/>
)}
renderItem={({ item }) => (
<TouchableOpacity
key={item}
onPress={() =>
this.setState({
permlink: item,
isValid: true,
permlinkSuggestions: [],
})
}
>
<Text style={styles.autocompleteItemText}>{item}</Text>
</TouchableOpacity>
)}
/>
</View>
<View style={styles.total}>
<Text style={styles.price}>
{` $${getESTMPrice(calculatedESTM).toFixed(3)} ~ `}
</Text>
<Text style={styles.esteem}>{`${calculatedESTM} ESTM`}</Text>
</View>
<View style={styles.quickButtonsWrapper}>
<MainButton
style={styles.quickButtons}
isDisable={!(calculatedESTM > 150)}
onPress={() =>
this.setState({
factor: calculatedESTM > 150 ? factor - 1 : factor,
})
}
>
<Icon
size={24}
style={{ color: 'white' }}
iconType="MaterialCommunityIcons"
name="minus"
/>
</MainButton>
<MainButton
style={styles.quickButtons}
isDisable={!((balance || _balance) / 50 > factor + 4)}
onPress={() =>
this.setState({
factor: (balance || _balance) / 50 > factor + 4 ? factor + 1 : factor,
})
}
>
<Icon size={24} style={{ color: 'white' }} iconType="MaterialIcons" name="add" />
</MainButton>
</View>
</View>
<View style={styles.bottomContent}>
<MainButton
style={styles.button}
isDisable={
(!permlink ? !get(navigationParams, 'permlink') : permlink) &&
(balance || _balance) > 150 &&
(isLoading || !isValid)
}
onPress={() => this.startActionSheet.current.show()}
isLoading={isLoading}
>
<Text style={styles.buttonText}>{intl.formatMessage({ id: 'transfer.next' })}</Text>
</MainButton>
</View>
</ScrollView>
</View>
<ActionSheet
ref={this.startActionSheet}
options={[
intl.formatMessage({ id: 'alert.confirm' }),
intl.formatMessage({ id: 'alert.cancel' }),
]}
title={intl.formatMessage({ id: 'promote.information' })}
cancelButtonIndex={1}
destructiveButtonIndex={0}
onPress={index => {
if (index === 0) if (index === 0) this._handleOnSubmit();
}}
/>
<Modal
isOpen={isSCModalOpen}
isFullScreen
isCloseButton
handleOnModalClose={handleOnSCModalClose}
title={intl.formatMessage({ id: 'transfer.steemconnect_title' })}
>
<WebView source={{ uri: `${steemConnectOptions.base_url}${SCPath}` }} />
</Modal>
</Fragment>
);
}
}
export default injectIntl(BoostPostScreen);

View File

@ -1,8 +1,9 @@
import React, { PureComponent } from 'react';
import { withNavigation } from 'react-navigation';
import { connect } from 'react-redux';
import get from 'lodash/get';
// Dsteem
// Services
import { getPost } from '../../../providers/steem/dsteem';
import PostCardView from '../view/postCardView';
@ -24,21 +25,21 @@ class PostCardContainer extends PureComponent {
}
componentWillReceiveProps(nextProps) {
if (nextProps.isRefresh) {
if (get(nextProps, 'isRefresh')) {
this._fetchPost();
}
}
_handleOnUserPress = () => {
const { navigation, currentAccount, content } = this.props;
if (content && currentAccount.name !== content.author) {
if (content && get(currentAccount, 'name') !== get(content, 'author')) {
navigation.navigate({
routeName: ROUTES.SCREENS.PROFILE,
params: {
username: content.author,
reputation: content.author_reputation,
username: get(content, 'author'),
reputation: get(content, 'author_reputation'),
},
key: content.author,
key: get(content, 'author'),
});
}
};
@ -52,7 +53,7 @@ class PostCardContainer extends PureComponent {
params: {
content,
},
key: content.permlink,
key: get(content, 'permlink'),
});
}
};
@ -65,14 +66,26 @@ class PostCardContainer extends PureComponent {
params: {
activeVotes,
},
key: content.permlink,
key: get(content, 'permlink'),
});
};
_handleOnReblogsPress = reblogs => {
const { navigation, content } = this.props;
navigation.navigate({
routeName: ROUTES.SCREENS.REBLOGS,
params: {
reblogs,
},
key: get(content, 'permlink', get(content, 'author', '')),
});
};
_fetchPost = async () => {
const { currentAccount, content } = this.props;
await getPost(content.author, content.permlink, currentAccount.username)
await getPost(get(content, 'author'), get(content, 'permlink'), get(currentAccount, 'username'))
.then(result => {
if (result) {
this.setState({ _content: result });
@ -82,7 +95,7 @@ class PostCardContainer extends PureComponent {
};
render() {
const { content, isHideImage, nsfw, isHideReblogOption } = this.props;
const { content, isHideImage, nsfw } = this.props;
const { _content } = this.state;
const isNsfwPost = nsfw === '1';
@ -92,11 +105,11 @@ class PostCardContainer extends PureComponent {
handleOnUserPress={this._handleOnUserPress}
handleOnContentPress={this._handleOnContentPress}
handleOnVotersPress={this._handleOnVotersPress}
handleOnReblogsPress={this._handleOnReblogsPress}
fetchPost={this._fetchPost}
content={_content || content}
isHideImage={isHideImage}
isNsfwPost={isNsfwPost}
isHideReblogOption={isHideReblogOption}
/>
);
}

View File

@ -15,12 +15,7 @@ export default EStyleSheet.create({
padding: 0,
margin: 0,
flexDirection: 'row',
},
comment: {
alignSelf: 'center',
fontSize: 10,
marginLeft: 3,
color: '$iconColor',
alignSelf: 'flex-end',
},
commentIcon: {
alignSelf: 'flex-start',
@ -28,6 +23,7 @@ export default EStyleSheet.create({
color: '$iconColor',
margin: 0,
width: 20,
marginLeft: 25,
},
postBodyWrapper: {
marginHorizontal: 9,
@ -60,7 +56,6 @@ export default EStyleSheet.create({
backgroundColor: '$primaryBackgroundColor',
flexDirection: 'row',
margin: 16,
justifyContent: 'space-between',
},
bodyHeader: {
backgroundColor: '$primaryBackgroundColor',
@ -70,7 +65,14 @@ export default EStyleSheet.create({
marginBottom: 12,
},
leftFooterWrapper: {
flex: 1,
flexDirection: 'row',
justifyContent: 'flex-start',
},
rightFooterWrapper: {
flex: 1,
flexDirection: 'row',
justifyContent: 'flex-end',
},
dropdownWrapper: {
position: 'absolute',

View File

@ -10,7 +10,6 @@ import { getTimeFromNow } from '../../../utils/time';
// Components
import { PostHeaderDescription } from '../../postElements';
import { PostDropdown } from '../../postDropdown';
import { Icon } from '../../icon';
import { TextWithIcon } from '../../basicUIElements';
// STEEM
@ -67,7 +66,15 @@ class PostCardView extends Component {
_handleOnVotersPress = () => {
const { handleOnVotersPress, content } = this.props;
handleOnVotersPress(content.active_votes);
handleOnVotersPress(get(content, 'active_votes'));
};
_handleOnReblogsPress = () => {
const { handleOnReblogsPress, content } = this.props;
if (content.reblogs.length > 0) {
handleOnReblogsPress(get(content, 'reblogs'));
}
};
_getPostImage = (content, isNsfwPost) => {
@ -81,7 +88,7 @@ class PostCardView extends Component {
};
render() {
const { content, isHideImage, fetchPost, isNsfwPost, isHideReblogOption, intl } = this.props;
const { content, isHideImage, fetchPost, isNsfwPost, intl } = this.props;
const { rebloggedBy } = this.state;
const _image = this._getPostImage(content, isNsfwPost);
@ -101,11 +108,7 @@ class PostCardView extends Component {
isPromoted={get(content, 'is_promoted')}
/>
<View style={styles.dropdownWrapper}>
<PostDropdown
isHideReblogOption={isHideReblogOption}
content={content}
fetchPost={fetchPost}
/>
<PostDropdown content={content} fetchPost={fetchPost} />
</View>
</View>
<View style={styles.postBodyWrapper}>
@ -134,17 +137,32 @@ class PostCardView extends Component {
<View style={styles.leftFooterWrapper}>
<Upvote fetchPost={fetchPost} isShowPayoutValue content={content} />
<TouchableOpacity style={styles.commentButton} onPress={this._handleOnVotersPress}>
<Icon
style={[styles.commentIcon, { marginLeft: 25 }]}
<TextWithIcon
iconName="people"
iconStyle={styles.commentIcon}
iconType="MaterialIcons"
name="people"
isClickable
text={get(content, 'vote_count', 0)}
onPress={this._handleOnVotersPress}
/>
<Text style={styles.comment}>{get(content, 'vote_count', 0)}</Text>
</TouchableOpacity>
</View>
<View style={styles.commentButton}>
<Icon style={[styles.commentIcon]} iconType="MaterialIcons" name="comment" />
<Text style={styles.comment}>{get(content, 'children')}</Text>
<View style={styles.rightFooterWrapper}>
<TextWithIcon
iconName="repeat"
iconStyle={styles.commentIcon}
iconType="MaterialIcons"
isClickable
text={get(content, 'reblogCount', 0)}
onPress={this._handleOnReblogsPress}
/>
<TextWithIcon
iconName="comment"
iconStyle={styles.commentIcon}
iconType="MaterialIcons"
isClickable
text={get(content, 'children', 0)}
/>
</View>
</View>
</View>

View File

@ -95,11 +95,11 @@ class PostDropdownContainer extends PureComponent {
break;
case 'PROMOTE':
this._redirectToPromote(ROUTES.SCREENS.PROMOTE, 1);
this._redirectToPromote(ROUTES.SCREENS.REDEEM, 1, 'promote');
break;
case 'BOOST':
this._redirectToPromote(ROUTES.SCREENS.BOOST_POST, 2);
this._redirectToPromote(ROUTES.SCREENS.REDEEM, 2, 'boost');
break;
default:
@ -112,14 +112,14 @@ class PostDropdownContainer extends PureComponent {
const postUrl = getPostUrl(get(content, 'url'));
Share.share({
message: `${content.title} ${postUrl}`,
message: `${get(content, 'title')} ${postUrl}`,
});
};
_addToBookmarks = () => {
const { content, currentAccount, dispatch, intl } = this.props;
addBookmark(currentAccount.name, content.author, content.permlink)
addBookmark(get(currentAccount, 'name'), get(content, 'author'), get(content, 'permlink'))
.then(() => {
dispatch(
toastNotification(
@ -143,7 +143,7 @@ class PostDropdownContainer extends PureComponent {
_reblog = () => {
const { content, currentAccount, dispatch, intl, isLoggedIn, pinCode } = this.props;
if (isLoggedIn) {
reblog(currentAccount, pinCode, content.author, content.permlink)
reblog(currentAccount, pinCode, content.author, get(content, 'permlink', ''))
.then(() => {
dispatch(
toastNotification(
@ -154,7 +154,7 @@ class PostDropdownContainer extends PureComponent {
);
})
.catch(error => {
if (error.jse_shortmsg && String(error.jse_shortmsg).indexOf('has already reblogged')) {
if (String(get(error, 'jse_shortmsg', '')).indexOf('has already reblogged')) {
dispatch(
toastNotification(
intl.formatMessage({
@ -184,11 +184,12 @@ class PostDropdownContainer extends PureComponent {
}
};
_redirectToPromote = (routeName, from) => {
_redirectToPromote = (routeName, from, redeemType) => {
const { content, isLoggedIn, navigation, dispatch, isPinCodeOpen } = this.props;
const params = {
from,
permlink: `${get(content, 'author')}/${get(content, 'permlink')}`,
redeemType,
};
if (isPinCodeOpen) {
@ -207,10 +208,14 @@ class PostDropdownContainer extends PureComponent {
};
render() {
const { intl, currentAccount, content, isHideReblogOption } = this.props;
const {
intl,
currentAccount: { name },
content,
} = this.props;
let _OPTIONS = OPTIONS;
if ((content && content.author === currentAccount.name) || isHideReblogOption) {
if ((content && content.author === name) || get(content, 'reblogged_by[0]', null) === name) {
_OPTIONS = OPTIONS.filter(item => item !== 'reblog');
}

View File

@ -65,14 +65,7 @@ export default EStyleSheet.create({
alignItems: 'center',
justifyContent: 'center',
},
postImage: {
marginTop: 10,
minHeight: 216,
width: '100%',
},
commentImage: {
marginTop: 10,
minHeight: 80,
width: '100%',
img: {
alignSelf: 'center',
},
});

View File

@ -2,8 +2,6 @@ import React, { PureComponent, Fragment } from 'react';
import { Dimensions, Linking, Alert, TouchableOpacity, Text } from 'react-native';
import { withNavigation } from 'react-navigation';
import { injectIntl } from 'react-intl';
import FastImage from 'react-native-fast-image';
import { proxifyImageSrc } from '@esteemapp/esteem-render-helpers';
import HTML from 'react-native-render-html';
import { getParentsTagsRecursively } from 'react-native-render-html/src/HTMLUtils';
@ -13,7 +11,6 @@ import styles from './postBodyStyles';
// Constants
import { default as ROUTES } from '../../../../constants/routeNames';
import DEFAULT_IMAGE from '../../../../assets/no_image.png';
// Components
const WIDTH = Dimensions.get('window').width;
@ -153,18 +150,6 @@ class PostBody extends PureComponent {
: { width: WIDTH, height: 216 };
const _customRenderer = {
img: (htmlAttribs, children, convertedCSSStyles, passProps) => (
<FastImage
key={passProps.key}
defaultSource={DEFAULT_IMAGE}
source={{
uri: proxifyImageSrc(htmlAttribs.src, _initialDimensions.width, 0),
priority: FastImage.priority.normal,
}}
style={isComment ? styles.commentImage : styles.postImage}
resizeMode={FastImage.resizeMode.contain}
/>
),
a: (htmlAttribs, children, convertedCSSStyles, passProps) => {
if (passProps.parentWrapper === 'Text') {
return (
@ -187,6 +172,9 @@ class PostBody extends PureComponent {
</TouchableOpacity>
);
},
br: (htmlAttribs, children, passProps) => {
return <Text {...passProps}>{'\n'}</Text>;
},
};
return (

View File

@ -74,7 +74,7 @@ class PostHeaderDescription extends PureComponent {
</TouchableOpacity>
{!!tag && (
<TouchableOpacity onPress={() => tagOnPress && tagOnPress()}>
<Tag isPostCardTag isPin value={tag} />
<Tag isPostCardTag={!isPromoted} isPin value={tag} />
</TouchableOpacity>
)}
<Text style={styles.date}>

View File

@ -3,5 +3,11 @@ import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
container: {
flexDirection: 'row',
marginLeft: -20,
marginRight: -20,
height: 30,
},
firstTag: {
marginLeft: 10,
},
});

View File

@ -27,7 +27,9 @@ class TagsView extends PureComponent {
data={tags}
horizontal
renderItem={({ item, index }) => (
<Tag key={index} value={item} isPin={index === 0} onPress={handleOnTagPress} />
<View style={index === 0 && styles.firstTag}>
<Tag key={index} value={item} isPin={index === 0} onPress={handleOnTagPress} />
</View>
)}
keyExtractor={item => item}
/>

View File

@ -47,10 +47,24 @@ class PostDisplayContainer extends Component {
activeVotes,
},
// TODO: make unic
key: post.permlink + Math.random(),
key: post.permlink + activeVotes.length,
});
};
_handleOnReblogsPress = reblogs => {
const { navigation, post } = this.props;
if (reblogs.length > 0) {
navigation.navigate({
routeName: ROUTES.SCREENS.REBLOGS,
params: {
reblogs,
},
key: post.permlink + reblogs.length,
});
}
};
_handleOnReplyPress = () => {
const { post, navigation } = this.props;
@ -123,6 +137,7 @@ class PostDisplayContainer extends Component {
handleOnRemovePress={this._handleDeleteComment}
handleOnReplyPress={this._handleOnReplyPress}
handleOnVotersPress={this._handleOnVotersPress}
handleOnReblogsPress={this._handleOnReblogsPress}
isLoggedIn={isLoggedIn}
isNewPost={isNewPost}
isPostUnavailable={isPostUnavailable}

View File

@ -62,7 +62,7 @@ export default EStyleSheet.create({
opacity: 0.7,
},
barIconButton: {
marginLeft: 16,
marginLeft: 0,
},
stickyRightWrapper: {
flexGrow: 1,

View File

@ -1,5 +1,5 @@
import React, { PureComponent, Fragment } from 'react';
import { View, Text, ScrollView, Dimensions } from 'react-native';
import { View, Text, ScrollView, Dimensions, SafeAreaView } from 'react-native';
import { injectIntl } from 'react-intl';
import get from 'lodash/get';
import ActionSheet from 'react-native-actionsheet';
@ -71,64 +71,76 @@ class PostDisplayView extends PureComponent {
handleOnEditPress,
handleOnReplyPress,
handleOnVotersPress,
handleOnReblogsPress,
isLoggedIn,
post,
} = this.props;
return (
<StickyBar isFixedFooter={isFixedFooter}>
<View style={styles.stickyWrapper}>
<Upvote fetchPost={fetchPost} isShowPayoutValue content={post} />
<TextWithIcon
iconName="people"
iconStyle={styles.barIcons}
iconType="MaterialIcons"
isClickable
onPress={() => handleOnVotersPress && handleOnVotersPress(get(post, 'active_votes'))}
text={get(post, 'vote_count')}
textMarginLeft={20}
/>
<TextWithIcon
iconName="comment"
iconStyle={styles.barIcons}
iconType="MaterialIcons"
isClickable
text={get(post, 'children')}
textMarginLeft={20}
/>
<View style={styles.stickyRightWrapper}>
{get(currentAccount, 'name') === get(post, 'author') && (
<Fragment>
{!get(post, 'children') && !get(post, 'vote_count') && (
<SafeAreaView>
<StickyBar isFixedFooter={isFixedFooter}>
<View style={styles.stickyWrapper}>
<Upvote fetchPost={fetchPost} isShowPayoutValue content={post} />
<TextWithIcon
iconName="people"
iconStyle={styles.barIcons}
iconType="MaterialIcons"
isClickable
onPress={() => handleOnVotersPress && handleOnVotersPress(get(post, 'active_votes'))}
text={get(post, 'vote_count', 0)}
textMarginLeft={20}
/>
<TextWithIcon
iconName="comment"
iconStyle={styles.barIcons}
iconType="MaterialIcons"
isClickable
text={get(post, 'children', 0)}
textMarginLeft={20}
/>
<TextWithIcon
iconName="repeat"
iconStyle={styles.barIcons}
iconType="MaterialIcons"
isClickable
onPress={() => handleOnReblogsPress && handleOnReblogsPress(get(post, 'reblogs'))}
text={get(post, 'reblogCount', 0)}
textMarginLeft={20}
/>
<View style={styles.stickyRightWrapper}>
{get(currentAccount, 'name') === get(post, 'author') && (
<Fragment>
{!get(post, 'children') && !get(post, 'vote_count') && (
<IconButton
iconStyle={styles.barIconRight}
iconType="MaterialIcons"
name="delete-forever"
onPress={() => this.ActionSheet.show()}
style={styles.barIconButton}
/>
)}
<IconButton
iconStyle={styles.barIconRight}
iconType="MaterialIcons"
name="delete-forever"
onPress={() => this.ActionSheet.show()}
name="create"
onPress={() => handleOnEditPress && handleOnEditPress()}
style={styles.barIconButton}
/>
)}
</Fragment>
)}
{isLoggedIn && (
<IconButton
iconStyle={styles.barIconRight}
iconType="MaterialIcons"
name="create"
onPress={() => handleOnEditPress && handleOnEditPress()}
name="reply"
onPress={() => handleOnReplyPress && handleOnReplyPress()}
style={styles.barIconButton}
/>
</Fragment>
)}
{isLoggedIn && (
<IconButton
iconStyle={styles.barIconRight}
iconType="MaterialIcons"
name="reply"
onPress={() => handleOnReplyPress && handleOnReplyPress()}
style={styles.barIconButton}
/>
)}
)}
</View>
</View>
</View>
</StickyBar>
</StickyBar>
</SafeAreaView>
);
};
@ -142,6 +154,7 @@ class PostDisplayView extends PureComponent {
author,
intl,
handleOnRemovePress,
handleOnVotersPress,
} = this.props;
const { postHeight, scrollHeight, isLoadedComments } = this.state;
@ -201,6 +214,7 @@ class PostDisplayView extends PureComponent {
permlink={post.permlink}
commentCount={post.children}
fetchPost={fetchPost}
handleOnVotersPress={handleOnVotersPress}
/>
)}
</ScrollView>

View File

@ -1,13 +1,14 @@
import React, { PureComponent, Fragment } from 'react';
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import get from 'lodash/get';
// Component
import PostsView from '../view/postsView';
import { PostCardPlaceHolder } from '../../basicUIElements';
// Actions
import { setFeedPosts } from '../../../redux/actions/postsAction';
import { hidePostsThumbnails } from '../../../redux/actions/uiAction';
/*
* Props Name Description Value
*@props --> props name here description here Value Type Here
@ -32,30 +33,55 @@ class PostsContainer extends PureComponent {
dispatch(setFeedPosts(posts));
};
render() {
const { currentAccount, isLoginDone, tag, feedPosts, isConnected } = this.props;
const { promotedPosts } = this.state;
_handleImagesHide = () => {
const { dispatch, isHideImages } = this.props;
if (!isLoginDone && !tag) {
return (
<Fragment>
<PostCardPlaceHolder />
<PostCardPlaceHolder />
</Fragment>
);
}
dispatch(hidePostsThumbnails(!isHideImages));
};
render() {
const {
changeForceLoadPostState,
currentAccount,
feedPosts,
filterOptions,
forceLoadPost,
getFor,
handleOnScroll,
isConnected,
isHideImages,
pageType,
selectedOptionIndex,
tag,
isLoginDone,
isLoggedIn,
isDarkTheme,
nsfw,
} = this.props;
const { promotedPosts } = this.state;
return (
<PostsView
promotedPosts={promotedPosts}
handleOnScrollStart={this._handleOnScrollStart}
currentAccountUsername={
currentAccount && (get(currentAccount, 'username') || get(currentAccount, 'name'))
}
setFeedPosts={this._setFeedPosts}
changeForceLoadPostState={changeForceLoadPostState}
currentAccountUsername={get(currentAccount, 'name', '')}
feedPosts={feedPosts}
filterOptions={filterOptions}
forceLoadPost={forceLoadPost}
getFor={getFor}
handleOnScroll={handleOnScroll}
handleImagesHide={this._handleImagesHide}
hidePostsThumbnails={hidePostsThumbnails}
isConnected={isConnected}
{...this.props}
isHideImage={isHideImages}
pageType={pageType}
promotedPosts={promotedPosts}
selectedOptionIndex={selectedOptionIndex}
setFeedPosts={this._setFeedPosts}
tag={tag}
isLoginDone={isLoginDone}
isLoggedIn={isLoggedIn}
isDarkTheme={isDarkTheme}
nsfw={nsfw}
/>
);
}
@ -69,6 +95,7 @@ const mapStateToProps = state => ({
nsfw: state.application.nsfw,
feedPosts: state.posts.feedPosts,
isConnected: state.application.isConnected,
isHideImages: state.ui.hidePostsThumbnails,
});
export default connect(mapStateToProps)(PostsContainer);

View File

@ -1,8 +1,9 @@
/* eslint-disable react/jsx-wrap-multilines */
import React, { Component, Fragment } from 'react';
import { FlatList, View, ActivityIndicator, RefreshControl } from 'react-native';
import { injectIntl } from 'react-intl';
import { withNavigation } from 'react-navigation';
import get from 'lodash/get';
import { get, isEqual, unionWith } from 'lodash';
// STEEM
import { getPostsSummary, getPost } from '../../../providers/steem/dsteem';
@ -28,11 +29,12 @@ class PostsView extends Component {
startPermlink: '',
refreshing: false,
isLoading: false,
isPostsLoading: true,
isHideImage: false,
isShowFilterBar: true,
selectedFilterIndex: get(props, 'selectedOptionIndex', 0),
isNoPost: false,
promotedPosts: [],
scrollOffsetY: 0,
lockFilterBar: false,
};
}
@ -46,15 +48,14 @@ class PostsView extends Component {
}
async componentDidMount() {
const { isConnected } = this.props;
const { isConnected, pageType } = this.props;
if (isConnected) {
await this._getPromotePosts();
if (pageType !== 'profiles') await this._getPromotePosts();
this._loadPosts();
} else {
this.setState({
refreshing: false,
isPostsLoading: false,
isLoading: false,
});
}
@ -77,8 +78,6 @@ class PostsView extends Component {
startPermlink: '',
refreshing: false,
isLoading: false,
isPostsLoading: false,
isHideImage: false,
selectedFilterIndex: get(nextProps, 'selectedOptionIndex', 0),
isNoPost: false,
},
@ -94,16 +93,19 @@ class PostsView extends Component {
_getPromotePosts = async () => {
const { currentAccountUsername } = this.props;
await getPromotePosts().then(async res => {
const promotedPosts = [];
res &&
res.length > 0 &&
res.map(async item => {
const post = await getPost(item.author, item.permlink, currentAccountUsername, true);
promotedPosts.push(post);
});
await this.setState({ promotedPosts });
await getPromotePosts().then(async res => {
if (res && res.length) {
const promotedPosts = await Promise.all(
res.map(item =>
getPost(get(item, 'author'), get(item, 'permlink'), currentAccountUsername, true).then(
post => post,
),
),
);
this.setState({ promotedPosts });
}
});
};
@ -135,13 +137,12 @@ class PostsView extends Component {
? POPULAR_FILTERS[selectedFilterIndex].toLowerCase()
: PROFILE_FILTERS[selectedFilterIndex].toLowerCase();
let options;
let newPosts = [];
const limit = promotedPosts ? (promotedPosts.length >= 3 ? 9 : 6) : 3;
const newPosts = [];
const limit = 3;
if (!isConnected) {
this.setState({
refreshing: false,
isPostsLoading: false,
isLoading: false,
});
return null;
@ -188,10 +189,7 @@ class PostsView extends Component {
if (_posts.length > 0) {
if (posts.length > 0) {
if (refreshing) {
// TODO: make sure post is not duplicated, because checking with `includes` might re-add post
// if there was change in post object from blockchain
newPosts = _posts.filter(post => posts.includes(post));
_posts = [...newPosts, ...posts];
_posts = unionWith(_posts, posts, isEqual);
} else {
_posts.shift();
_posts = [...posts, ..._posts];
@ -202,17 +200,38 @@ class PostsView extends Component {
setFeedPosts(_posts);
}
if (refreshing && newPosts.length > 0) {
// Promoted post start
if (promotedPosts && promotedPosts.length > 0) {
const insert = (arr, index, newItem) => [
...arr.slice(0, index),
newItem,
...arr.slice(index),
];
if (refreshing) {
_posts = _posts.filter(item => !item.is_promoted);
}
_posts.map((d, i) => {
if ([3, 6, 9].includes(i)) {
const ix = i / 3 - 1;
if (promotedPosts[ix] !== undefined) {
if (get(_posts, [i], {}).permlink !== promotedPosts[ix].permlink) {
_posts = insert(_posts, i, promotedPosts[ix]);
}
}
}
});
}
// Promoted post end
if (refreshing) {
this.setState({
posts: _posts,
});
} else if (!refreshing) {
if (!startAuthor) {
promotedPosts.map((promotedItem, i) => {
_posts.splice((i + 1) * 3, i * 3, promotedItem);
});
}
this.setState({
posts: _posts,
startAuthor: result[result.length - 1] && result[result.length - 1].author,
@ -222,7 +241,6 @@ class PostsView extends Component {
this.setState({
refreshing: false,
isPostsLoading: false,
isLoading: false,
});
}
@ -233,17 +251,20 @@ class PostsView extends Component {
.catch(() => {
this.setState({
refreshing: false,
isPostsLoading: false,
});
});
};
_handleOnRefreshPosts = () => {
const { pageType } = this.props;
this.setState(
{
refreshing: true,
},
() => {
async () => {
if (pageType !== 'profiles') await this._getPromotePosts();
this._loadPosts();
},
);
@ -264,7 +285,6 @@ class PostsView extends Component {
_handleOnDropdownSelect = async index => {
await this.setState({
isPostsLoading: true,
selectedFilterIndex: index,
posts: [],
startAuthor: '',
@ -274,34 +294,74 @@ class PostsView extends Component {
this._loadPosts();
};
_onRightIconPress = () => {
const { isHideImage } = this.state;
this.setState({ isHideImage: !isHideImage });
};
_handleOnPressLogin = () => {
const { navigation } = this.props;
navigation.navigate(ROUTES.SCREENS.LOGIN);
};
_renderEmptyContent = () => {
const { intl, getFor, isLoginDone, isLoggedIn, tag } = this.props;
const { isNoPost } = this.state;
if (getFor === 'feed' && isLoginDone && !isLoggedIn) {
return (
<NoPost
imageStyle={styles.noImage}
isButtonText
defaultText={intl.formatMessage({
id: 'profile.login_to_see',
})}
handleOnButtonPress={this._handleOnPressLogin}
/>
);
}
if (isNoPost) {
return (
<NoPost
imageStyle={styles.noImage}
name={tag}
text={intl.formatMessage({
id: 'profile.havent_posted',
})}
defaultText={intl.formatMessage({
id: 'profile.login_to_see',
})}
/>
);
}
return (
<Fragment>
<PostCardPlaceHolder />
<PostCardPlaceHolder />
</Fragment>
);
};
_handleOnScroll = event => {
const { scrollOffsetY } = this.state;
const { handleOnScroll } = this.props;
const currentOffset = event.nativeEvent.contentOffset.y;
if (handleOnScroll) handleOnScroll();
this.setState({ scrollOffsetY: currentOffset });
this.setState({ isShowFilterBar: scrollOffsetY > currentOffset || scrollOffsetY <= 0 });
};
render() {
const { refreshing, posts, isPostsLoading, isHideImage, isNoPost } = this.state;
const { refreshing, posts, isShowFilterBar } = this.state;
const {
filterOptions,
selectedOptionIndex,
intl,
isLoggedIn,
getFor,
isLoginDone,
tag,
isDarkTheme,
isHideReblogOption,
isHideImage,
handleImagesHide,
} = this.props;
return (
<View style={styles.container}>
{filterOptions && (
{filterOptions && isShowFilterBar && (
<FilterBar
dropdownIconName="arrow-drop-down"
options={filterOptions}
@ -310,75 +370,42 @@ class PostsView extends Component {
rightIconName="view-module"
rightIconType="MaterialIcons"
onDropdownSelect={this._handleOnDropdownSelect}
onRightIconPress={this._onRightIconPress}
onRightIconPress={handleImagesHide}
/>
)}
<Fragment>
{getFor === 'feed' && isLoginDone && !isLoggedIn && (
<NoPost
imageStyle={styles.noImage}
isButtonText
defaultText={intl.formatMessage({
id: 'profile.login_to_see',
})}
handleOnButtonPress={this._handleOnPressLogin}
<FlatList
data={posts}
showsVerticalScrollIndicator={false}
renderItem={({ item }) =>
get(item, 'author', null) && (
<PostCard isRefresh={refreshing} content={item} isHideImage={isHideImage} />
)
}
keyExtractor={(content, i) => `${get(content, 'permlink', '')}${i.toString()}`}
onEndReached={() => this._loadPosts()}
removeClippedSubviews
refreshing={refreshing}
onRefresh={() => this._handleOnRefreshPosts()}
onEndThreshold={0}
initialNumToRender={10}
ListFooterComponent={this._renderFooter}
onScrollEndDrag={this._handleOnScroll}
ListEmptyComponent={this._renderEmptyContent}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={this._handleOnRefreshPosts}
progressBackgroundColor="#357CE6"
tintColor={!isDarkTheme ? '#357ce6' : '#96c0ff'}
titleColor="#fff"
colors={['#fff']}
/>
)}
</Fragment>
{posts && posts.length > 0 && !isPostsLoading && (
<FlatList
data={posts}
showsVerticalScrollIndicator={false}
renderItem={({ item }) => (
<PostCard
isHideReblogOption={isHideReblogOption}
isRefresh={refreshing}
content={item}
isHideImage={isHideImage}
/>
)}
keyExtractor={(post, index) => index.toString()}
onEndReached={() => this._loadPosts()}
removeClippedSubviews
refreshing={refreshing}
onRefresh={() => this._handleOnRefreshPosts()}
onEndThreshold={0}
initialNumToRender={10}
ListFooterComponent={this._renderFooter}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={this._handleOnRefreshPosts}
progressBackgroundColor="#357CE6"
tintColor={!isDarkTheme ? '#357ce6' : '#96c0ff'}
titleColor="#fff"
colors={['#fff']}
/>
}
ref={ref => {
this.flatList = ref;
}}
/>
)}
{isNoPost ? (
<NoPost
imageStyle={styles.noImage}
name={tag}
text={intl.formatMessage({
id: 'profile.havent_posted',
})}
defaultText={intl.formatMessage({
id: 'profile.login_to_see',
})}
/>
) : (
<Fragment>
<PostCardPlaceHolder />
<PostCardPlaceHolder />
</Fragment>
)}
}
ref={ref => {
this.flatList = ref;
}}
/>
</View>
);
}

View File

@ -0,0 +1,238 @@
import React, { PureComponent, Fragment } from 'react';
import { View, ScrollView } from 'react-native';
import { injectIntl } from 'react-intl';
import get from 'lodash/get';
import ScrollableTabView from 'react-native-scrollable-tab-view';
// Components
import { CollapsibleCard } from '../collapsibleCard';
import { Comments } from '../comments';
import { Header } from '../header';
import { NoPost, ProfileSummaryPlaceHolder, WalletDetailsPlaceHolder } from '../basicUIElements';
import { Posts } from '../posts';
import { ProfileSummary } from '../profileSummary';
import { TabBar } from '../tabBar';
import { Wallet } from '../wallet';
// Constants
import { PROFILE_FILTERS } from '../../constants/options/filters';
// Utils
import { getFormatedCreatedDate } from '../../utils/time';
// Styles
import styles from './profileStyles';
import globalStyles from '../../globalStyles';
class ProfileView extends PureComponent {
constructor(props) {
super(props);
this.state = {
isSummaryOpen: true,
collapsibleMoreHeight: 0,
estimatedWalletValue: 0,
oldEstimatedWalletValue: 0,
};
}
_handleOnScroll = () => {
const { isSummaryOpen } = this.state;
if (isSummaryOpen) this.setState({ isSummaryOpen: false });
};
_handleOnSummaryExpanded = () => {
const { isSummaryOpen } = this.state;
if (!isSummaryOpen) this.setState({ isSummaryOpen: true });
};
_handleUIChange = height => {
this.setState({ collapsibleMoreHeight: height });
};
render() {
const {
about,
activePage,
changeForceLoadPostState,
comments,
currencyRate,
currencySymbol,
follows,
forceLoadPost,
getReplies,
handleFollowUnfollowUser,
handleMuteUnmuteUser,
handleOnBackPress,
handleOnFavoritePress,
handleOnFollowsPress,
handleOnPressProfileEdit,
intl,
isDarkTheme,
isFavorite,
isFollowing,
isLoggedIn,
isMuted,
isOwnProfile,
isProfileLoading,
isReady,
quickProfile,
resourceCredits,
selectedUser,
username,
votingPower,
} = this.props;
const {
isSummaryOpen,
collapsibleMoreHeight,
estimatedWalletValue,
oldEstimatedWalletValue,
} = this.state;
return (
<Fragment>
<Header
key={quickProfile && quickProfile.name}
selectedUser={quickProfile}
isReverse={!isOwnProfile}
handleOnBackPress={handleOnBackPress}
/>
<View style={styles.container}>
{!isReady ? (
<ProfileSummaryPlaceHolder />
) : (
<CollapsibleCard
title={get(about, 'about')}
isTitleCenter
defaultTitle={intl.formatMessage({
id: 'profile.details',
})}
expanded
isExpanded={isSummaryOpen}
handleOnExpanded={this._handleOnSummaryExpanded}
moreHeight={collapsibleMoreHeight}
// expanded={isLoggedIn}
// locked={!isLoggedIn}
>
<ProfileSummary
date={getFormatedCreatedDate(get(selectedUser, 'created'))}
about={about}
followerCount={follows.follower_count}
followingCount={follows.following_count}
handleFollowUnfollowUser={handleFollowUnfollowUser}
handleMuteUnmuteUser={handleMuteUnmuteUser}
handleOnFavoritePress={handleOnFavoritePress}
handleOnFollowsPress={handleOnFollowsPress}
handleUIChange={this._handleUIChange}
hoursRC={Math.ceil((100 - resourceCredits) * 0.833333) || null}
hoursVP={Math.ceil((100 - votingPower) * 0.833333) || null}
intl={intl}
isDarkTheme={isDarkTheme}
isFavorite={isFavorite}
isFollowing={isFollowing}
isLoggedIn={isLoggedIn}
isMuted={isMuted}
isOwnProfile={isOwnProfile}
isProfileLoading={isProfileLoading}
percentRC={resourceCredits}
percentVP={votingPower}
handleOnPressProfileEdit={handleOnPressProfileEdit}
/>
</CollapsibleCard>
)}
<ScrollableTabView
style={[globalStyles.tabView, styles.tabView]}
initialPage={activePage}
renderTabBar={() => (
<TabBar style={styles.tabbar} tabUnderlineDefaultWidth={80} tabUnderlineScaleX={2} />
)}
onChangeTab={({ i }) => {
if (i !== 2) {
this.setState({
estimatedWalletValue: 0,
oldEstimatedWalletValue: estimatedWalletValue,
});
} else this.setState({ estimatedWalletValue: oldEstimatedWalletValue });
}}
>
<View
tabLabel={intl.formatMessage({
id: 'profile.post',
})}
style={styles.postTabBar}
>
<Posts
filterOptions={PROFILE_FILTERS}
selectedOptionIndex={0}
pageType="profiles"
getFor="blog"
tag={username}
key={username}
handleOnScroll={isSummaryOpen ? this._handleOnScroll : null}
forceLoadPost={forceLoadPost}
changeForceLoadPostState={changeForceLoadPostState}
/>
</View>
<View
tabLabel={
!isOwnProfile
? intl.formatMessage({
id: 'profile.comments',
})
: intl.formatMessage({
id: 'profile.replies',
})
}
style={styles.commentsTabBar}
>
{comments && comments.length > 0 ? (
<ScrollView onScroll={this._handleOnScroll}>
<Comments
isProfilePreview
comments={comments}
fetchPost={getReplies}
isOwnProfile={isOwnProfile}
/>
</ScrollView>
) : (
<NoPost
name={username}
text={intl.formatMessage({
id: 'profile.havent_commented',
})}
defaultText={intl.formatMessage({
id: 'profile.login_to_see',
})}
/>
)}
</View>
<View
tabLabel={
estimatedWalletValue
? `${currencySymbol} ${(estimatedWalletValue * currencyRate).toFixed()}`
: intl.formatMessage({
id: 'profile.wallet',
})
}
>
{selectedUser ? (
<Wallet
setEstimatedWalletValue={value => this.setState({ estimatedWalletValue: value })}
selectedUser={selectedUser}
handleOnScroll={isSummaryOpen ? this._handleOnScroll : null}
/>
) : (
<WalletDetailsPlaceHolder />
)}
</View>
</ScrollableTabView>
</View>
</Fragment>
);
}
}
export default injectIntl(ProfileView);

View File

@ -0,0 +1,71 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
container: {
paddingHorizontal: 32,
paddingVertical: 16,
backgroundColor: '$primaryBackgroundColor',
flex: 1,
},
formStyle: {
backgroundColor: '$white',
height: 30,
marginTop: 8,
},
label: {
fontSize: 14,
color: '$primaryDarkText',
fontWeight: '500',
},
formItem: {
marginBottom: 24,
},
coverImg: {
borderRadius: 5,
height: 60,
marginBottom: 12,
alignSelf: 'stretch',
backgroundColor: '#296CC0',
},
coverImageWrapper: {},
addIcon: {
color: '$white',
textAlign: 'center',
},
addButton: {
backgroundColor: '$iconColor',
width: 20,
height: 20,
borderRadius: 20 / 2,
borderColor: '$white',
borderWidth: 1,
position: 'absolute',
bottom: 0,
right: 10,
},
saveButton: {
backgroundColor: '$primaryBlue',
width: 55,
height: 55,
borderRadius: 55 / 2,
position: 'absolute',
top: -25,
right: 10,
zIndex: 999,
borderWidth: 2,
borderColor: '$white',
},
saveIcon: {
color: '$white',
textAlign: 'center',
},
input: {
fontSize: 14,
color: '$primaryDarkText',
alignSelf: 'flex-start',
width: '100%',
height: 30,
},
});

Some files were not shown because too many files have changed in this diff Show More