* Add automatic and manual timezone support Update users timezone and scrollTo selected timezone in SelectTimezone coponent Clean styles for SelectTimezone screen Add auto-timezone update when login or enter app Hide timezone feature behind config Fix requested changes Parse SupportedTimezone from config Add localTime and localized post stamps Add trailing commas Include all timezone util methods from redux Remove EnableTimezoneSelection flag WIP get timezones from server Pull supportedTimezones from api Minor fixes Remove wrapWithPreventDoubleTap Revert back to react-intl formatDate Include timeZone prop in FormattedTime Refactor Timezone row into component Minor cosmetic changes Add minimum server support for the timezone feature Move getSupportedTimezones to componentWillMount Move autoUpdateTimezone function to handleSuccessfulLogin Specify user timezone in profile_header Remove format props from FormattedTime Add ExperimentalTimezone flag Replace Client().getServerVersion() with entities.general.serverVersion Add isTimezoneEnabled helper function Move isMinimumServerVersion to utils/timezone.js * Fix style errors * Remove date-time-format-timezone polyfill * Feedback changes * Use timezone selector from redux * Explicitly pass hour12 props to intl.formatDate * Update package-lock * Revert iOS project file changes * Fix license header * Include timezone related paths in modulePaths * Fix license header * Fix minor issue with rebasing * Fix issue with getconfig in GeneralSettings * Update package-lock
284 lines
8.7 KiB
JavaScript
284 lines
8.7 KiB
JavaScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import React, {PureComponent} from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import {
|
|
ScrollView,
|
|
Text,
|
|
View,
|
|
Linking,
|
|
} from 'react-native';
|
|
import {intlShape} from 'react-intl';
|
|
|
|
import {displayUsername} from 'mattermost-redux/utils/user_utils';
|
|
import {getUserCurrentTimezone} from 'mattermost-redux/utils/timezone_utils';
|
|
|
|
import ProfilePicture from 'app/components/profile_picture';
|
|
import FormattedText from 'app/components/formatted_text';
|
|
import FormattedTime from 'app/components/formatted_time';
|
|
import StatusBar from 'app/components/status_bar';
|
|
import {alertErrorWithFallback} from 'app/utils/general';
|
|
import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
|
|
|
|
import UserProfileRow from './user_profile_row';
|
|
import Config from 'assets/config';
|
|
|
|
export default class UserProfile extends PureComponent {
|
|
static propTypes = {
|
|
actions: PropTypes.shape({
|
|
makeDirectChannel: PropTypes.func.isRequired,
|
|
setChannelDisplayName: PropTypes.func.isRequired,
|
|
}).isRequired,
|
|
config: PropTypes.object.isRequired,
|
|
currentDisplayName: PropTypes.string,
|
|
navigator: PropTypes.object,
|
|
teammateNameDisplay: PropTypes.string,
|
|
theme: PropTypes.object.isRequired,
|
|
user: PropTypes.object.isRequired,
|
|
militaryTime: PropTypes.bool.isRequired,
|
|
enableTimezone: PropTypes.bool.isRequired,
|
|
};
|
|
|
|
static contextTypes = {
|
|
intl: intlShape.isRequired,
|
|
};
|
|
|
|
componentWillReceiveProps(nextProps) {
|
|
if (this.props.theme !== nextProps.theme) {
|
|
setNavigatorStyles(this.props.navigator, nextProps.theme);
|
|
}
|
|
}
|
|
|
|
close = () => {
|
|
const {navigator, theme} = this.props;
|
|
|
|
navigator.resetTo({
|
|
screen: 'Channel',
|
|
animated: true,
|
|
navigatorStyle: {
|
|
animated: true,
|
|
animationType: 'fade',
|
|
navBarHidden: true,
|
|
statusBarHidden: false,
|
|
statusBarHideWithNavBar: false,
|
|
screenBackgroundColor: theme.centerChannelBg,
|
|
},
|
|
});
|
|
};
|
|
|
|
getDisplayName = () => {
|
|
const {theme, teammateNameDisplay, user} = this.props;
|
|
const style = createStyleSheet(theme);
|
|
|
|
const displayName = displayUsername(user, teammateNameDisplay);
|
|
|
|
if (displayName) {
|
|
return <Text style={style.displayName}>{displayName}</Text>;
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
buildDisplayBlock = (property) => {
|
|
const {theme, user} = this.props;
|
|
const style = createStyleSheet(theme);
|
|
|
|
if (user.hasOwnProperty(property) && user[property].length > 0) {
|
|
return (
|
|
<View>
|
|
<Text style={style.header}>{property.toUpperCase()}</Text>
|
|
<Text style={style.text}>{user[property]}</Text>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
buildTimezoneBlock = () => {
|
|
const {theme, user, militaryTime} = this.props;
|
|
const style = createStyleSheet(theme);
|
|
|
|
const currentTimezone = getUserCurrentTimezone(user.timezone);
|
|
if (!currentTimezone) {
|
|
return null;
|
|
}
|
|
const nowDate = new Date();
|
|
|
|
return (
|
|
<View>
|
|
<FormattedText
|
|
id='mobile.routes.user_profile.local_time'
|
|
defaultMessage='LOCAL TIME'
|
|
style={style.header}
|
|
/>
|
|
<Text style={style.text}>
|
|
<FormattedTime
|
|
timeZone={currentTimezone}
|
|
hour12={!militaryTime}
|
|
value={nowDate}
|
|
/>
|
|
</Text>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
sendMessage = async () => {
|
|
const {intl} = this.context;
|
|
const {actions, currentDisplayName, teammateNameDisplay, user} = this.props;
|
|
|
|
// save the current channel display name in case it fails
|
|
const currentChannelDisplayName = currentDisplayName;
|
|
|
|
const userDisplayName = displayUsername(user, teammateNameDisplay);
|
|
actions.setChannelDisplayName(userDisplayName);
|
|
|
|
const result = await actions.makeDirectChannel(user.id);
|
|
if (result.error) {
|
|
actions.setChannelDisplayName(currentChannelDisplayName);
|
|
alertErrorWithFallback(
|
|
intl,
|
|
result.error,
|
|
{
|
|
id: 'mobile.open_dm.error',
|
|
defaultMessage: "We couldn't open a direct message with {displayName}. Please check your connection and try again.",
|
|
},
|
|
{
|
|
displayName: userDisplayName,
|
|
}
|
|
);
|
|
} else {
|
|
this.close();
|
|
}
|
|
};
|
|
|
|
handleLinkPress = (link) => {
|
|
const username = this.props.user.username;
|
|
const email = this.props.user.email;
|
|
|
|
return () => {
|
|
var hydrated = link.replace(/{email}/, email);
|
|
hydrated = hydrated.replace(/{username}/, username);
|
|
Linking.openURL(hydrated);
|
|
};
|
|
};
|
|
|
|
renderAdditionalOptions = () => {
|
|
if (!Config.ExperimentalProfileLinks) {
|
|
return null;
|
|
}
|
|
|
|
const profileLinks = Config.ExperimentalProfileLinks;
|
|
|
|
const additionalOptions = profileLinks.map((l) => {
|
|
var action;
|
|
if (l.type === 'link') {
|
|
action = this.handleLinkPress(l.url);
|
|
}
|
|
|
|
return (
|
|
<UserProfileRow
|
|
key={l.defaultMessage}
|
|
action={action}
|
|
defaultMessage={l.defaultMessage}
|
|
textId={l.textId}
|
|
icon={l.icon}
|
|
iconType={l.iconType}
|
|
theme={this.props.theme}
|
|
iconSize={l.iconSize}
|
|
/>
|
|
);
|
|
});
|
|
|
|
return additionalOptions;
|
|
};
|
|
|
|
render() {
|
|
const {config, theme, user, enableTimezone} = this.props;
|
|
const style = createStyleSheet(theme);
|
|
|
|
if (!user) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<View style={style.container}>
|
|
<StatusBar/>
|
|
<ScrollView
|
|
style={style.scrollView}
|
|
>
|
|
<View style={style.top}>
|
|
<ProfilePicture
|
|
userId={user.id}
|
|
size={150}
|
|
statusBorderWidth={6}
|
|
statusSize={40}
|
|
/>
|
|
{this.getDisplayName()}
|
|
<Text style={style.username}>{`@${user.username}`}</Text>
|
|
</View>
|
|
<View style={style.content}>
|
|
{enableTimezone && this.buildTimezoneBlock()}
|
|
{this.buildDisplayBlock('username')}
|
|
{config.ShowEmailAddress === 'true' && this.buildDisplayBlock('email')}
|
|
{this.buildDisplayBlock('position')}
|
|
</View>
|
|
<UserProfileRow
|
|
action={this.sendMessage}
|
|
defaultMessage='Send Message'
|
|
icon='paper-plane-o'
|
|
iconType='fontawesome'
|
|
textId='mobile.routes.user_profile.send_message'
|
|
theme={theme}
|
|
/>
|
|
{this.renderAdditionalOptions()}
|
|
</ScrollView>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
const createStyleSheet = makeStyleSheetFromTheme((theme) => {
|
|
return {
|
|
container: {
|
|
flex: 1,
|
|
},
|
|
content: {
|
|
marginBottom: 25,
|
|
marginHorizontal: 15,
|
|
},
|
|
displayName: {
|
|
marginTop: 15,
|
|
color: theme.centerChannelColor,
|
|
fontSize: 17,
|
|
fontWeight: '600',
|
|
},
|
|
header: {
|
|
fontSize: 13,
|
|
fontWeight: '600',
|
|
color: changeOpacity(theme.centerChannelColor, 0.5),
|
|
marginTop: 25,
|
|
marginBottom: 10,
|
|
},
|
|
scrollView: {
|
|
flex: 1,
|
|
backgroundColor: theme.centerChannelBg,
|
|
},
|
|
text: {
|
|
fontSize: 15,
|
|
color: theme.centerChannelColor,
|
|
},
|
|
top: {
|
|
padding: 25,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
},
|
|
username: {
|
|
marginTop: 15,
|
|
color: theme.centerChannelColor,
|
|
fontSize: 15,
|
|
},
|
|
};
|
|
});
|
|
|