mirror of
https://github.com/ecency/ecency-mobile.git
synced 2024-12-22 21:01:31 +03:00
Merge remote-tracking branch 'upstream/development' into nt/navigation
# Conflicts: # src/screens/register/registerScreen.js
This commit is contained in:
commit
6580f79153
@ -144,7 +144,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode versionMajor * 10000 + versionMinor * 100 + versionPatch
|
||||
versionName "3.0.33"
|
||||
versionName "3.0.34"
|
||||
resValue "string", "build_config_package", "app.esteem.mobile.android"
|
||||
multiDexEnabled true
|
||||
// react-native-image-crop-picker
|
||||
|
@ -10,6 +10,16 @@ public class SplashActivity extends AppCompatActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
|
||||
//workaround for getInitialNotification and onNotificationOpenedApp returning null always
|
||||
//TOOD: use react-native-bootsplash instead of react-native-splash-screen as recommended by firebase
|
||||
//firebase issue ref: https://github.com/invertase/react-native-firebase/issues/3469
|
||||
//ecency project card ref: https://github.com/orgs/ecency/projects/2#card-85455956
|
||||
Bundle extras = getIntent().getExtras();
|
||||
if (extras != null) {
|
||||
intent.putExtras(extras);
|
||||
}
|
||||
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
@ -15,11 +15,11 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.0.33</string>
|
||||
<string>3.0.34</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2812</string>
|
||||
<string>2813</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true />
|
||||
<key>NSAppTransportSecurity</key>
|
||||
|
@ -15,10 +15,10 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.0.33</string>
|
||||
<string>3.0.34</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2812</string>
|
||||
<string>2813</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -1132,7 +1132,7 @@
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 2812;
|
||||
CURRENT_PROJECT_VERSION = 2813;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = 75B6RXTKGT;
|
||||
EXCLUDED_ARCHS = "";
|
||||
@ -1211,7 +1211,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Ecency/Ecency.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 2812;
|
||||
CURRENT_PROJECT_VERSION = 2813;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = 75B6RXTKGT;
|
||||
EXCLUDED_ARCHS = "";
|
||||
|
@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.0.33</string>
|
||||
<string>3.0.34</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
@ -62,6 +62,8 @@
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>To access your photos, Ecency needs your permission to help you share your photos.</string>
|
||||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>Ecency requires FaceID access to allow you quick and secure access.</string>
|
||||
<key>NSLocationAlwaysUsageDescription</key>
|
||||
<string>To get accurate location for sharing</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
@ -74,8 +76,6 @@
|
||||
<string>Photo Library Access for allowing user to download and upload photos</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Photo Usage Access for allowing user to upload photos</string>
|
||||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>Ecency requires FaceID access to allow you quick and secure access.</string>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>Entypo.ttf</string>
|
||||
|
@ -15,10 +15,10 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.0.33</string>
|
||||
<string>3.0.34</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2812</string>
|
||||
<string>2813</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -17,9 +17,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.0.33</string>
|
||||
<string>3.0.34</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2812</string>
|
||||
<string>2813</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionAttributes</key>
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ecency",
|
||||
"version": "3.0.33",
|
||||
"version": "3.0.34",
|
||||
"displayName": "Ecency",
|
||||
"private": true,
|
||||
"rnpm": {
|
||||
|
@ -45,14 +45,15 @@ const CommentView = ({
|
||||
incrementRepliesCount
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const actionSheet = useRef(null);
|
||||
const dispatch = useDispatch();
|
||||
const actionSheet = useRef(null);
|
||||
const repliesContainerRef = useRef<AnimatedView>(null);
|
||||
|
||||
const isMuted = useAppSelector(state => state.account.currentAccount.mutes?.indexOf(comment.author) > -1);
|
||||
const lastCacheUpdate = useAppSelector((state) => state.cache.lastUpdate);
|
||||
const cachedComments = useAppSelector((state) => state.cache.comments);
|
||||
|
||||
const [_isShowSubComments, setIsShowSubComments] = useState(isShowSubComments || false);
|
||||
const [_isShowSubComments, setIsShowSubComments] = useState(false);
|
||||
const [isPressedShowButton, setIsPressedShowButton] = useState(false);
|
||||
const [activeVotes, setActiveVotes] = useState([]);
|
||||
const [cacheVoteIcrement, setCacheVoteIcrement] = useState(0);
|
||||
@ -61,6 +62,16 @@ const CommentView = ({
|
||||
const [replies, setReplies] = useState(comment.replies);
|
||||
|
||||
|
||||
useEffect(()=>{
|
||||
if(isShowSubComments){
|
||||
setTimeout(()=>{
|
||||
if(repliesContainerRef.current){
|
||||
setIsShowSubComments(true);
|
||||
repliesContainerRef.current.slideInRight(300);
|
||||
}
|
||||
},150)
|
||||
}
|
||||
},[])
|
||||
|
||||
useEffect(() => {
|
||||
if (comment) {
|
||||
@ -81,51 +92,63 @@ const CommentView = ({
|
||||
) {
|
||||
//TODO: update comment count and show sub comment if required;
|
||||
const cachedComment = cachedComments.get(postPath);
|
||||
if(cachedComment.updated === cachedComment.created){
|
||||
if(commentNumber > 1 && incrementRepliesCount){
|
||||
if (cachedComment.updated === cachedComment.created) {
|
||||
if (commentNumber > 1 && incrementRepliesCount) {
|
||||
incrementRepliesCount();
|
||||
}
|
||||
setChildCount(childCount + 1);
|
||||
setReplies(replies ? [...replies, cachedComment] : [cachedComment]);
|
||||
}
|
||||
|
||||
if(!_isShowSubComments){
|
||||
if (!_isShowSubComments) {
|
||||
_showSubCommentsToggle(true);
|
||||
}
|
||||
}
|
||||
}, [lastCacheUpdate]);
|
||||
|
||||
const _showSubCommentsToggle = (force) => {
|
||||
if(((replies && replies.length > 0) || force)){
|
||||
setIsShowSubComments(!_isShowSubComments);
|
||||
if (((replies && replies.length > 0) || force)) {
|
||||
|
||||
if (repliesContainerRef.current) {
|
||||
if (_isShowSubComments) {
|
||||
repliesContainerRef.current.slideOutRight(300).then(()=>{
|
||||
setIsShowSubComments(false);
|
||||
});
|
||||
} else {
|
||||
setIsShowSubComments(true);
|
||||
repliesContainerRef.current.slideInRight(300);
|
||||
}
|
||||
}
|
||||
|
||||
setIsPressedShowButton(true);
|
||||
} else if(openReplyThread) {
|
||||
|
||||
} else if (openReplyThread) {
|
||||
openReplyThread();
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
const _handleCacheVoteIncrement = () => {
|
||||
const _handleCacheVoteIncrement = () => {
|
||||
//fake increment vote using based on local change
|
||||
setCacheVoteIcrement(1);
|
||||
};
|
||||
};
|
||||
|
||||
const _incrementRepliesCount = () => {
|
||||
if(commentNumber > 1 && incrementRepliesCount){
|
||||
const _incrementRepliesCount = () => {
|
||||
if (commentNumber > 1 && incrementRepliesCount) {
|
||||
incrementRepliesCount();
|
||||
}
|
||||
setChildCount(childCount + 1);
|
||||
}
|
||||
}
|
||||
|
||||
const _handleOnReplyPress = () => {
|
||||
const _handleOnReplyPress = () => {
|
||||
if (isLoggedIn) {
|
||||
dispatch(showReplyModal(comment));
|
||||
} else {
|
||||
console.log('Not LoggedIn');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const _renderReadMoreButton = () => (
|
||||
const _renderReadMoreButton = () => (
|
||||
<TextWithIcon
|
||||
wrapperStyle={styles.rightButton}
|
||||
textStyle={!isPressedShowButton && styles.moreText}
|
||||
@ -141,11 +164,14 @@ const CommentView = ({
|
||||
}
|
||||
/>
|
||||
|
||||
)
|
||||
)
|
||||
|
||||
const _renderReplies = () => {
|
||||
|
||||
|
||||
const _renderReplies = () => {
|
||||
return (
|
||||
<AnimatedView animation="zoomIn" duration={300}>
|
||||
<AnimatedView ref={repliesContainerRef}>
|
||||
{_isShowSubComments &&
|
||||
<Comments
|
||||
isShowComments={isShowComments}
|
||||
commentNumber={commentNumber + 1}
|
||||
@ -163,14 +189,14 @@ const CommentView = ({
|
||||
fetchedAt={fetchedAt}
|
||||
incrementRepliesCount={_incrementRepliesCount}
|
||||
handleOnReplyPress={_handleOnReplyPress}
|
||||
/>
|
||||
/>}
|
||||
</AnimatedView>
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const _renderComment = () => {
|
||||
const _renderComment = () => {
|
||||
return ((
|
||||
<View style={[{ marginLeft: 2, marginTop: -6 }]}>
|
||||
<CommentBody
|
||||
@ -188,7 +214,7 @@ const CommentView = ({
|
||||
<View style={styles.footerWrapper}>
|
||||
{_renderActionPanel()}
|
||||
</View>
|
||||
{ commentNumber > 1 &&
|
||||
{commentNumber > 1 &&
|
||||
childCount > 0 &&
|
||||
!replies?.length &&
|
||||
_renderReadMoreButton()
|
||||
@ -196,10 +222,10 @@ const CommentView = ({
|
||||
</Fragment>
|
||||
</View>
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const _renderActionPanel = () => {
|
||||
const _renderActionPanel = () => {
|
||||
return (
|
||||
<>
|
||||
<Upvote
|
||||
@ -293,13 +319,13 @@ const CommentView = ({
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const customContainerStyle = commentNumber > 2 ? {marginLeft: 44}:null
|
||||
const customContainerStyle = commentNumber > 2 ? { marginLeft: 44 } : null
|
||||
|
||||
return (
|
||||
return (
|
||||
<Fragment>
|
||||
<View style={{...styles.commentContainer, ...customContainerStyle}}>
|
||||
<View style={{ ...styles.commentContainer, ...customContainerStyle }}>
|
||||
<PostHeaderDescription
|
||||
key={comment.permlink}
|
||||
date={getTimeFromNow(comment.created)}
|
||||
@ -310,16 +336,16 @@ const CommentView = ({
|
||||
isShowOwnerIndicator={mainAuthor === comment.author}
|
||||
isHideImage={isHideImage}
|
||||
inlineTime={true}
|
||||
customStyle={{alignItems:'flex-start', paddingLeft: 12}}
|
||||
customStyle={{ alignItems: 'flex-start', paddingLeft: 12 }}
|
||||
showDotMenuButton={true}
|
||||
handleOnDotPress={handleOnLongPress}
|
||||
secondaryContentComponent={_renderComment()}
|
||||
/>
|
||||
|
||||
{_isShowSubComments && commentNumber > 0 && _renderReplies()}
|
||||
{commentNumber > 0 && _renderReplies()}
|
||||
</View>
|
||||
</Fragment>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
export default CommentView;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Platform } from 'react-native';
|
||||
import { withNavigation } from '@react-navigation/compat';
|
||||
import { connect } from 'react-redux';
|
||||
@ -19,7 +19,8 @@ import ROUTES from '../../../constants/routeNames';
|
||||
// Component
|
||||
import CommentsView from '../view/commentsView';
|
||||
import { useAppSelector } from '../../../hooks';
|
||||
import { deleteCommentCacheEntry } from '../../../redux/actions/cacheActions';
|
||||
import { updateCommentCache } from '../../../redux/actions/cacheActions';
|
||||
import { CommentCacheStatus } from '../../../redux/reducers/cacheReducer';
|
||||
|
||||
const CommentsContainer = ({
|
||||
author,
|
||||
@ -66,7 +67,11 @@ const CommentsContainer = ({
|
||||
}, [commentCount, selectedFilter]);
|
||||
|
||||
useEffect(() => {
|
||||
setPropComments(comments);
|
||||
let _comments = comments;
|
||||
if (_comments) {
|
||||
_comments = _handleCachedComment(comments);
|
||||
}
|
||||
setPropComments(_comments);
|
||||
}, [comments]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -190,12 +195,15 @@ const CommentsContainer = ({
|
||||
|
||||
var ignoreCache = false;
|
||||
var replaceAtIndex = -1;
|
||||
var removeAtIndex = -1;
|
||||
_comments.forEach((comment, index) => {
|
||||
if (cachedComment.permlink === comment.permlink) {
|
||||
if (cachedComment.updated < comment.updated) {
|
||||
//comment is present with latest data
|
||||
ignoreCache = true;
|
||||
console.log('Ignore cache as comment is now present');
|
||||
} else if (cachedComment.status === CommentCacheStatus.DELETED) {
|
||||
removeAtIndex = index;
|
||||
} else {
|
||||
//comment is present in list but data is old
|
||||
replaceAtIndex = index;
|
||||
@ -203,10 +211,18 @@ const CommentsContainer = ({
|
||||
}
|
||||
});
|
||||
|
||||
//means deleted comment is not being retuend in fresh data, cache needs to be ignored
|
||||
if (cachedComment.status === CommentCacheStatus.DELETED && removeAtIndex < 0) {
|
||||
ignoreCache = true;
|
||||
}
|
||||
|
||||
//manipulate comments with cached data
|
||||
if (!ignoreCache) {
|
||||
let newComments = [];
|
||||
if (replaceAtIndex >= 0) {
|
||||
if (removeAtIndex >= 0) {
|
||||
newComments = _comments;
|
||||
newComments.splice(removeAtIndex, 1);
|
||||
} else if (replaceAtIndex >= 0) {
|
||||
_comments[replaceAtIndex] = cachedComment;
|
||||
newComments = [..._comments];
|
||||
} else {
|
||||
@ -266,10 +282,11 @@ const CommentsContainer = ({
|
||||
let filteredComments;
|
||||
|
||||
deleteComment(currentAccount, pinCode, _permlink).then(() => {
|
||||
let cachePath = null;
|
||||
let deletedItem = null;
|
||||
|
||||
const _applyFilter = (item) => {
|
||||
if (item.permlink === _permlink) {
|
||||
cachePath = `${item.parent_author}/${item.parent_permlink}`;
|
||||
deletedItem = item;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -284,7 +301,12 @@ const CommentsContainer = ({
|
||||
}
|
||||
|
||||
// remove cached entry based on parent
|
||||
dispatch(deleteCommentCacheEntry(cachePath));
|
||||
if (deletedItem) {
|
||||
const cachePath = `${deletedItem.parent_author}/${deletedItem.parent_permlink}`;
|
||||
deletedItem.status = CommentCacheStatus.DELETED;
|
||||
delete deletedItem.updated;
|
||||
dispatch(updateCommentCache(cachePath, deletedItem, { isUpdate: true }));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -18,4 +18,11 @@ export default EStyleSheet.create({
|
||||
color: '$white',
|
||||
fontSize: 10,
|
||||
},
|
||||
emptyText: {
|
||||
color: '$primaryDarkGray',
|
||||
fontSize: 16,
|
||||
justifyContent: 'center',
|
||||
marginTop: 5,
|
||||
padding: 32,
|
||||
},
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useState, Fragment, useRef } from 'react';
|
||||
import { FlatList } from 'react-native';
|
||||
import { FlatList, Text } from 'react-native';
|
||||
import get from 'lodash/get';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
@ -44,21 +44,21 @@ const CommentsView = ({
|
||||
|
||||
|
||||
const _openCommentMenu = (item) => {
|
||||
if(commentMenu.current){
|
||||
if (commentMenu.current) {
|
||||
setSelectedComment(item);
|
||||
commentMenu.current.show();
|
||||
}
|
||||
};
|
||||
|
||||
const _openReplyThread = (item) => {
|
||||
if(item && openReplyThread){
|
||||
if (item && openReplyThread) {
|
||||
openReplyThread(item)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const _readMoreComments = () => {
|
||||
if(comments[0] && openReplyThread){
|
||||
if (comments[0] && openReplyThread) {
|
||||
openReplyThread(comments[0])
|
||||
}
|
||||
};
|
||||
@ -111,7 +111,7 @@ const CommentsView = ({
|
||||
key={get(item, 'permlink')}
|
||||
marginLeft={marginLeft}
|
||||
handleOnLongPress={() => _openCommentMenu(item)}
|
||||
openReplyThread={()=> _openReplyThread(item)}
|
||||
openReplyThread={() => _openReplyThread(item)}
|
||||
fetchedAt={fetchedAt}
|
||||
incrementRepliesCount={incrementRepliesCount}
|
||||
/>
|
||||
@ -120,19 +120,34 @@ const CommentsView = ({
|
||||
|
||||
|
||||
const styleOerride = commentNumber > 1 ? {
|
||||
backgroundColor:EStyleSheet.value('$primaryLightBackground'),
|
||||
marginTop:8,
|
||||
}:null
|
||||
backgroundColor: EStyleSheet.value('$primaryLightBackground'),
|
||||
marginTop: 8,
|
||||
} : null
|
||||
|
||||
const _renderEmptyContent = () => {
|
||||
if(commentNumber > 1){
|
||||
return;
|
||||
}
|
||||
const _onPress = () => {
|
||||
handleOnReplyPress()
|
||||
}
|
||||
return (
|
||||
<Text onPress={_onPress} style={styles.emptyText}>
|
||||
{intl.formatMessage({ id: "comments.no_comments" })}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<FlatList
|
||||
style={{...styles.list, ...styleOerride }}
|
||||
contentContainerStyle={{padding:0}}
|
||||
style={{ ...styles.list, ...styleOerride }}
|
||||
contentContainerStyle={{ padding: 0 }}
|
||||
data={comments}
|
||||
renderItem={_renderItem}
|
||||
keyExtractor={(item) => get(item, 'permlink')}
|
||||
ListEmptyComponent={_renderEmptyContent()}
|
||||
{...flatListProps}
|
||||
/>
|
||||
<OptionsModal
|
||||
|
@ -0,0 +1,27 @@
|
||||
import EStyleSheet from 'react-native-extended-stylesheet';
|
||||
|
||||
export default EStyleSheet.create({
|
||||
container: {
|
||||
padding: 16,
|
||||
height: 40,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginVertical: 16,
|
||||
},
|
||||
inputContainer: {
|
||||
marginLeft: 12,
|
||||
justifyContent: 'center',
|
||||
height: 36,
|
||||
borderRadius: 12,
|
||||
flex: 1,
|
||||
backgroundColor: '$primaryLightBackground',
|
||||
borderWidth: EStyleSheet.hairlineWidth,
|
||||
borderColor: '$primaryDarkGray',
|
||||
},
|
||||
inputPlaceholder: {
|
||||
color: '$primaryDarkGray',
|
||||
fontSize: 16,
|
||||
marginTop: 5,
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
});
|
@ -1,3 +0,0 @@
|
||||
import EStyleSheet from 'react-native-extended-stylesheet';
|
||||
|
||||
export default EStyleSheet.create({});
|
@ -1,29 +1,43 @@
|
||||
import React, { useState, Fragment } from 'react';
|
||||
import React, { useState, Fragment, useImperativeHandle, forwardRef, useRef } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { injectIntl } from 'react-intl';
|
||||
import { injectIntl, useIntl } from 'react-intl';
|
||||
|
||||
// Components
|
||||
import { FilterBar } from '../../filterBar';
|
||||
import { Comments } from '../../comments';
|
||||
import COMMENT_FILTER, { VALUE } from '../../../constants/options/comment';
|
||||
import { WriteCommentButton } from './writeCommentButton';
|
||||
|
||||
// Styles
|
||||
import styles from './commentDisplayStyles';
|
||||
|
||||
const CommentsDisplayView = ({
|
||||
const CommentsDisplayView = forwardRef(
|
||||
(
|
||||
{
|
||||
author,
|
||||
commentCount,
|
||||
fetchPost,
|
||||
intl,
|
||||
permlink,
|
||||
mainAuthor,
|
||||
handleOnVotersPress,
|
||||
handleOnReplyPress,
|
||||
fetchedAt,
|
||||
}) => {
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const writeCommentRef = useRef(null);
|
||||
|
||||
const [selectedFilter, setSelectedFilter] = useState('trending');
|
||||
const [selectedOptionIndex, setSelectedOptionIndex] = useState(0);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
bounceCommentButton: () => {
|
||||
console.log('bouncing comment button');
|
||||
if (writeCommentRef.current) {
|
||||
writeCommentRef.current.bounce();
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
const _handleOnDropdownSelect = (option, index) => {
|
||||
setSelectedFilter(option);
|
||||
setSelectedOptionIndex(index);
|
||||
@ -31,8 +45,8 @@ const CommentsDisplayView = ({
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{commentCount > 0 && (
|
||||
<Fragment>
|
||||
<WriteCommentButton ref={writeCommentRef} onPress={handleOnReplyPress} />
|
||||
<FilterBar
|
||||
dropdownIconName="arrow-drop-down"
|
||||
options={VALUE.map((val) => intl.formatMessage({ id: `comment_filter.${val}` }))}
|
||||
@ -56,9 +70,9 @@ const CommentsDisplayView = ({
|
||||
/>
|
||||
</View>
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export default injectIntl(CommentsDisplayView);
|
||||
export default CommentsDisplayView;
|
||||
|
56
src/components/commentsDisplay/view/writeCommentButton.tsx
Normal file
56
src/components/commentsDisplay/view/writeCommentButton.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import { View, Text } from 'react-native'
|
||||
import React, { forwardRef, useImperativeHandle, useRef } from 'react'
|
||||
import UserAvatar from '../../userAvatar';
|
||||
import { View as AnimatedView } from 'react-native-animatable';
|
||||
import { TouchableOpacity } from 'react-native-gesture-handler';
|
||||
import styles from './WriteCommentButtonStyles';
|
||||
import { useAppSelector } from '../../../hooks';
|
||||
import showLoginAlert from '../../../utils/showLoginAlert';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
interface WriteCommentButton {
|
||||
onPress: () => void;
|
||||
}
|
||||
|
||||
export const WriteCommentButton = forwardRef(({ onPress }, ref) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const animatedContainer = useRef<AnimatedView>();
|
||||
|
||||
const isLoggedIn = useAppSelector(state => state.application.isLoggedIn);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
bounce: () => {
|
||||
console.log("bouncing")
|
||||
if (animatedContainer.current) {
|
||||
animatedContainer.current.swing(1000);
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
const _onPress = () => {
|
||||
if (!isLoggedIn) {
|
||||
showLoginAlert({ intl })
|
||||
return;
|
||||
}
|
||||
if (onPress) {
|
||||
onPress();
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<AnimatedView ref={animatedContainer}>
|
||||
<TouchableOpacity onPress={_onPress}>
|
||||
<View style={styles.container}>
|
||||
<UserAvatar username="demo.com" />
|
||||
<View style={styles.inputContainer}>
|
||||
<Text style={styles.inputPlaceholder}>
|
||||
{intl.formatMessage({id:'quick_reply.placeholder'})}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</AnimatedView>
|
||||
|
||||
)
|
||||
})
|
@ -1,6 +1,7 @@
|
||||
import { get, isEmpty, some } from 'lodash';
|
||||
import React, { useEffect, useRef, useState} from 'react';
|
||||
import { Animated, Text, TouchableOpacity, View } from 'react-native';
|
||||
import { get } from 'lodash';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { Text, TouchableOpacity, View } from 'react-native';
|
||||
import { View as AnimatedView } from 'react-native-animatable';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { IconButton } from '..';
|
||||
import { toastNotification } from '../../redux/actions/uiAction';
|
||||
@ -13,32 +14,33 @@ import { navigate } from '../../navigation/service';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
interface RemoteMessage {
|
||||
data:{
|
||||
id:string;
|
||||
source:string;
|
||||
target:string;
|
||||
permlink1:string;
|
||||
permlink2:string;
|
||||
permlink3:string;
|
||||
type:'mention'|'reply';
|
||||
data: {
|
||||
id: string;
|
||||
source: string;
|
||||
target: string;
|
||||
permlink1: string;
|
||||
permlink2: string;
|
||||
permlink3: string;
|
||||
type: 'mention' | 'reply';
|
||||
};
|
||||
notification:{
|
||||
body:string;
|
||||
title:string;
|
||||
notification: {
|
||||
body: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface Props {
|
||||
remoteMessage:RemoteMessage
|
||||
remoteMessage: RemoteMessage
|
||||
}
|
||||
|
||||
|
||||
const ForegroundNotification = ({remoteMessage}:Props) => {
|
||||
let hideTimeout = null;
|
||||
const dispatch = useDispatch();
|
||||
const ForegroundNotification = ({ remoteMessage }: Props) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const hideTimeoutRef = useRef<any>(null);
|
||||
const containerRef = useRef<AnimatedView>(null);
|
||||
|
||||
const [duration] = useState(5000);
|
||||
const [activeId, setActiveId] = useState('');
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
@ -47,17 +49,14 @@ const ForegroundNotification = ({remoteMessage}:Props) => {
|
||||
const [body, setBody] = useState('');
|
||||
|
||||
|
||||
const animatedValue = useRef(new Animated.Value(-CONTAINER_HEIGHT)).current;
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if(remoteMessage){
|
||||
const {source, target, type, id} = remoteMessage.data;
|
||||
if(activeId !== id && (type === 'reply' || type === 'mention')){
|
||||
if (remoteMessage) {
|
||||
const { source, target, type, id } = remoteMessage.data;
|
||||
if (activeId !== id && (type === 'reply' || type === 'mention')) {
|
||||
|
||||
let titlePrefixId = '';
|
||||
switch(type){
|
||||
switch (type) {
|
||||
case 'reply':
|
||||
titlePrefixId = 'notification.reply_on'
|
||||
break;
|
||||
@ -68,57 +67,47 @@ const ForegroundNotification = ({remoteMessage}:Props) => {
|
||||
|
||||
setActiveId(id);
|
||||
setUsername(source);
|
||||
setTitle(`${intl.formatMessage({id:titlePrefixId})} @${target}`)
|
||||
setBody(intl.formatMessage({id:'notification.reply_body'}));
|
||||
setTitle(`${intl.formatMessage({ id: titlePrefixId })} @${target}`)
|
||||
setBody(intl.formatMessage({ id: 'notification.reply_body' }));
|
||||
show();
|
||||
}
|
||||
}
|
||||
|
||||
return ()=>{
|
||||
if(hideTimeout){
|
||||
clearTimeout(hideTimeout);
|
||||
return () => {
|
||||
if (hideTimeoutRef.current) {
|
||||
clearTimeout(hideTimeoutRef.current);
|
||||
}
|
||||
}
|
||||
}, [remoteMessage]);
|
||||
|
||||
const show = () => {
|
||||
// Will change fadeAnim value to 1 in 5 seconds
|
||||
Animated.timing(animatedValue, {
|
||||
toValue: 0,
|
||||
duration: 350
|
||||
}).start();
|
||||
|
||||
setIsVisible(true);
|
||||
|
||||
hideTimeout = setTimeout(()=>{
|
||||
setIsVisible(true)
|
||||
hideTimeoutRef.current = setTimeout(() => {
|
||||
hide();
|
||||
}, duration)
|
||||
|
||||
};
|
||||
|
||||
const hide = () => {
|
||||
if(hideTimeout || isVisible){
|
||||
// Will change fadeAnim value to 0 in 3 seconds
|
||||
Animated.timing(animatedValue, {
|
||||
toValue: -CONTAINER_HEIGHT,
|
||||
duration: 200
|
||||
}).start(()=>{
|
||||
dispatch(toastNotification(''))
|
||||
});
|
||||
const hide = async () => {
|
||||
if (containerRef.current) {
|
||||
await containerRef.current.fadeOutUp(300);
|
||||
|
||||
setIsVisible(false);
|
||||
clearTimeout(hideTimeout);
|
||||
if(hideTimeoutRef.current){
|
||||
clearTimeout(hideTimeoutRef.current);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const _onPress = () => {
|
||||
const {data} = remoteMessage;
|
||||
const { data } = remoteMessage;
|
||||
const fullPermlink =
|
||||
get(data, 'permlink1', '') + get(data, 'permlink2', '') + get(data, 'permlink3', '');
|
||||
|
||||
let params = {
|
||||
author: get(remoteMessage, 'source', ''),
|
||||
author: get(data, 'source', ''),
|
||||
permlink: fullPermlink,
|
||||
};
|
||||
let key = fullPermlink
|
||||
@ -129,32 +118,32 @@ const ForegroundNotification = ({remoteMessage}:Props) => {
|
||||
params,
|
||||
key,
|
||||
});
|
||||
hide();
|
||||
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
style={{
|
||||
...styles.container,
|
||||
transform: [{ translateY: animatedValue }],
|
||||
}}
|
||||
>
|
||||
isVisible &&
|
||||
<AnimatedView
|
||||
ref={containerRef}
|
||||
style={styles.container}
|
||||
animation='slideInDown'
|
||||
duration={500}>
|
||||
|
||||
<View style={styles.contentContainer}>
|
||||
|
||||
<TouchableOpacity onPress={_onPress} style={{flexShrink:1}}>
|
||||
<View style={{flexDirection:'row', alignItems:'center', marginRight:24}}>
|
||||
<UserAvatar username={username}/>
|
||||
<TouchableOpacity onPress={_onPress} style={{ flexShrink: 1 }}>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', marginRight: 24 }}>
|
||||
<UserAvatar username={username} />
|
||||
|
||||
<View style={{flexShrink:1}}>
|
||||
<View style={{ flexShrink: 1 }}>
|
||||
<Text style={styles.text} numberOfLines={1}>{title}</Text>
|
||||
<Text style={styles.text} numberOfLines={1}>{body}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
|
||||
|
||||
|
||||
<IconButton
|
||||
name='close'
|
||||
color="white"
|
||||
@ -162,8 +151,7 @@ const ForegroundNotification = ({remoteMessage}:Props) => {
|
||||
onPress={hide}
|
||||
/>
|
||||
</View>
|
||||
|
||||
</Animated.View>
|
||||
</AnimatedView>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ export default EStyleSheet.create({
|
||||
ios:getStatusBarHeight() + 12,
|
||||
android:8,
|
||||
}),
|
||||
backgroundColor: '$primaryDarkText',
|
||||
backgroundColor: '$darkGrayBackground',
|
||||
shadowColor: '#5f5f5fbf',
|
||||
shadowOpacity: 0.3,
|
||||
shadowOffset: {
|
||||
|
@ -26,6 +26,8 @@ interface Props extends TextInputProps {
|
||||
inputStyle:TextStyle;
|
||||
isValid:boolean;
|
||||
onChange?:(value:string)=>void;
|
||||
onFocus?:()=>void;
|
||||
onBlur?:()=>void;
|
||||
}
|
||||
|
||||
const FormInputView = ({
|
||||
@ -44,6 +46,7 @@ const FormInputView = ({
|
||||
isValid,
|
||||
value,
|
||||
onBlur,
|
||||
onFocus,
|
||||
...props
|
||||
}:Props) => {
|
||||
const [_value, setValue] = useState(value || '');
|
||||
@ -62,6 +65,9 @@ const FormInputView = ({
|
||||
|
||||
const _handleOnFocus = () => {
|
||||
setInputBorderColor('#357ce6');
|
||||
if(onFocus){
|
||||
onFocus();
|
||||
}
|
||||
};
|
||||
|
||||
const _handleOnBlur = () => {
|
||||
|
@ -1,43 +1,67 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { View as AnimatedView } from 'react-native-animatable'
|
||||
import { Portal } from 'react-native-portalize';
|
||||
import styles from '../children/inputSupportModal.styles';
|
||||
import { KeyboardAvoidingView, Platform, View } from 'react-native';
|
||||
|
||||
export interface InputSupportModalProps {
|
||||
visible:boolean;
|
||||
onClose:()=>void;
|
||||
children?:any
|
||||
visible: boolean;
|
||||
onClose: () => void;
|
||||
children?: any
|
||||
}
|
||||
|
||||
export const InputSupportModal = ({children, visible, onClose}: InputSupportModalProps, ref) => {
|
||||
export const InputSupportModal = ({ children, visible, onClose }: InputSupportModalProps, ref) => {
|
||||
|
||||
const container = useRef<AnimatedView>(null);
|
||||
const innerContainer = useRef<AnimatedView>(null);
|
||||
|
||||
const [showModal, setShowModal] = useState(visible);
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
setShowModal(true);
|
||||
}
|
||||
else if (!visible && container.current && innerContainer.current) {
|
||||
innerContainer.current.slideOutDown(1000)
|
||||
setTimeout(async () => {
|
||||
await container.current?.fadeOut(200)
|
||||
setShowModal(false);
|
||||
}, 300)
|
||||
}
|
||||
}, [visible])
|
||||
|
||||
|
||||
return (
|
||||
return showModal && (
|
||||
<Portal>
|
||||
{
|
||||
visible && (
|
||||
|
||||
<AnimatedView
|
||||
style={styles.container}
|
||||
ref={container}
|
||||
animation='fadeIn'
|
||||
duration={300}
|
||||
animation='fadeInUp'>
|
||||
<>
|
||||
<View style={styles.container} onTouchEnd={onClose} />
|
||||
style={styles.container} >
|
||||
|
||||
<AnimatedView
|
||||
ref={innerContainer}
|
||||
style={{ flex: 1 }}
|
||||
animation='slideInUp'
|
||||
duration={300}>
|
||||
|
||||
<View
|
||||
style={{ flex: 1 }}
|
||||
onTouchEnd={onClose} />
|
||||
|
||||
{
|
||||
Platform.select({
|
||||
ios: (
|
||||
<KeyboardAvoidingView behavior="padding" style={{backgroundColor: 'rgba(0, 0, 0, 0.2)'}}>
|
||||
<KeyboardAvoidingView behavior="padding" style={{}}>
|
||||
{children}
|
||||
</KeyboardAvoidingView>
|
||||
),
|
||||
android: <View>{children}</View>,
|
||||
})
|
||||
}
|
||||
|
||||
</>
|
||||
</AnimatedView>
|
||||
)
|
||||
}
|
||||
</AnimatedView>
|
||||
</Portal>
|
||||
);
|
||||
};
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { Dimensions } from 'react-native';
|
||||
import EStyleSheet from 'react-native-extended-stylesheet';
|
||||
import getWindowDimensions from '../../../utils/getWindowDimensions';
|
||||
|
||||
@ -14,7 +13,8 @@ export default EStyleSheet.create({
|
||||
backgroundColor: '$primaryBackgroundColor',
|
||||
},
|
||||
headerLine: {
|
||||
bottom: 10,
|
||||
marginTop: -4,
|
||||
marginBottom: 4,
|
||||
},
|
||||
title: {
|
||||
fontSize: 24,
|
||||
|
@ -1,10 +1,9 @@
|
||||
import React, { useCallback, useEffect, useRef, useState, Fragment } from 'react';
|
||||
import { View, Text, ScrollView, SafeAreaView, RefreshControl } from 'react-native';
|
||||
import { View, Text, ScrollView, RefreshControl } from 'react-native';
|
||||
import { injectIntl } from 'react-intl';
|
||||
import get from 'lodash/get';
|
||||
|
||||
// Providers
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { userActivity } from '../../../providers/ecency/ePoint';
|
||||
|
||||
@ -22,7 +21,6 @@ import { ParentPost } from '../../parentPost';
|
||||
// Styles
|
||||
import styles from './postDisplayStyles';
|
||||
import { OptionsModal } from '../../atoms';
|
||||
import { QuickReplyModal } from '../..';
|
||||
import getWindowDimensions from '../../../utils/getWindowDimensions';
|
||||
import { useAppDispatch } from '../../../hooks';
|
||||
import { showReplyModal } from '../../../redux/actions/uiAction';
|
||||
@ -37,7 +35,6 @@ const PostDisplayView = ({
|
||||
isNewPost,
|
||||
fetchPost,
|
||||
handleOnEditPress,
|
||||
handleOnReplyPress,
|
||||
handleOnVotersPress,
|
||||
handleOnReblogsPress,
|
||||
post,
|
||||
@ -53,6 +50,11 @@ const PostDisplayView = ({
|
||||
const dispatch = useAppDispatch();
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
const commentsRef = useRef<CommentsDisplay>();
|
||||
const scrollRef = useRef<ScrollView>();
|
||||
const commentsReached = useRef<boolean>(false);
|
||||
|
||||
|
||||
const [postHeight, setPostHeight] = useState(0);
|
||||
const [scrollHeight, setScrollHeight] = useState(0);
|
||||
const [cacheVoteIcrement, setCacheVoteIcrement] = useState(0);
|
||||
@ -87,16 +89,30 @@ const PostDisplayView = ({
|
||||
|
||||
const _handleOnScroll = (event) => {
|
||||
const { y } = event.nativeEvent.contentOffset;
|
||||
|
||||
console.log("scroll height", y)
|
||||
setScrollHeight(HEIGHT + y);
|
||||
|
||||
const _commentButtonBounceOffset = y + (HEIGHT/1.7);
|
||||
if(!commentsReached.current && commentsRef.current && _commentButtonBounceOffset > postHeight ){
|
||||
commentsRef.current.bounceCommentButton();
|
||||
commentsReached.current = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const _handleOnPostLayout = (event) => {
|
||||
const { height } = event.nativeEvent.layout;
|
||||
|
||||
console.log('post height', height)
|
||||
setPostHeight(height);
|
||||
};
|
||||
|
||||
const _scrollToComments = () => {
|
||||
if(scrollRef.current){
|
||||
const pos = postHeight;
|
||||
scrollRef.current.scrollTo({y:pos})
|
||||
}
|
||||
}
|
||||
|
||||
const _handleOnReblogsPress = () => {
|
||||
if (reblogs.length > 0 && handleOnReblogsPress) {
|
||||
handleOnReblogsPress();
|
||||
@ -144,7 +160,7 @@ const PostDisplayView = ({
|
||||
isClickable
|
||||
text={get(post, 'children', 0)}
|
||||
textMarginLeft={20}
|
||||
onPress={() => _showQuickReplyModal(post)}
|
||||
onPress={() => _scrollToComments()}
|
||||
// onPress={() => handleOnReplyPress && handleOnReplyPress()}
|
||||
/>
|
||||
)}
|
||||
@ -211,9 +227,9 @@ const PostDisplayView = ({
|
||||
};
|
||||
|
||||
// show quick reply modal
|
||||
const _showQuickReplyModal = (post) => {
|
||||
const _showQuickReplyModal = (_post = post) => {
|
||||
if (isLoggedIn) {
|
||||
dispatch(showReplyModal(post));
|
||||
dispatch(showReplyModal(_post));
|
||||
} else {
|
||||
console.log('Not LoggedIn');
|
||||
}
|
||||
@ -222,6 +238,7 @@ const PostDisplayView = ({
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ScrollView
|
||||
ref={scrollRef}
|
||||
style={styles.scroll}
|
||||
contentContainerStyle={[styles.scrollContent]}
|
||||
onScroll={(event) => _handleOnScroll(event)}
|
||||
@ -262,7 +279,9 @@ const PostDisplayView = ({
|
||||
)}
|
||||
</View>
|
||||
{post && !postBodyLoading && (isGetComment || isLoadedComments) && (
|
||||
|
||||
<CommentsDisplay
|
||||
ref={commentsRef}
|
||||
author={author || post.author}
|
||||
mainAuthor={author || post.author}
|
||||
permlink={post.permlink}
|
@ -724,7 +724,8 @@
|
||||
"title": "Comments",
|
||||
"reveal_comment": "Reveal comment",
|
||||
"read_more": "Read more comments",
|
||||
"more_replies": "replies"
|
||||
"more_replies": "replies",
|
||||
"no_comments":"Be the first to respond..."
|
||||
},
|
||||
"search_result": {
|
||||
"others": "Others",
|
||||
@ -817,7 +818,7 @@
|
||||
"year":"years"
|
||||
},
|
||||
"quick_reply":{
|
||||
"placeholder":"Add a comment",
|
||||
"placeholder":"Add a comment...",
|
||||
"comment": "Comment",
|
||||
"reply": "REPLY",
|
||||
"close":"CLOSE"
|
||||
|
@ -252,7 +252,7 @@
|
||||
"feedback_fail": "Ocurrió un error al intentar abrir el cliente del correo electrónico",
|
||||
"server_fail": "Servidor no disponible",
|
||||
"show_imgs": "Mostrar imágenes",
|
||||
"delete_account": "Delete Account"
|
||||
"delete_account": "Eliminar cuenta"
|
||||
},
|
||||
"voters": {
|
||||
"voters_info": "Información de votantes",
|
||||
@ -550,9 +550,9 @@
|
||||
"confirm_report_body": "¿Estás seguro de que quieres reportar?"
|
||||
},
|
||||
"delete": {
|
||||
"confirm_delete_title": "Confirm Delete",
|
||||
"confirm_delete_body": "Are you sure you want to delete account? It will erase all of your data",
|
||||
"request_sent": "Your request for deletion is sent"
|
||||
"confirm_delete_title": "Confirmar eliminación",
|
||||
"confirm_delete_body": "¿Estás seguro de que deseas eliminar la cuenta? Se borrarán todos tus datos",
|
||||
"request_sent": "Su solicitud de eliminación está enviada"
|
||||
},
|
||||
"favorites": {
|
||||
"title": "Favoritos",
|
||||
|
@ -252,7 +252,7 @@
|
||||
"feedback_fail": "Sähköpostipalvelin alhaalla",
|
||||
"server_fail": "Ei yhteyttä palvelimeen",
|
||||
"show_imgs": "Näytä kuvat",
|
||||
"delete_account": "Delete Account"
|
||||
"delete_account": "Poista tili"
|
||||
},
|
||||
"voters": {
|
||||
"voters_info": "Äänestystiedot",
|
||||
@ -269,8 +269,8 @@
|
||||
"login": "KIRJAUDU SISÄÄN",
|
||||
"steemconnect_description": "Jos et halua säilyttää kryptattua salasanaa laitteessasi, voit käyttää Hivesigneriä.",
|
||||
"steemconnect_fee_description": "tietoa",
|
||||
"not_loggedin_alert": "Not LoggedIn",
|
||||
"not_loggedin_alert_desc": "Please login first"
|
||||
"not_loggedin_alert": "Ei Kirjautunut",
|
||||
"not_loggedin_alert_desc": "Kirjaudu ensin sisään"
|
||||
},
|
||||
"register": {
|
||||
"button": "Luo tili",
|
||||
@ -550,9 +550,9 @@
|
||||
"confirm_report_body": "Haluatko varmasti tehdä ilmiannon?"
|
||||
},
|
||||
"delete": {
|
||||
"confirm_delete_title": "Confirm Delete",
|
||||
"confirm_delete_body": "Are you sure you want to delete account? It will erase all of your data",
|
||||
"request_sent": "Your request for deletion is sent"
|
||||
"confirm_delete_title": "Vahvista poistaminen",
|
||||
"confirm_delete_body": "Oletko varma, että haluat poistaa tilin? Se poistaa kaikki tietosi",
|
||||
"request_sent": "Pyyntösi poistamisesta on lähetetty"
|
||||
},
|
||||
"favorites": {
|
||||
"title": "Suosikit",
|
||||
@ -592,7 +592,7 @@
|
||||
"pin-community": "Kiinnitä yhteisölle",
|
||||
"unpin-community": "Poista kiinnitys yhteisöstä",
|
||||
"edit-history": "Muokkaushistoria",
|
||||
"mute": "Mute / Block"
|
||||
"mute": "Mykistä / Estä"
|
||||
},
|
||||
"deep_link": {
|
||||
"no_existing_user": "Käyttäjää ei ole",
|
||||
|
@ -10,9 +10,9 @@ import {
|
||||
DELETE_DRAFT_CACHE_ENTRY,
|
||||
UPDATE_SUBSCRIBED_COMMUNITY_CACHE,
|
||||
DELETE_SUBSCRIBED_COMMUNITY_CACHE,
|
||||
CLEAR_SUBSCRIBED_COMMUNITIES_CACHE,
|
||||
CLEAR_SUBSCRIBED_COMMUNITIES_CACHE
|
||||
} from '../constants/constants';
|
||||
import { Comment, Draft, SubscribedCommunity, Vote } from '../reducers/cacheReducer';
|
||||
import { Comment, CommentCacheStatus, Draft, SubscribedCommunity, Vote } from '../reducers/cacheReducer';
|
||||
|
||||
|
||||
|
||||
@ -46,6 +46,8 @@ export const updateCommentCache = (commentPath: string, comment: Comment, option
|
||||
throw new Error("either of json_metadata in comment data or parentTags in options must be provided");
|
||||
}
|
||||
|
||||
|
||||
|
||||
comment.created = comment.created || updatedStamp; //created will be set only once for new comment;
|
||||
comment.updated = comment.updated || updatedStamp;
|
||||
comment.expiresAt = comment.expiresAt || updated.getTime() + 6000000;//600000;
|
||||
@ -55,6 +57,7 @@ export const updateCommentCache = (commentPath: string, comment: Comment, option
|
||||
comment.total_payout = comment.total_payout || 0;
|
||||
comment.json_metadata = comment.json_metadata || makeJsonMetadataReply(options.parentTags)
|
||||
comment.isDeletable = comment.isDeletable || true;
|
||||
comment.status = comment.status || CommentCacheStatus.PENDING;
|
||||
|
||||
comment.body = renderPostBody({
|
||||
author: comment.author,
|
||||
|
@ -1,4 +1,10 @@
|
||||
import { PURGE_EXPIRED_CACHE, UPDATE_VOTE_CACHE, UPDATE_COMMENT_CACHE, DELETE_COMMENT_CACHE_ENTRY, DELETE_DRAFT_CACHE_ENTRY, UPDATE_DRAFT_CACHE, UPDATE_SUBSCRIBED_COMMUNITY_CACHE, DELETE_SUBSCRIBED_COMMUNITY_CACHE, CLEAR_SUBSCRIBED_COMMUNITIES_CACHE, } from "../constants/constants";
|
||||
import { PURGE_EXPIRED_CACHE, UPDATE_VOTE_CACHE, UPDATE_COMMENT_CACHE, DELETE_COMMENT_CACHE_ENTRY, DELETE_DRAFT_CACHE_ENTRY, UPDATE_DRAFT_CACHE, UPDATE_SUBSCRIBED_COMMUNITY_CACHE, DELETE_SUBSCRIBED_COMMUNITY_CACHE, CLEAR_SUBSCRIBED_COMMUNITIES_CACHE, UPDATE_COMMENT_CACHE_ENTRY_STATUS, } from "../constants/constants";
|
||||
|
||||
export enum CommentCacheStatus {
|
||||
PENDING = 'PENDING',
|
||||
POSTPONED = 'PUBLISHED',
|
||||
DELETED = 'DELETED',
|
||||
}
|
||||
|
||||
export interface Vote {
|
||||
amount: number;
|
||||
@ -24,6 +30,7 @@ export interface Comment {
|
||||
created?: string, //handle created and updated separatly
|
||||
updated?: string,
|
||||
expiresAt?: number,
|
||||
status: CommentCacheStatus
|
||||
}
|
||||
|
||||
export interface Draft {
|
||||
|
@ -18,7 +18,6 @@ import SplashScreen from 'react-native-splash-screen'
|
||||
// Constants
|
||||
import AUTH_TYPE from '../../../constants/authType';
|
||||
import ROUTES from '../../../constants/routeNames';
|
||||
import postUrlParser from '../../../utils/postUrlParser';
|
||||
|
||||
// Services
|
||||
import {
|
||||
@ -34,7 +33,7 @@ import {
|
||||
setLastUpdateCheck,
|
||||
getTheme,
|
||||
} from '../../../realm/realm';
|
||||
import { getUser, getPost, getDigitPinCode, getMutes } from '../../../providers/hive/dhive';
|
||||
import { getUser, getDigitPinCode, getMutes } from '../../../providers/hive/dhive';
|
||||
import { getPointsSummary } from '../../../providers/ecency/ePoint';
|
||||
import {
|
||||
migrateToMasterKeyWithAccessToken,
|
||||
@ -62,7 +61,6 @@ import {
|
||||
login,
|
||||
logoutDone,
|
||||
setConnectivityStatus,
|
||||
setAnalyticsStatus,
|
||||
setPinCode as savePinCode,
|
||||
isRenderRequired,
|
||||
logout,
|
||||
@ -85,21 +83,12 @@ import lightTheme from '../../../themes/lightTheme';
|
||||
import persistAccountGenerator from '../../../utils/persistAccountGenerator';
|
||||
import parseVersionNumber from '../../../utils/parseVersionNumber';
|
||||
import { setMomentLocale } from '../../../utils/time';
|
||||
import parseAuthUrl from '../../../utils/parseAuthUrl';
|
||||
import { purgeExpiredCache } from '../../../redux/actions/cacheActions';
|
||||
import { fetchSubscribedCommunities } from '../../../redux/actions/communitiesAction';
|
||||
import MigrationHelpers from '../../../utils/migrationHelpers';
|
||||
import { deepLinkParser } from '../../../utils/deepLinkParser';
|
||||
import bugsnapInstance from '../../../config/bugsnag';
|
||||
|
||||
// Workaround
|
||||
let previousAppState = 'background';
|
||||
export const setPreviousAppState = () => {
|
||||
previousAppState = AppState.currentState;
|
||||
const appStateTimeout = setTimeout(() => {
|
||||
previousAppState = AppState.currentState;
|
||||
clearTimeout(appStateTimeout);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
let firebaseOnNotificationOpenedAppListener = null;
|
||||
let firebaseOnMessageListener = null;
|
||||
@ -131,7 +120,6 @@ class ApplicationContainer extends Component {
|
||||
});
|
||||
|
||||
AppState.addEventListener('change', this._handleAppStateChange);
|
||||
setPreviousAppState();
|
||||
|
||||
this.removeAppearanceListener = Appearance.addChangeListener(this._appearanceChangeListener);
|
||||
|
||||
@ -338,7 +326,7 @@ class ApplicationContainer extends Component {
|
||||
if (appState.match(/active|forground/) && nextAppState === 'inactive') {
|
||||
this._startPinCodeTimer();
|
||||
}
|
||||
setPreviousAppState();
|
||||
|
||||
this.setState({
|
||||
appState: nextAppState,
|
||||
});
|
||||
@ -375,7 +363,7 @@ class ApplicationContainer extends Component {
|
||||
let key = null;
|
||||
let routeName = null;
|
||||
|
||||
if (previousAppState !== 'active' && !!notification) {
|
||||
if (!!notification) {
|
||||
const push = get(notification, 'data');
|
||||
const type = get(push, 'type', '');
|
||||
const fullPermlink =
|
||||
@ -477,11 +465,11 @@ class ApplicationContainer extends Component {
|
||||
|
||||
firebaseOnMessageListener = messaging().onMessage((remoteMessage) => {
|
||||
console.log('Notification Received: foreground', remoteMessage);
|
||||
// this._showNotificationToast(remoteMessage);
|
||||
|
||||
this.setState({
|
||||
foregroundNotificationData: remoteMessage,
|
||||
});
|
||||
this._pushNavigate(remoteMessage);
|
||||
|
||||
});
|
||||
|
||||
firebaseOnNotificationOpenedAppListener = messaging().onNotificationOpenedApp(
|
||||
@ -672,26 +660,40 @@ class ApplicationContainer extends Component {
|
||||
|
||||
|
||||
//update notification settings and update push token for each signed accoutn useing access tokens
|
||||
_registerDeviceForNotifications = (settings?:any) => {
|
||||
const { otherAccounts, notificationDetails, isNotificationsEnabled } = this.props;
|
||||
_registerDeviceForNotifications = (settings?: any) => {
|
||||
const { currentAccount, otherAccounts, notificationDetails, isNotificationsEnabled } = this.props;
|
||||
|
||||
const isEnabled = settings ? !!settings.notification : isNotificationsEnabled;
|
||||
settings = settings || notificationDetails;
|
||||
|
||||
|
||||
//updateing fcm token with settings;
|
||||
otherAccounts.forEach((account) => {
|
||||
//since there can be more than one accounts, process access tokens separate
|
||||
const _enabledNotificationForAccount = (account) => {
|
||||
const encAccessToken = account?.local?.accessToken;
|
||||
//decrypt access token
|
||||
let accessToken = null;
|
||||
if (encAccessToken) {
|
||||
//NOTE: default pin decryption works also for custom pin as other account
|
||||
//keys are not yet being affected by changed pin, which I think we should dig more
|
||||
accessToken = decryptKey(encAccessToken, Config.DEFAULT_PIN);
|
||||
accessToken = decryptKey(account.name, Config.DEFAULT_PIN);
|
||||
}
|
||||
|
||||
this._enableNotification(account.name, isEnabled, settings, accessToken);
|
||||
}
|
||||
|
||||
|
||||
//updateing fcm token with settings;
|
||||
otherAccounts.forEach((account) => {
|
||||
//since there can be more than one accounts, process access tokens separate
|
||||
if (account?.local?.accessToken) {
|
||||
_enabledNotificationForAccount(account)
|
||||
} else {
|
||||
console.warn("access token not present, reporting to bugsnag")
|
||||
bugsnapInstance.notify(new Error(`Reporting missing access token in other accounts section: account:${account.name} with local data ${JSON.stringify(account?.local)}`))
|
||||
|
||||
//fallback to current account access token to register atleast logged in account
|
||||
if (currentAccount.name === account.name) {
|
||||
_enabledNotificationForAccount(currentAccount)
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1,8 +1,16 @@
|
||||
import React, { useState } from 'react';
|
||||
import { View, StatusBar, Platform, Image, Text, SafeAreaView } from 'react-native';
|
||||
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
View,
|
||||
StatusBar,
|
||||
Platform,
|
||||
Image,
|
||||
Text,
|
||||
SafeAreaView,
|
||||
Keyboard,
|
||||
KeyboardAvoidingView,
|
||||
} from 'react-native';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import * as Animatable from 'react-native-animatable';
|
||||
import RegisterContainer from './registerContainer';
|
||||
|
||||
// Internal Components
|
||||
@ -16,6 +24,7 @@ import styles from './registerStyles';
|
||||
|
||||
import ESTEEM_LOGO from '../../assets/like_new.png';
|
||||
import ESTEEM_SMALL_LOGO from '../../assets/ecency_logo_transparent.png';
|
||||
import getWindowDimensions from '../../utils/getWindowDimensions';
|
||||
|
||||
const RegisterScreen = ({ navigation, route }) => {
|
||||
const intl = useIntl();
|
||||
@ -27,6 +36,19 @@ const RegisterScreen = ({ navigation, route }) => {
|
||||
const [refUsername, setRefUsername] = useState(route.params?.referredUser ?? '');
|
||||
const [isRefUsernameValid, setIsRefUsernameValid] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', () => {
|
||||
setKeyboardIsOpen(true);
|
||||
});
|
||||
const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () => {
|
||||
setKeyboardIsOpen(false);
|
||||
});
|
||||
return () => {
|
||||
keyboardDidHideListener.remove();
|
||||
keyboardDidShowListener.remove();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const _handleEmailChange = (value) => {
|
||||
const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
setIsEmailValid(re.test(value));
|
||||
@ -74,7 +96,11 @@ const RegisterScreen = ({ navigation, route }) => {
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
{!keyboardIsOpen && (
|
||||
<Animatable.View
|
||||
animation={keyboardIsOpen ? hideAnimation : showAnimation}
|
||||
delay={0}
|
||||
duration={300}
|
||||
>
|
||||
<View style={styles.header}>
|
||||
<View style={styles.titleText}>
|
||||
<Text style={styles.title}>{intl.formatMessage({ id: 'register.title' })}</Text>
|
||||
@ -84,15 +110,13 @@ const RegisterScreen = ({ navigation, route }) => {
|
||||
</View>
|
||||
<Image style={styles.mascot} source={ESTEEM_LOGO} />
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.body}>
|
||||
<KeyboardAwareScrollView
|
||||
onKeyboardWillShow={() => setKeyboardIsOpen(true)}
|
||||
onKeyboardWillHide={() => setKeyboardIsOpen(false)}
|
||||
enableAutoAutomaticScroll={Platform.OS === 'ios'}
|
||||
contentContainerStyle={styles.formWrapper}
|
||||
enableOnAndroid={true}
|
||||
</Animatable.View>
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||
style={styles.formWrapper}
|
||||
keyboardShouldPersistTaps
|
||||
>
|
||||
<View style={styles.body}>
|
||||
<FormInput
|
||||
rightIconName="at"
|
||||
leftIconName="close"
|
||||
@ -107,6 +131,7 @@ const RegisterScreen = ({ navigation, route }) => {
|
||||
isFirstImage
|
||||
value={username}
|
||||
inputStyle={styles.input}
|
||||
onFocus={() => setKeyboardIsOpen(true)}
|
||||
/>
|
||||
<FormInput
|
||||
rightIconName="mail"
|
||||
@ -120,6 +145,7 @@ const RegisterScreen = ({ navigation, route }) => {
|
||||
type="emailAddress"
|
||||
value={email}
|
||||
inputStyle={styles.input}
|
||||
onFocus={() => setKeyboardIsOpen(true)}
|
||||
/>
|
||||
<FormInput
|
||||
rightIconName="person"
|
||||
@ -134,14 +160,14 @@ const RegisterScreen = ({ navigation, route }) => {
|
||||
isFirstImage
|
||||
value={refUsername}
|
||||
inputStyle={styles.input}
|
||||
onFocus={() => setKeyboardIsOpen(true)}
|
||||
/>
|
||||
<InformationArea
|
||||
description={intl.formatMessage({ id: 'register.form_description' })}
|
||||
iconName="ios-information-circle-outline"
|
||||
link="https://ecency.com/terms-of-service"
|
||||
/>
|
||||
</KeyboardAwareScrollView>
|
||||
|
||||
</View>
|
||||
<View style={styles.footerButtons}>
|
||||
<TextButton
|
||||
style={styles.cancelButton}
|
||||
@ -166,11 +192,34 @@ const RegisterScreen = ({ navigation, route }) => {
|
||||
style={styles.mainButton}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</SafeAreaView>
|
||||
)}
|
||||
</RegisterContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const { height } = getWindowDimensions();
|
||||
const bodyHeight = height / 5;
|
||||
const showAnimation = {
|
||||
from: {
|
||||
opacity: 0,
|
||||
height: 0,
|
||||
},
|
||||
to: {
|
||||
opacity: 1,
|
||||
height: bodyHeight,
|
||||
},
|
||||
};
|
||||
|
||||
const hideAnimation = {
|
||||
from: {
|
||||
opacity: 1,
|
||||
height: bodyHeight,
|
||||
},
|
||||
to: {
|
||||
opacity: 0,
|
||||
height: 0,
|
||||
},
|
||||
};
|
||||
export default RegisterScreen;
|
||||
|
@ -1,5 +1,4 @@
|
||||
import EStyleSheet from 'react-native-extended-stylesheet';
|
||||
import autoMergeLevel1 from 'redux-persist/es/stateReconciler/autoMergeLevel1';
|
||||
|
||||
export default EStyleSheet.create({
|
||||
container: {
|
||||
@ -12,12 +11,9 @@ export default EStyleSheet.create({
|
||||
},
|
||||
footerButtons: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
alignItems: 'center',
|
||||
alignSelf: 'flex-end',
|
||||
marginRight: 10,
|
||||
bottom: 24,
|
||||
right: 24,
|
||||
height: 80,
|
||||
},
|
||||
cancelButton: {
|
||||
marginRight: 10,
|
||||
@ -25,6 +21,7 @@ export default EStyleSheet.create({
|
||||
formWrapper: {
|
||||
marginHorizontal: 30,
|
||||
marginVertical: 10,
|
||||
flex: 1,
|
||||
},
|
||||
input: {
|
||||
color: '$primaryDarkText',
|
||||
|
@ -1,5 +1,9 @@
|
||||
import { Platform } from "react-native"
|
||||
|
||||
export default () => {
|
||||
return Platform.OS === 'android' && Platform.Version === 26
|
||||
return Platform.OS === 'android'
|
||||
&& (
|
||||
Platform.Version === 26
|
||||
|| Platform.Version === 27
|
||||
)
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
import { Alert } from 'react-native';
|
||||
import ROUTES from '../../src/constants/routeNames';
|
||||
import { navigate } from '../navigation/service';
|
||||
|
||||
const showLoginAlert = ({ navigation, intl }) => {
|
||||
const showLoginAlert = ({ intl }) => {
|
||||
return Alert.alert(
|
||||
intl.formatMessage({ id: 'login.not_loggedin_alert' }),
|
||||
intl.formatMessage({ id: 'login.not_loggedin_alert_desc' }),
|
||||
@ -14,7 +15,7 @@ const showLoginAlert = ({ navigation, intl }) => {
|
||||
{
|
||||
text: intl.formatMessage({ id: 'login.login' }),
|
||||
onPress: () => {
|
||||
navigation.navigate(ROUTES.SCREENS.LOGIN);
|
||||
navigate({routeName:ROUTES.SCREENS.LOGIN});
|
||||
},
|
||||
},
|
||||
],
|
||||
|
Loading…
Reference in New Issue
Block a user