From 0902d62481037b4bf17bc908fd0cd9c402b4c467 Mon Sep 17 00:00:00 2001 From: Nouman Tahir Date: Tue, 15 Jun 2021 22:23:33 +0500 Subject: [PATCH 1/9] invalidating cached avatars on every app session and avatart update --- .../avatarHeader/avatarHeaderView.js | 2 +- .../userAvatar/view/userAvatarView.js | 91 ------------------- .../userAvatar/view/userAvatarView.tsx | 84 +++++++++++++++++ src/containers/profileEditContainer.js | 42 +++++---- .../actions/{uiAction.js => uiAction.ts} | 19 ++-- src/redux/constants/constants.js | 1 + src/redux/reducers/uiReducer.js | 7 ++ .../container/applicationContainer.js | 6 +- .../profileEdit/screen/profileEditScreen.js | 2 +- 9 files changed, 136 insertions(+), 118 deletions(-) delete mode 100644 src/components/userAvatar/view/userAvatarView.js create mode 100644 src/components/userAvatar/view/userAvatarView.tsx rename src/redux/actions/{uiAction.js => uiAction.ts} (53%) diff --git a/src/components/avatarHeader/avatarHeaderView.js b/src/components/avatarHeader/avatarHeaderView.js index c1f0111f2..93c85386f 100644 --- a/src/components/avatarHeader/avatarHeaderView.js +++ b/src/components/avatarHeader/avatarHeaderView.js @@ -33,7 +33,7 @@ const AvatarHeader = ({ size={25} /> - + { - 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 ( - this._handleOnAvatarPress(username)}> - - - ); - } -} - -const mapStateToProps = (state) => ({ - currentUsername: state.account.currentAccount, -}); - -export default connect(mapStateToProps)(UserAvatarView); diff --git a/src/components/userAvatar/view/userAvatarView.tsx b/src/components/userAvatar/view/userAvatarView.tsx new file mode 100644 index 000000000..b2379ab3d --- /dev/null +++ b/src/components/userAvatar/view/userAvatarView.tsx @@ -0,0 +1,84 @@ +import React, { Component } from 'react'; +import { 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'; + +const DEFAULT_IMAGE = require('../../../assets/avatar_default.png'); + +/* Props + * ------------------------------------------------ + * @prop { type } name - Description.... + */ + +interface UserAvatarProps { + username:string; + size?:'xl'; + style?:ViewStyle; + disableSize?:boolean; + noAction?:boolean; +} + +const UserAvatarView = ({ + username, + size, + style, + disableSize, + noAction +}: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 = 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 ( + _handleOnAvatarPress(username)}> + + + ); +} + +export default UserAvatarView; diff --git a/src/containers/profileEditContainer.js b/src/containers/profileEditContainer.js index 2b4c55d7f..5756d24f4 100644 --- a/src/containers/profileEditContainer.js +++ b/src/containers/profileEditContainer.js @@ -10,6 +10,7 @@ import { uploadImage } from '../providers/ecency/ecency'; import { profileUpdate, signImage } from '../providers/hive/dhive'; import { updateCurrentAccount } from '../redux/actions/accountAction'; +import { setAvatarCacheStamp } from '../redux/actions/uiAction'; // import ROUTES from '../constants/routeNames'; @@ -150,7 +151,7 @@ class ProfileEditContainer extends Component { const { currentAccount, pinCode, dispatch, navigation, intl } = this.props; const { name, location, website, about, coverUrl, avatarUrl } = this.state; - await this.setState({ isLoading: true }); + this.setState({ isLoading: true }); const params = { profile_image: avatarUrl, @@ -161,26 +162,31 @@ class ProfileEditContainer extends Component { location, 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(); - navigation.goBack(); - }) - .catch((error) => { - Alert.alert( - intl.formatMessage({ - id: 'alert.fail', - }), - get(error, 'message', error.toString()), - ); - }); + const _currentAccount = { ...currentAccount, display_name: name, avatar: avatarUrl }; + _currentAccount.about.profile = { ...params }; - 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() { diff --git a/src/redux/actions/uiAction.js b/src/redux/actions/uiAction.ts similarity index 53% rename from src/redux/actions/uiAction.js rename to src/redux/actions/uiAction.ts index 40314d795..b2a5aa265 100644 --- a/src/redux/actions/uiAction.js +++ b/src/redux/actions/uiAction.ts @@ -1,3 +1,4 @@ +import { ButtonProps } from 'react-native'; import { TOAST_NOTIFICATION, UPDATE_ACTIVE_BOTTOM_TAB, @@ -6,19 +7,20 @@ import { TOGGLE_ACCOUNTS_BOTTOM_SHEET, SHOW_ACTION_MODAL, HIDE_ACTION_MODAL, + SET_AVATAR_CACHE_STAMP, } from '../constants/constants'; -export const updateActiveBottomTab = (payload) => ({ +export const updateActiveBottomTab = (payload:string) => ({ payload, type: UPDATE_ACTIVE_BOTTOM_TAB, }); -export const toastNotification = (payload) => ({ +export const toastNotification = (payload:string) => ({ payload, 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: { actionModalVisible: true, actionModalData: { @@ -36,17 +38,22 @@ export const hideActionModal = () => ({ type: HIDE_ACTION_MODAL, }); -export const setRcOffer = (payload) => ({ +export const setRcOffer = (payload:boolean) => ({ payload, type: RC_OFFER, }); -export const hidePostsThumbnails = (payload) => ({ +export const hidePostsThumbnails = (payload:boolean) => ({ payload, type: HIDE_POSTS_THUMBNAILS, }); -export const toggleAccountsBottomSheet = (payload) => ({ +export const toggleAccountsBottomSheet = (payload:boolean) => ({ payload, type: TOGGLE_ACCOUNTS_BOTTOM_SHEET, }); + +export const setAvatarCacheStamp = (payload:number) => ({ + payload, + type:SET_AVATAR_CACHE_STAMP +}) diff --git a/src/redux/constants/constants.js b/src/redux/constants/constants.js index d22cf9a92..8c5de7830 100644 --- a/src/redux/constants/constants.js +++ b/src/redux/constants/constants.js @@ -54,6 +54,7 @@ export const RC_OFFER = 'RC_OFFER'; export const TOGGLE_ACCOUNTS_BOTTOM_SHEET = 'TOGGLE_ACCOUNTS_BOTTOM_SHEET'; export const SHOW_ACTION_MODAL = 'SHOW_ACTION_MODAL'; export const HIDE_ACTION_MODAL = 'HIDE_ACTION_MODAL'; +export const SET_AVATAR_CACHE_STAMP = 'SET_AVATAR_CACHE_STAMP'; // POSTS export const SET_FEED_POSTS = 'SET_FEED_POSTS'; diff --git a/src/redux/reducers/uiReducer.js b/src/redux/reducers/uiReducer.js index 783bcdb62..042fbb58c 100644 --- a/src/redux/reducers/uiReducer.js +++ b/src/redux/reducers/uiReducer.js @@ -6,6 +6,7 @@ import { TOGGLE_ACCOUNTS_BOTTOM_SHEET, SHOW_ACTION_MODAL, HIDE_ACTION_MODAL, + SET_AVATAR_CACHE_STAMP, } from '../constants/constants'; const initialState = { @@ -16,6 +17,7 @@ const initialState = { isVisibleAccountsBottomSheet: false, actionModalVisible: false, actionModalData: null, + avatarCacheStamp: 0 }; export default function (state = initialState, action) { @@ -65,6 +67,11 @@ export default function (state = initialState, action) { ...state, isVisibleAccountsBottomSheet: action.payload, }; + case SET_AVATAR_CACHE_STAMP: + return { + ...state, + avatarCacheStamp: action.payload + } default: return state; } diff --git a/src/screens/application/container/applicationContainer.js b/src/screens/application/container/applicationContainer.js index 7b745c082..c50deb49e 100644 --- a/src/screens/application/container/applicationContainer.js +++ b/src/screens/application/container/applicationContainer.js @@ -77,6 +77,7 @@ import { } from '../../../redux/actions/applicationActions'; import { hideActionModal, + setAvatarCacheStamp, setRcOffer, toastNotification, updateActiveBottomTab, @@ -126,7 +127,6 @@ class ApplicationContainer extends Component { this._setNetworkListener(); Linking.addEventListener('url', this._handleOpenURL); - Linking.getInitialURL().then((url) => { this._handleDeepLink(url); }); @@ -145,6 +145,10 @@ class ApplicationContainer extends Component { if (!isIos) BackHandler.addEventListener('hardwareBackPress', this._onBackPress); + + //set avatar cache stamp to invalidate previous session avatars + dispatch(setAvatarCacheStamp(new Date().getTime())) + getVersionForWelcomeModal().then((version) => { if (version < parseVersionNumber(appVersion)) { getUserData().then((accounts) => { diff --git a/src/screens/profileEdit/screen/profileEditScreen.js b/src/screens/profileEdit/screen/profileEditScreen.js index 1971537ca..d6db6a5d0 100644 --- a/src/screens/profileEdit/screen/profileEditScreen.js +++ b/src/screens/profileEdit/screen/profileEditScreen.js @@ -26,7 +26,7 @@ class ProfileEditScreen extends PureComponent { // Component Functions _showImageUploadActions = async (action) => { - await this.setState({ selectedUploadAction: action }); + this.setState({ selectedUploadAction: action }); this.galleryRef.current.show(); }; From e8ea54eb5f87285be5a8b98c19b8c43bc2e19642 Mon Sep 17 00:00:00 2001 From: Nouman Tahir Date: Tue, 15 Jun 2021 22:27:39 +0500 Subject: [PATCH 2/9] added UiState interface --- src/containers/profileEditContainer.js | 12 ++++-------- src/redux/reducers/{uiReducer.js => uiReducer.ts} | 13 ++++++++++++- .../application/container/applicationContainer.js | 3 +-- 3 files changed, 17 insertions(+), 11 deletions(-) rename src/redux/reducers/{uiReducer.js => uiReducer.ts} (85%) diff --git a/src/containers/profileEditContainer.js b/src/containers/profileEditContainer.js index 5756d24f4..6f2df87e8 100644 --- a/src/containers/profileEditContainer.js +++ b/src/containers/profileEditContainer.js @@ -163,19 +163,18 @@ class ProfileEditContainer extends Component { version: 2, }; - try{ - await profileUpdate(params, pinCode, currentAccount) + try { + await profileUpdate(params, pinCode, currentAccount); const _currentAccount = { ...currentAccount, display_name: name, avatar: avatarUrl }; _currentAccount.about.profile = { ...params }; dispatch(updateCurrentAccount(_currentAccount)); - dispatch(setAvatarCacheStamp(new Date().getTime())) + dispatch(setAvatarCacheStamp(new Date().getTime())); this.setState({ isLoading: false }); navigation.state.params.fetchUser(); navigation.goBack(); - - }catch(err){ + } catch (err) { Alert.alert( intl.formatMessage({ id: 'alert.fail', @@ -184,9 +183,6 @@ class ProfileEditContainer extends Component { ); this.setState({ isLoading: false }); } - - - }; render() { diff --git a/src/redux/reducers/uiReducer.js b/src/redux/reducers/uiReducer.ts similarity index 85% rename from src/redux/reducers/uiReducer.js rename to src/redux/reducers/uiReducer.ts index 042fbb58c..b792ac16d 100644 --- a/src/redux/reducers/uiReducer.js +++ b/src/redux/reducers/uiReducer.ts @@ -9,7 +9,18 @@ import { SET_AVATAR_CACHE_STAMP, } 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', toastNotification: '', hidePostsThumbnails: false, diff --git a/src/screens/application/container/applicationContainer.js b/src/screens/application/container/applicationContainer.js index c50deb49e..04ecebfc7 100644 --- a/src/screens/application/container/applicationContainer.js +++ b/src/screens/application/container/applicationContainer.js @@ -145,9 +145,8 @@ class ApplicationContainer extends Component { if (!isIos) BackHandler.addEventListener('hardwareBackPress', this._onBackPress); - //set avatar cache stamp to invalidate previous session avatars - dispatch(setAvatarCacheStamp(new Date().getTime())) + dispatch(setAvatarCacheStamp(new Date().getTime())); getVersionForWelcomeModal().then((version) => { if (version < parseVersionNumber(appVersion)) { From 5449a001bce630c56f3a1c8624f4f97296583a53 Mon Sep 17 00:00:00 2001 From: Nouman Tahir Date: Wed, 16 Jun 2021 11:11:22 +0500 Subject: [PATCH 3/9] Improved avatar update behviour 1. fixed state update lag issue while uploading avatar 2. using avatarUrl as override for UserAvatar component --- src/components/avatarHeader/avatarHeaderView.js | 7 ++++++- src/components/userAvatar/view/userAvatarView.tsx | 4 +++- src/screens/profileEdit/screen/profileEditScreen.js | 7 ++++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/components/avatarHeader/avatarHeaderView.js b/src/components/avatarHeader/avatarHeaderView.js index 93c85386f..24ed8faed 100644 --- a/src/components/avatarHeader/avatarHeaderView.js +++ b/src/components/avatarHeader/avatarHeaderView.js @@ -33,7 +33,12 @@ const AvatarHeader = ({ size={25} /> - + { - this.setState({ selectedUploadAction: action }); - this.galleryRef.current.show(); + _showImageUploadActions = (action) => { + this.setState({ selectedUploadAction: action }, ()=>{ + this.galleryRef.current.show(); + }); }; render() { From 33c15800a4c2629aa6c8fc69880428735aaa5cb7 Mon Sep 17 00:00:00 2001 From: Nouman Tahir Date: Wed, 16 Jun 2021 12:24:37 +0500 Subject: [PATCH 4/9] showing avatar upload indicator on avatar instead of using save indicator --- .../avatarHeader/avatarHeaderView.js | 2 + ...ditFormView.js => profileEditFormView.tsx} | 16 +++++++- ...serAvatarStyles.js => userAvatarStyles.ts} | 6 +++ .../userAvatar/view/userAvatarView.tsx | 16 +++++++- src/containers/profileEditContainer.js | 39 ++++++++++--------- .../profileEdit/screen/profileEditScreen.js | 3 ++ 6 files changed, 59 insertions(+), 23 deletions(-) rename src/components/profileEditForm/{profileEditFormView.js => profileEditFormView.tsx} (90%) rename src/components/userAvatar/view/{userAvatarStyles.js => userAvatarStyles.ts} (66%) diff --git a/src/components/avatarHeader/avatarHeaderView.js b/src/components/avatarHeader/avatarHeaderView.js index 24ed8faed..5f0aeb736 100644 --- a/src/components/avatarHeader/avatarHeaderView.js +++ b/src/components/avatarHeader/avatarHeaderView.js @@ -16,6 +16,7 @@ const AvatarHeader = ({ navigation, avatarUrl, showImageUploadActions, + isUploading, }) => ( void; + handleOnSubmit:()=>void; + intl:any, + isDarkTheme:boolean, + isLoading:boolean, + isUploading:boolean, + showImageUploadActions:boolean, +} + const ProfileEditFormView = ({ - avatarUrl, coverUrl, formData, handleOnItemChange, @@ -29,7 +41,7 @@ const ProfileEditFormView = ({ isLoading, showImageUploadActions, ...props -}) => ( +}:ProfileEditFormProps) => ( { const curUsername = useAppSelector(state=>state.account.currentAccount.name); @@ -79,6 +82,15 @@ const UserAvatarView = ({ ]} source={_avatar} /> + { + isLoading && ( + + ) + } ); } diff --git a/src/containers/profileEditContainer.js b/src/containers/profileEditContainer.js index 6f2df87e8..1d3f8d72a 100644 --- a/src/containers/profileEditContainer.js +++ b/src/containers/profileEditContainer.js @@ -11,6 +11,7 @@ import { uploadImage } from '../providers/ecency/ecency'; import { profileUpdate, signImage } from '../providers/hive/dhive'; import { updateCurrentAccount } from '../redux/actions/accountAction'; import { setAvatarCacheStamp } from '../redux/actions/uiAction'; +import { transferTypes } from '../utils/wallet'; // import ROUTES from '../constants/routeNames'; @@ -51,6 +52,7 @@ class ProfileEditContainer extends Component { super(props); this.state = { isLoading: false, + isUploading: false, about: get(props.currentAccount, 'about.profile.about'), name: get(props.currentAccount, 'about.profile.name'), location: get(props.currentAccount, 'about.profile.location'), @@ -71,14 +73,14 @@ class ProfileEditContainer extends Component { _uploadImage = async (media, action) => { const { intl, currentAccount, pinCode } = this.props; - this.setState({ isLoading: true }); + this.setState({ isUploading: true }); let sign = await signImage(media, currentAccount, pinCode); uploadImage(media, currentAccount.name, sign) .then((res) => { if (res.data && res.data.url) { - this.setState({ [action]: res.data.url, isLoading: false }); + this.setState({ [action]: res.data.url, isUploading: false }); } }) .catch((error) => { @@ -90,7 +92,7 @@ class ProfileEditContainer extends Component { error.message || error.toString(), ); } - this.setState({ isLoading: false }); + this.setState({ isUploading: false }); }); }; @@ -103,11 +105,9 @@ class ProfileEditContainer extends Component { }; _handleOpenImagePicker = (action) => { - ImagePicker.openPicker({ - includeBase64: true, - }) - .then((image) => { - this._handleMediaOnSelected(image, action); + ImagePicker.openPicker(IMAGE_PICKER_OPTIONS) + .then((media) => { + this._uploadImage(media, action); }) .catch((e) => { this._handleMediaOnSelectFailure(e); @@ -115,22 +115,15 @@ class ProfileEditContainer extends Component { }; _handleOpenCamera = (action) => { - ImagePicker.openCamera({ - includeBase64: true, - }) - .then((image) => { - this._handleMediaOnSelected(image, action); + ImagePicker.openCamera(IMAGE_PICKER_OPTIONS) + .then((media) => { + this._uploadImage(media, action); }) .catch((e) => { this._handleMediaOnSelectFailure(e); }); }; - _handleMediaOnSelected = (media, action) => { - this.setState({ isLoading: true }, () => { - this._uploadImage(media, action); - }); - }; _handleMediaOnSelectFailure = (error) => { const { intl } = this.props; @@ -187,7 +180,7 @@ class ProfileEditContainer extends Component { render() { 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 } = this.state; return ( children && @@ -202,6 +195,7 @@ class ProfileEditContainer extends Component { handleOnSubmit: this._handleOnSubmit, isDarkTheme, isLoading, + isUploading, location, name, website, @@ -217,3 +211,10 @@ const mapStateToProps = (state) => ({ }); export default connect(mapStateToProps)(injectIntl(withNavigation(ProfileEditContainer))); + + +const IMAGE_PICKER_OPTIONS = { + includeBase64: true, + width: 512, + height: 512, +} \ No newline at end of file diff --git a/src/screens/profileEdit/screen/profileEditScreen.js b/src/screens/profileEdit/screen/profileEditScreen.js index 3529864eb..9f1d4438d 100644 --- a/src/screens/profileEdit/screen/profileEditScreen.js +++ b/src/screens/profileEdit/screen/profileEditScreen.js @@ -50,6 +50,7 @@ class ProfileEditScreen extends PureComponent { avatarUrl, coverUrl, isLoading, + isUploading, handleOnSubmit, }) => ( @@ -59,6 +60,7 @@ class ProfileEditScreen extends PureComponent { reputation={get(currentAccount, 'reputation')} avatarUrl={avatarUrl} showImageUploadActions={() => this._showImageUploadActions('avatarUrl')} + isUploading={isUploading && selectedUploadAction === 'avatarUrl'} /> this._showImageUploadActions('coverUrl')} handleOnItemChange={handleOnItemChange} isLoading={isLoading} + isUploading={isUploading && selectedUploadAction === 'coverUrl'} handleOnSubmit={handleOnSubmit} /> Date: Wed, 16 Jun 2021 12:30:58 +0500 Subject: [PATCH 5/9] Fine tuned profile edit form view --- .../avatarHeader/avatarHeaderView.js | 9 ++-- ...FormStyles.js => profileEditFormStyles.ts} | 15 ++++-- .../profileEditForm/profileEditFormView.tsx | 54 +++++++++++++------ src/containers/profileEditContainer.js | 27 +++++++--- .../profileEdit/screen/profileEditScreen.js | 3 +- 5 files changed, 77 insertions(+), 31 deletions(-) rename src/components/profileEditForm/{profileEditFormStyles.js => profileEditFormStyles.ts} (84%) diff --git a/src/components/avatarHeader/avatarHeaderView.js b/src/components/avatarHeader/avatarHeaderView.js index 5f0aeb736..65528d563 100644 --- a/src/components/avatarHeader/avatarHeaderView.js +++ b/src/components/avatarHeader/avatarHeaderView.js @@ -34,10 +34,11 @@ const AvatarHeader = ({ size={25} /> - diff --git a/src/components/profileEditForm/profileEditFormStyles.js b/src/components/profileEditForm/profileEditFormStyles.ts similarity index 84% rename from src/components/profileEditForm/profileEditFormStyles.js rename to src/components/profileEditForm/profileEditFormStyles.ts index f25d66add..ccd3b45b2 100644 --- a/src/components/profileEditForm/profileEditFormStyles.js +++ b/src/components/profileEditForm/profileEditFormStyles.ts @@ -13,6 +13,7 @@ export default EStyleSheet.create({ marginTop: 8, }, label: { + marginTop:8, fontSize: 14, color: '$primaryDarkText', fontWeight: '500', @@ -25,7 +26,7 @@ export default EStyleSheet.create({ height: 60, marginBottom: 12, alignSelf: 'stretch', - backgroundColor: '#296CC0', + backgroundColor: '$primaryGray', }, coverImageWrapper: {}, addIcon: { @@ -62,13 +63,19 @@ export default EStyleSheet.create({ }, input: { - fontSize: 14, - color: '$primaryDarkText', + fontSize: 20, + color: '$primaryBlack', alignSelf: 'flex-start', width: '100%', - height: 40, + paddingBottom:10, }, contentContainer: { flexGrow: 1, }, + activityIndicator: { + position:'absolute', + alignSelf:'center', + top:0, + bottom:8 + } }); diff --git a/src/components/profileEditForm/profileEditFormView.tsx b/src/components/profileEditForm/profileEditFormView.tsx index af475644a..10099ac04 100644 --- a/src/components/profileEditForm/profileEditFormView.tsx +++ b/src/components/profileEditForm/profileEditFormView.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { withNavigation } from 'react-navigation'; -import { View, TouchableOpacity, Image, Text, Platform } from 'react-native'; +import { View, TouchableOpacity, Image, Text, Platform, ActivityIndicator } from 'react-native'; import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; import { injectIntl } from 'react-intl'; @@ -17,6 +17,9 @@ import { getResizedImage } from '../../utils/image'; // Styles import styles from './profileEditFormStyles'; +import FastImage from 'react-native-fast-image'; +import EStyleSheet from 'react-native-extended-stylesheet'; +import { MainButton } from '../mainButton'; interface ProfileEditFormProps { @@ -39,30 +42,40 @@ const ProfileEditFormView = ({ intl, isDarkTheme, isLoading, + isUploading, showImageUploadActions, ...props }:ProfileEditFormProps) => ( + - + - + + + { + isUploading && ( + + ) + } + ))} + + + ); diff --git a/src/containers/profileEditContainer.js b/src/containers/profileEditContainer.js index 1d3f8d72a..f11b79991 100644 --- a/src/containers/profileEditContainer.js +++ b/src/containers/profileEditContainer.js @@ -105,7 +105,7 @@ class ProfileEditContainer extends Component { }; _handleOpenImagePicker = (action) => { - ImagePicker.openPicker(IMAGE_PICKER_OPTIONS) + ImagePicker.openPicker(action == 'avatarUrl' ? IMAGE_PICKER_AVATAR_OPTIONS : IMAGE_PICKER_COVER_OPTIONS) .then((media) => { this._uploadImage(media, action); }) @@ -115,7 +115,7 @@ class ProfileEditContainer extends Component { }; _handleOpenCamera = (action) => { - ImagePicker.openCamera(IMAGE_PICKER_OPTIONS) + ImagePicker.openCamera(action == 'avatarUrl' ? IMAGE_PICKER_AVATAR_OPTIONS : IMAGE_PICKER_COVER_OPTIONS) .then((media) => { this._uploadImage(media, action); }) @@ -124,7 +124,6 @@ class ProfileEditContainer extends Component { }); }; - _handleMediaOnSelectFailure = (error) => { const { intl } = this.props; @@ -180,7 +179,16 @@ class ProfileEditContainer extends Component { render() { const { children, currentAccount, isDarkTheme } = this.props; - const { isLoading, isUploading, name, location, website, about, coverUrl, avatarUrl } = this.state; + const { + isLoading, + isUploading, + name, + location, + website, + about, + coverUrl, + avatarUrl, + } = this.state; return ( children && @@ -212,9 +220,14 @@ const mapStateToProps = (state) => ({ export default connect(mapStateToProps)(injectIntl(withNavigation(ProfileEditContainer))); - -const IMAGE_PICKER_OPTIONS = { +const IMAGE_PICKER_AVATAR_OPTIONS = { includeBase64: true, width: 512, height: 512, -} \ No newline at end of file +}; + +const IMAGE_PICKER_COVER_OPTIONS = { + includeBase64: true, + width: 1440, + height: 1440, +}; diff --git a/src/screens/profileEdit/screen/profileEditScreen.js b/src/screens/profileEdit/screen/profileEditScreen.js index 9f1d4438d..72bc0a3c8 100644 --- a/src/screens/profileEdit/screen/profileEditScreen.js +++ b/src/screens/profileEdit/screen/profileEditScreen.js @@ -26,7 +26,7 @@ class ProfileEditScreen extends PureComponent { // Component Functions _showImageUploadActions = (action) => { - this.setState({ selectedUploadAction: action }, ()=>{ + this.setState({ selectedUploadAction: action }, () => { this.galleryRef.current.show(); }); }; @@ -76,6 +76,7 @@ class ProfileEditScreen extends PureComponent { isUploading={isUploading && selectedUploadAction === 'coverUrl'} handleOnSubmit={handleOnSubmit} /> + Date: Wed, 16 Jun 2021 13:05:20 +0500 Subject: [PATCH 6/9] animated save button to appear on change, limited crop to avatar selection --- .../profileEditForm/profileEditFormView.tsx | 28 ++++++++++++------- src/containers/profileEditContainer.js | 19 ++++++++----- .../profileEdit/screen/profileEditScreen.js | 2 ++ 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/components/profileEditForm/profileEditFormView.tsx b/src/components/profileEditForm/profileEditFormView.tsx index 10099ac04..41d43a9a6 100644 --- a/src/components/profileEditForm/profileEditFormView.tsx +++ b/src/components/profileEditForm/profileEditFormView.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { withNavigation } from 'react-navigation'; -import { View, TouchableOpacity, Image, Text, Platform, ActivityIndicator } 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 { injectIntl } from 'react-intl'; @@ -32,6 +33,7 @@ interface ProfileEditFormProps { isLoading:boolean, isUploading:boolean, showImageUploadActions:boolean, + saveEnabled:boolean, } const ProfileEditFormView = ({ @@ -44,6 +46,7 @@ const ProfileEditFormView = ({ isLoading, isUploading, showImageUploadActions, + saveEnabled, ...props }:ProfileEditFormProps) => ( @@ -109,15 +112,20 @@ const ProfileEditFormView = ({ ))} - + {saveEnabled && ( + + + + )} + ); diff --git a/src/containers/profileEditContainer.js b/src/containers/profileEditContainer.js index f11b79991..71990d26f 100644 --- a/src/containers/profileEditContainer.js +++ b/src/containers/profileEditContainer.js @@ -11,7 +11,6 @@ import { uploadImage } from '../providers/ecency/ecency'; import { profileUpdate, signImage } from '../providers/hive/dhive'; import { updateCurrentAccount } from '../redux/actions/accountAction'; import { setAvatarCacheStamp } from '../redux/actions/uiAction'; -import { transferTypes } from '../utils/wallet'; // import ROUTES from '../constants/routeNames'; @@ -53,6 +52,7 @@ class ProfileEditContainer extends Component { this.state = { isLoading: false, isUploading: false, + saveEnabled: false, about: get(props.currentAccount, 'about.profile.about'), name: get(props.currentAccount, 'about.profile.name'), location: get(props.currentAccount, 'about.profile.location'), @@ -67,7 +67,7 @@ class ProfileEditContainer extends Component { // Component Functions _handleOnItemChange = (val, item) => { - this.setState({ [item]: val }); + this.setState({ [item]: val, saveEnabled:true }); }; _uploadImage = async (media, action) => { @@ -80,7 +80,7 @@ class ProfileEditContainer extends Component { uploadImage(media, currentAccount.name, sign) .then((res) => { if (res.data && res.data.url) { - this.setState({ [action]: res.data.url, isUploading: false }); + this.setState({ [action]: res.data.url, isUploading: false, saveEnabled:true }); } }) .catch((error) => { @@ -105,7 +105,9 @@ class ProfileEditContainer extends Component { }; _handleOpenImagePicker = (action) => { - ImagePicker.openPicker(action == 'avatarUrl' ? IMAGE_PICKER_AVATAR_OPTIONS : IMAGE_PICKER_COVER_OPTIONS) + ImagePicker.openPicker( + action == 'avatarUrl' ? IMAGE_PICKER_AVATAR_OPTIONS : IMAGE_PICKER_COVER_OPTIONS, + ) .then((media) => { this._uploadImage(media, action); }) @@ -115,7 +117,9 @@ class ProfileEditContainer extends Component { }; _handleOpenCamera = (action) => { - ImagePicker.openCamera(action == 'avatarUrl' ? IMAGE_PICKER_AVATAR_OPTIONS : IMAGE_PICKER_COVER_OPTIONS) + ImagePicker.openCamera( + action == 'avatarUrl' ? IMAGE_PICKER_AVATAR_OPTIONS : IMAGE_PICKER_COVER_OPTIONS, + ) .then((media) => { this._uploadImage(media, action); }) @@ -188,6 +192,7 @@ class ProfileEditContainer extends Component { about, coverUrl, avatarUrl, + saveEnabled } = this.state; return ( @@ -207,6 +212,7 @@ class ProfileEditContainer extends Component { location, name, website, + saveEnabled }) ); } @@ -222,12 +228,11 @@ export default connect(mapStateToProps)(injectIntl(withNavigation(ProfileEditCon const IMAGE_PICKER_AVATAR_OPTIONS = { includeBase64: true, + cropping:true, width: 512, height: 512, }; const IMAGE_PICKER_COVER_OPTIONS = { includeBase64: true, - width: 1440, - height: 1440, }; diff --git a/src/screens/profileEdit/screen/profileEditScreen.js b/src/screens/profileEdit/screen/profileEditScreen.js index 72bc0a3c8..deaddc5ea 100644 --- a/src/screens/profileEdit/screen/profileEditScreen.js +++ b/src/screens/profileEdit/screen/profileEditScreen.js @@ -51,6 +51,7 @@ class ProfileEditScreen extends PureComponent { coverUrl, isLoading, isUploading, + saveEnabled, handleOnSubmit, }) => ( @@ -74,6 +75,7 @@ class ProfileEditScreen extends PureComponent { handleOnItemChange={handleOnItemChange} isLoading={isLoading} isUploading={isUploading && selectedUploadAction === 'coverUrl'} + saveEnabled={saveEnabled} handleOnSubmit={handleOnSubmit} /> From 163a4c11cd3ddadb88307ede9369cc5ed7ff303a Mon Sep 17 00:00:00 2001 From: Nouman Tahir Date: Wed, 16 Jun 2021 13:06:54 +0500 Subject: [PATCH 7/9] lint --- src/containers/profileEditContainer.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/containers/profileEditContainer.js b/src/containers/profileEditContainer.js index 71990d26f..41602d372 100644 --- a/src/containers/profileEditContainer.js +++ b/src/containers/profileEditContainer.js @@ -67,7 +67,7 @@ class ProfileEditContainer extends Component { // Component Functions _handleOnItemChange = (val, item) => { - this.setState({ [item]: val, saveEnabled:true }); + this.setState({ [item]: val, saveEnabled: true }); }; _uploadImage = async (media, action) => { @@ -80,7 +80,7 @@ class ProfileEditContainer extends Component { uploadImage(media, currentAccount.name, sign) .then((res) => { if (res.data && res.data.url) { - this.setState({ [action]: res.data.url, isUploading: false, saveEnabled:true }); + this.setState({ [action]: res.data.url, isUploading: false, saveEnabled: true }); } }) .catch((error) => { @@ -192,7 +192,7 @@ class ProfileEditContainer extends Component { about, coverUrl, avatarUrl, - saveEnabled + saveEnabled, } = this.state; return ( @@ -212,7 +212,7 @@ class ProfileEditContainer extends Component { location, name, website, - saveEnabled + saveEnabled, }) ); } @@ -228,7 +228,7 @@ export default connect(mapStateToProps)(injectIntl(withNavigation(ProfileEditCon const IMAGE_PICKER_AVATAR_OPTIONS = { includeBase64: true, - cropping:true, + cropping: true, width: 512, height: 512, }; From c20cad62b510ce19011db2b34eaed12dc0806c17 Mon Sep 17 00:00:00 2001 From: Nouman Tahir Date: Wed, 16 Jun 2021 13:20:51 +0500 Subject: [PATCH 8/9] header fine tuning --- .../avatarHeader/avatarHeaderStyles.js | 8 +++-- .../avatarHeader/avatarHeaderView.js | 36 ++++++++++--------- .../profileEdit/screen/profileEditScreen.js | 2 ++ 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/components/avatarHeader/avatarHeaderStyles.js b/src/components/avatarHeader/avatarHeaderStyles.js index 1840141d8..b58f022be 100644 --- a/src/components/avatarHeader/avatarHeaderStyles.js +++ b/src/components/avatarHeader/avatarHeaderStyles.js @@ -2,9 +2,11 @@ import EStyleSheet from 'react-native-extended-stylesheet'; export default EStyleSheet.create({ headerContainer: { - height: 100, flexDirection: 'row', - padding: 21, + paddingTop: 8, + paddingHorizontal: 24, + paddingBottom:24, + alignItems:'center' }, backIcon: { color: '$white', @@ -16,7 +18,7 @@ export default EStyleSheet.create({ alignItems: 'center', }, textWrapper: { - marginLeft: 16, + marginLeft: 24, }, name: { color: '$white', diff --git a/src/components/avatarHeader/avatarHeaderView.js b/src/components/avatarHeader/avatarHeaderView.js index 65528d563..c33b8a58d 100644 --- a/src/components/avatarHeader/avatarHeaderView.js +++ b/src/components/avatarHeader/avatarHeaderView.js @@ -8,6 +8,7 @@ import { IconButton } from '../iconButton'; // Styles import styles from './avatarHeaderStyles'; +import { TouchableOpacity } from 'react-native'; const AvatarHeader = ({ username, @@ -34,22 +35,25 @@ const AvatarHeader = ({ size={25} /> - - + + + + + {!!name && {name}} {`@${username} (${reputation})`} diff --git a/src/screens/profileEdit/screen/profileEditScreen.js b/src/screens/profileEdit/screen/profileEditScreen.js index deaddc5ea..5f07cc663 100644 --- a/src/screens/profileEdit/screen/profileEditScreen.js +++ b/src/screens/profileEdit/screen/profileEditScreen.js @@ -1,4 +1,5 @@ import React, { PureComponent, Fragment } from 'react'; +import {StatusBar} from 'react-native'; import { injectIntl } from 'react-intl'; import get from 'lodash/get'; import ActionSheet from 'react-native-actionsheet'; @@ -55,6 +56,7 @@ class ProfileEditScreen extends PureComponent { handleOnSubmit, }) => ( + Date: Wed, 16 Jun 2021 13:21:21 +0500 Subject: [PATCH 9/9] lint --- src/components/avatarHeader/avatarHeaderStyles.js | 4 ++-- src/components/avatarHeader/avatarHeaderView.js | 5 ++--- src/screens/profileEdit/screen/profileEditScreen.js | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/components/avatarHeader/avatarHeaderStyles.js b/src/components/avatarHeader/avatarHeaderStyles.js index b58f022be..9acafbef3 100644 --- a/src/components/avatarHeader/avatarHeaderStyles.js +++ b/src/components/avatarHeader/avatarHeaderStyles.js @@ -5,8 +5,8 @@ export default EStyleSheet.create({ flexDirection: 'row', paddingTop: 8, paddingHorizontal: 24, - paddingBottom:24, - alignItems:'center' + paddingBottom: 24, + alignItems: 'center', }, backIcon: { color: '$white', diff --git a/src/components/avatarHeader/avatarHeaderView.js b/src/components/avatarHeader/avatarHeaderView.js index c33b8a58d..32959effd 100644 --- a/src/components/avatarHeader/avatarHeaderView.js +++ b/src/components/avatarHeader/avatarHeaderView.js @@ -1,6 +1,6 @@ import React from 'react'; 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 { UserAvatar } from '../userAvatar'; @@ -8,7 +8,6 @@ import { IconButton } from '../iconButton'; // Styles import styles from './avatarHeaderStyles'; -import { TouchableOpacity } from 'react-native'; const AvatarHeader = ({ username, @@ -53,7 +52,7 @@ const AvatarHeader = ({ size={15} /> - + {!!name && {name}} {`@${username} (${reputation})`} diff --git a/src/screens/profileEdit/screen/profileEditScreen.js b/src/screens/profileEdit/screen/profileEditScreen.js index 5f07cc663..fa71e0702 100644 --- a/src/screens/profileEdit/screen/profileEditScreen.js +++ b/src/screens/profileEdit/screen/profileEditScreen.js @@ -1,5 +1,5 @@ import React, { PureComponent, Fragment } from 'react'; -import {StatusBar} from 'react-native'; +import { StatusBar } from 'react-native'; import { injectIntl } from 'react-intl'; import get from 'lodash/get'; import ActionSheet from 'react-native-actionsheet'; @@ -56,7 +56,7 @@ class ProfileEditScreen extends PureComponent { handleOnSubmit, }) => ( - +