Merge remote-tracking branch 'upstream/development' into nt/navigation

# Conflicts:
#	src/screens/register/registerScreen.js
This commit is contained in:
Nouman Tahir 2022-09-08 11:59:46 +05:00
commit 6580f79153
33 changed files with 761 additions and 486 deletions

View File

@ -144,7 +144,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode versionMajor * 10000 + versionMinor * 100 + versionPatch versionCode versionMajor * 10000 + versionMinor * 100 + versionPatch
versionName "3.0.33" versionName "3.0.34"
resValue "string", "build_config_package", "app.esteem.mobile.android" resValue "string", "build_config_package", "app.esteem.mobile.android"
multiDexEnabled true multiDexEnabled true
// react-native-image-crop-picker // react-native-image-crop-picker

View File

@ -10,6 +10,16 @@ public class SplashActivity extends AppCompatActivity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
Intent intent = new Intent(this, MainActivity.class); 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); startActivity(intent);
finish(); finish();
} }

View File

@ -15,11 +15,11 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>3.0.33</string> <string>3.0.34</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>2812</string> <string>2813</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true /> <true />
<key>NSAppTransportSecurity</key> <key>NSAppTransportSecurity</key>

View File

@ -15,10 +15,10 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>BNDL</string> <string>BNDL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>3.0.33</string> <string>3.0.34</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>2812</string> <string>2813</string>
</dict> </dict>
</plist> </plist>

View File

@ -1132,7 +1132,7 @@
CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_IDENTITY = "iPhone Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2812; CURRENT_PROJECT_VERSION = 2813;
DEAD_CODE_STRIPPING = NO; DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = 75B6RXTKGT; DEVELOPMENT_TEAM = 75B6RXTKGT;
EXCLUDED_ARCHS = ""; EXCLUDED_ARCHS = "";
@ -1211,7 +1211,7 @@
CODE_SIGN_ENTITLEMENTS = Ecency/Ecency.entitlements; CODE_SIGN_ENTITLEMENTS = Ecency/Ecency.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2812; CURRENT_PROJECT_VERSION = 2813;
DEAD_CODE_STRIPPING = NO; DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = 75B6RXTKGT; DEVELOPMENT_TEAM = 75B6RXTKGT;
EXCLUDED_ARCHS = ""; EXCLUDED_ARCHS = "";

View File

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>3.0.33</string> <string>3.0.34</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleURLTypes</key> <key>CFBundleURLTypes</key>
@ -62,6 +62,8 @@
</dict> </dict>
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>To access your photos, Ecency needs your permission to help you share your photos.</string> <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> <key>NSLocationAlwaysUsageDescription</key>
<string>To get accurate location for sharing</string> <string>To get accurate location for sharing</string>
<key>NSLocationWhenInUseUsageDescription</key> <key>NSLocationWhenInUseUsageDescription</key>
@ -74,8 +76,6 @@
<string>Photo Library Access for allowing user to download and upload photos</string> <string>Photo Library Access for allowing user to download and upload photos</string>
<key>NSPhotoLibraryUsageDescription</key> <key>NSPhotoLibraryUsageDescription</key>
<string>Photo Usage Access for allowing user to upload photos</string> <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> <key>UIAppFonts</key>
<array> <array>
<string>Entypo.ttf</string> <string>Entypo.ttf</string>

View File

@ -15,10 +15,10 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>BNDL</string> <string>BNDL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>3.0.33</string> <string>3.0.34</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>2812</string> <string>2813</string>
</dict> </dict>
</plist> </plist>

View File

@ -17,9 +17,9 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>3.0.33</string> <string>3.0.34</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>2812</string> <string>2813</string>
<key>NSExtension</key> <key>NSExtension</key>
<dict> <dict>
<key>NSExtensionAttributes</key> <key>NSExtensionAttributes</key>

View File

