Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
thalia
ThaliApp
Commits
c51c51a2
Verified
Commit
c51c51a2
authored
Jun 13, 2019
by
Sébastiaan Versteeg
Browse files
Refactor avatar modal to component
parent
f5f87ec0
Changes
20
Show whitespace changes
Inline
Side-by-side
__tests__/sagas/session.spec.js
View file @
c51c51a2
...
...
@@ -6,7 +6,7 @@ import AsyncStorage from '@react-native-community/async-storage';
import
{
Sentry
}
from
'
react-native-sentry
'
;
import
sessionSaga
,
{
DISPLAYNAMEKEY
,
PHOTOKEY
,
TOKENKEY
,
USERNAMEKEY
,
DISPLAYNAMEKEY
,
IDENTIFIERKEY
,
PHOTOKEY
,
TOKENKEY
,
USERNAMEKEY
,
}
from
'
../../app/sagas/session
'
;
import
{
apiRequest
}
from
'
../../app/utils/url
'
;
import
*
as
sessionActions
from
'
../../app/actions/session
'
;
...
...
@@ -141,19 +141,21 @@ describe('session saga', () => {
it
(
'
should put the result data when the request succeeds
'
,
()
=>
expectSaga
(
sessionSaga
)
.
provide
([
[
matchers
.
call
.
like
({
fn
:
apiRequest
,
args
:
[
'
members/me
'
]
}),
{
pk
:
12
,
display_name
:
'
Johnny Test
'
,
avatar
:
{
medium
:
'
http://example.org/photo.png
'
,
},
}],
])
.
put
(
sessionActions
.
setUserInfo
(
'
Johnny Test
'
,
'
http://example.org/photo.png
'
))
.
put
(
sessionActions
.
setUserInfo
(
12
,
'
Johnny Test
'
,
'
http://example.org/photo.png
'
))
.
dispatch
(
sessionActions
.
fetchUserInfo
())
.
silentRun
());
it
(
'
should save the token in the AsyncStorage when the request succeeds
'
,
()
=>
expectSaga
(
sessionSaga
)
.
provide
([
[
matchers
.
call
.
like
({
fn
:
apiRequest
,
args
:
[
'
members/me
'
]
}),
{
pk
:
12
,
display_name
:
'
Johnny Test
'
,
avatar
:
{
medium
:
'
http://example.org/photo.png
'
,
...
...
@@ -164,6 +166,7 @@ describe('session saga', () => {
.
silentRun
()
.
then
(()
=>
{
expect
(
AsyncStorage
.
multiSet
).
toBeCalledWith
([
[
IDENTIFIERKEY
,
12
],
[
DISPLAYNAMEKEY
,
'
Johnny Test
'
],
[
PHOTOKEY
,
'
http://example.org/photo.png
'
],
]);
...
...
app/actions/profile.js
View file @
c51c51a2
...
...
@@ -2,9 +2,11 @@ export const PROFILE = 'PROFILE_PROFILE';
export
const
FETCHING
=
'
PROFILE_FETCHING
'
;
export
const
SUCCESS
=
'
PROFILE_SUCCESS
'
;
export
const
FAILURE
=
'
PROFILE_FAILURE
'
;
export
const
UPDATE
=
'
PROFILE_UPDATE
'
;
export
const
UPDATING
=
'
PROFILE_UPDATING
'
;
export
const
UPDATE_SUCCESS
=
'
PROFILE_UPDATE_SUCCESS
'
;
export
const
UPDATE_FAIL
=
'
PROFILE_UPDATE_FAIL
'
;
export
const
CHANGE_AVATAR
=
'
PROFILE_CHANGE_AVATAR
'
;
export
function
profile
(
member
=
'
me
'
)
{
return
{
...
...
@@ -19,6 +21,18 @@ export function fetching() {
};
}
export
function
changeAvatar
()
{
return
{
type
:
CHANGE_AVATAR
,
};
}
export
function
update
()
{
return
{
type
:
UPDATE
,
};
}
export
function
updating
()
{
return
{
type
:
UPDATING
,
...
...
app/actions/session.js
View file @
c51c51a2
...
...
@@ -30,6 +30,6 @@ export function fetchUserInfo() {
return
{
type
:
FETCH_USER_INFO
};
}
export
function
setUserInfo
(
displayName
,
photo
,
pk
)
{
return
{
type
:
SET_USER_INFO
,
payload
:
{
displayName
,
photo
,
pk
}
};
export
function
setUserInfo
(
pk
,
displayName
,
photo
)
{
return
{
type
:
SET_USER_INFO
,
payload
:
{
pk
,
displayName
,
photo
}
};
}
app/reducers/profile.js
View file @
c51c51a2
...
...
@@ -6,7 +6,6 @@ const initialState = {
profile
:
{
pk
:
-
1
,
display_name
:
''
,
photo
:
defaultProfileImage
,
avatar
:
{
full
:
defaultProfileImage
,
large
:
defaultProfileImage
,
...
...
@@ -52,17 +51,11 @@ export default function profile(state = initialState, action = {}) {
...
state
,
updating
:
true
,
};
case
profileActions
.
UPDATE_SUCCESS
:
return
{
...
state
,
updating
:
false
,
success
:
true
,
};
case
profileActions
.
UPDATE_FAIL
:
case
profileActions
.
UPDATE_SUCCESS
:
return
{
...
state
,
updating
:
false
,
success
:
true
,
};
default
:
return
state
;
...
...
app/reducers/session.js
View file @
c51c51a2
...
...
@@ -32,9 +32,9 @@ export default function session(state = initialState, action = {}) {
case
sessionActions
.
SET_USER_INFO
:
return
{
...
state
,
pk
:
action
.
payload
.
pk
,
displayName
:
action
.
payload
.
displayName
,
photo
:
action
.
payload
.
photo
,
pk
:
action
.
payload
.
pk
,
};
case
sessionActions
.
TOKEN_INVALID
:
case
sessionActions
.
SIGN_OUT
:
...
...
app/sagas/profile.js
View file @
c51c51a2
import
ImagePicker
from
'
react-native-image-picker
'
;
import
{
call
,
put
,
select
,
takeEvery
,
call
,
put
,
select
,
takeEvery
,
cps
}
from
'
redux-saga/effects
'
;
import
{
Sentry
}
from
'
react-native-sentry
'
;
import
i18next
from
'
../utils/i18n
'
;
import
{
apiRequest
}
from
'
../utils/url
'
;
import
*
as
profileActions
from
'
../actions/profile
'
;
import
{
tokenSelector
}
from
'
../selectors/session
'
;
const
t
=
i18next
.
getFixedT
(
undefined
,
'
sagas/profile
'
);
const
openImageLibrary
=
options
=>
new
Promise
((
resolve
,
reject
)
=>
{
ImagePicker
.
launchImageLibrary
(
options
,
(
response
)
=>
{
if
(
response
.
error
||
response
.
didCancel
)
{
reject
(
response
);
}
resolve
(
response
);
});
});
function
*
profile
(
action
)
{
const
{
member
}
=
action
.
payload
;
const
token
=
yield
select
(
tokenSelector
);
...
...
@@ -31,30 +44,49 @@ function* profile(action) {
}
}
function
*
uploadProfilePicture
(
action
)
{
const
{
member
}
=
action
.
payload
;
const
token
=
yield
select
(
tokenSelector
);
yield
put
(
profileActions
.
updating
());
const
data
=
{
method
:
'
PUT
'
,
headers
:
{
Accept
:
'
application/json
'
,
'
Content-Type
'
:
'
application/json
'
,
Authorization
:
`Token
${
token
}
`
,
function
*
updateAvatar
()
{
const
options
=
{
title
:
t
(
'
Change profile picture
'
),
storageOptions
:
{
skipBackup
:
true
,
path
:
'
images
'
,
},
};
}
try
{
const
profileData
=
yield
call
(
apiRequest
,
`members/
${
member
}
`
,
data
);
yield
put
(
profileActions
.
updateSuccess
(
profileData
));
}
catch
(
error
)
{
Sentry
.
captureException
(
error
);
yield
put
(
profileActions
.
updateFail
());
const
response
=
yield
call
(
openImageLibrary
,
options
);
const
source
=
response
.
uri
;
console
.
log
(
source
);
}
catch
(
e
)
{
// eat error, om nom nom
// error from the picker that we cannot do anything about
}
// const { member } = action.payload;
// const token = yield select(tokenSelector);
// yield put(profileActions.updating());
//
// const data = {
// method: 'PUT',
// headers: {
// Accept: 'application/json',
// 'Content-Type': 'application/json',
// Authorization: `Token ${token}`,
// },
// };
//
// try {
// const profileData = yield call(apiRequest, `members/${member}`, data);
// yield put(profileActions.updateSuccess(profileData));
// } catch (error) {
// Sentry.captureException(error);
// yield put(profileActions.updateFail());
// }
}
function
*
event
Saga
()
{
function
*
profile
Saga
()
{
yield
takeEvery
(
profileActions
.
PROFILE
,
profile
);
yield
takeEvery
(
profileActions
.
CHANGE_AVATAR
,
updateAvatar
);
}
export
default
profileSaga
;
app/sagas/session.js
View file @
c51c51a2
...
...
@@ -12,6 +12,7 @@ import * as sessionActions from '../actions/session';
import
*
as
pushNotificationsActions
from
'
../actions/pushNotifications
'
;
import
{
tokenSelector
}
from
'
../selectors/session
'
;
export
const
IDENTIFIERKEY
=
'
@MyStore:identifier
'
;
export
const
USERNAMEKEY
=
'
@MyStore:username
'
;
export
const
TOKENKEY
=
'
@MyStore:token
'
;
export
const
DISPLAYNAMEKEY
=
'
@MyStore:displayName
'
;
...
...
@@ -29,10 +30,11 @@ const t = i18next.getFixedT(undefined, 'sagas/session');
function
*
init
()
{
try
{
const
result
=
yield
call
([
AsyncStorage
,
'
multiGet
'
],
[
USERNAMEKEY
,
TOKENKEY
,
DISPLAYNAMEKEY
,
PHOTOKEY
,
PUSHCATEGORYKEY
,
IDENTIFIERKEY
,
USERNAMEKEY
,
TOKENKEY
,
DISPLAYNAMEKEY
,
PHOTOKEY
,
PUSHCATEGORYKEY
,
]);
const
values
=
result
.
reduce
(
pairsToObject
,
{});
const
id
=
values
[
IDENTIFIERKEY
];
const
username
=
values
[
USERNAMEKEY
];
const
token
=
values
[
TOKENKEY
];
const
displayName
=
values
[
DISPLAYNAMEKEY
];
...
...
@@ -41,7 +43,7 @@ function* init() {
if
(
username
!==
null
&&
token
!==
null
)
{
yield
put
(
sessionActions
.
signedIn
(
username
,
token
));
yield
put
(
sessionActions
.
setUserInfo
(
displayName
,
photo
));
yield
put
(
sessionActions
.
setUserInfo
(
id
,
displayName
,
photo
));
yield
put
(
sessionActions
.
fetchUserInfo
());
yield
put
(
pushNotificationsActions
.
register
(
pushCategories
));
}
else
{
...
...
@@ -125,10 +127,13 @@ function* userInfo() {
const
userProfile
=
yield
call
(
apiRequest
,
'
members/me
'
,
data
);
yield
call
(
AsyncStorage
.
multiSet
,
[
[
IDENTIFIERKEY
,
userProfile
.
pk
],
[
DISPLAYNAMEKEY
,
userProfile
.
display_name
],
[
PHOTOKEY
,
userProfile
.
avatar
.
medium
],
]);
yield
put
(
sessionActions
.
setUserInfo
(
userProfile
.
display_name
,
userProfile
.
avatar
.
medium
,
userProfile
.
pk
));
yield
put
(
sessionActions
.
setUserInfo
(
userProfile
.
pk
,
userProfile
.
display_name
,
userProfile
.
avatar
.
medium
,
));
}
catch
(
error
)
{
Sentry
.
captureException
(
error
);
}
...
...
app/ui/components/button/IconButton.js
0 → 100644
View file @
c51c51a2
import
React
from
'
react
'
;
import
{
TouchableOpacity
,
ViewPropTypes
,
}
from
'
react-native
'
;
import
Icon
from
'
react-native-vector-icons/MaterialIcons
'
;
import
PropTypes
from
'
prop-types
'
;
import
Colors
from
'
../../style/Colors
'
;
const
IconButton
=
props
=>
(
<
TouchableOpacity
disabled
=
{
props
.
disabled
}
onPress
=
{
props
.
disabled
?
null
:
props
.
onPress
}
>
<
Icon
name
=
{
props
.
name
}
style
=
{
props
.
style
}
color
=
{
props
.
color
}
size
=
{
props
.
size
}
/
>
<
/TouchableOpacity
>
);
IconButton
.
propTypes
=
{
name
:
PropTypes
.
string
.
isRequired
,
onPress
:
PropTypes
.
func
.
isRequired
,
color
:
PropTypes
.
string
,
disabled
:
PropTypes
.
bool
,
size
:
PropTypes
.
number
,
style
:
Icon
.
propTypes
.
style
,
};
IconButton
.
defaultProps
=
{
color
:
Colors
.
white
,
disabled
:
false
,
size
:
24
,
style
:
Icon
.
defaultProps
.
style
,
};
export
default
IconButton
;
app/ui/components/searchHeader/SearchHeader.js
View file @
c51c51a2
import
React
,
{
Component
}
from
'
react
'
;
import
{
Animated
,
BackHandler
,
Easing
,
Platform
,
Text
,
TextInput
,
TouchableOpacity
,
View
,
SafeAreaView
,
Animated
,
BackHandler
,
Easing
,
Platform
,
SafeAreaView
,
Text
,
TextInput
,
View
,
}
from
'
react-native
'
;
import
PropTypes
from
'
prop-types
'
;
import
StatusBar
from
'
@react-native-community/status-bar
'
;
import
Icon
from
'
react-native-vector-icons/MaterialIcons
'
;
import
styles
from
'
./style/SearchHeader
'
;
import
Colors
from
'
../../style/Colors
'
;
import
IconButton
from
'
../button/IconButton
'
;
class
SearchHeader
extends
Component
{
constructor
(
props
)
{
...
...
@@ -43,15 +34,11 @@ class SearchHeader extends Component {
const
{
leftIcon
,
leftIconAction
}
=
this
.
props
;
const
{
isSearching
}
=
this
.
state
;
return
(
<
TouchableOpacity
<
IconButton
onPress
=
{()
=>
(
isSearching
?
this
.
updateSearch
(
false
)
:
leftIconAction
())}
>
<
Icon
name
=
{
isSearching
?
'
arrow-back
'
:
leftIcon
}
style
=
{[
styles
.
leftIcon
,
isSearching
?
styles
.
magenta
:
styles
.
white
]}
size
=
{
24
}
/
>
<
/TouchableOpacity
>
);
};
...
...
@@ -80,27 +67,19 @@ class SearchHeader extends Component {
const
{
searchKey
,
isSearching
}
=
this
.
state
;
if
(
!
isSearching
)
{
return
(
<
TouchableOpacity
<
IconButton
onPress
=
{()
=>
this
.
updateSearch
(
true
)}
>
<
Icon
name
=
"
search
"
style
=
{[
styles
.
rightIcon
,
styles
.
white
]}
size
=
{
24
}
/
>
<
/TouchableOpacity
>
);
}
if
(
searchKey
)
{
return
(
<
TouchableOpacity
<
IconButton
onPress
=
{()
=>
this
.
updateSearchKey
(
''
)}
>
<
Icon
name
=
"
close
"
style
=
{[
styles
.
rightIcon
,
styles
.
gray
]}
size
=
{
24
}
/
>
<
/TouchableOpacity
>
);
}
return
null
;
...
...
app/ui/screens/profile/AchievementSection.js
View file @
c51c51a2
...
...
@@ -4,7 +4,7 @@ import { withTranslation } from 'react-i18next';
import
{
Text
,
View
}
from
'
react-native
'
;
import
Moment
from
'
moment
'
;
import
CardSection
from
'
../../components/cardSection/CardSection
'
;
import
styles
from
'
./style/Profile
'
;
import
styles
from
'
./style/Profile
Screen
'
;
const
AchievementSection
=
({
profile
,
t
,
type
})
=>
{
const
memberships
=
profile
[
type
];
...
...
app/ui/screens/profile/AvatarModal.js
0 → 100644
View file @
c51c51a2
import
PropTypes
from
'
prop-types
'
;
import
React
from
'
react
'
;
import
{
Image
,
Modal
,
View
}
from
'
react-native
'
;
import
styles
from
'
./style/AvatarModal
'
;
import
IconButton
from
'
../../components/button/IconButton
'
;
const
AvatarModal
=
props
=>
(
<
Modal
visible
=
{
props
.
visible
}
transparent
animationType
=
"
fade
"
>
<
View
style
=
{
styles
.
background
}
>
<
Image
source
=
{{
uri
:
props
.
image
}}
style
=
{
styles
.
image
}
/
>
<
View
style
=
{
styles
.
buttonLayout
}
>
{
props
.
canEdit
&&
(
<
IconButton
name
=
"
edit
"
onPress
=
{
props
.
changeAvatar
}
/
>
)}
<
IconButton
name
=
"
cancel
"
onPress
=
{
props
.
close
}
/
>
<
/View
>
<
/View
>
<
/Modal
>
);
AvatarModal
.
defaultProps
=
{
visible
:
false
,
canEdit
:
false
,
};
AvatarModal
.
propTypes
=
{
visible
:
PropTypes
.
bool
,
canEdit
:
PropTypes
.
bool
,
image
:
PropTypes
.
string
.
isRequired
,
close
:
PropTypes
.
func
.
isRequired
,
changeAvatar
:
PropTypes
.
func
.
isRequired
,
};
export
default
AvatarModal
;
app/ui/screens/profile/DescriptionSection.js
View file @
c51c51a2
...
...
@@ -3,7 +3,7 @@ import React from 'react';
import
{
withTranslation
}
from
'
react-i18next
'
;
import
{
Text
}
from
'
react-native
'
;
import
CardSection
from
'
../../components/cardSection/CardSection
'
;
import
styles
from
'
./style/Profile
'
;
import
styles
from
'
./style/Profile
Screen
'
;
const
DescriptionSection
=
(
{
profile
:
{
display_name
:
name
,
profile_description
:
description
},
t
},
...
...
app/ui/screens/profile/PersonalInfoSection.js
View file @
c51c51a2
...
...
@@ -4,7 +4,7 @@ import { withTranslation } from 'react-i18next';
import
{
Text
,
View
}
from
'
react-native
'
;
import
Moment
from
'
moment
'
;
import
CardSection
from
'
../../components/cardSection/CardSection
'
;
import
styles
from
'
./style/Profile
'
;
import
styles
from
'
./style/Profile
Screen
'
;
const
PersonalInfoSection
=
({
profile
,
t
,
openUrl
})
=>
{
const
profileInfo
=
{
...
...
app/ui/screens/profile/ProfileScreen.js
View file @
c51c51a2
import
PropTypes
from
'
prop-types
'
;
import
React
,
{
Component
}
from
'
react
'
;
import
{
Animated
,
ImageBackground
,
Platform
,
ScrollView
,
TouchableOpacity
,
TouchableWithoutFeedback
,
View
,
Image
,
Modal
,
Animated
,
ImageBackground
,
Platform
,
ScrollView
,
TouchableHighlight
,
View
,
}
from
'
react-native
'
;
import
StatusBar
from
'
@react-native-community/status-bar
'
;
import
{
withTranslation
}
from
'
react-i18next
'
;
import
LinearGradient
from
'
react-native-linear-gradient
'
;
import
Icon
from
'
react-native-vector-icons/MaterialIcons
'
;
import
Moment
from
'
moment
'
;
import
ImagePicker
from
'
react-native-image-picker
'
;
import
StandardHeader
from
'
../../components/standardHeader/StandardHeader
'
;
import
LoadingScreen
from
'
../../components/loadingScreen/LoadingScreen
'
;
import
ErrorScreen
from
'
../../components/errorScreen/ErrorScreen
'
;
import
LoadingScreen
from
'
../../components/loadingScreen/LoadingScreen
'
;
import
StandardHeader
from
'
../../components/standardHeader/StandardHeader
'
;
import
{
STATUSBAR_HEIGHT
}
from
'
../../components/standardHeader/style/StandardHeader
'
;
import
Colors
from
'
../../style/Colors
'
;
import
AvatarModal
from
'
./AvatarModal
'
;
import
AchievementSection
from
'
./AchievementSection
'
;
import
DescriptionSection
from
'
./DescriptionSection
'
;
import
PersonalInfoSection
from
'
./PersonalInfoSection
'
;
import
styles
,
{
HEADER_MAX_HEIGHT
,
HEADER_MIN_HEIGHT
,
HEADER_SCROLL_DISTANCE
,
}
from
'
./style/Profile
'
;
import
styles
,
{
HEADER_MAX_HEIGHT
,
HEADER_MIN_HEIGHT
,
HEADER_SCROLL_DISTANCE
}
from
'
./style/ProfileScreen
'
;
import
IconButton
from
'
../../components/button/IconButton
'
;
class
ProfileScreen
extends
Component
{
constructor
(
props
)
{
...
...
@@ -43,8 +27,21 @@ class ProfileScreen extends Component {
this
.
textHeight
=
Platform
.
OS
===
'
android
'
?
27
:
22
;
this
.
state
=
{
modalVisible
:
false
,
image
:
this
.
props
.
profile
.
avatar
.
full
,
headerEnabled
:
true
,
};
this
.
scrollY
.
addListener
(({
value
})
=>
{
console
.
log
(
'
scrolling
'
,
value
,
this
.
state
.
headerEnabled
,
value
>
(
HEADER_SCROLL_DISTANCE
/
2
));
if
(
this
.
state
.
headerEnabled
&&
value
>
(
HEADER_SCROLL_DISTANCE
/
2
))
{
this
.
setState
({
headerEnabled
:
false
,
});
}
else
if
(
!
this
.
state
.
headerEnabled
&&
value
<
(
HEADER_SCROLL_DISTANCE
/
2
))
{
this
.
setState
({
headerEnabled
:
true
,
});
}
});
}
setModalVisible
=
(
visible
)
=>
{
...
...
@@ -53,9 +50,7 @@ class ProfileScreen extends Component {
});
};
isOwnProfilePage
=
()
=>
{
return
this
.
props
.
pk
===
this
.
props
.
profile
.
pk
;
};
isOwnProfilePage
=
()
=>
this
.
props
.
userPk
===
this
.
props
.
profile
.
pk
;
getAppbar
=
()
=>
{
const
headerHeight
=
this
.
scrollY
.
interpolate
({
...
...
@@ -120,9 +115,6 @@ class ProfileScreen extends Component {
}
return
(
<
TouchableWithoutFeedback
onPress
=
{()
=>
this
.
setModalVisible
(
true
)}
>
<
Animated
.
View
style
=
{[
styles
.
header
,
{
height
:
headerHeight
}]}
>
<
Animated
.
View
style
=
{[
...
...
@@ -134,61 +126,37 @@ class ProfileScreen extends Component {
]}
>
<
ImageBackground
source
=
{{
uri
:
this
.
state
.
image
}}
source
=
{{
uri
:
this
.
props
.
profile
.
avatar
.
full
}}
style
=
{
styles
.
backgroundImage
}
resizeMode
=
"
cover
"
>
<
LinearGradient
colors
=
{[
'
#55000000
'
,
'
#000000
'
]}
style
=
{
styles
.
overlayGradient
}
/
>
<
LinearGradient
colors
=
{[
'
#55000000
'
,
'
#000000
'
]}
style
=
{
styles
.
overlayGradient
}
/
>
<
/ImageBackground
>
<
/Animated.View
>
<
Animated
.
View
style
=
{[
styles
.
appBar
,
appBarBorderStyle
]}
>
<
TouchableOpacity
onPress
=
{
this
.
props
.
goBack
}
>
<
Icon
name
=
"
arrow-back
"
style
=
{
styles
.
icon
}
size
=
{
24
}
/
>
<
/TouchableOpacity
>
<
Animated
.
Text
style
=
{[
styles
.
title
,
textStyle
]}
>
{
this
.
props
.
profile
.
display_name
}
<
/Animated.Text
>
<
TouchableHighlight
activeOpacity
=
{
0
}
style
=
{
styles
.
touchableHeader
}
underlayColor
=
{
this
.
state
.
headerEnabled
?
Colors
.
semiTransparent
:
Colors
.
transparent
}
onPress
=
{()
=>
(
this
.
state
.
headerEnabled
?
this
.
setModalVisible
(
true
)
:
null
)}
>
<
View
style
=
{
styles
.
touchableHeader
}
/
>
<
/TouchableHighlight
>
<
IconButton
onPress
=
{
this
.
props
.
goBack
}
name
=
"
arrow-back
"
style
=
{
styles
.
icon
}
/
>
<
/Animated.View
>
<
/Animated.View
>
<
/TouchableWithoutFeedback
>
);
};
handleEditPress
=
()
=>
{
const
options
=
{
title
:
'
Select Avatar
'
,
storageOptions
:
{
skipBackup
:
true
,
path
:
'
images
'
,
},
};
ImagePicker
.
launchImageLibrary
(
options
,
(
response
)
=>
{
console
.
log
(
'
Response =
'
,
response
);
if
(
response
.
didCancel
)
{
console
.
log
(
'
User cancelled image picker
'
);
}
else
if
(
response
.
error
)
{
console
.
log
(
'
ImagePicker Error:
'
,
response
.
error
);
}
else
if
(
response
.
customButton
)
{