summaryrefslogtreecommitdiffstats
path: root/webapp
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2016-11-24 09:08:46 -0500
committerChristopher Speller <crspeller@gmail.com>2016-11-24 09:08:46 -0500
commitc96ecae6da31aceabf29586cde872876b81d11d9 (patch)
tree94df09df540da846bb4c6ed50e8bd7879cff63ba /webapp
parentb212acf312ad640455fa715427ac19e6930dc61d (diff)
parent36f62c9e82350f58c902f64a5d3304872431ad41 (diff)
downloadchat-c96ecae6da31aceabf29586cde872876b81d11d9.tar.gz
chat-c96ecae6da31aceabf29586cde872876b81d11d9.tar.bz2
chat-c96ecae6da31aceabf29586cde872876b81d11d9.zip
Merge branch 'release-3.5'
Diffstat (limited to 'webapp')
-rw-r--r--webapp/actions/user_actions.jsx1
-rw-r--r--webapp/actions/websocket_actions.jsx11
-rw-r--r--webapp/components/channel_header.jsx18
-rw-r--r--webapp/components/code_preview.jsx7
-rw-r--r--webapp/components/post_view/post_view_cache.jsx7
-rw-r--r--webapp/components/post_view/post_view_controller.jsx9
-rw-r--r--webapp/components/rhs_root_post.jsx2
-rw-r--r--webapp/components/sidebar_header_dropdown.jsx7
-rw-r--r--webapp/routes/route_team.jsx9
-rw-r--r--webapp/sass/layout/_headers.scss2
-rw-r--r--webapp/stores/channel_store.jsx6
-rw-r--r--webapp/utils/async_client.jsx60
-rw-r--r--webapp/utils/constants.jsx2
13 files changed, 97 insertions, 44 deletions
diff --git a/webapp/actions/user_actions.jsx b/webapp/actions/user_actions.jsx
index 5d07bed14..fefca79f7 100644
--- a/webapp/actions/user_actions.jsx
+++ b/webapp/actions/user_actions.jsx
@@ -135,6 +135,7 @@ function populateDMChannelsWithProfiles(userIds) {
const currentUserId = UserStore.getCurrentId();
for (let i = 0; i < userIds.length; i++) {
+ // TODO There's a race condition here for DM channels if those channels aren't loaded yet
const channelName = getDirectChannelName(currentUserId, userIds[i]);
const channel = ChannelStore.getByName(channelName);
if (channel) {
diff --git a/webapp/actions/websocket_actions.jsx b/webapp/actions/websocket_actions.jsx
index 8632f4135..bf56a148a 100644
--- a/webapp/actions/websocket_actions.jsx
+++ b/webapp/actions/websocket_actions.jsx
@@ -18,7 +18,7 @@ import * as Utils from 'utils/utils.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
-import {handleNewPost, loadPosts} from 'actions/post_actions.jsx';
+import {handleNewPost, loadPosts, loadProfilesForPosts} from 'actions/post_actions.jsx';
import {loadProfilesAndTeamMembersForDMSidebar} from 'actions/user_actions.jsx';
import {loadChannelsForCurrentUser} from 'actions/channel_actions.jsx';
import * as StatusActions from 'actions/status_actions.jsx';
@@ -172,6 +172,10 @@ function handleNewPostEvent(msg) {
const post = JSON.parse(msg.data.post);
handleNewPost(post, msg);
+ const posts = {};
+ posts[post.id] = post;
+ loadProfilesForPosts(posts);
+
if (UserStore.getStatus(post.user_id) !== UserStatuses.ONLINE) {
StatusActions.loadStatusesByIds([post.user_id]);
}
@@ -207,6 +211,11 @@ function handleNewUserEvent(msg) {
return;
}
+ if (msg.data.user_id === UserStore.getCurrentId()) {
+ // We should already have ourselves
+ return;
+ }
+
AsyncClient.getUser(msg.data.user_id);
AsyncClient.getChannelStats();
loadProfilesAndTeamMembersForDMSidebar();
diff --git a/webapp/components/channel_header.jsx b/webapp/components/channel_header.jsx
index 83f667322..d8110aa5a 100644
--- a/webapp/components/channel_header.jsx
+++ b/webapp/components/channel_header.jsx
@@ -68,12 +68,18 @@ export default class ChannelHeader extends React.Component {
const stats = ChannelStore.getStats(this.props.channelId);
const users = UserStore.getProfileListInChannel(this.props.channelId);
+ let otherUserId = null;
+ if (channel && channel.type === 'D') {
+ otherUserId = Utils.getUserIdFromChannelName(channel);
+ }
+
return {
channel,
memberChannel: ChannelStore.getMyMember(this.props.channelId),
users,
userCount: stats.member_count,
currentUser: UserStore.getCurrentUser(),
+ otherUserId,
enableFormatting: PreferenceStore.getBool(Preferences.CATEGORY_ADVANCED_SETTINGS, 'formatting', true),
isBusy: WebrtcStore.isBusy(),
isFavorite: channel && ChannelUtils.isFavoriteChannel(channel)
@@ -84,7 +90,6 @@ export default class ChannelHeader extends React.Component {
if (!this.state.channel ||
!this.state.memberChannel ||
!this.state.users ||
- (Object.keys(this.state.users).length === 0 && this.state.channel.type === 'D') ||
!this.state.userCount ||
!this.state.currentUser) {
return false;
@@ -240,7 +245,10 @@ export default class ChannelHeader extends React.Component {
const flagIcon = Constants.FLAG_ICON_SVG;
if (!this.validState()) {
- return null;
+ // Use an empty div to make sure the header's height stays constant
+ return (
+ <div className='channel-header'/>
+ );
}
const channel = this.state.channel;
@@ -285,7 +293,7 @@ export default class ChannelHeader extends React.Component {
if (isDirect) {
const userMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
- const contact = this.state.users[0];
+ const otherUserId = this.state.otherUserId;
const teammateId = Utils.getUserIdFromChannelName(channel);
channelTitle = Utils.displayUsername(teammateId);
@@ -293,7 +301,7 @@ export default class ChannelHeader extends React.Component {
const webrtcEnabled = global.mm_config.EnableWebrtc === 'true' && userMedia && Utils.isFeatureEnabled(PreReleaseFeatures.WEBRTC_PREVIEW);
if (webrtcEnabled) {
- const isOffline = UserStore.getStatus(contact.id) === UserStatuses.OFFLINE;
+ const isOffline = UserStore.getStatus(otherUserId) === UserStatuses.OFFLINE;
const busy = this.state.isBusy;
let circleClass = '';
let webrtcMessage;
@@ -332,7 +340,7 @@ export default class ChannelHeader extends React.Component {
<div className='webrtc__header'>
<a
href='#'
- onClick={() => this.initWebrtc(contact.id, !isOffline)}
+ onClick={() => this.initWebrtc(otherUserId, !isOffline)}
disabled={isOffline}
>
<OverlayTrigger
diff --git a/webapp/components/code_preview.jsx b/webapp/components/code_preview.jsx
index 46fe51a61..6afe45c2e 100644
--- a/webapp/components/code_preview.jsx
+++ b/webapp/components/code_preview.jsx
@@ -51,6 +51,7 @@ export default class CodePreview extends React.Component {
async: true,
url: props.fileUrl,
type: 'GET',
+ dataType: 'text',
error: this.handleReceivedError,
success: this.handleReceivedCode
});
@@ -61,7 +62,11 @@ export default class CodePreview extends React.Component {
if (data.nodeName === '#document') {
code = new XMLSerializer().serializeToString(data);
}
- this.setState({code, loading: false, success: true});
+ this.setState({
+ code,
+ loading: false,
+ success: true
+ });
}
handleReceivedError() {
diff --git a/webapp/components/post_view/post_view_cache.jsx b/webapp/components/post_view/post_view_cache.jsx
index c1b278c35..3b6123b09 100644
--- a/webapp/components/post_view/post_view_cache.jsx
+++ b/webapp/components/post_view/post_view_cache.jsx
@@ -17,11 +17,12 @@ export default class PostViewCache extends React.Component {
this.onChannelChange = this.onChannelChange.bind(this);
+ const currentChannelId = ChannelStore.getCurrentId();
const channel = ChannelStore.getCurrent();
this.state = {
- currentChannelId: channel.id,
- channels: [channel]
+ currentChannelId,
+ channels: channel ? [channel] : []
};
}
@@ -40,7 +41,7 @@ export default class PostViewCache extends React.Component {
const channels = Object.assign([], this.state.channels);
const currentChannel = ChannelStore.getCurrent();
- if (currentChannel == null) {
+ if (!currentChannel) {
return;
}
diff --git a/webapp/components/post_view/post_view_controller.jsx b/webapp/components/post_view/post_view_controller.jsx
index 57b488b54..53cd0b28c 100644
--- a/webapp/components/post_view/post_view_controller.jsx
+++ b/webapp/components/post_view/post_view_controller.jsx
@@ -202,8 +202,13 @@ export default class PostViewController extends React.Component {
}
}
- onSetNewMessageIndicator(lastViewed, ownNewMessage) {
- this.setState({lastViewed, ownNewMessage});
+ onSetNewMessageIndicator() {
+ let lastViewed = Number.MAX_VALUE;
+ const member = ChannelStore.getMyMember(this.props.channel.id);
+ if (member != null) {
+ lastViewed = member.last_viewed_at;
+ }
+ this.setState({lastViewed});
}
onPostListScroll(atBottom) {
diff --git a/webapp/components/rhs_root_post.jsx b/webapp/components/rhs_root_post.jsx
index 0dae5976f..22795967a 100644
--- a/webapp/components/rhs_root_post.jsx
+++ b/webapp/components/rhs_root_post.jsx
@@ -90,7 +90,7 @@ export default class RhsRootPost extends React.Component {
var isOwner = this.props.currentUser.id === post.user_id;
var isAdmin = TeamStore.isTeamAdminForCurrentTeam() || UserStore.isSystemAdminForCurrentUser();
const isSystemMessage = post.type && post.type.startsWith(Constants.SYSTEM_MESSAGE_PREFIX);
- var timestamp = user.update_at;
+ var timestamp = user ? user.update_at : 0;
var channel = ChannelStore.get(post.channel_id);
const flagIcon = Constants.FLAG_ICON_SVG;
diff --git a/webapp/components/sidebar_header_dropdown.jsx b/webapp/components/sidebar_header_dropdown.jsx
index b665eef52..aa5226702 100644
--- a/webapp/components/sidebar_header_dropdown.jsx
+++ b/webapp/components/sidebar_header_dropdown.jsx
@@ -45,6 +45,7 @@ export default class SidebarHeaderDropdown extends React.Component {
this.showGetTeamInviteLinkModal = this.showGetTeamInviteLinkModal.bind(this);
this.showTeamMembersModal = this.showTeamMembersModal.bind(this);
this.hideTeamMembersModal = this.hideTeamMembersModal.bind(this);
+ this.handleSwitchTeams = this.handleSwitchTeams.bind(this);
this.onTeamChange = this.onTeamChange.bind(this);
this.openAccountSettings = this.openAccountSettings.bind(this);
@@ -131,6 +132,11 @@ export default class SidebarHeaderDropdown extends React.Component {
});
}
+ handleSwitchTeams() {
+ // The actual switching of teams is handled by the react-router Link
+ this.setState({showDropdown: false});
+ }
+
componentDidMount() {
TeamStore.addChangeListener(this.onTeamChange);
document.addEventListener('keydown', this.openAccountSettings);
@@ -367,6 +373,7 @@ export default class SidebarHeaderDropdown extends React.Component {
<li key={'team_' + team.name}>
<Link
to={'/' + team.name + '/channels/town-square'}
+ onClick={this.handleSwitchTeams}
>
<FormattedMessage
id='navbar_dropdown.switchTo'
diff --git a/webapp/routes/route_team.jsx b/webapp/routes/route_team.jsx
index 4bdfd1cc6..7d4c0c80c 100644
--- a/webapp/routes/route_team.jsx
+++ b/webapp/routes/route_team.jsx
@@ -29,13 +29,16 @@ function doChannelChange(state, replace, callback) {
channel = JSON.parse(state.location.query.fakechannel);
} else {
channel = ChannelStore.getByName(state.params.channel);
- if (!channel) {
- channel = ChannelStore.getMoreByName(state.params.channel);
- }
+
if (!channel) {
Client.joinChannelByName(
state.params.channel,
(data) => {
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_CHANNEL,
+ channel: data
+ });
+
GlobalActions.emitChannelClickEvent(data);
callback();
},
diff --git a/webapp/sass/layout/_headers.scss b/webapp/sass/layout/_headers.scss
index a8344b1c5..fd1c42135 100644
--- a/webapp/sass/layout/_headers.scss
+++ b/webapp/sass/layout/_headers.scss
@@ -1,7 +1,7 @@
@charset 'UTF-8';
.channel-header {
- @include flex(0 0 56px);
+ @include flex(0 0 57px);
border-left: none;
font-size: 14px;
line-height: 56px;
diff --git a/webapp/stores/channel_store.jsx b/webapp/stores/channel_store.jsx
index 136423d45..d578a5d29 100644
--- a/webapp/stores/channel_store.jsx
+++ b/webapp/stores/channel_store.jsx
@@ -59,8 +59,8 @@ class ChannelStoreClass extends EventEmitter {
this.removeListener(STATS_EVENT, callback);
}
- emitLastViewed(lastViewed, ownNewMessage) {
- this.emit(LAST_VIEVED_EVENT, lastViewed, ownNewMessage);
+ emitLastViewed() {
+ this.emit(LAST_VIEVED_EVENT);
}
addLastViewedListener(callback) {
@@ -373,6 +373,7 @@ ChannelStore.dispatchToken = AppDispatcher.register((payload) => {
}
ChannelStore.setUnreadCountsByMembers(action.members);
ChannelStore.emitChange();
+ ChannelStore.emitLastViewed();
break;
case ActionTypes.RECEIVED_CHANNEL_MEMBER:
ChannelStore.storeMyChannelMember(action.member);
@@ -382,6 +383,7 @@ ChannelStore.dispatchToken = AppDispatcher.register((payload) => {
}
ChannelStore.setUnreadCountsByCurrentMembers();
ChannelStore.emitChange();
+ ChannelStore.emitLastViewed();
break;
case ActionTypes.RECEIVED_MORE_CHANNELS:
ChannelStore.storeMoreChannels(action.channels);
diff --git a/webapp/utils/async_client.jsx b/webapp/utils/async_client.jsx
index fe31d4ef8..71fbc8db0 100644
--- a/webapp/utils/async_client.jsx
+++ b/webapp/utils/async_client.jsx
@@ -298,15 +298,17 @@ export function getChannelMember(channelId, userId) {
}
export function getUser(userId) {
- if (isCallInProgress(`getUser${userId}`)) {
+ const callName = `getUser${userId}`;
+
+ if (isCallInProgress(callName)) {
return;
}
- callTracker[`getUser${userId}`] = utils.getTimestamp();
+ callTracker[callName] = utils.getTimestamp();
Client.getUser(
userId,
(data) => {
- callTracker[`getUser${userId}`] = 0;
+ callTracker[callName] = 0;
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_PROFILE,
@@ -314,23 +316,25 @@ export function getUser(userId) {
});
},
(err) => {
- callTracker[`getUser${userId}`] = 0;
+ callTracker[callName] = 0;
dispatchError(err, 'getUser');
}
);
}
export function getProfiles(offset = UserStore.getPagingOffset(), limit = Constants.PROFILE_CHUNK_SIZE) {
- if (isCallInProgress(`getProfiles${offset}${limit}`)) {
+ const callName = `getProfiles${offset}${limit}`;
+
+ if (isCallInProgress(callName)) {
return;
}
- callTracker[`getProfiles${offset}${limit}`] = utils.getTimestamp();
+ callTracker[callName] = utils.getTimestamp();
Client.getProfiles(
offset,
limit,
(data) => {
- callTracker[`getProfiles${offset}${limit}`] = 0;
+ callTracker[callName] = 0;
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_PROFILES,
@@ -338,24 +342,26 @@ export function getProfiles(offset = UserStore.getPagingOffset(), limit = Consta
});
},
(err) => {
- callTracker[`getProfiles${offset}${limit}`] = 0;
+ callTracker[callName] = 0;
dispatchError(err, 'getProfiles');
}
);
}
export function getProfilesInTeam(teamId = TeamStore.getCurrentId(), offset = UserStore.getInTeamPagingOffset(), limit = Constants.PROFILE_CHUNK_SIZE) {
- if (isCallInProgress(`getProfilesInTeam${offset}${limit}`)) {
+ const callName = `getProfilesInTeam${teamId}${offset}${limit}`;
+
+ if (isCallInProgress(callName)) {
return;
}
- callTracker[`getProfilesInTeam${offset}${limit}`] = utils.getTimestamp();
+ callTracker[callName] = utils.getTimestamp();
Client.getProfilesInTeam(
teamId,
offset,
limit,
(data) => {
- callTracker[`getProfilesInTeam${offset}${limit}`] = 0;
+ callTracker[callName] = 0;
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_PROFILES_IN_TEAM,
@@ -366,24 +372,26 @@ export function getProfilesInTeam(teamId = TeamStore.getCurrentId(), offset = Us
});
},
(err) => {
- callTracker[`getProfilesInTeam${offset}${limit}`] = 0;
+ callTracker[callName] = 0;
dispatchError(err, 'getProfilesInTeam');
}
);
}
export function getProfilesInChannel(channelId = ChannelStore.getCurrentId(), offset = UserStore.getInChannelPagingOffset(), limit = Constants.PROFILE_CHUNK_SIZE) {
- if (isCallInProgress(`getProfilesInChannel${offset}${limit}`)) {
+ const callName = `getProfilesInChannel${channelId}${offset}${limit}`;
+
+ if (isCallInProgress()) {
return;
}
- callTracker[`getProfilesInChannel${offset}${limit}`] = utils.getTimestamp();
+ callTracker[callName] = utils.getTimestamp();
Client.getProfilesInChannel(
channelId,
offset,
limit,
(data) => {
- callTracker[`getProfilesInChannel${offset}${limit}`] = 0;
+ callTracker[callName] = 0;
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_PROFILES_IN_CHANNEL,
@@ -396,24 +404,26 @@ export function getProfilesInChannel(channelId = ChannelStore.getCurrentId(), of
loadStatusesForProfilesMap(data);
},
(err) => {
- callTracker[`getProfilesInChannel${offset}${limit}`] = 0;
+ callTracker[callName] = 0;
dispatchError(err, 'getProfilesInChannel');
}
);
}
export function getProfilesNotInChannel(channelId = ChannelStore.getCurrentId(), offset = UserStore.getNotInChannelPagingOffset(), limit = Constants.PROFILE_CHUNK_SIZE) {
- if (isCallInProgress(`getProfilesNotInChannel${offset}${limit}`)) {
+ const callName = `getProfilesNotInChannel${channelId}${offset}${limit}`;
+
+ if (isCallInProgress(callName)) {
return;
}
- callTracker[`getProfilesNotInChannel${offset}${limit}`] = utils.getTimestamp();
+ callTracker[callName] = utils.getTimestamp();
Client.getProfilesNotInChannel(
channelId,
offset,
limit,
(data) => {
- callTracker[`getProfilesNotInChannel${offset}${limit}`] = 0;
+ callTracker[callName] = 0;
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_PROFILES_NOT_IN_CHANNEL,
@@ -426,14 +436,16 @@ export function getProfilesNotInChannel(channelId = ChannelStore.getCurrentId(),
loadStatusesForProfilesMap(data);
},
(err) => {
- callTracker[`getProfilesNotInChannel${offset}${limit}`] = 0;
+ callTracker[callName] = 0;
dispatchError(err, 'getProfilesNotInChannel');
}
);
}
export function getProfilesByIds(userIds) {
- if (isCallInProgress('getProfilesByIds')) {
+ const callName = 'getProfilesByIds' + JSON.stringify(userIds);
+
+ if (isCallInProgress(callName)) {
return;
}
@@ -441,11 +453,11 @@ export function getProfilesByIds(userIds) {
return;
}
- callTracker.getProfilesByIds = utils.getTimestamp();
+ callTracker[callName] = utils.getTimestamp();
Client.getProfilesByIds(
userIds,
(data) => {
- callTracker.getProfilesByIds = 0;
+ callTracker[callName] = 0;
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_PROFILES,
@@ -453,7 +465,7 @@ export function getProfilesByIds(userIds) {
});
},
(err) => {
- callTracker.getProfilesByIds = 0;
+ callTracker[callName] = 0;
dispatchError(err, 'getProfilesByIds');
}
);
diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx
index b3cafe254..21ec07db3 100644
--- a/webapp/utils/constants.jsx
+++ b/webapp/utils/constants.jsx
@@ -854,7 +854,7 @@ export const Constants = {
MENTION_SPECIAL: 'mention.special',
DEFAULT_NOTIFICATION_DURATION: 5000,
STATUS_INTERVAL: 60000,
- AUTOCOMPLETE_TIMEOUT: 200
+ AUTOCOMPLETE_TIMEOUT: 100
};
export default Constants;