@ -1,6 +1,6 @@
{ {
"name": "ecency", "name": "ecency",
"version": "3.0.33", "version": "3.0.34",
"displayName": "Ecency", "displayName": "Ecency",
"private": true, "private": true,
"rnpm": { "rnpm": {

View File

@ -45,14 +45,15 @@ const CommentView = ({
incrementRepliesCount incrementRepliesCount
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const actionSheet = useRef(null);
const dispatch = useDispatch(); 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 isMuted = useAppSelector(state => state.account.currentAccount.mutes?.indexOf(comment.author) > -1);
const lastCacheUpdate = useAppSelector((state) => state.cache.lastUpdate); const lastCacheUpdate = useAppSelector((state) => state.cache.lastUpdate);
const cachedComments = useAppSelector((state) => state.cache.comments); const cachedComments = useAppSelector((state) => state.cache.comments);
const [_isShowSubComments, setIsShowSubComments] = useState(isShowSubComments || false); const [_isShowSubComments, setIsShowSubComments] = useState(false);
const [isPressedShowButton, setIsPressedShowButton] = useState(false); const [isPressedShowButton, setIsPressedShowButton] = useState(false);
const [activeVotes, setActiveVotes] = useState([]); const [activeVotes, setActiveVotes] = useState([]);
const [cacheVoteIcrement, setCacheVoteIcrement] = useState(0); const [cacheVoteIcrement, setCacheVoteIcrement] = useState(0);
@ -61,6 +62,16 @@ const CommentView = ({
const [replies, setReplies] = useState(comment.replies); const [replies, setReplies] = useState(comment.replies);
useEffect(()=>{
if(isShowSubComments){
setTimeout(()=>{
if(repliesContainerRef.current){
setIsShowSubComments(true);
repliesContainerRef.current.slideInRight(300);
}
},150)
}
},[])
useEffect(() => { useEffect(() => {
if (comment) { if (comment) {
@ -97,8 +108,20 @@ const CommentView = ({
const _showSubCommentsToggle = (force) => { const _showSubCommentsToggle = (force) => {
if (((replies && replies.length > 0) || force)) { if (((replies && replies.length > 0) || force)) {
setIsShowSubComments(!_isShowSubComments);
if (repliesContainerRef.current) {
if (_isShowSubComments) {
repliesContainerRef.current.slideOutRight(300).then(()=>{
setIsShowSubComments(false);
});
} else {
setIsShowSubComments(true);
repliesContainerRef.current.slideInRight(300);
}
}
setIsPressedShowButton(true); setIsPressedShowButton(true);
} else if (openReplyThread) { } else if (openReplyThread) {
openReplyThread(); openReplyThread();
} }
@ -144,8 +167,11 @@ const CommentView = ({
) )
const _renderReplies = () => { const _renderReplies = () => {
return ( return (
<AnimatedView animation="zoomIn" duration={300}> <AnimatedView ref={repliesContainerRef}>
{_isShowSubComments &&
<Comments <Comments
isShowComments={isShowComments} isShowComments={isShowComments}
commentNumber={commentNumber + 1} commentNumber={commentNumber + 1}
@ -163,7 +189,7 @@ const CommentView = ({
fetchedAt={fetchedAt} fetchedAt={fetchedAt}
incrementRepliesCount={_incrementRepliesCount} incrementRepliesCount={_incrementRepliesCount}
handleOnReplyPress={_handleOnReplyPress} handleOnReplyPress={_handleOnReplyPress}
/> />}
</AnimatedView> </AnimatedView>
) )
@ -316,7 +342,7 @@ const CommentView = ({
secondaryContentComponent={_renderComment()} secondaryContentComponent={_renderComment()}
/> />
{_isShowSubComments && commentNumber > 0 && _renderReplies()} {commentNumber > 0 && _renderReplies()}
</View> </View>
</Fragment> </Fragment>
); );

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect } from 'react';
import { Platform } from 'react-native'; import { Platform } from 'react-native';
import { withNavigation } from '@react-navigation/compat'; import { withNavigation } from '@react-navigation/compat';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
@ -19,7 +19,8 @@ import ROUTES from '../../../constants/routeNames';
// Component // Component
import CommentsView from '../view/commentsView'; import CommentsView from '../view/commentsView';
import { useAppSelector } from '../../../hooks'; import { useAppSelector } from '../../../hooks';
import { deleteCommentCacheEntry } from '../../../redux/actions/cacheActions'; import { updateCommentCache } from '../../../redux/actions/cacheActions';
import { CommentCacheStatus } from '../../../redux/reducers/cacheReducer';
const CommentsContainer = ({ const CommentsContainer = ({
author, author,
@ -66,7 +67,11 @@ const CommentsContainer = ({
}, [commentCount, selectedFilter]); }, [commentCount, selectedFilter]);
useEffect(() => { useEffect(() => {
setPropComments(comments); let _comments = comments;
if (_comments) {
_comments = _handleCachedComment(comments);
}
setPropComments(_comments);
}, [comments]); }, [comments]);
useEffect(() => { useEffect(() => {
@ -190,12 +195,15 @@ const CommentsContainer = ({
var ignoreCache = false; var ignoreCache = false;
var replaceAtIndex = -1; var replaceAtIndex = -1;
var removeAtIndex = -1;
_comments.forEach((comment, index) => { _comments.forEach((comment, index) => {
if (cachedComment.permlink === comment.permlink) { if (cachedComment.permlink === comment.permlink) {
if (cachedComment.updated < comment.updated) { if (cachedComment.updated < comment.updated) {
//comment is present with latest data //comment is present with latest data
ignoreCache = true; ignoreCache = true;
console.log('Ignore cache as comment is now present'); console.log('Ignore cache as comment is now present');
} else if (cachedComment.status === CommentCacheStatus.DELETED) {
removeAtIndex = index;
} else { } else {
//comment is present in list but data is old //comment is present in list but data is old
replaceAtIndex = index; 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 //manipulate comments with cached data
if (!ignoreCache) { if (!ignoreCache) {
let newComments = []; let newComments = [];
if (replaceAtIndex >= 0) { if (removeAtIndex >= 0) {
newComments = _comments;
newComments.splice(removeAtIndex, 1);
} else if (replaceAtIndex >= 0) {
_comments[replaceAtIndex] = cachedComment; _comments[replaceAtIndex] = cachedComment;
newComments = [..._comments]; newComments = [..._comments];
} else { } else {
@ -266,10 +282,11 @@ const CommentsContainer = ({
let filteredComments; let filteredComments;
deleteComment(currentAccount, pinCode, _permlink).then(() => { deleteComment(currentAccount, pinCode, _permlink).then(() => {
let cachePath = null; let deletedItem = null;
const _applyFilter = (item) => { const _applyFilter = (item) => {
if (item.permlink === _permlink) { if (item.permlink === _permlink) {
cachePath = `${item.parent_author}/${item.parent_permlink}`; deletedItem = item;
return false; return false;
} }
return true; return true;
@ -284,7 +301,12 @@ const CommentsContainer = ({
} }
// remove cached entry based on parent // 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 }));
}
}); });
}; };

View File

@ -18,4 +18,11 @@ export default EStyleSheet.create({
color: '$white', color: '$white',
fontSize: 10, fontSize: 10,
}, },
emptyText: {
color: '$primaryDarkGray',
fontSize: 16,
justifyContent: 'center',
marginTop: 5,
padding: 32,
},
}); });

View File

@ -1,5 +1,5 @@
import React, { useState, Fragment, useRef } from 'react'; import React, { useState, Fragment, useRef } from 'react';
import { FlatList } from 'react-native'; import { FlatList, Text } from 'react-native';
import get from 'lodash/get'; import get from 'lodash/get';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
@ -124,6 +124,20 @@ const CommentsView = ({
marginTop: 8, marginTop: 8,
} : null } : 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 ( return (
<Fragment> <Fragment>
@ -133,6 +147,7 @@ const CommentsView = ({
data={comments} data={comments}
renderItem={_renderItem} renderItem={_renderItem}
keyExtractor={(item) => get(item, 'permlink')} keyExtractor={(item) => get(item, 'permlink')}
ListEmptyComponent={_renderEmptyContent()}
{...flatListProps} {...flatListProps}
/> />
<OptionsModal <OptionsModal

View File

@ -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,
},
});

View File

@ -1,3 +0,0 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({});

View File

@ -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 { View } from 'react-native';
import { injectIntl } from 'react-intl'; import { injectIntl, useIntl } from 'react-intl';
// Components // Components
import { FilterBar } from '../../filterBar'; import { FilterBar } from '../../filterBar';
import { Comments } from '../../comments'; import { Comments } from '../../comments';
import COMMENT_FILTER, { VALUE } from '../../../constants/options/comment'; import COMMENT_FILTER, { VALUE } from '../../../constants/options/comment';
import { WriteCommentButton } from './writeCommentButton';
// Styles const CommentsDisplayView = forwardRef(
import styles from './commentDisplayStyles'; (
{
const CommentsDisplayView = ({
author, author,
commentCount, commentCount,
fetchPost, fetchPost,
intl,
permlink, permlink,
mainAuthor, mainAuthor,
handleOnVotersPress, handleOnVotersPress,
handleOnReplyPress, handleOnReplyPress,
fetchedAt, fetchedAt,
}) => { },
ref,
) => {
const intl = useIntl();
const writeCommentRef = useRef(null);
const [selectedFilter, setSelectedFilter] = useState('trending'); const [selectedFilter, setSelectedFilter] = useState('trending');
const [selectedOptionIndex, setSelectedOptionIndex] = useState(0); const [selectedOptionIndex, setSelectedOptionIndex] = useState(0);
useImperativeHandle(ref, () => ({
bounceCommentButton: () => {
console.log('bouncing comment button');
if (writeCommentRef.current) {
writeCommentRef.current.bounce();
}
},
}));
const _handleOnDropdownSelect = (option, index) => { const _handleOnDropdownSelect = (option, index) => {
setSelectedFilter(option); setSelectedFilter(option);
setSelectedOptionIndex(index); setSelectedOptionIndex(index);
@ -31,8 +45,8 @@ const CommentsDisplayView = ({
return ( return (
<Fragment> <Fragment>
{commentCount > 0 && (
<Fragment> <Fragment>
<WriteCommentButton ref={writeCommentRef} onPress={handleOnReplyPress} />
<FilterBar <FilterBar
dropdownIconName="arrow-drop-down" dropdownIconName="arrow-drop-down"
options={VALUE.map((val) => intl.formatMessage({ id: `comment_filter.${val}` }))} options={VALUE.map((val) => intl.formatMessage({ id: `comment_filter.${val}` }))}
@ -56,9 +70,9 @@ const CommentsDisplayView = ({
/> />
</View> </View>
</Fragment> </Fragment>
)}
</Fragment> </Fragment>
); );
}; },
);
export default injectIntl(CommentsDisplayView); export default CommentsDisplayView;

View 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>
)
})

View File

@ -1,6 +1,7 @@
import { get, isEmpty, some } from 'lodash'; import { get } from 'lodash';
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { Animated, Text, TouchableOpacity, View } from 'react-native'; import { Text, TouchableOpacity, View } from 'react-native';
import { View as AnimatedView } from 'react-native-animatable';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import { IconButton } from '..'; import { IconButton } from '..';
import { toastNotification } from '../../redux/actions/uiAction'; import { toastNotification } from '../../redux/actions/uiAction';
@ -35,10 +36,11 @@ interface Props {
const ForegroundNotification = ({ remoteMessage }: Props) => { const ForegroundNotification = ({ remoteMessage }: Props) => {
let hideTimeout = null;
const dispatch = useDispatch();
const intl = useIntl(); const intl = useIntl();
const hideTimeoutRef = useRef<any>(null);
const containerRef = useRef<AnimatedView>(null);
const [duration] = useState(5000); const [duration] = useState(5000);
const [activeId, setActiveId] = useState(''); const [activeId, setActiveId] = useState('');
const [isVisible, setIsVisible] = useState(false); const [isVisible, setIsVisible] = useState(false);
@ -47,9 +49,6 @@ const ForegroundNotification = ({remoteMessage}:Props) => {
const [body, setBody] = useState(''); const [body, setBody] = useState('');
const animatedValue = useRef(new Animated.Value(-CONTAINER_HEIGHT)).current;
useEffect(() => { useEffect(() => {
if (remoteMessage) { if (remoteMessage) {
@ -75,39 +74,29 @@ const ForegroundNotification = ({remoteMessage}:Props) => {
} }
return () => { return () => {
if(hideTimeout){ if (hideTimeoutRef.current) {
clearTimeout(hideTimeout); clearTimeout(hideTimeoutRef.current);
} }
} }
}, [remoteMessage]); }, [remoteMessage]);
const show = () => { const show = () => {
// Will change fadeAnim value to 1 in 5 seconds setIsVisible(true)
Animated.timing(animatedValue, { hideTimeoutRef.current = setTimeout(() => {
toValue: 0,
duration: 350
}).start();
setIsVisible(true);
hideTimeout = setTimeout(()=>{
hide(); hide();
}, duration) }, duration)
}; };
const hide = () => { const hide = async () => {
if(hideTimeout || isVisible){ if (containerRef.current) {
// Will change fadeAnim value to 0 in 3 seconds await containerRef.current.fadeOutUp(300);
Animated.timing(animatedValue, {
toValue: -CONTAINER_HEIGHT,
duration: 200
}).start(()=>{
dispatch(toastNotification(''))
});
setIsVisible(false); setIsVisible(false);
clearTimeout(hideTimeout); if(hideTimeoutRef.current){
clearTimeout(hideTimeoutRef.current);
}
} }
}; };
@ -118,7 +107,7 @@ const ForegroundNotification = ({remoteMessage}:Props) => {
get(data, 'permlink1', '') + get(data, 'permlink2', '') + get(data, 'permlink3', ''); get(data, 'permlink1', '') + get(data, 'permlink2', '') + get(data, 'permlink3', '');
let params = { let params = {
author: get(remoteMessage, 'source', ''), author: get(data, 'source', ''),
permlink: fullPermlink, permlink: fullPermlink,
}; };
let key = fullPermlink let key = fullPermlink
@ -129,17 +118,19 @@ const ForegroundNotification = ({remoteMessage}:Props) => {
params, params,
key, key,
}); });
hide();
} }
return ( return (
<Animated.View isVisible &&
style={{ <AnimatedView
...styles.container, ref={containerRef}
transform: [{ translateY: animatedValue }], style={styles.container}
}} animation='slideInDown'
> duration={500}>
<View style={styles.contentContainer}> <View style={styles.contentContainer}>
<TouchableOpacity onPress={_onPress} style={{ flexShrink: 1 }}> <TouchableOpacity onPress={_onPress} style={{ flexShrink: 1 }}>
@ -153,8 +144,6 @@ const ForegroundNotification = ({remoteMessage}:Props) => {
</View> </View>
</TouchableOpacity> </TouchableOpacity>
<IconButton <IconButton
name='close' name='close'
color="white" color="white"
@ -162,8 +151,7 @@ const ForegroundNotification = ({remoteMessage}:Props) => {
onPress={hide} onPress={hide}
/> />
</View> </View>
</AnimatedView>
</Animated.View>
) )
} }

View File

@ -16,7 +16,7 @@ export default EStyleSheet.create({
ios:getStatusBarHeight() + 12, ios:getStatusBarHeight() + 12,
android:8, android:8,
}), }),
backgroundColor: '$primaryDarkText', backgroundColor: '$darkGrayBackground',
shadowColor: '#5f5f5fbf', shadowColor: '#5f5f5fbf',
shadowOpacity: 0.3, shadowOpacity: 0.3,
shadowOffset: { shadowOffset: {

View File

@ -26,6 +26,8 @@ interface Props extends TextInputProps {
inputStyle:TextStyle; inputStyle:TextStyle;
isValid:boolean; isValid:boolean;
onChange?:(value:string)=>void; onChange?:(value:string)=>void;
onFocus?:()=>void;
onBlur?:()=>void;
} }
const FormInputView = ({ const FormInputView = ({
@ -44,6 +46,7 @@ const FormInputView = ({
isValid, isValid,
value, value,
onBlur, onBlur,
onFocus,
...props ...props
}:Props) => { }:Props) => {
const [_value, setValue] = useState(value || ''); const [_value, setValue] = useState(value || '');
@ -62,6 +65,9 @@ const FormInputView = ({
const _handleOnFocus = () => { const _handleOnFocus = () => {
setInputBorderColor('#357ce6'); setInputBorderColor('#357ce6');
if(onFocus){
onFocus();
}
}; };
const _handleOnBlur = () => { const _handleOnBlur = () => {

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { View as AnimatedView } from 'react-native-animatable' import { View as AnimatedView } from 'react-native-animatable'
import { Portal } from 'react-native-portalize'; import { Portal } from 'react-native-portalize';
import styles from '../children/inputSupportModal.styles'; import styles from '../children/inputSupportModal.styles';
@ -12,32 +12,56 @@ export interface InputSupportModalProps {
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);
return ( 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 showModal && (
<Portal> <Portal>
{
visible && (
<AnimatedView <AnimatedView
style={styles.container} ref={container}
animation='fadeIn'
duration={300} duration={300}
animation='fadeInUp'> style={styles.container} >
<>
<View style={styles.container} onTouchEnd={onClose} /> <AnimatedView
ref={innerContainer}
style={{ flex: 1 }}
animation='slideInUp'
duration={300}>
<View
style={{ flex: 1 }}
onTouchEnd={onClose} />
{ {
Platform.select({ Platform.select({
ios: ( ios: (
<KeyboardAvoidingView behavior="padding" style={{backgroundColor: 'rgba(0, 0, 0, 0.2)'}}> <KeyboardAvoidingView behavior="padding" style={{}}>
{children} {children}
</KeyboardAvoidingView> </KeyboardAvoidingView>
), ),
android: <View>{children}</View>, android: <View>{children}</View>,
}) })
} }
</>
</AnimatedView> </AnimatedView>
) </AnimatedView>
}
</Portal> </Portal>
); );
}; };

View File

@ -1,4 +1,3 @@
import { Dimensions } from 'react-native';
import EStyleSheet from 'react-native-extended-stylesheet'; import EStyleSheet from 'react-native-extended-stylesheet';
import getWindowDimensions from '../../../utils/getWindowDimensions'; import getWindowDimensions from '../../../utils/getWindowDimensions';
@ -14,7 +13,8 @@ export default EStyleSheet.create({
backgroundColor: '$primaryBackgroundColor', backgroundColor: '$primaryBackgroundColor',
}, },
headerLine: { headerLine: {
bottom: 10, marginTop: -4,
marginBottom: 4,
}, },
title: { title: {
fontSize: 24, fontSize: 24,

View File

@ -1,10 +1,9 @@
import React, { useCallback, useEffect, useRef, useState, Fragment } from 'react'; 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 { injectIntl } from 'react-intl';
import get from 'lodash/get'; import get from 'lodash/get';
// Providers // Providers
import { useSelector } from 'react-redux';
import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { userActivity } from '../../../providers/ecency/ePoint'; import { userActivity } from '../../../providers/ecency/ePoint';
@ -22,7 +21,6 @@ import { ParentPost } from '../../parentPost';
// Styles // Styles
import styles from './postDisplayStyles'; import styles from './postDisplayStyles';
import { OptionsModal } from '../../atoms'; import { OptionsModal } from '../../atoms';
import { QuickReplyModal } from '../..';
import getWindowDimensions from '../../../utils/getWindowDimensions'; import getWindowDimensions from '../../../utils/getWindowDimensions';
import { useAppDispatch } from '../../../hooks'; import { useAppDispatch } from '../../../hooks';
import { showReplyModal } from '../../../redux/actions/uiAction'; import { showReplyModal } from '../../../redux/actions/uiAction';
@ -37,7 +35,6 @@ const PostDisplayView = ({
isNewPost, isNewPost,
fetchPost, fetchPost,
handleOnEditPress, handleOnEditPress,
handleOnReplyPress,
handleOnVotersPress, handleOnVotersPress,
handleOnReblogsPress, handleOnReblogsPress,
post, post,
@ -53,6 +50,11 @@ const PostDisplayView = ({
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const commentsRef = useRef<CommentsDisplay>();
const scrollRef = useRef<ScrollView>();
const commentsReached = useRef<boolean>(false);
const [postHeight, setPostHeight] = useState(0); const [postHeight, setPostHeight] = useState(0);
const [scrollHeight, setScrollHeight] = useState(0); const [scrollHeight, setScrollHeight] = useState(0);
const [cacheVoteIcrement, setCacheVoteIcrement] = useState(0); const [cacheVoteIcrement, setCacheVoteIcrement] = useState(0);
@ -87,16 +89,30 @@ const PostDisplayView = ({
const _handleOnScroll = (event) => { const _handleOnScroll = (event) => {
const { y } = event.nativeEvent.contentOffset; const { y } = event.nativeEvent.contentOffset;
console.log("scroll height", y)
setScrollHeight(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 _handleOnPostLayout = (event) => {
const { height } = event.nativeEvent.layout; const { height } = event.nativeEvent.layout;
console.log('post height', height)
setPostHeight(height); setPostHeight(height);
}; };
const _scrollToComments = () => {
if(scrollRef.current){
const pos = postHeight;
scrollRef.current.scrollTo({y:pos})
}
}
const _handleOnReblogsPress = () => { const _handleOnReblogsPress = () => {
if (reblogs.length > 0 && handleOnReblogsPress) { if (reblogs.length > 0 && handleOnReblogsPress) {
handleOnReblogsPress(); handleOnReblogsPress();
@ -144,7 +160,7 @@ const PostDisplayView = ({
isClickable isClickable
text={get(post, 'children', 0)} text={get(post, 'children', 0)}
textMarginLeft={20} textMarginLeft={20}
onPress={() => _showQuickReplyModal(post)} onPress={() => _scrollToComments()}
// onPress={() => handleOnReplyPress && handleOnReplyPress()} // onPress={() => handleOnReplyPress && handleOnReplyPress()}
/> />
)} )}
@ -211,9 +227,9 @@ const PostDisplayView = ({
}; };
// show quick reply modal // show quick reply modal
const _showQuickReplyModal = (post) => { const _showQuickReplyModal = (_post = post) => {
if (isLoggedIn) { if (isLoggedIn) {
dispatch(showReplyModal(post)); dispatch(showReplyModal(_post));
} else { } else {
console.log('Not LoggedIn'); console.log('Not LoggedIn');
} }
@ -222,6 +238,7 @@ const PostDisplayView = ({
return ( return (
<View style={styles.container}> <View style={styles.container}>
<ScrollView <ScrollView
ref={scrollRef}
style={styles.scroll} style={styles.scroll}
contentContainerStyle={[styles.scrollContent]} contentContainerStyle={[styles.scrollContent]}
onScroll={(event) => _handleOnScroll(event)} onScroll={(event) => _handleOnScroll(event)}
@ -262,7 +279,9 @@ const PostDisplayView = ({
)} )}
</View> </View>
{post && !postBodyLoading && (isGetComment || isLoadedComments) && ( {post && !postBodyLoading && (isGetComment || isLoadedComments) && (
<CommentsDisplay <CommentsDisplay
ref={commentsRef}
author={author || post.author} author={author || post.author}
mainAuthor={author || post.author} mainAuthor={author || post.author}
permlink={post.permlink} permlink={post.permlink}

View File

@ -724,7 +724,8 @@
"title": "Comments", "title": "Comments",
"reveal_comment": "Reveal comment", "reveal_comment": "Reveal comment",
"read_more": "Read more comments", "read_more": "Read more comments",
"more_replies": "replies" "more_replies": "replies",
"no_comments":"Be the first to respond..."
}, },
"search_result": { "search_result": {
"others": "Others", "others": "Others",
@ -817,7 +818,7 @@
"year":"years" "year":"years"
}, },
"quick_reply":{ "quick_reply":{
"placeholder":"Add a comment", "placeholder":"Add a comment...",
"comment": "Comment", "comment": "Comment",
"reply": "REPLY", "reply": "REPLY",
"close":"CLOSE" "close":"CLOSE"

View File

@ -252,7 +252,7 @@
"feedback_fail": "Ocurrió un error al intentar abrir el cliente del correo electrónico", "feedback_fail": "Ocurrió un error al intentar abrir el cliente del correo electrónico",
"server_fail": "Servidor no disponible", "server_fail": "Servidor no disponible",
"show_imgs": "Mostrar imágenes", "show_imgs": "Mostrar imágenes",
"delete_account": "Delete Account" "delete_account": "Eliminar cuenta"
}, },
"voters": { "voters": {
"voters_info": "Información de votantes", "voters_info": "Información de votantes",
@ -550,9 +550,9 @@
"confirm_report_body": "¿Estás seguro de que quieres reportar?" "confirm_report_body": "¿Estás seguro de que quieres reportar?"
}, },
"delete": { "delete": {
"confirm_delete_title": "Confirm Delete", "confirm_delete_title": "Confirmar eliminación",
"confirm_delete_body": "Are you sure you want to delete account? It will erase all of your data", "confirm_delete_body": "¿Estás seguro de que deseas eliminar la cuenta? Se borrarán todos tus datos",
"request_sent": "Your request for deletion is sent" "request_sent": "Su solicitud de eliminación está enviada"
}, },
"favorites": { "favorites": {
"title": "Favoritos", "title": "Favoritos",

View File

@ -252,7 +252,7 @@
"feedback_fail": "Sähköpostipalvelin alhaalla", "feedback_fail": "Sähköpostipalvelin alhaalla",
"server_fail": "Ei yhteyttä palvelimeen", "server_fail": "Ei yhteyttä palvelimeen",
"show_imgs": "Näytä kuvat", "show_imgs": "Näytä kuvat",
"delete_account": "Delete Account" "delete_account": "Poista tili"
}, },
"voters": { "voters": {
"voters_info": "Äänestystiedot", "voters_info": "Äänestystiedot",
@ -269,8 +269,8 @@
"login": "KIRJAUDU SISÄÄN", "login": "KIRJAUDU SISÄÄN",
"steemconnect_description": "Jos et halua säilyttää kryptattua salasanaa laitteessasi, voit käyttää Hivesigneriä.", "steemconnect_description": "Jos et halua säilyttää kryptattua salasanaa laitteessasi, voit käyttää Hivesigneriä.",
"steemconnect_fee_description": "tietoa", "steemconnect_fee_description": "tietoa",
"not_loggedin_alert": "Not LoggedIn", "not_loggedin_alert": "Ei Kirjautunut",
"not_loggedin_alert_desc": "Please login first" "not_loggedin_alert_desc": "Kirjaudu ensin sisään"
}, },
"register": { "register": {
"button": "Luo tili", "button": "Luo tili",
@ -550,9 +550,9 @@
"confirm_report_body": "Haluatko varmasti tehdä ilmiannon?" "confirm_report_body": "Haluatko varmasti tehdä ilmiannon?"
}, },
"delete": { "delete": {
"confirm_delete_title": "Confirm Delete", "confirm_delete_title": "Vahvista poistaminen",
"confirm_delete_body": "Are you sure you want to delete account? It will erase all of your data", "confirm_delete_body": "Oletko varma, että haluat poistaa tilin? Se poistaa kaikki tietosi",
"request_sent": "Your request for deletion is sent" "request_sent": "Pyyntösi poistamisesta on lähetetty"
}, },
"favorites": { "favorites": {
"title": "Suosikit", "title": "Suosikit",
@ -592,7 +592,7 @@
"pin-community": "Kiinnitä yhteisölle", "pin-community": "Kiinnitä yhteisölle",
"unpin-community": "Poista kiinnitys yhteisöstä", "unpin-community": "Poista kiinnitys yhteisöstä",
"edit-history": "Muokkaushistoria", "edit-history": "Muokkaushistoria",
"mute": "Mute / Block" "mute": "Mykistä / Estä"
}, },
"deep_link": { "deep_link": {
"no_existing_user": "Käyttäjää ei ole", "no_existing_user": "Käyttäjää ei ole",

View File

@ -10,9 +10,9 @@ import {
DELETE_DRAFT_CACHE_ENTRY, DELETE_DRAFT_CACHE_ENTRY,
UPDATE_SUBSCRIBED_COMMUNITY_CACHE, UPDATE_SUBSCRIBED_COMMUNITY_CACHE,
DELETE_SUBSCRIBED_COMMUNITY_CACHE, DELETE_SUBSCRIBED_COMMUNITY_CACHE,
CLEAR_SUBSCRIBED_COMMUNITIES_CACHE, CLEAR_SUBSCRIBED_COMMUNITIES_CACHE
} from '../constants/constants'; } 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"); 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.created = comment.created || updatedStamp; //created will be set only once for new comment;
comment.updated = comment.updated || updatedStamp; comment.updated = comment.updated || updatedStamp;
comment.expiresAt = comment.expiresAt || updated.getTime() + 6000000;//600000; 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.total_payout = comment.total_payout || 0;
comment.json_metadata = comment.json_metadata || makeJsonMetadataReply(options.parentTags) comment.json_metadata = comment.json_metadata || makeJsonMetadataReply(options.parentTags)
comment.isDeletable = comment.isDeletable || true; comment.isDeletable = comment.isDeletable || true;
comment.status = comment.status || CommentCacheStatus.PENDING;
comment.body = renderPostBody({ comment.body = renderPostBody({
author: comment.author, author: comment.author,

View File

@ -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 { export interface Vote {
amount: number; amount: number;
@ -24,6 +30,7 @@ export interface Comment {
created?: string, //handle created and updated separatly created?: string, //handle created and updated separatly
updated?: string, updated?: string,
expiresAt?: number, expiresAt?: number,
status: CommentCacheStatus
} }
export interface Draft { export interface Draft {

View File

@ -18,7 +18,6 @@ import SplashScreen from 'react-native-splash-screen'
// Constants // Constants
import AUTH_TYPE from '../../../constants/authType'; import AUTH_TYPE from '../../../constants/authType';
import ROUTES from '../../../constants/routeNames'; import ROUTES from '../../../constants/routeNames';
import postUrlParser from '../../../utils/postUrlParser';
// Services // Services
import { import {
@ -34,7 +33,7 @@ import {
setLastUpdateCheck, setLastUpdateCheck,
getTheme, getTheme,
} from '../../../realm/realm'; } 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 { getPointsSummary } from '../../../providers/ecency/ePoint';
import { import {
migrateToMasterKeyWithAccessToken, migrateToMasterKeyWithAccessToken,
@ -62,7 +61,6 @@ import {
login, login,
logoutDone, logoutDone,
setConnectivityStatus, setConnectivityStatus,
setAnalyticsStatus,
setPinCode as savePinCode, setPinCode as savePinCode,
isRenderRequired, isRenderRequired,
logout, logout,
@ -85,21 +83,12 @@ import lightTheme from '../../../themes/lightTheme';
import persistAccountGenerator from '../../../utils/persistAccountGenerator'; import persistAccountGenerator from '../../../utils/persistAccountGenerator';
import parseVersionNumber from '../../../utils/parseVersionNumber'; import parseVersionNumber from '../../../utils/parseVersionNumber';
import { setMomentLocale } from '../../../utils/time'; import { setMomentLocale } from '../../../utils/time';
import parseAuthUrl from '../../../utils/parseAuthUrl';
import { purgeExpiredCache } from '../../../redux/actions/cacheActions'; import { purgeExpiredCache } from '../../../redux/actions/cacheActions';
import { fetchSubscribedCommunities } from '../../../redux/actions/communitiesAction'; import { fetchSubscribedCommunities } from '../../../redux/actions/communitiesAction';
import MigrationHelpers from '../../../utils/migrationHelpers'; import MigrationHelpers from '../../../utils/migrationHelpers';
import { deepLinkParser } from '../../../utils/deepLinkParser'; 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 firebaseOnNotificationOpenedAppListener = null;
let firebaseOnMessageListener = null; let firebaseOnMessageListener = null;
@ -131,7 +120,6 @@ class ApplicationContainer extends Component {
}); });
AppState.addEventListener('change', this._handleAppStateChange); AppState.addEventListener('change', this._handleAppStateChange);
setPreviousAppState();
this.removeAppearanceListener = Appearance.addChangeListener(this._appearanceChangeListener); this.removeAppearanceListener = Appearance.addChangeListener(this._appearanceChangeListener);
@ -338,7 +326,7 @@ class ApplicationContainer extends Component {
if (appState.match(/active|forground/) && nextAppState === 'inactive') { if (appState.match(/active|forground/) && nextAppState === 'inactive') {
this._startPinCodeTimer(); this._startPinCodeTimer();
} }
setPreviousAppState();
this.setState({ this.setState({
appState: nextAppState, appState: nextAppState,
}); });
@ -375,7 +363,7 @@ class ApplicationContainer extends Component {
let key = null; let key = null;
let routeName = null; let routeName = null;
if (previousAppState !== 'active' && !!notification) { if (!!notification) {
const push = get(notification, 'data'); const push = get(notification, 'data');
const type = get(push, 'type', ''); const type = get(push, 'type', '');
const fullPermlink = const fullPermlink =
@ -477,11 +465,11 @@ class ApplicationContainer extends Component {
firebaseOnMessageListener = messaging().onMessage((remoteMessage) => { firebaseOnMessageListener = messaging().onMessage((remoteMessage) => {
console.log('Notification Received: foreground', remoteMessage); console.log('Notification Received: foreground', remoteMessage);
// this._showNotificationToast(remoteMessage);
this.setState({ this.setState({
foregroundNotificationData: remoteMessage, foregroundNotificationData: remoteMessage,
}); });
this._pushNavigate(remoteMessage);
}); });
firebaseOnNotificationOpenedAppListener = messaging().onNotificationOpenedApp( firebaseOnNotificationOpenedAppListener = messaging().onNotificationOpenedApp(
@ -673,25 +661,39 @@ class ApplicationContainer extends Component {
//update notification settings and update push token for each signed accoutn useing access tokens //update notification settings and update push token for each signed accoutn useing access tokens
_registerDeviceForNotifications = (settings?: any) => { _registerDeviceForNotifications = (settings?: any) => {
const { otherAccounts, notificationDetails, isNotificationsEnabled } = this.props; const { currentAccount, otherAccounts, notificationDetails, isNotificationsEnabled } = this.props;
const isEnabled = settings ? !!settings.notification : isNotificationsEnabled; const isEnabled = settings ? !!settings.notification : isNotificationsEnabled;
settings = settings || notificationDetails; settings = settings || notificationDetails;
const _enabledNotificationForAccount = (account) => {
//updateing fcm token with settings;
otherAccounts.forEach((account) => {
//since there can be more than one accounts, process access tokens separate
const encAccessToken = account?.local?.accessToken; const encAccessToken = account?.local?.accessToken;
//decrypt access token //decrypt access token
let accessToken = null; let accessToken = null;
if (encAccessToken) { if (encAccessToken) {
//NOTE: default pin decryption works also for custom pin as other account //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 //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); 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)
}
}
}); });
}; };

View File

@ -1,8 +1,16 @@
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import { View, StatusBar, Platform, Image, Text, SafeAreaView } from 'react-native'; import {
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; View,
StatusBar,
Platform,
Image,
Text,
SafeAreaView,
Keyboard,
KeyboardAvoidingView,
} from 'react-native';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import * as Animatable from 'react-native-animatable';
import RegisterContainer from './registerContainer'; import RegisterContainer from './registerContainer';
// Internal Components // Internal Components
@ -16,6 +24,7 @@ import styles from './registerStyles';
import ESTEEM_LOGO from '../../assets/like_new.png'; import ESTEEM_LOGO from '../../assets/like_new.png';
import ESTEEM_SMALL_LOGO from '../../assets/ecency_logo_transparent.png'; import ESTEEM_SMALL_LOGO from '../../assets/ecency_logo_transparent.png';
import getWindowDimensions from '../../utils/getWindowDimensions';
const RegisterScreen = ({ navigation, route }) => { const RegisterScreen = ({ navigation, route }) => {
const intl = useIntl(); const intl = useIntl();
@ -27,6 +36,19 @@ const RegisterScreen = ({ navigation, route }) => {
const [refUsername, setRefUsername] = useState(route.params?.referredUser ?? ''); const [refUsername, setRefUsername] = useState(route.params?.referredUser ?? '');
const [isRefUsernameValid, setIsRefUsernameValid] = useState(true); 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 _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,}))$/; 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)); setIsEmailValid(re.test(value));
@ -74,7 +96,11 @@ const RegisterScreen = ({ navigation, route }) => {
/> />
</View> </View>
</View> </View>
{!keyboardIsOpen && ( <Animatable.View
animation={keyboardIsOpen ? hideAnimation : showAnimation}
delay={0}
duration={300}
>
<View style={styles.header}> <View style={styles.header}>
<View style={styles.titleText}> <View style={styles.titleText}>
<Text style={styles.title}>{intl.formatMessage({ id: 'register.title' })}</Text> <Text style={styles.title}>{intl.formatMessage({ id: 'register.title' })}</Text>
@ -84,15 +110,13 @@ const RegisterScreen = ({ navigation, route }) => {
</View> </View>
<Image style={styles.mascot} source={ESTEEM_LOGO} /> <Image style={styles.mascot} source={ESTEEM_LOGO} />
</View> </View>
)} </Animatable.View>
<View style={styles.body}> <KeyboardAvoidingView
<KeyboardAwareScrollView behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
onKeyboardWillShow={() => setKeyboardIsOpen(true)} style={styles.formWrapper}
onKeyboardWillHide={() => setKeyboardIsOpen(false)} keyboardShouldPersistTaps
enableAutoAutomaticScroll={Platform.OS === 'ios'}
contentContainerStyle={styles.formWrapper}
enableOnAndroid={true}
> >
<View style={styles.body}>
<FormInput <FormInput
rightIconName="at" rightIconName="at"
leftIconName="close" leftIconName="close"
@ -107,6 +131,7 @@ const RegisterScreen = ({ navigation, route }) => {
isFirstImage isFirstImage
value={username} value={username}
inputStyle={styles.input} inputStyle={styles.input}
onFocus={() => setKeyboardIsOpen(true)}
/> />
<FormInput <FormInput
rightIconName="mail" rightIconName="mail"
@ -120,6 +145,7 @@ const RegisterScreen = ({ navigation, route }) => {
type="emailAddress" type="emailAddress"
value={email} value={email}
inputStyle={styles.input} inputStyle={styles.input}
onFocus={() => setKeyboardIsOpen(true)}
/> />
<FormInput <FormInput
rightIconName="person" rightIconName="person"
@ -134,14 +160,14 @@ const RegisterScreen = ({ navigation, route }) => {
isFirstImage isFirstImage
value={refUsername} value={refUsername}
inputStyle={styles.input} inputStyle={styles.input}
onFocus={() => setKeyboardIsOpen(true)}
/> />
<InformationArea <InformationArea
description={intl.formatMessage({ id: 'register.form_description' })} description={intl.formatMessage({ id: 'register.form_description' })}
iconName="ios-information-circle-outline" iconName="ios-information-circle-outline"
link="https://ecency.com/terms-of-service" link="https://ecency.com/terms-of-service"
/> />
</KeyboardAwareScrollView> </View>
<View style={styles.footerButtons}> <View style={styles.footerButtons}>
<TextButton <TextButton
style={styles.cancelButton} style={styles.cancelButton}
@ -166,11 +192,34 @@ const RegisterScreen = ({ navigation, route }) => {
style={styles.mainButton} style={styles.mainButton}
/> />
</View> </View>
</View> </KeyboardAvoidingView>
</SafeAreaView> </SafeAreaView>
)} )}
</RegisterContainer> </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; export default RegisterScreen;

View File

@ -1,5 +1,4 @@
import EStyleSheet from 'react-native-extended-stylesheet'; import EStyleSheet from 'react-native-extended-stylesheet';
import autoMergeLevel1 from 'redux-persist/es/stateReconciler/autoMergeLevel1';
export default EStyleSheet.create({ export default EStyleSheet.create({
container: { container: {
@ -12,12 +11,9 @@ export default EStyleSheet.create({
}, },
footerButtons: { footerButtons: {
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'center', justifyContent: 'flex-end',
alignItems: 'center', alignItems: 'center',
alignSelf: 'flex-end', height: 80,
marginRight: 10,
bottom: 24,
right: 24,
}, },
cancelButton: { cancelButton: {
marginRight: 10, marginRight: 10,
@ -25,6 +21,7 @@ export default EStyleSheet.create({
formWrapper: { formWrapper: {
marginHorizontal: 30, marginHorizontal: 30,
marginVertical: 10, marginVertical: 10,
flex: 1,
}, },
input: { input: {
color: '$primaryDarkText', color: '$primaryDarkText',

View File

@ -1,5 +1,9 @@
import { Platform } from "react-native" import { Platform } from "react-native"
export default () => { export default () => {
return Platform.OS === 'android' && Platform.Version === 26 return Platform.OS === 'android'
&& (
Platform.Version === 26
|| Platform.Version === 27
)
} }

View File

@ -1,7 +1,8 @@
import { Alert } from 'react-native'; import { Alert } from 'react-native';
import ROUTES from '../../src/constants/routeNames'; import ROUTES from '../../src/constants/routeNames';
import { navigate } from '../navigation/service';
const showLoginAlert = ({ navigation, intl }) => { const showLoginAlert = ({ intl }) => {
return Alert.alert( return Alert.alert(
intl.formatMessage({ id: 'login.not_loggedin_alert' }), intl.formatMessage({ id: 'login.not_loggedin_alert' }),
intl.formatMessage({ id: 'login.not_loggedin_alert_desc' }), intl.formatMessage({ id: 'login.not_loggedin_alert_desc' }),
@ -14,7 +15,7 @@ const showLoginAlert = ({ navigation, intl }) => {
{ {
text: intl.formatMessage({ id: 'login.login' }), text: intl.formatMessage({ id: 'login.login' }),
onPress: () => { onPress: () => {
navigation.navigate(ROUTES.SCREENS.LOGIN); navigate({routeName:ROUTES.SCREENS.LOGIN});
}, },
}, },
], ],