[FIX] Load avatar on servers that prevent unauthenticated avatar access (#604)

App would show an empty space on servers that require authentication on avatar access
This commit is contained in:
David Lougheed 2019-02-07 14:58:20 -05:00 committed by Diego Mello
parent db0cd5abd1
commit e5930cc0fe
13 changed files with 136 additions and 50 deletions

View File

@ -12,7 +12,11 @@ export default class Avatar extends PureComponent {
size: PropTypes.number,
borderRadius: PropTypes.number,
type: PropTypes.string,
children: PropTypes.object
children: PropTypes.object,
user: PropTypes.shape({
id: PropTypes.string,
token: PropTypes.string
})
}
static defaultProps = {
@ -24,7 +28,7 @@ export default class Avatar extends PureComponent {
render() {
const {
text, size, baseUrl, borderRadius, style, avatar, type, children
text, size, baseUrl, borderRadius, style, avatar, type, children, user
} = this.props;
const avatarStyle = {
@ -38,9 +42,16 @@ export default class Avatar extends PureComponent {
}
const room = type === 'd' ? text : `@${ text }`;
// Avoid requesting several sizes by having only two sizes on cache
const uriSize = size === 100 ? 100 : 50;
const uri = avatar || `${ baseUrl }/avatar/${ room }?format=png&width=${ uriSize }&height=${ uriSize }`;
let avatarAuthURLFragment = '';
if (user && user.id && user.token) {
avatarAuthURLFragment = `&rc_token=${ user.token }&rc_uid=${ user.id }`;
}
const uri = avatar || `${ baseUrl }/avatar/${ room }?format=png&width=${ uriSize }&height=${ uriSize }${ avatarAuthURLFragment }`;
const image = (
<FastImage

View File

@ -53,7 +53,11 @@ const imagePickerConfig = {
replying: state.messages.replyMessage && !!state.messages.replyMessage.msg,
editing: state.messages.editing,
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
username: state.login.user && state.login.user.username
user: {
id: state.login.user && state.login.user.id,
username: state.login.user && state.login.user.username,
token: state.login.user && state.login.user.token
}
}), dispatch => ({
editCancel: () => dispatch(editCancelAction()),
editRequest: message => dispatch(editRequestAction(message)),
@ -68,7 +72,11 @@ export default class MessageBox extends Component {
replyMessage: PropTypes.object,
replying: PropTypes.bool,
editing: PropTypes.bool,
username: PropTypes.string,
user: PropTypes.shape({
id: PropTypes.string,
username: PropTypes.string,
token: PropTypes.string
}),
roomType: PropTypes.string,
editCancel: PropTypes.func.isRequired,
editRequest: PropTypes.func.isRequired,
@ -529,13 +537,13 @@ export default class MessageBox extends Component {
editRequest({ _id, msg: message, rid });
} else if (replying) {
const {
username, replyMessage, roomType, closeReply
user, replyMessage, roomType, closeReply
} = this.props;
const permalink = await this.getPermalink(replyMessage);
let msg = `[ ](${ permalink }) `;
// if original message wasn't sent by current user and neither from a direct room
if (username !== replyMessage.u.username && roomType !== 'd' && replyMessage.mention) {
if (user.username !== replyMessage.u.username && roomType !== 'd' && replyMessage.mention) {
msg += `@${ replyMessage.u.username } `;
}
@ -617,7 +625,7 @@ export default class MessageBox extends Component {
renderMentionItem = (item) => {
const { trackingType } = this.state;
const { baseUrl } = this.props;
const { baseUrl, user } = this.props;
if (item.username === 'all' || item.username === 'here') {
return this.renderFixedMentionItem(item);
@ -641,6 +649,7 @@ export default class MessageBox extends Component {
size={30}
type={item.username ? 'd' : 'c'}
baseUrl={baseUrl}
user={user}
/>,
<Text key='mention-item-name'>{ item.username || item.name }</Text>
]
@ -669,12 +678,12 @@ export default class MessageBox extends Component {
renderReplyPreview = () => {
const {
replyMessage, replying, closeReply, username
replyMessage, replying, closeReply, user
} = this.props;
if (!replying) {
return null;
}
return <ReplyPreview key='reply-preview' message={replyMessage} close={closeReply} username={username} />;
return <ReplyPreview key='reply-preview' message={replyMessage} close={closeReply} username={user.username} />;
};
renderFilesActions = () => {

View File

@ -174,7 +174,7 @@ export default class Message extends PureComponent {
renderAvatar = () => {
const {
header, avatar, author, baseUrl
header, avatar, author, baseUrl, user
} = this.props;
if (header) {
return (
@ -185,6 +185,7 @@ export default class Message extends PureComponent {
borderRadius={4}
avatar={avatar}
baseUrl={baseUrl}
user={user}
/>
);
}

View File

@ -125,7 +125,11 @@ const renderNumber = (unread, userMentions) => {
const attrs = ['name', 'unread', 'userMentions', 'alert', 'showLastMessage', 'type'];
@connect(state => ({
username: state.login.user && state.login.user.username,
user: {
id: state.login.user && state.login.user.id,
username: state.login.user && state.login.user.username,
token: state.login.user && state.login.user.token
},
StoreLastMessage: state.settings.Store_Last_Message,
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
}))
@ -144,7 +148,11 @@ export default class RoomItem extends React.Component {
userMentions: PropTypes.number,
id: PropTypes.string,
onPress: PropTypes.func,
username: PropTypes.string,
user: PropTypes.shape({
id: PropTypes.string,
username: PropTypes.string,
token: PropTypes.string
}),
avatarSize: PropTypes.number,
testID: PropTypes.string,
height: PropTypes.number
@ -172,14 +180,14 @@ export default class RoomItem extends React.Component {
get avatar() {
const {
type, name, avatarSize, baseUrl
type, name, avatarSize, baseUrl, user
} = this.props;
return <Avatar text={name} size={avatarSize} type={type} baseUrl={baseUrl} style={{ marginHorizontal: 15 }} />;
return <Avatar text={name} size={avatarSize} type={type} baseUrl={baseUrl} style={{ marginHorizontal: 15 }} user={user} />;
}
get lastMessage() {
const {
lastMessage, type, showLastMessage, StoreLastMessage, username
lastMessage, type, showLastMessage, StoreLastMessage, user
} = this.props;
if (!StoreLastMessage || !showLastMessage) {
@ -190,7 +198,7 @@ export default class RoomItem extends React.Component {
}
let prefix = '';
const me = lastMessage.u.username === username;
const me = lastMessage.u.username === user.username;
if (!lastMessage.msg && Object.keys(lastMessage.attachments).length > 0) {
if (me) {

View File

@ -45,11 +45,11 @@ const styles = StyleSheet.create({
});
const UserItem = ({
name, username, onPress, testID, onLongPress, style, icon, baseUrl
name, username, onPress, testID, onLongPress, style, icon, baseUrl, user
}) => (
<Touch onPress={onPress} onLongPress={onLongPress} style={styles.button} testID={testID}>
<View style={[styles.container, style]}>
<Avatar text={username} size={30} type='d' style={styles.avatar} baseUrl={baseUrl} />
<Avatar text={username} size={30} type='d' style={styles.avatar} baseUrl={baseUrl} user={user} />
<View style={styles.textContainer}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.username}>@{username}</Text>
@ -62,6 +62,10 @@ const UserItem = ({
UserItem.propTypes = {
name: PropTypes.string.isRequired,
username: PropTypes.string.isRequired,
user: PropTypes.shape({
id: PropTypes.string,
token: PropTypes.string
}),
baseUrl: PropTypes.string.isRequired,
onPress: PropTypes.func.isRequired,
testID: PropTypes.string.isRequired,

View File

@ -80,7 +80,11 @@ const styles = StyleSheet.create({
failure: state.createChannel.failure,
isFetching: state.createChannel.isFetching,
result: state.createChannel.result,
users: state.selectedUsers.users
users: state.selectedUsers.users,
user: {
id: state.login.user && state.login.user.id,
token: state.login.user && state.login.user.token
}
}), dispatch => ({
create: data => dispatch(createChannelRequestAction(data)),
removeUser: user => dispatch(removeUserAction(user))
@ -106,7 +110,11 @@ export default class CreateChannelView extends LoggedView {
failure: PropTypes.bool,
isFetching: PropTypes.bool,
result: PropTypes.object,
users: PropTypes.array.isRequired
users: PropTypes.array.isRequired,
user: PropTypes.shape({
id: PropTypes.string,
token: PropTypes.string
})
};
constructor(props) {
@ -305,7 +313,7 @@ export default class CreateChannelView extends LoggedView {
renderFormSeparator = () => <View style={[sharedStyles.separator, styles.formSeparator]} />
renderItem = ({ item }) => {
const { baseUrl } = this.props;
const { baseUrl, user } = this.props;
return (
<UserItem
@ -314,6 +322,7 @@ export default class CreateChannelView extends LoggedView {
onPress={() => this.removeUser(item)}
testID={`create-channel-view-item-${ item.name }`}
baseUrl={baseUrl}
user={user}
/>
);
}

View File

@ -49,7 +49,11 @@ const styles = StyleSheet.create({
});
@connect(state => ({
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
user: {
id: state.login.user && state.login.user.id,
token: state.login.user && state.login.user.token
}
}))
/** @extends React.Component */
export default class NewMessageView extends LoggedView {
@ -68,7 +72,11 @@ export default class NewMessageView extends LoggedView {
static propTypes = {
componentId: PropTypes.string,
baseUrl: PropTypes.string,
onPressItem: PropTypes.func.isRequired
onPressItem: PropTypes.func.isRequired,
user: PropTypes.shape({
id: PropTypes.string,
token: PropTypes.string
})
};
constructor(props) {
@ -162,7 +170,7 @@ export default class NewMessageView extends LoggedView {
renderItem = ({ item, index }) => {
const { search } = this.state;
const { baseUrl } = this.props;
const { baseUrl, user } = this.props;
let style = {};
if (index === 0) {
@ -182,6 +190,7 @@ export default class NewMessageView extends LoggedView {
baseUrl={baseUrl}
testID={`new-message-view-item-${ item.name }`}
style={style}
user={user}
/>
);
}

View File

@ -36,7 +36,8 @@ import Icons from '../../lib/Icons';
name: state.login.user && state.login.user.name,
username: state.login.user && state.login.user.username,
customFields: state.login.user && state.login.user.customFields,
emails: state.login.user && state.login.user.emails
emails: state.login.user && state.login.user.emails,
token: state.login.user && state.login.user.token
},
Accounts_CustomFields: state.settings.Accounts_CustomFields,
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
@ -326,7 +327,7 @@ export default class ProfileView extends LoggedView {
return (
<View style={styles.avatarButtons}>
{this.renderAvatarButton({
child: <Avatar text={`@${ user.username }`} size={50} baseUrl={baseUrl} />,
child: <Avatar text={`@${ user.username }`} size={50} baseUrl={baseUrl} user={user} />,
onPress: () => this.resetAvatar(),
key: 'profile-view-reset-avatar'
})}
@ -345,7 +346,7 @@ export default class ProfileView extends LoggedView {
const { url, blob, contentType } = avatarSuggestions[service];
return this.renderAvatarButton({
key: `profile-view-avatar-${ service }`,
child: <Avatar avatar={url} size={50} baseUrl={baseUrl} />,
child: <Avatar avatar={url} size={50} baseUrl={baseUrl} user={user} />,
onPress: () => this.setAvatar({
url, data: blob, service, contentType
})
@ -419,7 +420,7 @@ export default class ProfileView extends LoggedView {
const {
name, username, email, newPassword, avatarUrl, customFields, avatar, saving, showPasswordAlert
} = this.state;
const { baseUrl } = this.props;
const { baseUrl, user } = this.props;
return (
<KeyboardView
@ -438,6 +439,7 @@ export default class ProfileView extends LoggedView {
avatar={avatar && avatar.url}
size={100}
baseUrl={baseUrl}
user={user}
/>
</View>
<RCTextInput

View File

@ -27,8 +27,10 @@ import scrollPersistTaps from '../../utils/scrollPersistTaps';
const renderSeparator = () => <View style={styles.separator} />;
@connect(state => ({
userId: state.login.user && state.login.user.id,
username: state.login.user && state.login.user.username,
user: {
id: state.login.user && state.login.user.id,
token: state.login.user && state.login.user.token
},
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
room: state.room
}), dispatch => ({
@ -50,8 +52,10 @@ export default class RoomActionsView extends LoggedView {
baseUrl: PropTypes.string,
rid: PropTypes.string,
componentId: PropTypes.string,
userId: PropTypes.string,
username: PropTypes.string,
user: PropTypes.shape({
id: PropTypes.string,
token: PropTypes.string
}),
room: PropTypes.object,
leaveRoom: PropTypes.func
}
@ -333,10 +337,10 @@ export default class RoomActionsView extends LoggedView {
updateRoomMember = async() => {
const { room } = this.state;
const { rid } = room;
const { userId } = this.props;
const { user } = this.props;
try {
const member = await RocketChat.getRoomMember(rid, userId);
const member = await RocketChat.getRoomMember(rid, user.id);
this.setState({ member: member || {} });
} catch (e) {
log('RoomActions updateRoomMember', e);
@ -391,7 +395,7 @@ export default class RoomActionsView extends LoggedView {
renderRoomInfo = ({ item }) => {
const { room, member } = this.state;
const { name, t, topic } = room;
const { baseUrl } = this.props;
const { baseUrl, user } = this.props;
return (
this.renderTouchableItem([
@ -402,6 +406,7 @@ export default class RoomActionsView extends LoggedView {
style={styles.avatar}
type={t}
baseUrl={baseUrl}
user={user}
>
{t === 'd' ? <Status style={sharedStyles.status} id={member._id} /> : null }
</Avatar>,

View File

@ -34,7 +34,10 @@ const getRoomTitle = room => (room.t === 'd'
@connect(state => ({
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
userId: state.login.user && state.login.user.id,
user: {
id: state.login.user && state.login.user.id,
token: state.login.user && state.login.user.token
},
activeUsers: state.activeUsers, // TODO: remove it
Message_TimeFormat: state.settings.Message_TimeFormat,
allRoles: state.roles,
@ -55,7 +58,10 @@ export default class RoomInfoView extends LoggedView {
static propTypes = {
componentId: PropTypes.string,
rid: PropTypes.string,
userId: PropTypes.string,
user: PropTypes.shape({
id: PropTypes.string,
token: PropTypes.string
}),
baseUrl: PropTypes.string,
activeUsers: PropTypes.object,
Message_TimeFormat: PropTypes.string,
@ -105,8 +111,8 @@ export default class RoomInfoView extends LoggedView {
if (room) {
if (room.t === 'd') {
try {
const { userId, activeUsers } = this.props;
const roomUser = await RocketChat.getRoomMember(room.rid, userId);
const { user, activeUsers } = this.props;
const roomUser = await RocketChat.getRoomMember(room.rid, user.id);
this.setState({ roomUser: roomUser || {} });
const username = room.name;
@ -120,7 +126,7 @@ export default class RoomInfoView extends LoggedView {
// get all users roles
// needs to be changed by a better method
const allUsersRoles = await RocketChat.getUserRoles();
const userRoles = allUsersRoles.find(user => user.username === username);
const userRoles = allUsersRoles.find(u => u.username === username);
if (userRoles) {
this.setState({ roles: userRoles.roles || [] });
}
@ -246,7 +252,7 @@ export default class RoomInfoView extends LoggedView {
}
renderAvatar = (room, roomUser) => {
const { baseUrl } = this.props;
const { baseUrl, user } = this.props;
return (
<Avatar
@ -255,6 +261,7 @@ export default class RoomInfoView extends LoggedView {
style={styles.avatar}
type={room.t}
baseUrl={baseUrl}
user={user}
>
{room.t === 'd' ? <Status style={[sharedStyles.status, styles.status]} id={roomUser._id} /> : null}
</Avatar>

View File

@ -23,7 +23,11 @@ import SearchBox from '../../containers/SearchBox';
@connect(state => ({
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
room: state.room
room: state.room,
user: {
id: state.login.user && state.login.user.id,
token: state.login.user && state.login.user.token
}
}))
/** @extends React.Component */
export default class RoomMembersView extends LoggedView {
@ -48,7 +52,11 @@ export default class RoomMembersView extends LoggedView {
rid: PropTypes.string,
members: PropTypes.array,
baseUrl: PropTypes.string,
room: PropTypes.object
room: PropTypes.object,
user: PropTypes.shape({
id: PropTypes.string,
token: PropTypes.string
})
}
constructor(props) {
@ -243,7 +251,7 @@ export default class RoomMembersView extends LoggedView {
renderSeparator = () => <View style={styles.separator} />;
renderItem = ({ item }) => {
const { baseUrl } = this.props;
const { baseUrl, user } = this.props;
return (
<UserItem
@ -253,6 +261,7 @@ export default class RoomMembersView extends LoggedView {
onLongPress={() => this.onLongPressUser(item)}
baseUrl={baseUrl}
testID={`room-members-view-item-${ item.username }`}
user={user}
/>
);
}

View File

@ -39,7 +39,11 @@ const styles = StyleSheet.create({
@connect(state => ({
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
users: state.selectedUsers.users,
loading: state.selectedUsers.loading
loading: state.selectedUsers.loading,
user: {
id: state.login.user && state.login.user.id,
token: state.login.user && state.login.user.token
}
}), dispatch => ({
addUser: user => dispatch(addUserAction(user)),
removeUser: user => dispatch(removeUserAction(user)),
@ -58,7 +62,11 @@ export default class SelectedUsersView extends LoggedView {
reset: PropTypes.func.isRequired,
users: PropTypes.array,
loading: PropTypes.bool,
setLoadingInvite: PropTypes.func
setLoadingInvite: PropTypes.func,
user: PropTypes.shape({
id: PropTypes.string,
token: PropTypes.string
})
};
constructor(props) {
@ -209,7 +217,7 @@ export default class SelectedUsersView extends LoggedView {
}
renderSelectedItem = ({ item }) => {
const { baseUrl } = this.props;
const { baseUrl, user } = this.props;
return (
<UserItem
name={item.fname}
@ -218,6 +226,7 @@ export default class SelectedUsersView extends LoggedView {
testID={`selected-user-${ item.name }`}
baseUrl={baseUrl}
style={{ paddingRight: 15 }}
user={user}
/>
);
}
@ -226,7 +235,7 @@ export default class SelectedUsersView extends LoggedView {
renderItem = ({ item, index }) => {
const { search } = this.state;
const { baseUrl } = this.props;
const { baseUrl, user } = this.props;
const name = item.search ? item.name : item.fname;
const username = item.search ? item.username : item.name;
@ -249,6 +258,7 @@ export default class SelectedUsersView extends LoggedView {
icon={this.isChecked(username) ? 'check' : null}
baseUrl={baseUrl}
style={style}
user={user}
/>
);
}

View File

@ -90,7 +90,8 @@ const keyExtractor = item => item.id;
id: state.login.user && state.login.user.id,
language: state.login.user && state.login.user.language,
status: state.login.user && state.login.user.status,
username: state.login.user && state.login.user.username
username: state.login.user && state.login.user.username,
token: state.login.user && state.login.user.token
},
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
}), dispatch => ({
@ -340,6 +341,7 @@ export default class Sidebar extends Component {
size={30}
style={styles.avatar}
baseUrl={baseUrl}
user={user}
/>
<View style={styles.headerTextContainer}>
<View style={styles.headerUsername}>