Merge pull request #1973 from ecency/nt/avatar-update-bug

Nt/avatar update bug
This commit is contained in:
Feruz M 2021-06-16 11:32:16 +03:00 committed by GitHub
commit 9a117c5e85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 311 additions and 175 deletions

View File

@ -2,9 +2,11 @@ import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({ export default EStyleSheet.create({
headerContainer: { headerContainer: {
height: 100,
flexDirection: 'row', flexDirection: 'row',
padding: 21, paddingTop: 8,
paddingHorizontal: 24,
paddingBottom: 24,
alignItems: 'center',
}, },
backIcon: { backIcon: {
color: '$white', color: '$white',
@ -16,7 +18,7 @@ export default EStyleSheet.create({
alignItems: 'center', alignItems: 'center',
}, },
textWrapper: { textWrapper: {
marginLeft: 16, marginLeft: 24,
}, },
name: { name: {
color: '$white', color: '$white',

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { withNavigation } from 'react-navigation'; import { withNavigation } from 'react-navigation';
import { View, Text, SafeAreaView } from 'react-native'; import { View, Text, SafeAreaView, TouchableOpacity } from 'react-native';
import LinearGradient from 'react-native-linear-gradient'; import LinearGradient from 'react-native-linear-gradient';
import { UserAvatar } from '../userAvatar'; import { UserAvatar } from '../userAvatar';
@ -16,6 +16,7 @@ const AvatarHeader = ({
navigation, navigation,
avatarUrl, avatarUrl,
showImageUploadActions, showImageUploadActions,
isUploading,
}) => ( }) => (
<LinearGradient <LinearGradient
start={{ x: 0, y: 0 }} start={{ x: 0, y: 0 }}
@ -33,15 +34,25 @@ const AvatarHeader = ({
size={25} size={25}
/> />
<View style={styles.wrapper}> <View style={styles.wrapper}>
<UserAvatar key={avatarUrl || username} noAction size="xl" username={username} /> <TouchableOpacity onPress={showImageUploadActions}>
<IconButton <UserAvatar
iconStyle={styles.addIcon} key={`${avatarUrl}-${username}`}
style={styles.addButton} noAction
iconType="MaterialCommunityIcons" size="xl"
name="plus" username={username}
onPress={showImageUploadActions} avatarUrl={avatarUrl}
size={15} isLoading={isUploading}
/> />
<IconButton
iconStyle={styles.addIcon}
style={styles.addButton}
iconType="MaterialCommunityIcons"
name="plus"
onPress={showImageUploadActions}
size={15}
/>
</TouchableOpacity>
<View style={styles.textWrapper}> <View style={styles.textWrapper}>
{!!name && <Text style={styles.name}>{name}</Text>} {!!name && <Text style={styles.name}>{name}</Text>}
<Text style={styles.username}>{`@${username} (${reputation})`}</Text> <Text style={styles.username}>{`@${username} (${reputation})`}</Text>

View File

@ -13,6 +13,7 @@ export default EStyleSheet.create({
marginTop: 8, marginTop: 8,
}, },
label: { label: {
marginTop:8,
fontSize: 14, fontSize: 14,
color: '$primaryDarkText', color: '$primaryDarkText',
fontWeight: '500', fontWeight: '500',
@ -25,7 +26,7 @@ export default EStyleSheet.create({
height: 60, height: 60,
marginBottom: 12, marginBottom: 12,
alignSelf: 'stretch', alignSelf: 'stretch',
backgroundColor: '#296CC0', backgroundColor: '$primaryGray',
}, },
coverImageWrapper: {}, coverImageWrapper: {},
addIcon: { addIcon: {
@ -62,13 +63,19 @@ export default EStyleSheet.create({
}, },
input: { input: {
fontSize: 14, fontSize: 20,
color: '$primaryDarkText', color: '$primaryBlack',
alignSelf: 'flex-start', alignSelf: 'flex-start',
width: '100%', width: '100%',
height: 40, paddingBottom:10,
}, },
contentContainer: { contentContainer: {
flexGrow: 1, flexGrow: 1,
}, },
activityIndicator: {
position:'absolute',
alignSelf:'center',
top:0,
bottom:8
}
}); });

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import { withNavigation } from 'react-navigation'; import { withNavigation } from 'react-navigation';
import { View, TouchableOpacity, Image, Text, Platform } from 'react-native'; import { View, TouchableOpacity, Text, Platform, ActivityIndicator } from 'react-native';
import { View as AnimatedView } from 'react-native-animatable';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import { injectIntl } from 'react-intl'; import { injectIntl } from 'react-intl';
@ -17,9 +18,25 @@ import { getResizedImage } from '../../utils/image';
// Styles // Styles
import styles from './profileEditFormStyles'; import styles from './profileEditFormStyles';
import FastImage from 'react-native-fast-image';
import EStyleSheet from 'react-native-extended-stylesheet';
import { MainButton } from '../mainButton';
interface ProfileEditFormProps {
coverUrl:string;
formData:any;
handleOnItemChange:()=>void;
handleOnSubmit:()=>void;
intl:any,
isDarkTheme:boolean,
isLoading:boolean,
isUploading:boolean,
showImageUploadActions:boolean,
saveEnabled:boolean,
}
const ProfileEditFormView = ({ const ProfileEditFormView = ({
avatarUrl,
coverUrl, coverUrl,
formData, formData,
handleOnItemChange, handleOnItemChange,
@ -27,30 +44,41 @@ const ProfileEditFormView = ({
intl, intl,
isDarkTheme, isDarkTheme,
isLoading, isLoading,
isUploading,
showImageUploadActions, showImageUploadActions,
saveEnabled,
...props ...props
}) => ( }:ProfileEditFormProps) => (
<View style={styles.container}> <View style={styles.container}>
<IconButton
iconStyle={styles.saveIcon}
style={styles.saveButton}
iconType="MaterialIcons"
name="save"
onPress={handleOnSubmit}
size={30}
isLoading={isLoading}
/>
<KeyboardAwareScrollView <KeyboardAwareScrollView
enableAutoAutomaticScroll={Platform.OS === 'ios'} enableAutoAutomaticScroll={Platform.OS === 'ios'}
contentContainerStyle={styles.contentContainer} contentContainerStyle={styles.contentContainer}
enableOnAndroid={true} enableOnAndroid={true}
> >
<TouchableOpacity style={styles.coverImgWrapper} onPress={showImageUploadActions}> <TouchableOpacity style={styles.coverImgWrapper} onPress={showImageUploadActions}>
<Image
style={styles.coverImg} <FastImage
source={{ uri: getResizedImage(coverUrl, 600) }} style={styles.coverImg}
defaultSource={isDarkTheme ? DARK_COVER_IMAGE : LIGHT_COVER_IMAGE} source={
/> coverUrl
? { uri: getResizedImage(coverUrl, 600) }
: isDarkTheme
? DARK_COVER_IMAGE
: LIGHT_COVER_IMAGE
}
/>
{
isUploading && (
<ActivityIndicator
style={styles.activityIndicator}
color={EStyleSheet.value('$white')}
size='large'
/>
)
}
<IconButton <IconButton
iconStyle={styles.addIcon} iconStyle={styles.addIcon}
@ -83,6 +111,22 @@ const ProfileEditFormView = ({
</View> </View>
))} ))}
</KeyboardAwareScrollView> </KeyboardAwareScrollView>
{saveEnabled && (
<AnimatedView style={styles.floatingContainer} animation="bounceInRight">
<MainButton
style={{ width: isLoading ? null : 120, marginBottom:24, alignSelf:'flex-end' }}
onPress={handleOnSubmit}
iconName="save"
iconType="MaterialIcons"
iconColor="white"
text="SAVE"
isLoading={isLoading}
/>
</AnimatedView>
)}
</View> </View>
); );

View File

@ -6,4 +6,10 @@ export default EStyleSheet.create({
borderColor: '$borderColor', borderColor: '$borderColor',
backgroundColor: '$pureWhite', backgroundColor: '$pureWhite',
}, },
activityIndicator: {
position:'absolute',
alignSelf:'center',
top:0,
bottom:0
}
}); });

View File

@ -1,91 +0,0 @@
import React, { Component } from 'react';
import { TouchableOpacity } from 'react-native';
import { connect } from 'react-redux';
import FastImage from 'react-native-fast-image';
import styles from './userAvatarStyles';
import { navigate } from '../../../navigation/service';
// Constants
import ROUTES from '../../../constants/routeNames';
// Utils
import { getResizedAvatar } from '../../../utils/image';
const DEFAULT_IMAGE = require('../../../assets/avatar_default.png');
/* Props
* ------------------------------------------------
* @prop { type } name - Description....
*/
class UserAvatarView extends Component {
// Component Life Cycles
shouldComponentUpdate(nextProps) {
const { username } = this.props;
return nextProps.username !== username;
}
// Component Functions
_handleOnAvatarPress = (username) => {
const {
currentUsername: { name },
} = this.props;
const routeName = name === username ? ROUTES.TABBAR.PROFILE : ROUTES.SCREENS.PROFILE;
navigate({
routeName,
params: {
username,
},
key: username,
});
};
render() {
const {
username,
size,
style,
disableSize,
noAction,
currentUsername: { name, avatar },
} = this.props;
const imageSize = 'large';
let _size;
const _avatar = username
? {
uri: getResizedAvatar(username, imageSize),
}
: DEFAULT_IMAGE;
if (!disableSize) {
_size = 32;
if (size === 'xl') {
_size = 64;
}
}
return (
<TouchableOpacity disabled={noAction} onPress={() => this._handleOnAvatarPress(username)}>
<FastImage
style={[
styles.avatar,
style,
!disableSize && { width: _size, height: _size, borderRadius: _size / 2 },
]}
source={_avatar}
/>
</TouchableOpacity>
);
}
}
const mapStateToProps = (state) => ({
currentUsername: state.account.currentAccount,
});
export default connect(mapStateToProps)(UserAvatarView);

View File

@ -0,0 +1,98 @@
import React, { Component } from 'react';
import { ActivityIndicator, TouchableOpacity, ViewStyle } from 'react-native';
import { connect } from 'react-redux';
import FastImage from 'react-native-fast-image';
import styles from './userAvatarStyles';
import { navigate } from '../../../navigation/service';
// Constants
import ROUTES from '../../../constants/routeNames';
// Utils
import { getResizedAvatar } from '../../../utils/image';
import { useAppSelector } from '../../../hooks';
import EStyleSheet from 'react-native-extended-stylesheet';
const DEFAULT_IMAGE = require('../../../assets/avatar_default.png');
/* Props
* ------------------------------------------------
* @prop { type } name - Description....
*/
interface UserAvatarProps {
username:string;
avatarUrl?:string;
size?:'xl';
style?:ViewStyle;
disableSize?:boolean;
noAction?:boolean;
isLoading?:boolean;
}
const UserAvatarView = ({
username,
avatarUrl,
size,
style,
disableSize,
noAction,
isLoading
}:UserAvatarProps) => {
const curUsername = useAppSelector(state=>state.account.currentAccount.name);
const avatarCacheStamp = useAppSelector(state=>state.ui.avatarCacheStamp);
// Component Functions
const _handleOnAvatarPress = (username:string) => {
const routeName = curUsername === username ? ROUTES.TABBAR.PROFILE : ROUTES.SCREENS.PROFILE;
navigate({
routeName,
params: {
username,
},
key: username,
});
};
const uri = avatarUrl ? avatarUrl : getResizedAvatar(username, 'large');
const _avatar = username
? { uri : `${uri}?stamp=${avatarCacheStamp}` }
: DEFAULT_IMAGE;
let _size:number;
if (!disableSize) {
_size = 32;
if (size === 'xl') {
_size = 64;
}
}
return (
<TouchableOpacity disabled={noAction} onPress={() => _handleOnAvatarPress(username)}>
<FastImage
style={[
styles.avatar,
style,
!disableSize && { width: _size, height: _size, borderRadius: _size / 2 },
]}
source={_avatar}
/>
{
isLoading && (
<ActivityIndicator
style={styles.activityIndicator}
size='large'
color={EStyleSheet.value('$white')}
/>
)
}
</TouchableOpacity>
);
}
export default UserAvatarView;

View File

@ -10,6 +10,7 @@ import { uploadImage } from '../providers/ecency/ecency';
import { profileUpdate, signImage } from '../providers/hive/dhive'; import { profileUpdate, signImage } from '../providers/hive/dhive';
import { updateCurrentAccount } from '../redux/actions/accountAction'; import { updateCurrentAccount } from '../redux/actions/accountAction';
import { setAvatarCacheStamp } from '../redux/actions/uiAction';
// import ROUTES from '../constants/routeNames'; // import ROUTES from '../constants/routeNames';
@ -50,6 +51,8 @@ class ProfileEditContainer extends Component {
super(props); super(props);
this.state = { this.state = {
isLoading: false, isLoading: false,
isUploading: false,
saveEnabled: false,
about: get(props.currentAccount, 'about.profile.about'), about: get(props.currentAccount, 'about.profile.about'),
name: get(props.currentAccount, 'about.profile.name'), name: get(props.currentAccount, 'about.profile.name'),
location: get(props.currentAccount, 'about.profile.location'), location: get(props.currentAccount, 'about.profile.location'),
@ -64,20 +67,20 @@ class ProfileEditContainer extends Component {
// Component Functions // Component Functions
_handleOnItemChange = (val, item) => { _handleOnItemChange = (val, item) => {
this.setState({ [item]: val }); this.setState({ [item]: val, saveEnabled: true });
}; };
_uploadImage = async (media, action) => { _uploadImage = async (media, action) => {
const { intl, currentAccount, pinCode } = this.props; const { intl, currentAccount, pinCode } = this.props;
this.setState({ isLoading: true }); this.setState({ isUploading: true });
let sign = await signImage(media, currentAccount, pinCode); let sign = await signImage(media, currentAccount, pinCode);
uploadImage(media, currentAccount.name, sign) uploadImage(media, currentAccount.name, sign)
.then((res) => { .then((res) => {
if (res.data && res.data.url) { if (res.data && res.data.url) {
this.setState({ [action]: res.data.url, isLoading: false }); this.setState({ [action]: res.data.url, isUploading: false, saveEnabled: true });
} }
}) })
.catch((error) => { .catch((error) => {
@ -89,7 +92,7 @@ class ProfileEditContainer extends Component {
error.message || error.toString(), error.message || error.toString(),
); );
} }
this.setState({ isLoading: false }); this.setState({ isUploading: false });
}); });
}; };
@ -102,11 +105,11 @@ class ProfileEditContainer extends Component {
}; };
_handleOpenImagePicker = (action) => { _handleOpenImagePicker = (action) => {
ImagePicker.openPicker({ ImagePicker.openPicker(
includeBase64: true, action == 'avatarUrl' ? IMAGE_PICKER_AVATAR_OPTIONS : IMAGE_PICKER_COVER_OPTIONS,
}) )
.then((image) => { .then((media) => {
this._handleMediaOnSelected(image, action); this._uploadImage(media, action);
}) })
.catch((e) => { .catch((e) => {
this._handleMediaOnSelectFailure(e); this._handleMediaOnSelectFailure(e);
@ -114,23 +117,17 @@ class ProfileEditContainer extends Component {
}; };
_handleOpenCamera = (action) => { _handleOpenCamera = (action) => {
ImagePicker.openCamera({ ImagePicker.openCamera(
includeBase64: true, action == 'avatarUrl' ? IMAGE_PICKER_AVATAR_OPTIONS : IMAGE_PICKER_COVER_OPTIONS,
}) )
.then((image) => { .then((media) => {
this._handleMediaOnSelected(image, action); this._uploadImage(media, action);
}) })
.catch((e) => { .catch((e) => {
this._handleMediaOnSelectFailure(e); this._handleMediaOnSelectFailure(e);
}); });
}; };
_handleMediaOnSelected = (media, action) => {
this.setState({ isLoading: true }, () => {
this._uploadImage(media, action);
});
};
_handleMediaOnSelectFailure = (error) => { _handleMediaOnSelectFailure = (error) => {
const { intl } = this.props; const { intl } = this.props;
@ -150,7 +147,7 @@ class ProfileEditContainer extends Component {
const { currentAccount, pinCode, dispatch, navigation, intl } = this.props; const { currentAccount, pinCode, dispatch, navigation, intl } = this.props;
const { name, location, website, about, coverUrl, avatarUrl } = this.state; const { name, location, website, about, coverUrl, avatarUrl } = this.state;
await this.setState({ isLoading: true }); this.setState({ isLoading: true });
const params = { const params = {
profile_image: avatarUrl, profile_image: avatarUrl,
@ -161,31 +158,42 @@ class ProfileEditContainer extends Component {
location, location,
version: 2, version: 2,
}; };
await profileUpdate(params, pinCode, currentAccount)
.then(async () => {
const _currentAccount = { ...currentAccount, display_name: name, avatar: avatarUrl };
_currentAccount.about.profile = { ...params };
dispatch(updateCurrentAccount(_currentAccount)); try {
await profileUpdate(params, pinCode, currentAccount);
navigation.state.params.fetchUser(); const _currentAccount = { ...currentAccount, display_name: name, avatar: avatarUrl };
navigation.goBack(); _currentAccount.about.profile = { ...params };
})
.catch((error) => {
Alert.alert(
intl.formatMessage({
id: 'alert.fail',
}),
get(error, 'message', error.toString()),
);
});
this.setState({ isLoading: false }); dispatch(updateCurrentAccount(_currentAccount));
dispatch(setAvatarCacheStamp(new Date().getTime()));
this.setState({ isLoading: false });
navigation.state.params.fetchUser();
navigation.goBack();
} catch (err) {
Alert.alert(
intl.formatMessage({
id: 'alert.fail',
}),
get(error, 'message', error.toString()),
);
this.setState({ isLoading: false });
}
}; };
render() { render() {
const { children, currentAccount, isDarkTheme } = this.props; const { children, currentAccount, isDarkTheme } = this.props;
const { isLoading, name, location, website, about, coverUrl, avatarUrl } = this.state; const {
isLoading,
isUploading,
name,
location,
website,
about,
coverUrl,
avatarUrl,
saveEnabled,
} = this.state;
return ( return (
children && children &&
@ -200,9 +208,11 @@ class ProfileEditContainer extends Component {
handleOnSubmit: this._handleOnSubmit, handleOnSubmit: this._handleOnSubmit,
isDarkTheme, isDarkTheme,
isLoading, isLoading,
isUploading,
location, location,
name, name,
website, website,
saveEnabled,
}) })
); );
} }
@ -215,3 +225,14 @@ const mapStateToProps = (state) => ({
}); });
export default connect(mapStateToProps)(injectIntl(withNavigation(ProfileEditContainer))); export default connect(mapStateToProps)(injectIntl(withNavigation(ProfileEditContainer)));
const IMAGE_PICKER_AVATAR_OPTIONS = {
includeBase64: true,
cropping: true,
width: 512,
height: 512,
};
const IMAGE_PICKER_COVER_OPTIONS = {
includeBase64: true,
};

View File

@ -1,3 +1,4 @@
import { ButtonProps } from 'react-native';
import { import {
TOAST_NOTIFICATION, TOAST_NOTIFICATION,
UPDATE_ACTIVE_BOTTOM_TAB, UPDATE_ACTIVE_BOTTOM_TAB,
@ -6,19 +7,20 @@ import {
TOGGLE_ACCOUNTS_BOTTOM_SHEET, TOGGLE_ACCOUNTS_BOTTOM_SHEET,
SHOW_ACTION_MODAL, SHOW_ACTION_MODAL,
HIDE_ACTION_MODAL, HIDE_ACTION_MODAL,
SET_AVATAR_CACHE_STAMP,
} from '../constants/constants'; } from '../constants/constants';
export const updateActiveBottomTab = (payload) => ({ export const updateActiveBottomTab = (payload:string) => ({
payload, payload,
type: UPDATE_ACTIVE_BOTTOM_TAB, type: UPDATE_ACTIVE_BOTTOM_TAB,
}); });
export const toastNotification = (payload) => ({ export const toastNotification = (payload:string) => ({
payload, payload,
type: TOAST_NOTIFICATION, type: TOAST_NOTIFICATION,
}); });
export const showActionModal = (title, body, buttons, headerImage, onClosed) => ({ export const showActionModal = (title:string, body:string, buttons:ButtonProps[], headerImage:any, onClosed:()=>void) => ({
payload: { payload: {
actionModalVisible: true, actionModalVisible: true,
actionModalData: { actionModalData: {
@ -36,17 +38,22 @@ export const hideActionModal = () => ({
type: HIDE_ACTION_MODAL, type: HIDE_ACTION_MODAL,
}); });
export const setRcOffer = (payload) => ({ export const setRcOffer = (payload:boolean) => ({
payload, payload,
type: RC_OFFER, type: RC_OFFER,
}); });
export const hidePostsThumbnails = (payload) => ({ export const hidePostsThumbnails = (payload:boolean) => ({
payload, payload,
type: HIDE_POSTS_THUMBNAILS, type: HIDE_POSTS_THUMBNAILS,
}); });
export const toggleAccountsBottomSheet = (payload) => ({ export const toggleAccountsBottomSheet = (payload:boolean) => ({
payload, payload,
type: TOGGLE_ACCOUNTS_BOTTOM_SHEET, type: TOGGLE_ACCOUNTS_BOTTOM_SHEET,
}); });
export const setAvatarCacheStamp = (payload:number) => ({
payload,
type:SET_AVATAR_CACHE_STAMP
})

View File

@ -54,6 +54,7 @@ export const RC_OFFER = 'RC_OFFER';
export const TOGGLE_ACCOUNTS_BOTTOM_SHEET = 'TOGGLE_ACCOUNTS_BOTTOM_SHEET'; export const TOGGLE_ACCOUNTS_BOTTOM_SHEET = 'TOGGLE_ACCOUNTS_BOTTOM_SHEET';
export const SHOW_ACTION_MODAL = 'SHOW_ACTION_MODAL'; export const SHOW_ACTION_MODAL = 'SHOW_ACTION_MODAL';
export const HIDE_ACTION_MODAL = 'HIDE_ACTION_MODAL'; export const HIDE_ACTION_MODAL = 'HIDE_ACTION_MODAL';
export const SET_AVATAR_CACHE_STAMP = 'SET_AVATAR_CACHE_STAMP';
// POSTS // POSTS
export const SET_FEED_POSTS = 'SET_FEED_POSTS'; export const SET_FEED_POSTS = 'SET_FEED_POSTS';

View File

@ -6,9 +6,21 @@ import {
TOGGLE_ACCOUNTS_BOTTOM_SHEET, TOGGLE_ACCOUNTS_BOTTOM_SHEET,
SHOW_ACTION_MODAL, SHOW_ACTION_MODAL,
HIDE_ACTION_MODAL, HIDE_ACTION_MODAL,
SET_AVATAR_CACHE_STAMP,
} from '../constants/constants'; } from '../constants/constants';
const initialState = { interface UiState {
activeBottomTab:string;
toastNotification:string;
hidePostsThumbnails:boolean;
rcOffer:boolean;
isVisibleAccountsBottomSheet:boolean;
actionModalVisible:boolean;
actionModalData:any;
avatarCacheStamp:number
}
const initialState:UiState = {
activeBottomTab: 'HomeTabbar', activeBottomTab: 'HomeTabbar',
toastNotification: '', toastNotification: '',
hidePostsThumbnails: false, hidePostsThumbnails: false,
@ -16,6 +28,7 @@ const initialState = {
isVisibleAccountsBottomSheet: false, isVisibleAccountsBottomSheet: false,
actionModalVisible: false, actionModalVisible: false,
actionModalData: null, actionModalData: null,
avatarCacheStamp: 0
}; };
export default function (state = initialState, action) { export default function (state = initialState, action) {
@ -65,6 +78,11 @@ export default function (state = initialState, action) {
...state, ...state,
isVisibleAccountsBottomSheet: action.payload, isVisibleAccountsBottomSheet: action.payload,
}; };
case SET_AVATAR_CACHE_STAMP:
return {
...state,
avatarCacheStamp: action.payload
}
default: default:
return state; return state;
} }

View File

@ -77,6 +77,7 @@ import {
} from '../../../redux/actions/applicationActions'; } from '../../../redux/actions/applicationActions';
import { import {
hideActionModal, hideActionModal,
setAvatarCacheStamp,
setRcOffer, setRcOffer,
toastNotification, toastNotification,
updateActiveBottomTab, updateActiveBottomTab,
@ -126,7 +127,6 @@ class ApplicationContainer extends Component {
this._setNetworkListener(); this._setNetworkListener();
Linking.addEventListener('url', this._handleOpenURL); Linking.addEventListener('url', this._handleOpenURL);
Linking.getInitialURL().then((url) => { Linking.getInitialURL().then((url) => {
this._handleDeepLink(url); this._handleDeepLink(url);
}); });
@ -145,6 +145,9 @@ class ApplicationContainer extends Component {
if (!isIos) BackHandler.addEventListener('hardwareBackPress', this._onBackPress); if (!isIos) BackHandler.addEventListener('hardwareBackPress', this._onBackPress);
//set avatar cache stamp to invalidate previous session avatars
dispatch(setAvatarCacheStamp(new Date().getTime()));
getVersionForWelcomeModal().then((version) => { getVersionForWelcomeModal().then((version) => {
if (version < parseVersionNumber(appVersion)) { if (version < parseVersionNumber(appVersion)) {
getUserData().then((accounts) => { getUserData().then((accounts) => {

View File

@ -1,4 +1,5 @@
import React, { PureComponent, Fragment } from 'react'; import React, { PureComponent, Fragment } from 'react';
import { StatusBar } from 'react-native';
import { injectIntl } from 'react-intl'; import { injectIntl } from 'react-intl';
import get from 'lodash/get'; import get from 'lodash/get';
import ActionSheet from 'react-native-actionsheet'; import ActionSheet from 'react-native-actionsheet';
@ -25,9 +26,10 @@ class ProfileEditScreen extends PureComponent {
// Component Life Cycles // Component Life Cycles
// Component Functions // Component Functions
_showImageUploadActions = async (action) => { _showImageUploadActions = (action) => {
await this.setState({ selectedUploadAction: action }); this.setState({ selectedUploadAction: action }, () => {
this.galleryRef.current.show(); this.galleryRef.current.show();
});
}; };
render() { render() {
@ -49,15 +51,19 @@ class ProfileEditScreen extends PureComponent {
avatarUrl, avatarUrl,
coverUrl, coverUrl,
isLoading, isLoading,
isUploading,
saveEnabled,
handleOnSubmit, handleOnSubmit,
}) => ( }) => (
<Fragment> <Fragment>
<StatusBar barStyle="light-content" />
<AvatarHeader <AvatarHeader
username={get(currentAccount, 'name')} username={get(currentAccount, 'name')}
name={name} name={name}
reputation={get(currentAccount, 'reputation')} reputation={get(currentAccount, 'reputation')}
avatarUrl={avatarUrl} avatarUrl={avatarUrl}
showImageUploadActions={() => this._showImageUploadActions('avatarUrl')} showImageUploadActions={() => this._showImageUploadActions('avatarUrl')}
isUploading={isUploading && selectedUploadAction === 'avatarUrl'}
/> />
<ProfileEditForm <ProfileEditForm
formData={formData} formData={formData}
@ -70,8 +76,11 @@ class ProfileEditScreen extends PureComponent {
showImageUploadActions={() => this._showImageUploadActions('coverUrl')} showImageUploadActions={() => this._showImageUploadActions('coverUrl')}
handleOnItemChange={handleOnItemChange} handleOnItemChange={handleOnItemChange}
isLoading={isLoading} isLoading={isLoading}
isUploading={isUploading && selectedUploadAction === 'coverUrl'}
saveEnabled={saveEnabled}
handleOnSubmit={handleOnSubmit} handleOnSubmit={handleOnSubmit}
/> />
<ActionSheet <ActionSheet
ref={this.galleryRef} ref={this.galleryRef}
options={[ options={[