Move to mattermost-redux master branch (#484)
This commit is contained in:
parent
311cc4028d
commit
7a3c99c267
66 changed files with 880 additions and 806 deletions
|
|
@ -1,6 +1,6 @@
|
|||
# Mattermost Mobile (unreleased)
|
||||
|
||||
**Supported Server Versions:** Master (no released versions are currently supported)
|
||||
**Supported Server Versions:** 3.8+ (no released versions are currently supported)
|
||||
|
||||
This is an unreleased project for replacing the Mattermost iOS and Android apps with new mobile apps using React Native and Redux. The project is not yet stable, and the instructions are for internal use currently (i.e. probably out-of-date until we stablize).
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ Follow the [React Native Getting Started Guide](https://facebook.github.io/react
|
|||
|
||||
- General requirements
|
||||
|
||||
- XCode 8.1
|
||||
- XCode 8.3
|
||||
- Install watchman
|
||||
$ brew install watchman
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import {NavigationTypes} from 'app/constants';
|
||||
import Routes from 'app/navigation/routes';
|
||||
import {Constants} from 'mattermost-redux/constants';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
import {selectPost} from 'mattermost-redux/actions/posts';
|
||||
|
||||
export function closeDrawers() {
|
||||
|
|
@ -90,11 +90,11 @@ export function goToCreateChannel(channelType) {
|
|||
let type;
|
||||
let route;
|
||||
switch (channelType) {
|
||||
case Constants.OPEN_CHANNEL:
|
||||
case General.OPEN_CHANNEL:
|
||||
type = NavigationTypes.NAVIGATION_PUSH;
|
||||
route = Routes.CreatePublicChannel;
|
||||
break;
|
||||
case Constants.PRIVATE_CHANNEL:
|
||||
case General.PRIVATE_CHANNEL:
|
||||
type = NavigationTypes.NAVIGATION_MODAL;
|
||||
route = Routes.CreatePrivateChannel;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {updateUserNotifyProps} from 'mattermost-redux/actions/users';
|
||||
import {updateMe} from 'mattermost-redux/actions/users';
|
||||
import {Preferences} from 'mattermost-redux/constants';
|
||||
import {savePreferences} from 'mattermost-redux/actions/preferences';
|
||||
|
||||
|
|
@ -9,6 +9,7 @@ export function handleUpdateUserNotifyProps(notifyProps) {
|
|||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const config = state.entities.general.config;
|
||||
const {currentUserId} = state.entities.users;
|
||||
|
||||
const {interval, ...otherProps} = notifyProps;
|
||||
|
||||
|
|
@ -21,9 +22,10 @@ export function handleUpdateUserNotifyProps(notifyProps) {
|
|||
value: interval
|
||||
}];
|
||||
|
||||
savePreferences(emailInterval)(dispatch, getState);
|
||||
savePreferences(currentUserId, emailInterval)(dispatch, getState);
|
||||
}
|
||||
|
||||
updateUserNotifyProps({...otherProps, email})(dispatch, getState);
|
||||
const props = {...otherProps, email};
|
||||
updateMe({notify_props: props})(dispatch, getState);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {batchActions} from 'redux-batched-actions';
|
|||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
|
||||
import {UserTypes} from 'mattermost-redux/action_types';
|
||||
import {
|
||||
fetchMyChannelsAndMembers,
|
||||
getChannelStats,
|
||||
|
|
@ -17,7 +18,7 @@ import {getFilesForPost} from 'mattermost-redux/actions/files';
|
|||
import {savePreferences, deletePreferences} from 'mattermost-redux/actions/preferences';
|
||||
import {getTeamMembersByIds} from 'mattermost-redux/actions/teams';
|
||||
import {getProfilesInChannel} from 'mattermost-redux/actions/users';
|
||||
import {Constants, Preferences, UsersTypes} from 'mattermost-redux/constants';
|
||||
import {General, Posts, Preferences} from 'mattermost-redux/constants';
|
||||
import {
|
||||
getChannelByName,
|
||||
getDirectChannelName,
|
||||
|
|
@ -27,6 +28,7 @@ import {
|
|||
isDirectChannel,
|
||||
isGroupChannel
|
||||
} from 'mattermost-redux/utils/channel_utils';
|
||||
import {getLastUpdateAt} from 'mattermost-redux/utils/post_utils';
|
||||
import {getPreferencesByCategory} from 'mattermost-redux/utils/preference_utils';
|
||||
|
||||
export function loadChannelsIfNecessary(teamId) {
|
||||
|
|
@ -100,7 +102,7 @@ export function loadProfilesAndTeamMembersForDMSidebar(teamId) {
|
|||
});
|
||||
|
||||
if (prefs.length) {
|
||||
savePreferences(prefs)(dispatch, getState);
|
||||
savePreferences(currentUserId, prefs)(dispatch, getState);
|
||||
}
|
||||
|
||||
for (const [key, pref] of dmPrefs) {
|
||||
|
|
@ -118,7 +120,7 @@ export function loadProfilesAndTeamMembersForDMSidebar(teamId) {
|
|||
if (loadProfilesForChannels.length) {
|
||||
for (let i = 0; i < loadProfilesForChannels.length; i++) {
|
||||
const channelId = loadProfilesForChannels[i];
|
||||
getProfilesInChannel(teamId, channelId, 0)(dispatch, getState);
|
||||
getProfilesInChannel(channelId, 0)(dispatch, getState);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -137,7 +139,7 @@ export function loadProfilesAndTeamMembersForDMSidebar(teamId) {
|
|||
const channel = getChannelByName(channels, channelName);
|
||||
if (channel) {
|
||||
actions.push({
|
||||
type: UsersTypes.RECEIVED_PROFILE_IN_CHANNEL,
|
||||
type: UserTypes.RECEIVED_PROFILE_IN_CHANNEL,
|
||||
data: {user_id: members[i]},
|
||||
id: channel.id
|
||||
});
|
||||
|
|
@ -153,30 +155,32 @@ export function loadProfilesAndTeamMembersForDMSidebar(teamId) {
|
|||
export function loadPostsIfNecessary(channel) {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const postsInChannel = state.entities.posts.postsByChannel[channel.id];
|
||||
|
||||
// Make sure we include a team id for DM channels
|
||||
const teamId = channel.team_id || state.entities.teams.currentTeamId;
|
||||
const {posts, postsInChannel} = state.entities.posts;
|
||||
const postsIds = postsInChannel[channel.id];
|
||||
|
||||
// Get the first page of posts if it appears we haven't gotten it yet, like the webapp
|
||||
if (!postsInChannel || postsInChannel.length < Constants.POST_CHUNK_SIZE) {
|
||||
return getPosts(teamId, channel.id)(dispatch, getState);
|
||||
if (!postsIds || postsIds.length < Posts.POST_CHUNK_SIZE) {
|
||||
return getPosts(channel.id)(dispatch, getState);
|
||||
}
|
||||
|
||||
const latestPostInChannel = state.entities.posts.posts[postsInChannel[0]];
|
||||
const postsForChannel = postsIds.map((id) => posts[id]);
|
||||
const latestPostTime = getLastUpdateAt(postsForChannel);
|
||||
|
||||
return getPostsSince(teamId, channel.id, latestPostInChannel.create_at)(dispatch, getState);
|
||||
if (latestPostTime) {
|
||||
return getPostsSince(channel.id, latestPostTime)(dispatch, getState);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
export function loadFilesForPostIfNecessary(post) {
|
||||
return async (dispatch, getState) => {
|
||||
const {files, teams} = getState().entities;
|
||||
const {files} = getState().entities;
|
||||
const fileIdsForPost = files.fileIdsByPostId[post.id];
|
||||
|
||||
if (!fileIdsForPost) {
|
||||
const {currentTeamId} = teams;
|
||||
await getFilesForPost(currentTeamId, post.channel_id, post.id)(dispatch, getState);
|
||||
await getFilesForPost(post.id)(dispatch, getState);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -189,10 +193,10 @@ export function selectInitialChannel(teamId) {
|
|||
const currentChannel = channels[currentChannelId];
|
||||
const {myPreferences} = state.entities.preferences;
|
||||
|
||||
const isDMVisible = currentChannel && currentChannel.type === Constants.DM_CHANNEL &&
|
||||
const isDMVisible = currentChannel && currentChannel.type === General.DM_CHANNEL &&
|
||||
isDirectChannelVisible(currentUserId, myPreferences, currentChannel);
|
||||
|
||||
const isGMVisible = currentChannel && currentChannel.type === Constants.GM_CHANNEL &&
|
||||
const isGMVisible = currentChannel && currentChannel.type === General.GM_CHANNEL &&
|
||||
isGroupChannelVisible(myPreferences, currentChannel);
|
||||
|
||||
if (currentChannel && myMembers[currentChannelId] &&
|
||||
|
|
@ -201,7 +205,7 @@ export function selectInitialChannel(teamId) {
|
|||
return;
|
||||
}
|
||||
|
||||
const channel = Object.values(channels).find((c) => c.team_id === teamId && c.name === Constants.DEFAULT_CHANNEL);
|
||||
const channel = Object.values(channels).find((c) => c.team_id === teamId && c.name === General.DEFAULT_CHANNEL);
|
||||
if (channel) {
|
||||
await handleSelectChannel(channel.id)(dispatch, getState);
|
||||
} else {
|
||||
|
|
@ -223,7 +227,7 @@ export function handleSelectChannel(channelId) {
|
|||
teamId: currentTeamId,
|
||||
channelId
|
||||
});
|
||||
getChannelStats(currentTeamId, channelId)(dispatch, getState);
|
||||
getChannelStats(channelId)(dispatch, getState);
|
||||
selectChannel(channelId)(dispatch, getState);
|
||||
};
|
||||
}
|
||||
|
|
@ -250,7 +254,7 @@ export function toggleDMChannel(otherUserId, visible) {
|
|||
value: visible
|
||||
}];
|
||||
|
||||
savePreferences(dm)(dispatch, getState);
|
||||
savePreferences(currentUserId, dm)(dispatch, getState);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -266,7 +270,7 @@ export function toggleGMChannel(channelId, visible) {
|
|||
value: visible
|
||||
}];
|
||||
|
||||
savePreferences(gm)(dispatch, getState);
|
||||
savePreferences(currentUserId, gm)(dispatch, getState);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -303,9 +307,9 @@ export function closeGMChannel(channel) {
|
|||
export function closeDirectChannel(channel) {
|
||||
return async (dispatch, getState) => {
|
||||
switch (channel.type) {
|
||||
case Constants.DM_CHANNEL:
|
||||
case General.DM_CHANNEL:
|
||||
return closeDMChannel(channel)(dispatch, getState);
|
||||
case Constants.GM_CHANNEL:
|
||||
case General.GM_CHANNEL:
|
||||
return closeGMChannel(channel)(dispatch, getState);
|
||||
}
|
||||
|
||||
|
|
@ -318,12 +322,12 @@ export function markFavorite(channelId) {
|
|||
const {currentUserId} = getState().entities.users;
|
||||
const fav = [{
|
||||
user_id: currentUserId,
|
||||
category: Constants.CATEGORY_FAVORITE_CHANNEL,
|
||||
category: Preferences.CATEGORY_FAVORITE_CHANNEL,
|
||||
name: channelId,
|
||||
value: 'true'
|
||||
}];
|
||||
|
||||
savePreferences(fav)(dispatch, getState);
|
||||
savePreferences(currentUserId, fav)(dispatch, getState);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -332,18 +336,18 @@ export function unmarkFavorite(channelId) {
|
|||
const {currentUserId} = getState().entities.users;
|
||||
const fav = [{
|
||||
user_id: currentUserId,
|
||||
category: Constants.CATEGORY_FAVORITE_CHANNEL,
|
||||
category: Preferences.CATEGORY_FAVORITE_CHANNEL,
|
||||
name: channelId
|
||||
}];
|
||||
|
||||
deletePreferences(fav)(dispatch, getState);
|
||||
deletePreferences(currentUserId, fav)(dispatch, getState);
|
||||
};
|
||||
}
|
||||
|
||||
export function leaveChannel(channel, reset = false) {
|
||||
return async (dispatch, getState) => {
|
||||
const {currentTeamId} = getState().entities.teams;
|
||||
await serviceLeaveChannel(currentTeamId, channel.id)(dispatch, getState);
|
||||
await serviceLeaveChannel(channel.id)(dispatch, getState);
|
||||
if (channel.isCurrent || reset) {
|
||||
await selectInitialChannel(currentTeamId)(dispatch, getState);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@
|
|||
|
||||
import {removeChannelMember} from 'mattermost-redux/actions/channels';
|
||||
|
||||
export function handleRemoveChannelMembers(teamId, channelId, members) {
|
||||
export function handleRemoveChannelMembers(channelId, members) {
|
||||
return async (dispatch, getState) => {
|
||||
try {
|
||||
const requests = members.map((m) => dispatch(removeChannelMember(teamId, channelId, m, getState)));
|
||||
const requests = members.map((m) => dispatch(removeChannelMember(channelId, m, getState)));
|
||||
|
||||
await Promise.all(requests);
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -9,11 +9,10 @@ import {lookupMimeType} from 'mattermost-redux/utils/file_utils';
|
|||
import {generateId} from 'app/utils/file';
|
||||
import {ViewTypes} from 'app/constants';
|
||||
|
||||
export function handleUploadFiles(files, rootId, requestId) {
|
||||
export function handleUploadFiles(files, rootId) {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
|
||||
const teamId = state.entities.teams.currentTeamId;
|
||||
const channelId = state.entities.channels.currentChannelId;
|
||||
const formData = new FormData();
|
||||
const clientIds = [];
|
||||
|
|
@ -48,7 +47,7 @@ export function handleUploadFiles(files, rootId, requestId) {
|
|||
rootId
|
||||
});
|
||||
|
||||
await uploadFile(teamId, channelId, formData, formBoundary, rootId, requestId)(dispatch, getState);
|
||||
await uploadFile(channelId, rootId, formData, formBoundary)(dispatch, getState);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
import {GeneralTypes} from 'mattermost-redux/constants';
|
||||
import {GeneralTypes} from 'mattermost-redux/action_types';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import Client from 'mattermost-redux/client';
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
|
||||
export function handleLoginIdChanged(loginId) {
|
||||
return async (dispatch, getState) => {
|
||||
|
|
@ -28,8 +28,8 @@ export function handleSuccessfulLogin() {
|
|||
dispatch({
|
||||
type: GeneralTypes.RECEIVED_APP_CREDENTIALS,
|
||||
data: {
|
||||
url: Client.getUrl(),
|
||||
token: Client.getToken()
|
||||
url: Client4.getUrl(),
|
||||
token: Client4.getToken()
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ export function makeDirectChannel(otherUserId) {
|
|||
const channelName = getDirectChannelName(currentUserId, otherUserId);
|
||||
const {channels, myMembers} = state.entities.channels;
|
||||
const channel = Object.values(channels).find((c) => c.name === channelName);
|
||||
const {currentTeamId} = state.entities.teams;
|
||||
|
||||
getProfilesByIds([otherUserId])(dispatch, getState);
|
||||
getStatusesByIds([otherUserId])(dispatch, getState);
|
||||
|
|
@ -22,7 +21,7 @@ export function makeDirectChannel(otherUserId) {
|
|||
toggleDMChannel(otherUserId, 'true')(dispatch, getState);
|
||||
handleSelectChannel(channel.id)(dispatch, getState);
|
||||
} else {
|
||||
const created = await createDirectChannel(currentTeamId, currentUserId, otherUserId)(dispatch, getState);
|
||||
const created = await createDirectChannel(currentUserId, otherUserId)(dispatch, getState);
|
||||
if (created) {
|
||||
toggleDMChannel(otherUserId, 'true')(dispatch, getState);
|
||||
handleSelectChannel(created.id)(dispatch, getState);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import {batchActions} from 'redux-batched-actions';
|
||||
|
||||
import {ChannelTypes, TeamsTypes} from 'mattermost-redux/constants';
|
||||
import {ChannelTypes, TeamTypes} from 'mattermost-redux/action_types';
|
||||
|
||||
export function handleTeamChange(team) {
|
||||
return async (dispatch, getState) => {
|
||||
|
|
@ -16,7 +16,7 @@ export function handleTeamChange(team) {
|
|||
const lastChannelId = state.views.team.lastChannelForTeam[team.id] || '';
|
||||
|
||||
dispatch(batchActions([
|
||||
{type: TeamsTypes.SELECT_TEAM, data: team.id},
|
||||
{type: TeamTypes.SELECT_TEAM, data: team.id},
|
||||
{type: ChannelTypes.SELECT_CHANNEL, data: lastChannelId}
|
||||
]), getState);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,6 +19,262 @@ import {RequestStatus} from 'mattermost-redux/constants';
|
|||
|
||||
const AT_MENTION_REGEX = /\B(@([^@\r\n\s]*))$/i;
|
||||
|
||||
export default class AtMention extends Component {
|
||||
static propTypes = {
|
||||
currentUserId: PropTypes.string.isRequired,
|
||||
currentChannelId: PropTypes.string.isRequired,
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
cursorPosition: PropTypes.number.isRequired,
|
||||
defaultChannel: PropTypes.object.isRequired,
|
||||
autocompleteUsersInCurrentChannel: PropTypes.object.isRequired,
|
||||
postDraft: PropTypes.string,
|
||||
requestStatus: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
onChangeText: PropTypes.func.isRequired,
|
||||
actions: PropTypes.shape({
|
||||
autocompleteUsers: PropTypes.func.isRequired
|
||||
})
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
autocompleteUsersInCurrentChannel: {},
|
||||
postDraft: ''
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const ds = new ListView.DataSource({
|
||||
sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
|
||||
rowHasChanged: (r1, r2) => r1 !== r2
|
||||
});
|
||||
const data = {};
|
||||
this.state = {
|
||||
active: false,
|
||||
dataSource: ds.cloneWithRowsAndSections(data)
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const match = nextProps.postDraft.substring(0, nextProps.cursorPosition).match(AT_MENTION_REGEX);
|
||||
|
||||
if (!match || this.state.mentionComplete) {
|
||||
this.setState({
|
||||
active: false,
|
||||
matchTerm: null,
|
||||
mentionComplete: false
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const matchTerm = match[2];
|
||||
|
||||
if (matchTerm !== this.state.matchTerm) {
|
||||
this.setState({
|
||||
matchTerm
|
||||
});
|
||||
|
||||
const {currentTeamId, currentChannelId} = this.props;
|
||||
this.props.actions.autocompleteUsers(matchTerm, currentTeamId, currentChannelId);
|
||||
}
|
||||
|
||||
if (nextProps.requestStatus !== RequestStatus.STARTED) {
|
||||
const membersInChannel = this.filter(nextProps.autocompleteUsersInCurrentChannel.inChannel, matchTerm) || [];
|
||||
const membersOutOfChannel = this.filter(nextProps.autocompleteUsersInCurrentChannel.outChannel, matchTerm) || [];
|
||||
|
||||
let data = {};
|
||||
if (membersInChannel.length > 0) {
|
||||
data = Object.assign({}, data, {inChannel: membersInChannel});
|
||||
}
|
||||
if (this.checkSpecialMentions(matchTerm)) {
|
||||
data = Object.assign({}, data, {specialMentions: this.getSpecialMentions()});
|
||||
}
|
||||
if (membersOutOfChannel.length > 0) {
|
||||
data = Object.assign({}, data, {notInChannel: membersOutOfChannel});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
active: data.hasOwnProperty('inChannel') || data.hasOwnProperty('specialMentions') || data.hasOwnProperty('notInChannel'),
|
||||
dataSource: this.state.dataSource.cloneWithRowsAndSections(data)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
filter = (profiles, matchTerm) => {
|
||||
return profiles.filter((p) => {
|
||||
return ((p.id !== this.props.currentUserId) && (
|
||||
p.username.toLowerCase().includes(matchTerm) || p.email.toLowerCase().includes(matchTerm) ||
|
||||
p.first_name.toLowerCase().includes(matchTerm) || p.last_name.toLowerCase().includes(matchTerm)));
|
||||
});
|
||||
};
|
||||
|
||||
getSpecialMentions = () => {
|
||||
return [{
|
||||
completeHandle: 'all',
|
||||
id: 'suggestion.mention.all',
|
||||
defaultMessage: 'Notifies everyone in the channel, use in {townsquare} to notify the whole team',
|
||||
values: {
|
||||
townsquare: this.props.defaultChannel.display_name
|
||||
}
|
||||
}, {
|
||||
completeHandle: 'channel',
|
||||
id: 'suggestion.mention.channel',
|
||||
defaultMessage: 'Notifies everyone in the channel'
|
||||
}, {
|
||||
completeHandle: 'here',
|
||||
id: 'suggestions.mention.here',
|
||||
defaultMessage: 'Notifies everyone in the channel and online'
|
||||
}];
|
||||
};
|
||||
|
||||
checkSpecialMentions = (term) => {
|
||||
return this.getSpecialMentions().filter((m) => m.completeHandle.startsWith(term)).length > 0;
|
||||
};
|
||||
|
||||
completeMention = (mention) => {
|
||||
const mentionPart = this.props.postDraft.substring(0, this.props.cursorPosition);
|
||||
|
||||
let completedDraft = mentionPart.replace(AT_MENTION_REGEX, `@${mention} `);
|
||||
if (this.props.postDraft.length > this.props.cursorPosition) {
|
||||
completedDraft += this.props.postDraft.substring(this.props.cursorPosition);
|
||||
}
|
||||
|
||||
this.props.onChangeText(completedDraft);
|
||||
this.setState({
|
||||
active: false,
|
||||
mentionComplete: true
|
||||
});
|
||||
};
|
||||
|
||||
renderSectionHeader = (sectionData, sectionId) => {
|
||||
const style = getStyleFromTheme(this.props.theme);
|
||||
|
||||
const localization = {
|
||||
inChannel: {
|
||||
id: 'suggestion.mention.members',
|
||||
defaultMessage: 'Channel Members'
|
||||
},
|
||||
notInChannel: {
|
||||
id: 'suggestion.mention.nonmembers',
|
||||
defaultMessage: 'Not in Channel'
|
||||
},
|
||||
specialMentions: {
|
||||
id: 'suggestion.mention.special',
|
||||
defaultMessage: 'Special Mentions'
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={style.sectionWrapper}>
|
||||
<View style={style.section}>
|
||||
<FormattedText
|
||||
id={localization[sectionId].id}
|
||||
defaultMessage={localization[sectionId].defaultMessage}
|
||||
style={style.sectionText}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
renderRow = (data, sectionId) => {
|
||||
if (sectionId === 'specialMentions') {
|
||||
return this.renderSpecialMentions(data);
|
||||
}
|
||||
|
||||
const style = getStyleFromTheme(this.props.theme);
|
||||
const hasFullName = data.first_name.length > 0 && data.last_name.length > 0;
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => this.completeMention(data.username)}
|
||||
style={style.row}
|
||||
>
|
||||
<View style={style.rowPicture}>
|
||||
<ProfilePicture
|
||||
user={data}
|
||||
theme={this.props.theme}
|
||||
size={20}
|
||||
status={null}
|
||||
/>
|
||||
</View>
|
||||
<Text style={style.rowUsername}>{`@${data.username}`}</Text>
|
||||
{hasFullName && <Text style={style.rowUsername}>{' - '}</Text>}
|
||||
{hasFullName && <Text style={style.rowFullname}>{`${data.first_name} ${data.last_name}`}</Text>}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
renderSpecialMentions = (data) => {
|
||||
const style = getStyleFromTheme(this.props.theme);
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => this.completeMention(data.completeHandle)}
|
||||
style={style.row}
|
||||
>
|
||||
<View style={style.rowPicture}>
|
||||
<Icon
|
||||
name='users'
|
||||
style={style.rowIcon}
|
||||
/>
|
||||
</View>
|
||||
<Text style={style.textWrapper}>
|
||||
<Text style={style.rowUsername}>{`@${data.completeHandle}`}</Text>
|
||||
<Text style={style.rowUsername}>{' - '}</Text>
|
||||
<FormattedText
|
||||
id={data.id}
|
||||
defaultMessage={data.defaultMessage}
|
||||
values={data.values}
|
||||
style={[style.rowFullname, {flex: 1}]}
|
||||
/>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {autocompleteUsersInCurrentChannel, requestStatus} = this.props;
|
||||
if (!this.state.active && (requestStatus !== RequestStatus.STARTED || requestStatus !== RequestStatus.SUCCESS)) {
|
||||
// If we are not in an active state return null so nothing is rendered
|
||||
// other components are not blocked.
|
||||
return null;
|
||||
}
|
||||
|
||||
const style = getStyleFromTheme(this.props.theme);
|
||||
|
||||
if (
|
||||
!autocompleteUsersInCurrentChannel.inChannel &&
|
||||
!autocompleteUsersInCurrentChannel.outChannel &&
|
||||
requestStatus === RequestStatus.STARTED
|
||||
) {
|
||||
return (
|
||||
<View style={style.loading}>
|
||||
<FormattedText
|
||||
id='analytics.chart.loading": "Loading...'
|
||||
defaultMessage='Loading...'
|
||||
style={style.sectionText}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ListView
|
||||
keyboardShouldPersistTaps='always'
|
||||
style={style.listView}
|
||||
enableEmptySections={true}
|
||||
dataSource={this.state.dataSource}
|
||||
renderSectionHeader={this.renderSectionHeader}
|
||||
renderRow={this.renderRow}
|
||||
renderFooter={this.renderFooter}
|
||||
pageSize={10}
|
||||
initialListSize={10}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
return StyleSheet.create({
|
||||
section: {
|
||||
|
|
@ -90,254 +346,3 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
export default class AtMention extends Component {
|
||||
static propTypes = {
|
||||
currentUserId: PropTypes.string.isRequired,
|
||||
currentChannelId: PropTypes.string.isRequired,
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
cursorPosition: PropTypes.number.isRequired,
|
||||
defaultChannel: PropTypes.object.isRequired,
|
||||
autocompleteUsersInCurrentChannel: PropTypes.object.isRequired,
|
||||
postDraft: PropTypes.string,
|
||||
requestStatus: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
onChangeText: PropTypes.func.isRequired,
|
||||
actions: PropTypes.shape({
|
||||
autocompleteUsersInChannel: PropTypes.func.isRequired
|
||||
})
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
autocompleteUsersInCurrentChannel: {},
|
||||
postDraft: ''
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const ds = new ListView.DataSource({
|
||||
sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
|
||||
rowHasChanged: (r1, r2) => r1 !== r2
|
||||
});
|
||||
const data = {};
|
||||
this.state = {
|
||||
active: false,
|
||||
dataSource: ds.cloneWithRowsAndSections(data)
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const match = nextProps.postDraft.substring(0, nextProps.cursorPosition).match(AT_MENTION_REGEX);
|
||||
|
||||
if (!match || this.state.mentionComplete) {
|
||||
this.setState({
|
||||
active: false,
|
||||
matchTerm: null,
|
||||
mentionComplete: false
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const matchTerm = match[2];
|
||||
|
||||
if (matchTerm !== this.state.matchTerm) {
|
||||
this.setState({
|
||||
matchTerm
|
||||
});
|
||||
|
||||
const {currentTeamId, currentChannelId} = this.props;
|
||||
this.props.actions.autocompleteUsersInChannel(currentTeamId, currentChannelId, matchTerm);
|
||||
}
|
||||
|
||||
if (nextProps.requestStatus !== RequestStatus.STARTED) {
|
||||
let membersInChannel = nextProps.autocompleteUsersInCurrentChannel.in_channel || [];
|
||||
const membersOutOfChannel = nextProps.autocompleteUsersInCurrentChannel.out_of_channel || [];
|
||||
|
||||
// filter the current user out
|
||||
membersInChannel = membersInChannel.filter((m) => m.id !== this.props.currentUserId);
|
||||
|
||||
let data = {};
|
||||
if (membersInChannel.length > 0) {
|
||||
data = Object.assign({}, data, {inChannel: membersInChannel});
|
||||
}
|
||||
if (this.checkSpecialMentions(matchTerm)) {
|
||||
data = Object.assign({}, data, {specialMentions: this.getSpecialMentions()});
|
||||
}
|
||||
if (membersOutOfChannel.length > 0) {
|
||||
data = Object.assign({}, data, {notInChannel: membersOutOfChannel});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
active: data.hasOwnProperty('inChannel') || data.hasOwnProperty('specialMentions') || data.hasOwnProperty('notInChannel'),
|
||||
dataSource: this.state.dataSource.cloneWithRowsAndSections(data)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getSpecialMentions = () => {
|
||||
return [{
|
||||
completeHandle: 'all',
|
||||
id: 'suggestion.mention.all',
|
||||
defaultMessage: 'Notifies everyone in the channel, use in {townsquare} to notify the whole team',
|
||||
values: {
|
||||
townsquare: this.props.defaultChannel.display_name
|
||||
}
|
||||
}, {
|
||||
completeHandle: 'channel',
|
||||
id: 'suggestion.mention.channel',
|
||||
defaultMessage: 'Notifies everyone in the channel'
|
||||
}, {
|
||||
completeHandle: 'here',
|
||||
id: 'suggestions.mention.here',
|
||||
defaultMessage: 'Notifies everyone in the channel and online'
|
||||
}];
|
||||
}
|
||||
|
||||
checkSpecialMentions = (term) => {
|
||||
return this.getSpecialMentions().filter((m) => m.completeHandle.startsWith(term)).length > 0;
|
||||
}
|
||||
|
||||
completeMention = (mention) => {
|
||||
const mentionPart = this.props.postDraft.substring(0, this.props.cursorPosition);
|
||||
|
||||
let completedDraft = mentionPart.replace(AT_MENTION_REGEX, `@${mention} `);
|
||||
if (this.props.postDraft.length > this.props.cursorPosition) {
|
||||
completedDraft += this.props.postDraft.substring(this.props.cursorPosition);
|
||||
}
|
||||
|
||||
this.props.onChangeText(completedDraft);
|
||||
this.setState({
|
||||
active: false,
|
||||
mentionComplete: true
|
||||
});
|
||||
}
|
||||
|
||||
renderSectionHeader = (sectionData, sectionId) => {
|
||||
const style = getStyleFromTheme(this.props.theme);
|
||||
|
||||
const localization = {
|
||||
inChannel: {
|
||||
id: 'suggestion.mention.members',
|
||||
defaultMessage: 'Channel Members'
|
||||
},
|
||||
notInChannel: {
|
||||
id: 'suggestion.mention.nonmembers',
|
||||
defaultMessage: 'Not in Channel'
|
||||
},
|
||||
specialMentions: {
|
||||
id: 'suggestion.mention.special',
|
||||
defaultMessage: 'Special Mentions'
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={style.sectionWrapper}>
|
||||
<View style={style.section}>
|
||||
<FormattedText
|
||||
id={localization[sectionId].id}
|
||||
defaultMessage={localization[sectionId].defaultMessage}
|
||||
style={style.sectionText}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
renderRow = (data, sectionId) => {
|
||||
if (sectionId === 'specialMentions') {
|
||||
return this.renderSpecialMentions(data);
|
||||
}
|
||||
|
||||
const style = getStyleFromTheme(this.props.theme);
|
||||
const hasFullName = data.first_name.length > 0 && data.last_name.length > 0;
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => this.completeMention(data.username)}
|
||||
style={style.row}
|
||||
>
|
||||
<View style={style.rowPicture}>
|
||||
<ProfilePicture
|
||||
user={data}
|
||||
theme={this.props.theme}
|
||||
size={20}
|
||||
status={null}
|
||||
/>
|
||||
</View>
|
||||
<Text style={style.rowUsername}>{`@${data.username}`}</Text>
|
||||
{hasFullName && <Text style={style.rowUsername}>{' - '}</Text>}
|
||||
{hasFullName && <Text style={style.rowFullname}>{`${data.first_name} ${data.last_name}`}</Text>}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
renderSpecialMentions = (data) => {
|
||||
const style = getStyleFromTheme(this.props.theme);
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => this.completeMention(data.completeHandle)}
|
||||
style={style.row}
|
||||
>
|
||||
<View style={style.rowPicture}>
|
||||
<Icon
|
||||
name='users'
|
||||
style={style.rowIcon}
|
||||
/>
|
||||
</View>
|
||||
<Text style={style.textWrapper}>
|
||||
<Text style={style.rowUsername}>{`@${data.completeHandle}`}</Text>
|
||||
<Text style={style.rowUsername}>{' - '}</Text>
|
||||
<FormattedText
|
||||
id={data.id}
|
||||
defaultMessage={data.defaultMessage}
|
||||
values={data.values}
|
||||
style={[style.rowFullname, {flex: 1}]}
|
||||
/>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {autocompleteUsersInCurrentChannel, requestStatus} = this.props;
|
||||
if (!this.state.active && (requestStatus !== RequestStatus.STARTED || requestStatus !== RequestStatus.SUCCESS)) {
|
||||
// If we are not in an active state return null so nothing is rendered
|
||||
// other components are not blocked.
|
||||
return null;
|
||||
}
|
||||
|
||||
const style = getStyleFromTheme(this.props.theme);
|
||||
|
||||
if (
|
||||
!autocompleteUsersInCurrentChannel.in_channel &&
|
||||
!autocompleteUsersInCurrentChannel.out_of_channel &&
|
||||
requestStatus === RequestStatus.STARTED
|
||||
) {
|
||||
return (
|
||||
<View style={style.loading}>
|
||||
<FormattedText
|
||||
id='analytics.chart.loading": "Loading...'
|
||||
defaultMessage='Loading...'
|
||||
style={style.sectionText}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ListView
|
||||
keyboardShouldPersistTaps='always'
|
||||
style={style.listView}
|
||||
enableEmptySections={true}
|
||||
dataSource={this.state.dataSource}
|
||||
renderSectionHeader={this.renderSectionHeader}
|
||||
renderRow={this.renderRow}
|
||||
renderFooter={this.renderFooter}
|
||||
pageSize={10}
|
||||
initialListSize={10}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import {bindActionCreators} from 'redux';
|
|||
import {connect} from 'react-redux';
|
||||
|
||||
import {getTheme} from 'app/selectors/preferences';
|
||||
import {autocompleteUsersInChannel} from 'mattermost-redux/actions/users';
|
||||
import {autocompleteUsers} from 'mattermost-redux/actions/users';
|
||||
import {getDefaultChannel} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getAutocompleteUsersInCurrentChannel} from 'mattermost-redux/selectors/entities/users';
|
||||
import {getProfilesInCurrentChannel, getProfilesNotInCurrentChannel} from 'mattermost-redux/selectors/entities/users';
|
||||
|
||||
import AtMention from './at_mention';
|
||||
|
||||
|
|
@ -28,8 +28,11 @@ function mapStateToProps(state, ownProps) {
|
|||
currentTeamId: state.entities.teams.currentTeamId,
|
||||
defaultChannel: getDefaultChannel(state),
|
||||
postDraft,
|
||||
autocompleteUsersInCurrentChannel: getAutocompleteUsersInCurrentChannel(state),
|
||||
requestStatus: state.requests.users.autocompleteUsersInChannel.status,
|
||||
autocompleteUsersInCurrentChannel: {
|
||||
inChannel: getProfilesInCurrentChannel(state),
|
||||
outChannel: getProfilesNotInCurrentChannel(state)
|
||||
},
|
||||
requestStatus: state.requests.users.autocompleteUsers.status,
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
|
@ -37,7 +40,7 @@ function mapStateToProps(state, ownProps) {
|
|||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
autocompleteUsersInChannel
|
||||
autocompleteUsers
|
||||
}, dispatch)
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,205 @@ import {RequestStatus} from 'mattermost-redux/constants';
|
|||
|
||||
const CHANNEL_MENTION_REGEX = /\B(~([^~\r\n]*))$/i;
|
||||
|
||||
export default class ChannelMention extends Component {
|
||||
static propTypes = {
|
||||
currentChannelId: PropTypes.string.isRequired,
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
cursorPosition: PropTypes.number.isRequired,
|
||||
autocompleteChannels: PropTypes.object.isRequired,
|
||||
postDraft: PropTypes.string,
|
||||
requestStatus: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
onChangeText: PropTypes.func.isRequired,
|
||||
actions: PropTypes.shape({
|
||||
searchMoreChannels: PropTypes.func.isRequired
|
||||
})
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
postDraft: ''
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const ds = new ListView.DataSource({
|
||||
sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
|
||||
rowHasChanged: (r1, r2) => r1 !== r2
|
||||
});
|
||||
|
||||
this.state = {
|
||||
active: false,
|
||||
dataSource: ds.cloneWithRowsAndSections(props.autocompleteChannels)
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const match = nextProps.postDraft.substring(0, nextProps.cursorPosition).match(CHANNEL_MENTION_REGEX);
|
||||
|
||||
// If not match or if user clicked on a channel
|
||||
if (!match || this.state.mentionComplete) {
|
||||
const nextState = {
|
||||
active: false,
|
||||
mentionComplete: false
|
||||
};
|
||||
|
||||
// Handle the case where the user typed a ~ first and then backspaced
|
||||
if (nextProps.postDraft.length < this.props.postDraft.length) {
|
||||
nextState.matchTerm = null;
|
||||
}
|
||||
|
||||
this.setState(nextState);
|
||||
return;
|
||||
}
|
||||
|
||||
const matchTerm = match[2];
|
||||
const myChannels = this.filter(nextProps.autocompleteChannels.myChannels, matchTerm);
|
||||
const otherChannels = this.filter(nextProps.autocompleteChannels.otherChannels, matchTerm);
|
||||
|
||||
// Show loading indicator on first pull for channels
|
||||
if (nextProps.requestStatus === RequestStatus.STARTED && ((myChannels.length === 0 && otherChannels.length === 0) || matchTerm === '')) {
|
||||
this.setState({
|
||||
active: true,
|
||||
loading: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Still matching the same term that didn't return any results
|
||||
if (match[0].startsWith(`~${this.state.matchTerm}`) && (myChannels.length === 0 && otherChannels.length === 0)) {
|
||||
this.setState({
|
||||
active: false
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (matchTerm !== this.state.matchTerm) {
|
||||
this.setState({
|
||||
matchTerm
|
||||
});
|
||||
|
||||
const {currentTeamId} = this.props;
|
||||
this.props.actions.searchMoreChannels(currentTeamId, matchTerm);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nextProps.requestStatus !== RequestStatus.STARTED && this.props.autocompleteChannels !== nextProps.autocompleteChannels) {
|
||||
let data = {};
|
||||
if (myChannels.length > 0) {
|
||||
data = Object.assign({}, data, {myChannels});
|
||||
}
|
||||
if (otherChannels.length > 0) {
|
||||
data = Object.assign({}, data, {otherChannels});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
active: true,
|
||||
loading: false,
|
||||
dataSource: this.state.dataSource.cloneWithRowsAndSections(data)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
filter = (channels, matchTerm) => {
|
||||
return channels.filter((c) => c.name.includes(matchTerm) || c.display_name.includes(matchTerm));
|
||||
};
|
||||
|
||||
completeMention = (mention) => {
|
||||
const mentionPart = this.props.postDraft.substring(0, this.props.cursorPosition);
|
||||
|
||||
let completedDraft = mentionPart.replace(CHANNEL_MENTION_REGEX, `~${mention} `);
|
||||
if (this.props.postDraft.length > this.props.cursorPosition) {
|
||||
completedDraft += this.props.postDraft.substring(this.props.cursorPosition);
|
||||
}
|
||||
|
||||
this.props.onChangeText(completedDraft);
|
||||
this.setState({
|
||||
active: false,
|
||||
mentionComplete: true,
|
||||
matchTerm: `${mention} `
|
||||
});
|
||||
};
|
||||
|
||||
renderSectionHeader = (sectionData, sectionId) => {
|
||||
const style = getStyleFromTheme(this.props.theme);
|
||||
|
||||
const localization = {
|
||||
myChannels: {
|
||||
id: 'suggestion.mention.channels',
|
||||
defaultMessage: 'My Channels'
|
||||
},
|
||||
otherChannels: {
|
||||
id: 'suggestion.mention.morechannels',
|
||||
defaultMessage: 'Other Channels'
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={style.sectionWrapper}>
|
||||
<View style={style.section}>
|
||||
<FormattedText
|
||||
id={localization[sectionId].id}
|
||||
defaultMessage={localization[sectionId].defaultMessage}
|
||||
style={style.sectionText}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
renderRow = (data) => {
|
||||
const style = getStyleFromTheme(this.props.theme);
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => this.completeMention(data.name)}
|
||||
style={style.row}
|
||||
>
|
||||
<Text style={style.rowDisplayName}>{data.display_name}</Text>
|
||||
<Text style={style.rowName}>{` (~${data.name})`}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
if (!this.state.active) {
|
||||
// If we are not in an active state return null so nothing is rendered
|
||||
// other components are not blocked.
|
||||
return null;
|
||||
}
|
||||
|
||||
const {requestStatus, theme} = this.props;
|
||||
|
||||
const style = getStyleFromTheme(theme);
|
||||
|
||||
if (this.state.loading && requestStatus === RequestStatus.STARTED) {
|
||||
return (
|
||||
<View style={style.loading}>
|
||||
<FormattedText
|
||||
id='analytics.chart.loading": "Loading...'
|
||||
defaultMessage='Loading...'
|
||||
style={style.sectionText}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ListView
|
||||
keyboardShouldPersistTaps='always'
|
||||
style={style.listView}
|
||||
enableEmptySections={true}
|
||||
dataSource={this.state.dataSource}
|
||||
renderSectionHeader={this.renderSectionHeader}
|
||||
renderRow={this.renderRow}
|
||||
pageSize={10}
|
||||
initialListSize={10}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
return StyleSheet.create({
|
||||
section: {
|
||||
|
|
@ -73,198 +272,3 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
export default class ChannelMention extends Component {
|
||||
static propTypes = {
|
||||
currentChannelId: PropTypes.string.isRequired,
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
cursorPosition: PropTypes.number.isRequired,
|
||||
autocompleteChannels: PropTypes.object.isRequired,
|
||||
postDraft: PropTypes.string,
|
||||
requestStatus: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
onChangeText: PropTypes.func.isRequired,
|
||||
actions: PropTypes.shape({
|
||||
autocompleteChannels: PropTypes.func.isRequired
|
||||
})
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
postDraft: ''
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const ds = new ListView.DataSource({
|
||||
sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
|
||||
rowHasChanged: (r1, r2) => r1 !== r2
|
||||
});
|
||||
|
||||
this.state = {
|
||||
active: false,
|
||||
dataSource: ds.cloneWithRowsAndSections(props.autocompleteChannels)
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const match = nextProps.postDraft.substring(0, nextProps.cursorPosition).match(CHANNEL_MENTION_REGEX);
|
||||
|
||||
// If not match or if user clicked on a channel
|
||||
if (!match || this.state.mentionComplete) {
|
||||
const nextState = {
|
||||
active: false,
|
||||
mentionComplete: false
|
||||
};
|
||||
|
||||
// Handle the case where the user typed a ~ first and then backspaced
|
||||
if (nextProps.postDraft.length < this.props.postDraft.length) {
|
||||
nextState.matchTerm = null;
|
||||
}
|
||||
|
||||
this.setState(nextState);
|
||||
return;
|
||||
}
|
||||
|
||||
const matchTerm = match[2];
|
||||
const myChannels = nextProps.autocompleteChannels.myChannels;
|
||||
const otherChannels = nextProps.autocompleteChannels.otherChannels;
|
||||
|
||||
// Show loading indicator on first pull for channels
|
||||
if (nextProps.requestStatus === RequestStatus.STARTED && ((myChannels.length === 0 && otherChannels.length === 0) || matchTerm === '')) {
|
||||
this.setState({
|
||||
active: true,
|
||||
loading: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Still matching the same term that didn't return any results
|
||||
if (match[0].startsWith(`~${this.state.matchTerm}`) && (myChannels.length === 0 && otherChannels.length === 0)) {
|
||||
this.setState({
|
||||
active: false
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (matchTerm !== this.state.matchTerm) {
|
||||
this.setState({
|
||||
matchTerm
|
||||
});
|
||||
|
||||
const {currentTeamId} = this.props;
|
||||
this.props.actions.autocompleteChannels(currentTeamId, matchTerm);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nextProps.requestStatus !== RequestStatus.STARTED && this.props.autocompleteChannels !== nextProps.autocompleteChannels) {
|
||||
let data = {};
|
||||
if (myChannels.length > 0) {
|
||||
data = Object.assign({}, data, {myChannels});
|
||||
}
|
||||
if (otherChannels.length > 0) {
|
||||
data = Object.assign({}, data, {otherChannels});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
active: true,
|
||||
loading: false,
|
||||
dataSource: this.state.dataSource.cloneWithRowsAndSections(data)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
completeMention = (mention) => {
|
||||
const mentionPart = this.props.postDraft.substring(0, this.props.cursorPosition);
|
||||
|
||||
let completedDraft = mentionPart.replace(CHANNEL_MENTION_REGEX, `~${mention} `);
|
||||
if (this.props.postDraft.length > this.props.cursorPosition) {
|
||||
completedDraft += this.props.postDraft.substring(this.props.cursorPosition);
|
||||
}
|
||||
|
||||
this.props.onChangeText(completedDraft);
|
||||
this.setState({
|
||||
active: false,
|
||||
mentionComplete: true,
|
||||
matchTerm: `${mention} `
|
||||
});
|
||||
}
|
||||
|
||||
renderSectionHeader = (sectionData, sectionId) => {
|
||||
const style = getStyleFromTheme(this.props.theme);
|
||||
|
||||
const localization = {
|
||||
myChannels: {
|
||||
id: 'suggestion.mention.channels',
|
||||
defaultMessage: 'My Channels'
|
||||
},
|
||||
otherChannels: {
|
||||
id: 'suggestion.mention.morechannels',
|
||||
defaultMessage: 'Other Channels'
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={style.sectionWrapper}>
|
||||
<View style={style.section}>
|
||||
<FormattedText
|
||||
id={localization[sectionId].id}
|
||||
defaultMessage={localization[sectionId].defaultMessage}
|
||||
style={style.sectionText}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
renderRow = (data) => {
|
||||
const style = getStyleFromTheme(this.props.theme);
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => this.completeMention(data.name)}
|
||||
style={style.row}
|
||||
>
|
||||
<Text style={style.rowDisplayName}>{data.display_name}</Text>
|
||||
<Text style={style.rowName}>{` (~${data.name})`}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.state.active) {
|
||||
// If we are not in an active state return null so nothing is rendered
|
||||
// other components are not blocked.
|
||||
return null;
|
||||
}
|
||||
|
||||
const {requestStatus, theme} = this.props;
|
||||
|
||||
const style = getStyleFromTheme(theme);
|
||||
|
||||
if (this.state.loading && requestStatus === RequestStatus.STARTED) {
|
||||
return (
|
||||
<View style={style.loading}>
|
||||
<FormattedText
|
||||
id='analytics.chart.loading": "Loading...'
|
||||
defaultMessage='Loading...'
|
||||
style={style.sectionText}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ListView
|
||||
keyboardShouldPersistTaps='always'
|
||||
style={style.listView}
|
||||
enableEmptySections={true}
|
||||
dataSource={this.state.dataSource}
|
||||
renderSectionHeader={this.renderSectionHeader}
|
||||
renderRow={this.renderRow}
|
||||
pageSize={10}
|
||||
initialListSize={10}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@
|
|||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {autocompleteChannels} from 'mattermost-redux/actions/channels';
|
||||
import {searchMoreChannels} from 'mattermost-redux/actions/channels';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
import {getMyChannels, getOtherChannels} from 'mattermost-redux/selectors/entities/channels';
|
||||
|
||||
import {getTheme} from 'app/selectors/preferences';
|
||||
import {getAutocompleteChannelWithSections} from 'mattermost-redux/selectors/entities/channels';
|
||||
|
||||
import ChannelMention from './channel_mention';
|
||||
|
||||
|
|
@ -20,13 +22,18 @@ function mapStateToProps(state, ownProps) {
|
|||
postDraft = state.views.channel.drafts[currentChannelId].draft;
|
||||
}
|
||||
|
||||
const autocompleteChannels = {
|
||||
myChannels: getMyChannels(state).filter((c) => c.type !== General.DM_CHANNEL && c.type !== General.GM_CHANNEL),
|
||||
otherChannels: getOtherChannels(state)
|
||||
};
|
||||
|
||||
return {
|
||||
...ownProps,
|
||||
currentChannelId,
|
||||
currentTeamId: state.entities.teams.currentTeamId,
|
||||
postDraft,
|
||||
autocompleteChannels: getAutocompleteChannelWithSections(state),
|
||||
requestStatus: state.requests.channels.autocompleteChannels.status,
|
||||
autocompleteChannels,
|
||||
requestStatus: state.requests.channels.getChannels.status,
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
|
@ -34,7 +41,7 @@ function mapStateToProps(state, ownProps) {
|
|||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
autocompleteChannels
|
||||
searchMoreChannels
|
||||
}, dispatch)
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export default class Autocomplete extends Component {
|
|||
this.setState({
|
||||
cursorPosition: event.nativeEvent.selection.end
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import FormattedText from 'app/components/formatted_text';
|
|||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
import {Constants} from 'mattermost-redux/constants';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
|
||||
import ChannelDrawerItem from './channel_drawer_item';
|
||||
import UnreadIndicator from './unread_indicator';
|
||||
|
|
@ -132,7 +132,7 @@ class ChannelDrawerList extends Component {
|
|||
mentions = member.mention_count;
|
||||
unreadCount = channel.total_msg_count - member.msg_count;
|
||||
|
||||
if (member.notify_props && member.notify_props.mark_unread === Constants.MENTION) {
|
||||
if (member.notify_props && member.notify_props.mark_unread === General.MENTION) {
|
||||
unreadCount = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -178,7 +178,7 @@ class ChannelDrawerList extends Component {
|
|||
};
|
||||
|
||||
createPrivateChannel = () => {
|
||||
this.props.actions.goToCreateChannel(Constants.PRIVATE_CHANNEL);
|
||||
this.props.actions.goToCreateChannel(General.PRIVATE_CHANNEL);
|
||||
};
|
||||
|
||||
buildData = (props) => {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import {
|
|||
unmarkFavorite
|
||||
} from 'app/actions/views/channel';
|
||||
|
||||
import {Constants} from 'mattermost-redux/constants';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
import {getCurrentUserId, getCurrentUserRoles} from 'mattermost-redux/selectors/entities/users';
|
||||
import {showCreateOption} from 'mattermost-redux/utils/channel_utils';
|
||||
import {isAdmin, isSystemAdmin} from 'mattermost-redux/utils/user_utils';
|
||||
|
|
@ -32,7 +32,7 @@ function mapStateToProps(state, ownProps) {
|
|||
const roles = getCurrentUserId(state) ? getCurrentUserRoles(state) : '';
|
||||
|
||||
return {
|
||||
canCreatePrivateChannels: showCreateOption(config, license, Constants.PRIVATE_CHANNEL, isAdmin(roles), isSystemAdmin(roles)),
|
||||
canCreatePrivateChannels: showCreateOption(config, license, General.PRIVATE_CHANNEL, isAdmin(roles), isSystemAdmin(roles)),
|
||||
...ownProps
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import Icon from 'react-native-vector-icons/FontAwesome';
|
|||
|
||||
import {OnlineStatus, AwayStatus, OfflineStatus} from 'app/components/status_icons';
|
||||
|
||||
import {Constants} from 'mattermost-redux/constants';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
|
|
@ -44,21 +44,21 @@ function channelIcon(props) {
|
|||
activeGroup = style.groupInfo;
|
||||
}
|
||||
|
||||
if (type === Constants.OPEN_CHANNEL) {
|
||||
if (type === General.OPEN_CHANNEL) {
|
||||
return (
|
||||
<Icon
|
||||
name='globe'
|
||||
style={[style.icon, unreadIcon, activeIcon, {fontSize: size}]}
|
||||
/>
|
||||
);
|
||||
} else if (type === Constants.PRIVATE_CHANNEL) {
|
||||
} else if (type === General.PRIVATE_CHANNEL) {
|
||||
return (
|
||||
<Icon
|
||||
name='lock'
|
||||
style={[style.icon, unreadIcon, activeIcon, {fontSize: size}]}
|
||||
/>
|
||||
);
|
||||
} else if (type === Constants.GM_CHANNEL) {
|
||||
} else if (type === General.GM_CHANNEL) {
|
||||
return (
|
||||
<View style={style.groupContainer}>
|
||||
<View style={[style.groupBox, unreadGroupBox, activeGroupBox, {width: size, height: size}]}>
|
||||
|
|
@ -70,7 +70,7 @@ function channelIcon(props) {
|
|||
);
|
||||
}
|
||||
switch (status) {
|
||||
case Constants.ONLINE:
|
||||
case General.ONLINE:
|
||||
return (
|
||||
<View style={style.statusIcon}>
|
||||
<OnlineStatus
|
||||
|
|
@ -80,7 +80,7 @@ function channelIcon(props) {
|
|||
/>
|
||||
</View>
|
||||
);
|
||||
case Constants.AWAY:
|
||||
case General.AWAY:
|
||||
return (
|
||||
<View style={style.statusIcon}>
|
||||
<AwayStatus
|
||||
|
|
|
|||
|
|
@ -1,42 +1,67 @@
|
|||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
import React, {PropTypes, PureComponent} from 'react';
|
||||
import {Text} from 'react-native';
|
||||
import {StyleSheet, Text} from 'react-native';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
|
||||
import {getTheme} from 'app/selectors/preferences';
|
||||
import {GlobalStyles} from 'app/styles';
|
||||
import {makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
export default class ErrorText extends PureComponent {
|
||||
class ErrorText extends PureComponent {
|
||||
static propTypes = {
|
||||
error: PropTypes.oneOfType([PropTypes.string, PropTypes.object])
|
||||
error: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
theme: PropTypes.object
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
error: {}
|
||||
error: {},
|
||||
theme: {}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (!this.props.error) {
|
||||
const {error, theme} = this.props;
|
||||
if (!error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.props.error.hasOwnProperty('intl')) {
|
||||
const {intl} = this.props.error;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
if (error.hasOwnProperty('intl')) {
|
||||
const {intl} = error;
|
||||
return (
|
||||
<FormattedText
|
||||
id={intl.id}
|
||||
defaultMessage={intl.defaultMessage}
|
||||
values={intl.values}
|
||||
style={GlobalStyles.errorLabel}
|
||||
style={[GlobalStyles.errorLabel, style.errorLabel]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Text style={GlobalStyles.errorLabel}>
|
||||
{this.props.error.message || this.props.error}
|
||||
<Text style={[GlobalStyles.errorLabel, style.errorLabel]}>
|
||||
{error.message || error}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return StyleSheet.create({
|
||||
errorLabel: {
|
||||
color: (theme.errorTextColor || '#DA4A4A')
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
...ownProps,
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(ErrorText);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
StyleSheet
|
||||
} from 'react-native';
|
||||
|
||||
import Client from 'mattermost-redux/client';
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
|
||||
import imageIcon from 'assets/images/icons/image.png';
|
||||
|
||||
|
|
@ -25,6 +25,8 @@ const IMAGE_SIZE = {
|
|||
Thumbnail: 'thumbnail'
|
||||
};
|
||||
|
||||
const IMAGE_HEADERS = Client4.getOptions().headers;
|
||||
|
||||
export default class FileAttachmentImage extends PureComponent {
|
||||
static propTypes = {
|
||||
addFileToFetchCache: PropTypes.func.isRequired,
|
||||
|
|
@ -103,14 +105,14 @@ export default class FileAttachmentImage extends PureComponent {
|
|||
|
||||
switch (imageSize) {
|
||||
case IMAGE_SIZE.Fullsize:
|
||||
return Client.getFileUrl(file.id, this.state.timestamp);
|
||||
return Client4.getFileUrl(file.id, this.state.timestamp);
|
||||
case IMAGE_SIZE.Preview:
|
||||
return Client.getFilePreviewUrl(file.id, this.state.timestamp);
|
||||
return Client4.getFilePreviewUrl(file.id, this.state.timestamp);
|
||||
case IMAGE_SIZE.Thumbnail:
|
||||
default:
|
||||
return Client.getFileThumbnailUrl(file.id, this.state.timestamp);
|
||||
return Client4.getFileThumbnailUrl(file.id, this.state.timestamp);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
|
|
@ -131,7 +133,7 @@ export default class FileAttachmentImage extends PureComponent {
|
|||
if (this.state.retry === 4) {
|
||||
source = imageIcon;
|
||||
} else if (file.id) {
|
||||
source = {uri: this.handleGetImageURL()};
|
||||
source = {uri: this.handleGetImageURL(), headers: IMAGE_HEADERS};
|
||||
}
|
||||
|
||||
const isInFetchCache = fetchCache[source.uri];
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@
|
|||
|
||||
import {connect} from 'react-redux';
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {deletePost, flagPost, unflagPost} from 'mattermost-redux/actions/posts';
|
||||
import {deletePost, flagPost, removePost, unflagPost} from 'mattermost-redux/actions/posts';
|
||||
import {getMyPreferences} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {makeGetCommentCountForPost} from 'mattermost-redux/selectors/entities/posts';
|
||||
import {getCurrentUserId, getCurrentUserRoles, getUser} from 'mattermost-redux/selectors/entities/users';
|
||||
import {isPostFlagged} from 'mattermost-redux/utils/post_utils';
|
||||
|
|
@ -30,7 +29,6 @@ function makeMapStateToProps() {
|
|||
config,
|
||||
commentCount: getCommentCountForPost(state, ownProps),
|
||||
commentedOnDisplayName: displayUsername(commentedOnUser, myPreferences),
|
||||
currentTeamId: getCurrentTeamId(state),
|
||||
currentUserId: getCurrentUserId(state),
|
||||
displayName: displayUsername(user, myPreferences),
|
||||
isFlagged: isPostFlagged(ownProps.post.id, myPreferences),
|
||||
|
|
@ -49,6 +47,7 @@ function mapDispatchToProps(dispatch) {
|
|||
flagPost,
|
||||
goToUserProfile,
|
||||
openEditPostModal,
|
||||
removePost,
|
||||
unflagPost
|
||||
}, dispatch)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
|||
|
||||
import webhookIcon from 'assets/images/icons/webhook.jpg';
|
||||
|
||||
import {Constants} from 'mattermost-redux/constants';
|
||||
import {Posts} from 'mattermost-redux/constants';
|
||||
import DelayedAction from 'mattermost-redux/utils/delayed_action';
|
||||
import {canDeletePost, canEditPost, isSystemMessage} from 'mattermost-redux/utils/post_utils';
|
||||
import {isAdmin, isSystemAdmin} from 'mattermost-redux/utils/user_utils';
|
||||
|
|
@ -38,7 +38,6 @@ class Post extends PureComponent {
|
|||
static propTypes = {
|
||||
config: PropTypes.object.isRequired,
|
||||
commentCount: PropTypes.number.isRequired,
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
currentUserId: PropTypes.string.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
style: View.propTypes.style,
|
||||
|
|
@ -60,6 +59,7 @@ class Post extends PureComponent {
|
|||
flagPost: PropTypes.func.isRequired,
|
||||
goToUserProfile: PropTypes.func.isRequired,
|
||||
openEditPostModal: PropTypes.func.isRequired,
|
||||
removePost: PropTypes.func.isRequired,
|
||||
unflagPost: PropTypes.func.isRequired
|
||||
}).isRequired
|
||||
};
|
||||
|
|
@ -93,7 +93,7 @@ class Post extends PureComponent {
|
|||
|
||||
handlePostDelete = () => {
|
||||
const {formatMessage} = this.props.intl;
|
||||
const {currentTeamId, post, actions} = this.props;
|
||||
const {post, actions} = this.props;
|
||||
|
||||
Alert.alert(
|
||||
formatMessage({id: 'mobile.post.delete_title', defaultMessage: 'Delete Post'}),
|
||||
|
|
@ -106,7 +106,7 @@ class Post extends PureComponent {
|
|||
style: 'destructive',
|
||||
onPress: () => {
|
||||
this.editDisableAction.cancel();
|
||||
actions.deletePost(currentTeamId, post);
|
||||
actions.deletePost(post);
|
||||
}
|
||||
}]
|
||||
);
|
||||
|
|
@ -119,8 +119,10 @@ class Post extends PureComponent {
|
|||
|
||||
handlePress = () => {
|
||||
const {post, onPress} = this.props;
|
||||
if (onPress && post.state !== Constants.POST_DELETED && !isSystemMessage(post)) {
|
||||
if (onPress && post.state !== Posts.POST_DELETED && !isSystemMessage(post)) {
|
||||
preventDoubleTap(onPress, null, post);
|
||||
} else if (post.state === Posts.POST_DELETED) {
|
||||
preventDoubleTap(this.onRemovePost, this, post);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -130,6 +132,11 @@ class Post extends PureComponent {
|
|||
}
|
||||
};
|
||||
|
||||
onRemovePost = (post) => {
|
||||
const {removePost} = this.props.actions;
|
||||
removePost(post);
|
||||
};
|
||||
|
||||
showOptionsContext = () => {
|
||||
if (Platform.OS === 'ios') {
|
||||
return this.refs.tooltip.show();
|
||||
|
|
@ -236,13 +243,13 @@ class Post extends PureComponent {
|
|||
actions.push({text: formatMessage({id: 'post_info.edit', defaultMessage: 'Edit'}), onPress: () => this.handlePostEdit()});
|
||||
}
|
||||
|
||||
if (this.state.canDelete && post.state !== Constants.POST_DELETED) {
|
||||
if (this.state.canDelete && post.state !== Posts.POST_DELETED) {
|
||||
actions.push({text: formatMessage({id: 'post_info.del', defaultMessage: 'Delete'}), onPress: () => this.handlePostDelete()});
|
||||
}
|
||||
|
||||
let messageContainer;
|
||||
let message;
|
||||
if (post.state === Constants.POST_DELETED) {
|
||||
if (post.state === Posts.POST_DELETED) {
|
||||
message = (
|
||||
<FormattedText
|
||||
style={messageStyle}
|
||||
|
|
|
|||
|
|
@ -12,23 +12,6 @@ import {makeStyleSheetFromTheme} from 'app/utils/theme';
|
|||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return StyleSheet.create({
|
||||
container: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: 28,
|
||||
marginVertical: 10
|
||||
},
|
||||
text: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
color: theme.linkColor
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
export default class LoadMorePosts extends PureComponent {
|
||||
static propTypes = {
|
||||
loading: PropTypes.bool.isRequired,
|
||||
|
|
@ -71,3 +54,20 @@ export default class LoadMorePosts extends PureComponent {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return StyleSheet.create({
|
||||
container: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: 28,
|
||||
marginVertical: 10
|
||||
},
|
||||
text: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
color: theme.linkColor
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ import {
|
|||
StyleSheet,
|
||||
View
|
||||
} from 'react-native';
|
||||
import {Constants} from 'mattermost-redux/constants';
|
||||
|
||||
import {General, Posts} from 'mattermost-redux/constants';
|
||||
import {addDatesToPostList} from 'mattermost-redux/utils/post_utils';
|
||||
|
||||
import ChannelIntro from 'app/components/channel_intro';
|
||||
|
|
@ -77,7 +78,7 @@ export default class PostList extends Component {
|
|||
|
||||
const firstPostHasRendered = channel.total_msg_count ? posts.length > 0 : true;
|
||||
const messageCount = channel.total_msg_count - posts.length;
|
||||
if (channelIsLoading || !firstPostHasRendered || messageCount > Constants.POST_CHUNK_SIZE) {
|
||||
if (channelIsLoading || !firstPostHasRendered || messageCount > Posts.POST_CHUNK_SIZE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -92,7 +93,7 @@ export default class PostList extends Component {
|
|||
if (row instanceof Date) {
|
||||
return this.renderDateHeader(row);
|
||||
}
|
||||
if (row === Constants.START_OF_NEW_MESSAGES) {
|
||||
if (row === General.START_OF_NEW_MESSAGES) {
|
||||
return (
|
||||
<NewMessagesDivider
|
||||
theme={this.props.theme}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ class PostTextbox extends PureComponent {
|
|||
intl: intlShape.isRequired,
|
||||
onChangeText: PropTypes.func.isRequired,
|
||||
rootId: PropTypes.string,
|
||||
teamId: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
typing: PropTypes.array.isRequired,
|
||||
uploadFileRequestStatus: PropTypes.string.isRequired,
|
||||
|
|
@ -147,7 +146,7 @@ class PostTextbox extends PureComponent {
|
|||
message: this.props.value
|
||||
};
|
||||
|
||||
this.props.actions.createPost(this.props.teamId, post, this.props.files);
|
||||
this.props.actions.createPost(post, this.props.files);
|
||||
this.handleTextChange('');
|
||||
};
|
||||
|
||||
|
|
@ -185,7 +184,7 @@ class PostTextbox extends PureComponent {
|
|||
// If user cancels it's considered
|
||||
// an error and we have to catch it.
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
attachFileFromLibrary = async () => {
|
||||
try {
|
||||
|
|
@ -200,11 +199,11 @@ class PostTextbox extends PureComponent {
|
|||
// If user cancels it's considered
|
||||
// an error and we have to catch it.
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
uploadFiles = (images) => {
|
||||
this.props.actions.handleUploadFiles(images, this.props.rootId);
|
||||
}
|
||||
};
|
||||
|
||||
showFileAttachmentOptions = () => {
|
||||
this.blur();
|
||||
|
|
@ -227,7 +226,7 @@ class PostTextbox extends PureComponent {
|
|||
};
|
||||
|
||||
this.props.actions.showOptionsModal(options);
|
||||
}
|
||||
};
|
||||
|
||||
renderTyping = () => {
|
||||
const {typing} = this.props;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {makeStyleSheetFromTheme} from 'app/utils/theme';
|
|||
|
||||
import placeholder from 'assets/images/profile.jpg';
|
||||
|
||||
import Client from 'mattermost-redux/client';
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
|
||||
const statusToIcon = {
|
||||
online: 'check',
|
||||
|
|
@ -21,6 +21,8 @@ const STATUS_BUFFER = Platform.select({
|
|||
android: 2
|
||||
});
|
||||
|
||||
const IMAGE_HEADERS = Client4.getOptions().headers;
|
||||
|
||||
export default class ProfilePicture extends React.PureComponent {
|
||||
static propTypes = {
|
||||
size: React.PropTypes.number,
|
||||
|
|
@ -53,7 +55,7 @@ export default class ProfilePicture extends React.PureComponent {
|
|||
|
||||
let pictureUrl;
|
||||
if (this.props.user) {
|
||||
pictureUrl = Client.getProfilePictureUrl(this.props.user.id, this.props.user.last_picture_update);
|
||||
pictureUrl = Client4.getProfilePictureUrl(this.props.user.id, this.props.user.last_picture_update);
|
||||
}
|
||||
|
||||
let statusIcon;
|
||||
|
|
@ -85,7 +87,7 @@ export default class ProfilePicture extends React.PureComponent {
|
|||
<View style={{width: this.props.size + STATUS_BUFFER, height: this.props.size + STATUS_BUFFER}}>
|
||||
<Image
|
||||
style={{width: this.props.size, height: this.props.size, borderRadius: this.props.size / 2}}
|
||||
source={{uri: pictureUrl}}
|
||||
source={{uri: pictureUrl, headers: IMAGE_HEADERS}}
|
||||
defaultSource={placeholder}
|
||||
/>
|
||||
{this.props.status &&
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {changeOpacity} from 'app/utils/theme';
|
|||
import icon from 'assets/images/icon.png';
|
||||
import {GooglePlaySenderId} from 'assets/config.json';
|
||||
|
||||
import {Constants} from 'mattermost-redux/constants';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
|
||||
export default class PushNotification extends PureComponent {
|
||||
static propTypes = {
|
||||
|
|
@ -63,7 +63,7 @@ export default class PushNotification extends PureComponent {
|
|||
};
|
||||
|
||||
onRegisterDevice = (data) => {
|
||||
const prefix = Platform.OS === 'ios' ? Constants.PUSH_NOTIFY_APPLE_REACT_NATIVE : Constants.PUSH_NOTIFY_ANDROID_REACT_NATIVE;
|
||||
const prefix = Platform.OS === 'ios' ? General.PUSH_NOTIFY_APPLE_REACT_NATIVE : General.PUSH_NOTIFY_ANDROID_REACT_NATIVE;
|
||||
this.props.actions.setDeviceToken(`${prefix}:${data.token}`);
|
||||
this.isConfigured = true;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import {getTranslations} from 'app/i18n';
|
|||
|
||||
import Config from 'assets/config';
|
||||
|
||||
import Client from 'mattermost-redux/client';
|
||||
import {Constants} from 'mattermost-redux/constants';
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
|
||||
export default class Root extends Component {
|
||||
|
|
@ -45,8 +45,8 @@ export default class Root extends Component {
|
|||
componentDidMount() {
|
||||
this.props.actions.setAppState(AppState.currentState === 'active');
|
||||
AppState.addEventListener('change', this.handleAppStateChange);
|
||||
EventEmitter.on(Constants.CONFIG_CHANGED, this.handleConfigChanged);
|
||||
Client.setUserAgent(DeviceInfo.getUserAgent());
|
||||
EventEmitter.on(General.CONFIG_CHANGED, this.handleConfigChanged);
|
||||
Client4.setUserAgent(DeviceInfo.getUserAgent());
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
BackAndroid.addEventListener('hardwareBackPress', this.handleAndroidBack);
|
||||
|
|
@ -55,7 +55,7 @@ export default class Root extends Component {
|
|||
|
||||
componentWillUnmount() {
|
||||
AppState.removeEventListener('change', this.handleAppStateChange);
|
||||
EventEmitter.off(Constants.CONFIG_CHANGED, this.handleConfigChanged);
|
||||
EventEmitter.off(General.CONFIG_CHANGED, this.handleConfigChanged);
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
BackAndroid.removeEventListener('hardwareBackPress', this.handleAndroidBack);
|
||||
|
|
@ -116,7 +116,7 @@ export default class Root extends Component {
|
|||
handleVersionUpgrade = async () => {
|
||||
const {closeDrawers, logout, unrenderDrawer} = this.props.actions;
|
||||
|
||||
Client.serverVersion = '';
|
||||
Client4.serverVersion = '';
|
||||
|
||||
closeDrawers();
|
||||
unrenderDrawer();
|
||||
|
|
|
|||
|
|
@ -30,8 +30,7 @@ const state = {
|
|||
teams: {},
|
||||
myMembers: {},
|
||||
membersInTeam: {},
|
||||
stats: {},
|
||||
openTeamIds: new Set()
|
||||
stats: {}
|
||||
},
|
||||
channels: {
|
||||
currentChannelId: '',
|
||||
|
|
@ -41,7 +40,7 @@ const state = {
|
|||
},
|
||||
posts: {
|
||||
posts: {},
|
||||
postsByChannel: {},
|
||||
postsInChannel: {},
|
||||
selectedPostId: '',
|
||||
currentFocusedPostId: ''
|
||||
},
|
||||
|
|
@ -93,10 +92,6 @@ const state = {
|
|||
status: 'not_started',
|
||||
error: null
|
||||
},
|
||||
getMoreChannels: {
|
||||
status: 'not_started',
|
||||
error: null
|
||||
},
|
||||
getChannelStats: {
|
||||
status: 'not_started',
|
||||
error: null
|
||||
|
|
@ -141,7 +136,7 @@ const state = {
|
|||
status: 'not_started',
|
||||
error: null
|
||||
},
|
||||
getPost: {
|
||||
getPostThread: {
|
||||
status: 'not_started',
|
||||
error: null
|
||||
},
|
||||
|
|
@ -163,11 +158,11 @@ const state = {
|
|||
}
|
||||
},
|
||||
teams: {
|
||||
allTeams: {
|
||||
getMyTeams: {
|
||||
status: 'not_started',
|
||||
error: null
|
||||
},
|
||||
getAllTeamListings: {
|
||||
getTeams: {
|
||||
status: 'not_started',
|
||||
error: null
|
||||
},
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ const store = configureStore(initialState);
|
|||
export default class Mattermost extends React.Component {
|
||||
state = {
|
||||
hydrated: false
|
||||
}
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
this.unsubscribeFromStore = store.subscribe(this.listenForHydration);
|
||||
|
|
@ -31,7 +31,7 @@ export default class Mattermost extends React.Component {
|
|||
hydrated: true
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (!this.state.hydrated) {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import {NavigationExperimental} from 'react-native';
|
||||
|
||||
import {UsersTypes} from 'mattermost-redux/constants';
|
||||
import {UserTypes} from 'mattermost-redux/action_types';
|
||||
import {NavigationTypes} from 'app/constants';
|
||||
|
||||
import Routes from 'app/navigation/routes';
|
||||
|
|
@ -220,7 +220,7 @@ export default function(state = initialState, action) {
|
|||
};
|
||||
}
|
||||
|
||||
case UsersTypes.LOGOUT_SUCCESS:
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return NavigationExperimental.StateUtils.reset(state, initialState.routes, initialState.index);
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// See License.txt for license information.
|
||||
|
||||
import {combineReducers} from 'redux';
|
||||
import {ChannelTypes, FilesTypes, UsersTypes} from 'mattermost-redux/constants';
|
||||
import {ChannelTypes, FileTypes, UserTypes} from 'mattermost-redux/action_types';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ function drafts(state = {}, action) {
|
|||
[action.channelId]: Object.assign({}, state[action.channelId], {files})
|
||||
};
|
||||
}
|
||||
case FilesTypes.RECEIVED_UPLOAD_FILES: {
|
||||
case FileTypes.RECEIVED_UPLOAD_FILES: {
|
||||
if (action.rootId || !state[action.channelId].files) {
|
||||
return state;
|
||||
}
|
||||
|
|
@ -128,7 +128,7 @@ function appInitializing(state = true, action) {
|
|||
switch (action.type) {
|
||||
case ViewTypes.APPLICATION_INITIALIZED:
|
||||
return false;
|
||||
case UsersTypes.RESET_LOGOUT_STATE:
|
||||
case UserTypes.RESET_LOGOUT_STATE:
|
||||
return true;
|
||||
default:
|
||||
return state;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {UsersTypes} from 'mattermost-redux/constants';
|
||||
import {UserTypes} from 'mattermost-redux/action_types';
|
||||
import {ViewTypes} from 'app/constants';
|
||||
|
||||
export default function connection(state = true, action) {
|
||||
|
|
@ -9,7 +9,7 @@ export default function connection(state = true, action) {
|
|||
case ViewTypes.CONNECTION_CHANGED:
|
||||
return action.data;
|
||||
|
||||
case UsersTypes.LOGOUT_SUCCESS:
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,14 +5,15 @@ import {combineReducers} from 'redux';
|
|||
|
||||
import Config from 'assets/config.json';
|
||||
|
||||
import {UsersTypes} from 'mattermost-redux/constants';
|
||||
import {UserTypes} from 'mattermost-redux/action_types';
|
||||
|
||||
function locale(state = Config.DefaultLocale, action) {
|
||||
switch (action.type) {
|
||||
case UsersTypes.RECEIVED_ME:
|
||||
return action.data.locale;
|
||||
|
||||
case UsersTypes.LOGOUT_SUCCESS:
|
||||
case UserTypes.RECEIVED_ME: {
|
||||
const data = action.data || action.payload;
|
||||
return data.locale;
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return Config.DefaultLocale;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@
|
|||
// See License.txt for license information.
|
||||
|
||||
import {combineReducers} from 'redux';
|
||||
import {UsersTypes} from 'mattermost-redux/constants';
|
||||
import {UserTypes} from 'mattermost-redux/action_types';
|
||||
import {ViewTypes} from 'app/constants';
|
||||
|
||||
function loginId(state = '', action) {
|
||||
switch (action.type) {
|
||||
case ViewTypes.LOGIN_ID_CHANGED:
|
||||
return action.loginId.trim();
|
||||
case UsersTypes.LOGOUT_SUCCESS:
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return '';
|
||||
default:
|
||||
return state;
|
||||
|
|
@ -20,7 +20,7 @@ function password(state = '', action) {
|
|||
switch (action.type) {
|
||||
case ViewTypes.PASSWORD_CHANGED:
|
||||
return action.password;
|
||||
case UsersTypes.LOGOUT_SUCCESS:
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return '';
|
||||
default:
|
||||
return state;
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@
|
|||
|
||||
import {combineReducers} from 'redux';
|
||||
|
||||
import {UsersTypes} from 'mattermost-redux/constants';
|
||||
import {UserTypes} from 'mattermost-redux/action_types';
|
||||
import {ViewTypes} from 'app/constants';
|
||||
|
||||
function title(state = '', action) {
|
||||
switch (action.type) {
|
||||
case ViewTypes.OPTIONS_MODAL_CHANGED:
|
||||
return action.data.title;
|
||||
case UsersTypes.LOGOUT_SUCCESS:
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return '';
|
||||
default:
|
||||
return state;
|
||||
|
|
@ -21,7 +21,7 @@ function options(state = [], action) {
|
|||
switch (action.type) {
|
||||
case ViewTypes.OPTIONS_MODAL_CHANGED:
|
||||
return action.data.options;
|
||||
case UsersTypes.LOGOUT_SUCCESS:
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return [];
|
||||
default:
|
||||
return state;
|
||||
|
|
@ -32,7 +32,7 @@ function visible(state = false, action) {
|
|||
switch (action.type) {
|
||||
case ViewTypes.OPTIONS_MODAL_CHANGED:
|
||||
return action.data.visible;
|
||||
case UsersTypes.LOGOUT_SUCCESS:
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return false;
|
||||
default:
|
||||
return state;
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
// See License.txt for license information.
|
||||
|
||||
import {combineReducers} from 'redux';
|
||||
import {Constants} from 'mattermost-redux/constants';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
|
||||
function hydrationComplete(state = false, action) {
|
||||
switch (action.type) {
|
||||
case Constants.STORE_REHYDRATION_COMPLETE:
|
||||
case General.STORE_REHYDRATION_COMPLETE:
|
||||
return true;
|
||||
default:
|
||||
return state;
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
// See License.txt for license information.
|
||||
|
||||
import {combineReducers} from 'redux';
|
||||
import {TeamsTypes} from 'mattermost-redux/constants';
|
||||
import {TeamTypes} from 'mattermost-redux/action_types';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
|
||||
function lastTeamId(state = '', action) {
|
||||
switch (action.type) {
|
||||
case TeamsTypes.SELECT_TEAM:
|
||||
case TeamTypes.SELECT_TEAM:
|
||||
return action.data;
|
||||
default:
|
||||
return state;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// See License.txt for license information.
|
||||
|
||||
import {combineReducers} from 'redux';
|
||||
import {FilesTypes, PostsTypes} from 'mattermost-redux/constants';
|
||||
import {FileTypes, PostTypes} from 'mattermost-redux/action_types';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ function drafts(state = {}, action) {
|
|||
files: action.files
|
||||
}
|
||||
};
|
||||
case PostsTypes.RECEIVED_POST_SELECTED: {
|
||||
case PostTypes.RECEIVED_POST_SELECTED: {
|
||||
let data = {...state};
|
||||
|
||||
if (!data[action.data]) {
|
||||
|
|
@ -52,7 +52,7 @@ function drafts(state = {}, action) {
|
|||
[action.rootId]: Object.assign({}, state[action.rootId], {files})
|
||||
};
|
||||
}
|
||||
case FilesTypes.RECEIVED_UPLOAD_FILES: {
|
||||
case FileTypes.RECEIVED_UPLOAD_FILES: {
|
||||
if (!action.rootId) {
|
||||
return state;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export default class AccountNotifications extends PureComponent {
|
|||
renderRightComponent: (props, emitter) => {
|
||||
return <SaveNotificationsButton emitter={emitter}/>;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import OfflineIndicator from 'app/components/offline_indicator';
|
|||
import PostTextbox from 'app/components/post_textbox';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
|
||||
import {Constants, RequestStatus} from 'mattermost-redux/constants';
|
||||
import {RequestStatus} from 'mattermost-redux/constants';
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
|
||||
import ChannelDrawerButton from './channel_drawer_button';
|
||||
|
|
@ -159,11 +159,6 @@ export default class Channel extends PureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
let teamId = currentChannel.team_id;
|
||||
if (currentChannel.type === Constants.DM_CHANNEL || currentChannel.type === Constants.GM_CHANNEL) {
|
||||
teamId = currentTeam.id;
|
||||
}
|
||||
|
||||
const channelDraft = this.props.drafts[this.props.currentChannel.id];
|
||||
|
||||
return (
|
||||
|
|
@ -179,7 +174,6 @@ export default class Channel extends PureComponent {
|
|||
ref={this.attachPostTextbox}
|
||||
files={channelDraft.files}
|
||||
value={channelDraft.draft}
|
||||
teamId={teamId}
|
||||
channelId={currentChannel.id}
|
||||
onChangeText={this.handleDraftChanged}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
View
|
||||
} from 'react-native';
|
||||
|
||||
import {Constants, RequestStatus} from 'mattermost-redux/constants';
|
||||
import {General, Posts, RequestStatus} from 'mattermost-redux/constants';
|
||||
|
||||
import ChannelLoader from 'app/components/channel_loader';
|
||||
import PostList from 'app/components/post_list';
|
||||
|
|
@ -26,7 +26,6 @@ export default class ChannelPostList extends PureComponent {
|
|||
}).isRequired,
|
||||
applicationInitializing: PropTypes.bool.isRequired,
|
||||
channel: PropTypes.object.isRequired,
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
channelIsLoading: PropTypes.bool,
|
||||
myMember: PropTypes.object.isRequired,
|
||||
postsRequests: PropTypes.shape({
|
||||
|
|
@ -60,9 +59,9 @@ export default class ChannelPostList extends PureComponent {
|
|||
const didMorePostsLoad = this.didPostsLoad(nextProps, 'getPostsBefore');
|
||||
let hasFirstPost = false;
|
||||
if (didInitialPostsLoad) {
|
||||
hasFirstPost = nextProps.posts.length < Constants.POST_CHUNK_SIZE;
|
||||
hasFirstPost = nextProps.posts.length < Posts.POST_CHUNK_SIZE;
|
||||
} else if (didMorePostsLoad) {
|
||||
hasFirstPost = (nextProps.posts.length - this.props.posts.length) < Constants.POST_CHUNK_SIZE;
|
||||
hasFirstPost = (nextProps.posts.length - this.props.posts.length) < General.POST_CHUNK_SIZE;
|
||||
}
|
||||
if (hasFirstPost) {
|
||||
this.setState({hasFirstPost});
|
||||
|
|
@ -95,19 +94,12 @@ export default class ChannelPostList extends PureComponent {
|
|||
}
|
||||
|
||||
loadMorePosts = () => {
|
||||
const {currentTeamId, channel, posts, postsRequests} = this.props;
|
||||
const {team_id: teamId, id: channelId} = channel;
|
||||
const {channel, posts, postsRequests} = this.props;
|
||||
const {id: channelId} = channel;
|
||||
const oldestPost = posts[posts.length - 1];
|
||||
const {didInitialPostsLoad, hasFirstPost} = this.state;
|
||||
if (didInitialPostsLoad && !hasFirstPost && oldestPost && postsRequests.getPostsBefore.status !== RequestStatus.STARTED) {
|
||||
let postsForTeamId = teamId;
|
||||
switch (channel.type) {
|
||||
case Constants.DM_CHANNEL:
|
||||
case Constants.GM_CHANNEL:
|
||||
postsForTeamId = currentTeamId;
|
||||
break;
|
||||
}
|
||||
return this.props.actions.getPostsBefore(postsForTeamId, channelId, oldestPost.id);
|
||||
return this.props.actions.getPostsBefore(channelId, oldestPost.id);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ import {connect} from 'react-redux';
|
|||
import {createSelector} from 'reselect';
|
||||
import {getPostsBefore} from 'mattermost-redux/actions/posts';
|
||||
import {getAllPosts, getPostsInCurrentChannel} from 'mattermost-redux/selectors/entities/posts';
|
||||
import {getCurrentChannelMembership} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getMyCurrentChannelMembership} from 'mattermost-redux/selectors/entities/channels';
|
||||
|
||||
import {goToThread} from 'app/actions/navigation';
|
||||
import {loadPostsIfNecessary} from 'app/actions/views/channel';
|
||||
|
|
@ -69,9 +68,8 @@ function mapStateToProps(state, ownProps) {
|
|||
return {
|
||||
...ownProps,
|
||||
applicationInitializing: state.views.channel.appInitializing,
|
||||
currentTeamId: getCurrentTeamId(state),
|
||||
channelIsLoading: state.views.channel.loading,
|
||||
myMember: getCurrentChannelMembership(state),
|
||||
myMember: getMyCurrentChannelMembership(state),
|
||||
postsRequests: state.requests.posts,
|
||||
posts: getPostsInCurrentChannelWithReplyProps(state),
|
||||
theme: getTheme(state),
|
||||
|
|
|
|||
|
|
@ -18,8 +18,9 @@ import SearchBar from 'app/components/search_bar';
|
|||
import {createMembersSections, loadingText, renderMemberRow} from 'app/utils/member_list';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
import {Constants, RequestStatus} from 'mattermost-redux/constants';
|
||||
import {General, RequestStatus} from 'mattermost-redux/constants';
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
import {filterProfiles} from 'mattermost-redux/utils/user_utils';
|
||||
|
||||
class ChannelAddMembers extends PureComponent {
|
||||
static propTypes = {
|
||||
|
|
@ -61,11 +62,21 @@ class ChannelAddMembers extends PureComponent {
|
|||
}
|
||||
};
|
||||
|
||||
state = {
|
||||
canSelect: true,
|
||||
profiles: [],
|
||||
selectedMembers: {}
|
||||
};
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.searchTimeoutId = 0;
|
||||
|
||||
this.state = {
|
||||
canSelect: true,
|
||||
next: true,
|
||||
page: 0,
|
||||
profiles: [],
|
||||
searching: false,
|
||||
selectedMembers: {},
|
||||
showNoResults: false
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.props.subscribeToHeaderEvent('add_members', this.handleAddMembersPress);
|
||||
|
|
@ -96,15 +107,13 @@ class ChannelAddMembers extends PureComponent {
|
|||
const {loadMoreRequestStatus} = this.props;
|
||||
if (loadMoreRequestStatus === RequestStatus.STARTED &&
|
||||
nextProps.loadMoreRequestStatus === RequestStatus.SUCCESS) {
|
||||
this.setState({profiles: nextProps.membersNotInChannel});
|
||||
const {page} = this.state;
|
||||
const profiles = nextProps.membersNotInChannel.splice(0, (page + 1) * General.PROFILE_CHUNK_SIZE);
|
||||
this.setState({profiles, showNoResults: true});
|
||||
} else if (this.state.searching &&
|
||||
nextProps.searchRequestStatus === RequestStatus.SUCCESS) {
|
||||
const results = nextProps.membersNotInChannel.filter((p) => {
|
||||
const {term} = this.state;
|
||||
return p.username.toLowerCase().includes(term) || p.email.toLowerCase().includes(term) ||
|
||||
p.first_name.toLowerCase().includes(term) || p.last_name.toLowerCase().includes(term);
|
||||
});
|
||||
this.setState({profiles: results});
|
||||
const results = filterProfiles(nextProps.membersNotInChannel, this.state.term);
|
||||
this.setState({profiles: results, showNoResults: true});
|
||||
}
|
||||
|
||||
const {addChannelMemberStatus} = nextProps;
|
||||
|
|
@ -145,9 +154,21 @@ class ChannelAddMembers extends PureComponent {
|
|||
};
|
||||
|
||||
loadMoreMembers = () => {
|
||||
const {currentChannelMemberCount, currentTeamMemberCount, membersNotInChannel, loadMoreRequestStatus} = this.props;
|
||||
if (loadMoreRequestStatus !== RequestStatus.STARTED && membersNotInChannel.length < (currentTeamMemberCount - currentChannelMemberCount - 1)) {
|
||||
this.props.actions.getProfilesNotInChannel(this.props.currentTeam.id, this.props.currentChannel.id, this.props.membersNotInChannel.length);
|
||||
const {actions, loadMoreRequestStatus, currentChannel, currentTeam} = this.props;
|
||||
const {next, searching} = this.state;
|
||||
let {page} = this.state;
|
||||
if (loadMoreRequestStatus !== RequestStatus.STARTED && next && !searching) {
|
||||
page = page + 1;
|
||||
actions.getProfilesNotInChannel(currentTeam.id, currentChannel.id, page, General.PROFILE_CHUNK_SIZE).
|
||||
then((data) => {
|
||||
if (data && data.length) {
|
||||
this.setState({
|
||||
page
|
||||
});
|
||||
} else {
|
||||
this.setState({next: false});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -183,14 +204,15 @@ class ChannelAddMembers extends PureComponent {
|
|||
|
||||
searchProfiles = (event) => {
|
||||
const term = event.nativeEvent.text.toLowerCase();
|
||||
const {actions, currentChannel, currentTeam} = this.props;
|
||||
|
||||
if (term) {
|
||||
this.setState({searching: true, term});
|
||||
clearTimeout(this.searchTimeoutId);
|
||||
|
||||
this.searchTimeoutId = setTimeout(() => {
|
||||
this.props.actions.searchProfiles(term, {not_in_channel_id: this.props.currentChannel.id});
|
||||
}, Constants.SEARCH_TIMEOUT_MILLISECONDS);
|
||||
actions.searchProfiles(term, {not_in_channel_id: currentChannel.id, team_id: currentTeam.id});
|
||||
}, General.SEARCH_TIMEOUT_MILLISECONDS);
|
||||
} else {
|
||||
this.cancelSearch();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,9 +42,9 @@ export default class ChannelDrawer extends PureComponent {
|
|||
viewChannel
|
||||
} = actions;
|
||||
|
||||
markChannelAsRead(currentChannel.id);
|
||||
markChannelAsRead(id, currentChannel.id);
|
||||
setChannelLoading();
|
||||
viewChannel(currentTeam.id, id);
|
||||
viewChannel(currentTeam.id);
|
||||
closeDrawers();
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
handleSelectChannel(id);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
import {Constants} from 'mattermost-redux/constants';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
|
||||
import ChannelInfoHeader from './channel_info_header';
|
||||
import ChannelInfoRow from './channel_info_row';
|
||||
|
|
@ -22,7 +22,6 @@ class ChannelInfo extends PureComponent {
|
|||
static propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
canDeleteChannel: PropTypes.bool.isRequired,
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
currentChannel: PropTypes.object.isRequired,
|
||||
currentChannelCreatorName: PropTypes.string,
|
||||
currentChannelMemberCount: PropTypes.number,
|
||||
|
|
@ -30,7 +29,6 @@ class ChannelInfo extends PureComponent {
|
|||
theme: PropTypes.object.isRequired,
|
||||
isCurrent: PropTypes.bool.isRequired,
|
||||
isFavorite: PropTypes.bool.isRequired,
|
||||
leaveChannelRequest: PropTypes.object.isRequired,
|
||||
canManageUsers: PropTypes.bool.isRequired,
|
||||
actions: PropTypes.shape({
|
||||
closeDMChannel: PropTypes.func.isRequired,
|
||||
|
|
@ -55,7 +53,7 @@ class ChannelInfo extends PureComponent {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.actions.getChannelStats(this.props.currentTeamId, this.props.currentChannel.id);
|
||||
this.props.actions.getChannelStats(this.props.currentChannel.id);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
|
|
@ -68,7 +66,7 @@ class ChannelInfo extends PureComponent {
|
|||
handleDeleteOrLeave(eventType) {
|
||||
const {formatMessage} = this.props.intl;
|
||||
const channel = this.props.currentChannel;
|
||||
const term = channel.type === Constants.OPEN_CHANNEL ?
|
||||
const term = channel.type === General.OPEN_CHANNEL ?
|
||||
formatMessage({id: 'mobile.channel_info.publicChannel', defaultMessage: 'Public Channel'}) :
|
||||
formatMessage({id: 'mobile.channel_info.privateChannel', defaultMessage: 'Private Channel'});
|
||||
let title;
|
||||
|
|
@ -90,7 +88,7 @@ class ChannelInfo extends PureComponent {
|
|||
defaultMessage: 'Are you sure you want to delete the {term} {name}?'
|
||||
};
|
||||
onPressAction = () => {
|
||||
this.props.actions.deleteChannel(channel.team_id, channel.id).then(this.props.actions.goBack);
|
||||
this.props.actions.deleteChannel(channel.id).then(this.props.actions.goBack);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -118,10 +116,10 @@ class ChannelInfo extends PureComponent {
|
|||
const {closeDMChannel, closeGMChannel, goBack} = this.props.actions;
|
||||
|
||||
switch (channel.type) {
|
||||
case Constants.DM_CHANNEL:
|
||||
case General.DM_CHANNEL:
|
||||
closeDMChannel(channel).then(goBack);
|
||||
break;
|
||||
case Constants.GM_CHANNEL:
|
||||
case General.GM_CHANNEL:
|
||||
closeGMChannel(channel).then(goBack);
|
||||
break;
|
||||
}
|
||||
|
|
@ -137,17 +135,17 @@ class ChannelInfo extends PureComponent {
|
|||
|
||||
renderLeaveOrDeleteChannelRow() {
|
||||
const channel = this.props.currentChannel;
|
||||
const isDefaultChannel = channel.name === Constants.DEFAULT_CHANNEL;
|
||||
const isDirectMessage = channel.type === Constants.DM_CHANNEL;
|
||||
const isGroupMessage = channel.type === Constants.GM_CHANNEL;
|
||||
const isDefaultChannel = channel.name === General.DEFAULT_CHANNEL;
|
||||
const isDirectMessage = channel.type === General.DM_CHANNEL;
|
||||
const isGroupMessage = channel.type === General.GM_CHANNEL;
|
||||
|
||||
return !isDefaultChannel && !isDirectMessage && !isGroupMessage;
|
||||
}
|
||||
|
||||
renderCloseDirect() {
|
||||
const channel = this.props.currentChannel;
|
||||
const isDirectMessage = channel.type === Constants.DM_CHANNEL;
|
||||
const isGroupMessage = channel.type === Constants.GM_CHANNEL;
|
||||
const isDirectMessage = channel.type === General.DM_CHANNEL;
|
||||
const isGroupMessage = channel.type === General.GM_CHANNEL;
|
||||
|
||||
return isDirectMessage || isGroupMessage;
|
||||
}
|
||||
|
|
@ -168,11 +166,11 @@ class ChannelInfo extends PureComponent {
|
|||
let i18nId;
|
||||
let defaultMessage;
|
||||
switch (currentChannel.type) {
|
||||
case Constants.DM_CHANNEL:
|
||||
case General.DM_CHANNEL:
|
||||
i18nId = 'mobile.channel_list.closeDM';
|
||||
defaultMessage = 'Close Direct Message';
|
||||
break;
|
||||
case Constants.GM_CHANNEL:
|
||||
case General.GM_CHANNEL:
|
||||
i18nId = 'mobile.channel_list.closeGM';
|
||||
defaultMessage = 'Close Group Message';
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -14,34 +14,6 @@ import Icon from 'react-native-vector-icons/FontAwesome';
|
|||
import FormattedText from 'app/components/formatted_text';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
paddingHorizontal: 15,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
detail: {
|
||||
marginHorizontal: 15,
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
fontSize: 15
|
||||
},
|
||||
label: {
|
||||
flex: 1,
|
||||
marginLeft: 15,
|
||||
fontSize: 15,
|
||||
paddingVertical: 15
|
||||
},
|
||||
leftIcon: {
|
||||
width: 17
|
||||
},
|
||||
rightIcon: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function createTouchableComponent(children, action) {
|
||||
return (
|
||||
<TouchableHighlight onPress={action}>
|
||||
|
|
@ -115,4 +87,32 @@ channelInfoRow.defaultProps = {
|
|||
shouldRender: true
|
||||
};
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
paddingHorizontal: 15,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
detail: {
|
||||
marginHorizontal: 15,
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
fontSize: 15
|
||||
},
|
||||
label: {
|
||||
flex: 1,
|
||||
marginLeft: 15,
|
||||
fontSize: 15,
|
||||
paddingVertical: 15
|
||||
},
|
||||
leftIcon: {
|
||||
width: 17
|
||||
},
|
||||
rightIcon: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.5)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
export default channelInfoRow;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import navigationSceneConnect from 'app/scenes/navigationSceneConnect';
|
|||
import {getTheme} from 'app/selectors/preferences';
|
||||
|
||||
import {getChannelStats, deleteChannel} from 'mattermost-redux/actions/channels';
|
||||
import {Constants} from 'mattermost-redux/constants';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
import {
|
||||
getCurrentChannel,
|
||||
getCurrentChannelStats,
|
||||
|
|
@ -38,11 +38,10 @@ function mapStateToProps(state, ownProps) {
|
|||
const favoriteChannels = getChannelsByCategory(state).favoriteChannels.map((f) => f.id);
|
||||
const isCurrent = currentChannel.id === state.entities.channels.currentChannelId;
|
||||
const isFavorite = favoriteChannels.indexOf(currentChannel.id) > -1;
|
||||
const leaveChannelRequest = state.requests.channels.leaveChannel;
|
||||
const roles = getCurrentUserRoles(state);
|
||||
|
||||
let status;
|
||||
if (currentChannel.type === Constants.DM_CHANNEL) {
|
||||
if (currentChannel.type === General.DM_CHANNEL) {
|
||||
const teammateId = getUserIdFromChannelName(currentUserId, currentChannel.name);
|
||||
status = getStatusForUserId(state, teammateId);
|
||||
}
|
||||
|
|
@ -50,13 +49,11 @@ function mapStateToProps(state, ownProps) {
|
|||
return {
|
||||
...ownProps,
|
||||
canDeleteChannel: showDeleteOption(config, license, currentChannel, isAdmin(roles), isSystemAdmin(roles), isChannelAdmin(roles)),
|
||||
currentTeamId: state.entities.teams.currentTeamId,
|
||||
currentChannel,
|
||||
currentChannelCreatorName,
|
||||
currentChannelMemberCount,
|
||||
isCurrent,
|
||||
isFavorite,
|
||||
leaveChannelRequest,
|
||||
status,
|
||||
theme: getTheme(state),
|
||||
canManageUsers: canManageChannelMembers(state)
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ import MemberList from 'app/components/custom_list';
|
|||
import SearchBar from 'app/components/search_bar';
|
||||
import {createMembersSections, loadingText} from 'app/utils/member_list';
|
||||
import MemberListRow from 'app/components/custom_list/member_list_row';
|
||||
import {displayUsername} from 'mattermost-redux/utils/user_utils';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
import {Constants, RequestStatus} from 'mattermost-redux/constants';
|
||||
import {General, RequestStatus} from 'mattermost-redux/constants';
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
import {displayUsername, filterProfiles} from 'mattermost-redux/utils/user_utils';
|
||||
|
||||
import ChannelMembersTitle from './channel_members_title';
|
||||
import RemoveMemberButton from './remove_member_button';
|
||||
|
|
@ -33,7 +33,6 @@ class ChannelMembers extends PureComponent {
|
|||
currentChannelMembers: PropTypes.array.isRequired,
|
||||
currentChannelMemberCount: PropTypes.number.isRequired,
|
||||
currentUserId: PropTypes.string.isRequired,
|
||||
currentTeam: PropTypes.object,
|
||||
preferences: PropTypes.object,
|
||||
requestStatus: PropTypes.string,
|
||||
searchRequestStatus: PropTypes.string,
|
||||
|
|
@ -58,11 +57,21 @@ class ChannelMembers extends PureComponent {
|
|||
}
|
||||
};
|
||||
|
||||
state = {
|
||||
canSelect: true,
|
||||
profiles: [],
|
||||
selectedMembers: {}
|
||||
};
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.searchTimeoutId = 0;
|
||||
|
||||
this.state = {
|
||||
canSelect: true,
|
||||
next: true,
|
||||
page: 0,
|
||||
profiles: [],
|
||||
searching: false,
|
||||
selectedMembers: {},
|
||||
showNoResults: false
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.props.subscribeToHeaderEvent('remove_members', this.handleRemoveMembersPress);
|
||||
|
|
@ -74,7 +83,7 @@ class ChannelMembers extends PureComponent {
|
|||
}
|
||||
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
this.props.actions.getProfilesInChannel(this.props.currentTeam.id, this.props.currentChannel.id, 0);
|
||||
this.props.actions.getProfilesInChannel(this.props.currentChannel.id, 0);
|
||||
});
|
||||
|
||||
this.emitCanRemoveMembers(false);
|
||||
|
|
@ -92,15 +101,13 @@ class ChannelMembers extends PureComponent {
|
|||
const {requestStatus} = this.props;
|
||||
if (requestStatus === RequestStatus.STARTED &&
|
||||
nextProps.requestStatus === RequestStatus.SUCCESS) {
|
||||
this.setState({profiles: nextProps.currentChannelMembers});
|
||||
const {page} = this.state;
|
||||
const profiles = nextProps.currentChannelMembers.splice(0, (page + 1) * General.PROFILE_CHUNK_SIZE);
|
||||
this.setState({profiles, showNoResults: true});
|
||||
} else if (this.state.searching &&
|
||||
nextProps.searchRequestStatus === RequestStatus.SUCCESS) {
|
||||
const results = nextProps.currentChannelMembers.filter((p) => {
|
||||
const {term} = this.state;
|
||||
return p.username.toLowerCase().includes(term) || p.email.toLowerCase().includes(term) ||
|
||||
p.first_name.toLowerCase().includes(term) || p.last_name.toLowerCase().includes(term);
|
||||
});
|
||||
this.setState({profiles: results});
|
||||
const results = filterProfiles(nextProps.currentChannelMembers, this.state.term);
|
||||
this.setState({profiles: results, showNoResults: true});
|
||||
}
|
||||
|
||||
const {removeMembersStatus} = nextProps;
|
||||
|
|
@ -165,13 +172,26 @@ class ChannelMembers extends PureComponent {
|
|||
};
|
||||
|
||||
removeMembers = (membersToRemove) => {
|
||||
const {actions, currentTeam, currentChannel} = this.props;
|
||||
actions.handleRemoveChannelMembers(currentTeam.id, currentChannel.id, membersToRemove);
|
||||
const {actions, currentChannel} = this.props;
|
||||
actions.handleRemoveChannelMembers(currentChannel.id, membersToRemove);
|
||||
};
|
||||
|
||||
loadMoreMembers = () => {
|
||||
if (this.props.requestStatus !== 'started' && this.props.currentChannelMembers.length < this.props.currentChannelMemberCount - 1) {
|
||||
this.props.actions.getProfilesInChannel(this.props.currentTeam.id, this.props.currentChannel.id, this.props.currentChannelMembers.length);
|
||||
const {actions, requestStatus, currentChannel} = this.props;
|
||||
const {next, searching} = this.state;
|
||||
let {page} = this.state;
|
||||
if (requestStatus !== RequestStatus.STARTED && next && !searching) {
|
||||
page = page + 1;
|
||||
actions.getProfilesInChannel(currentChannel.id, page, General.PROFILE_CHUNK_SIZE).
|
||||
then((data) => {
|
||||
if (data && data.length) {
|
||||
this.setState({
|
||||
page
|
||||
});
|
||||
} else {
|
||||
this.setState({next: false});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -213,7 +233,7 @@ class ChannelMembers extends PureComponent {
|
|||
|
||||
this.searchTimeoutId = setTimeout(() => {
|
||||
this.props.actions.searchProfiles(term, {in_channel_id: this.props.currentChannel.id});
|
||||
}, Constants.SEARCH_TIMEOUT_MILLISECONDS);
|
||||
}, General.SEARCH_TIMEOUT_MILLISECONDS);
|
||||
} else {
|
||||
this.cancelSearch();
|
||||
}
|
||||
|
|
@ -257,8 +277,8 @@ class ChannelMembers extends PureComponent {
|
|||
render() {
|
||||
const {canManageUsers, intl, preferences, requestStatus, searchRequestStatus, theme} = this.props;
|
||||
const {formatMessage} = intl;
|
||||
const {profiles, searching} = this.state;
|
||||
const isLoading = (requestStatus === RequestStatus.STARTED) ||
|
||||
const {profiles, searching, showNoResults} = this.state;
|
||||
const isLoading = (requestStatus === RequestStatus.STARTED) || (requestStatus.status === RequestStatus.NOT_STARTED) ||
|
||||
(searchRequestStatus === RequestStatus.STARTED);
|
||||
const more = searching ? () => true : this.loadMoreMembers;
|
||||
const style = getStyleFromTheme(theme);
|
||||
|
|
@ -294,6 +314,7 @@ class ChannelMembers extends PureComponent {
|
|||
onRowSelect={this.handleRowSelect}
|
||||
renderRow={this.renderMemberRow}
|
||||
createSections={createMembersSections}
|
||||
showNoResults={showNoResults}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import {handleRemoveChannelMembers} from 'app/actions/views/channel_members';
|
|||
import {getTheme} from 'app/selectors/preferences';
|
||||
import {getCurrentChannel, getCurrentChannelStats, canManageChannelMembers} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getMyPreferences} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {getCurrentTeam} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getProfilesInCurrentChannel} from 'mattermost-redux/selectors/entities/users';
|
||||
import {getProfilesInChannel, searchProfiles} from 'mattermost-redux/actions/users';
|
||||
|
||||
|
|
@ -25,7 +24,6 @@ function mapStateToProps(state) {
|
|||
currentChannelMembers: getProfilesInCurrentChannel(state),
|
||||
currentChannelMemberCount,
|
||||
currentUserId: state.entities.users.currentUserId,
|
||||
currentTeam: getCurrentTeam(state),
|
||||
preferences: getMyPreferences(state),
|
||||
requestStatus: state.requests.users.getProfilesInChannel.status,
|
||||
searchRequestStatus: state.requests.users.searchProfiles.status,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_lo
|
|||
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
import {Constants, RequestStatus} from 'mattermost-redux/constants';
|
||||
import {General, RequestStatus} from 'mattermost-redux/constants';
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
|
||||
import ActionButton from 'app/components/action_button';
|
||||
|
|
@ -41,7 +41,7 @@ class CreateChannel extends PureComponent {
|
|||
};
|
||||
|
||||
static defaultProps = {
|
||||
channelType: Constants.OPEN_CHANNEL
|
||||
channelType: General.OPEN_CHANNEL
|
||||
};
|
||||
|
||||
static navigationProps = {
|
||||
|
|
@ -191,9 +191,9 @@ class CreateChannel extends PureComponent {
|
|||
const style = getStyleSheet(theme);
|
||||
|
||||
let term;
|
||||
if (channelType === Constants.OPEN_CHANNEL) {
|
||||
if (channelType === General.OPEN_CHANNEL) {
|
||||
term = formatMessage({id: 'channel_modal.channel', defaultMessage: 'Channel'});
|
||||
} else if (channelType === Constants.PRIVATE_CHANNEL) {
|
||||
} else if (channelType === General.PRIVATE_CHANNEL) {
|
||||
term = formatMessage({id: 'channel_modal.group', defaultMessage: 'Group'});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ function mapStateToProps(state) {
|
|||
return {
|
||||
config: state.entities.general.config,
|
||||
theme: getTheme(state),
|
||||
teamsRequest: state.requests.teams.allTeams,
|
||||
teamsRequest: state.requests.teams.getMyTeams,
|
||||
teams: state.entities.teams.teams,
|
||||
currentTeam: getCurrentTeam(state),
|
||||
myMembers: state.entities.teams.myMembers,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import ActionButton from 'app/components/action_button';
|
|||
|
||||
import {getTheme} from 'app/selectors/preferences';
|
||||
|
||||
import {Constants} from 'mattermost-redux/constants';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
import {getCurrentUserRoles} from 'mattermost-redux/selectors/entities/users';
|
||||
import {showCreateOption} from 'mattermost-redux/utils/channel_utils';
|
||||
import {isAdmin, isSystemAdmin} from 'mattermost-redux/utils/user_utils';
|
||||
|
|
@ -46,7 +46,7 @@ function mapStateToProps(state) {
|
|||
const roles = getCurrentUserRoles(state);
|
||||
|
||||
return {
|
||||
canCreateChannels: showCreateOption(config, license, Constants.PRIVATE_CHANNEL, isAdmin(roles), isSystemAdmin(roles)),
|
||||
canCreateChannels: showCreateOption(config, license, General.PRIVATE_CHANNEL, isAdmin(roles), isSystemAdmin(roles)),
|
||||
theme: getTheme(state)
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,17 +7,17 @@ import navigationSceneConnect from '../navigationSceneConnect';
|
|||
|
||||
import {closeDrawers, goBack, goToCreateChannel} from 'app/actions/navigation';
|
||||
import {getTheme} from 'app/selectors/preferences';
|
||||
import {getMoreChannels as getMoreChannelsSelector} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getOtherChannels} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {handleSelectChannel} from 'app/actions/views/channel';
|
||||
import {getMoreChannels, joinChannel, searchMoreChannels} from 'mattermost-redux/actions/channels';
|
||||
import {getChannels, joinChannel, searchMoreChannels} from 'mattermost-redux/actions/channels';
|
||||
|
||||
import MoreChannels from './more_channels';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const {currentUserId} = state.entities.users;
|
||||
const {currentTeamId} = state.entities.teams;
|
||||
const {getMoreChannels: requestStatus} = state.requests.channels;
|
||||
const channels = getMoreChannelsSelector(state);
|
||||
const {getChannels: requestStatus} = state.requests.channels;
|
||||
const channels = getOtherChannels(state);
|
||||
|
||||
return {
|
||||
currentUserId,
|
||||
|
|
@ -36,7 +36,7 @@ function mapDispatchToProps(dispatch) {
|
|||
handleSelectChannel,
|
||||
goToCreateChannel,
|
||||
joinChannel,
|
||||
getMoreChannels,
|
||||
getChannels,
|
||||
searchMoreChannels
|
||||
}, dispatch)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import Loading from 'app/components/loading';
|
|||
import SearchBar from 'app/components/search_bar';
|
||||
import {makeStyleSheetFromTheme, changeOpacity} from 'app/utils/theme';
|
||||
|
||||
import {Constants, RequestStatus} from 'mattermost-redux/constants';
|
||||
import {General, RequestStatus} from 'mattermost-redux/constants';
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
|
||||
import CreateButton from './create_button';
|
||||
|
|
@ -40,7 +40,7 @@ class MoreChannels extends PureComponent {
|
|||
handleSelectChannel: PropTypes.func.isRequired,
|
||||
goToCreateChannel: PropTypes.func.isRequired,
|
||||
joinChannel: PropTypes.func.isRequired,
|
||||
getMoreChannels: PropTypes.func.isRequired,
|
||||
getChannels: PropTypes.func.isRequired,
|
||||
searchMoreChannels: PropTypes.func.isRequired
|
||||
}).isRequired
|
||||
};
|
||||
|
|
@ -71,7 +71,7 @@ class MoreChannels extends PureComponent {
|
|||
this.searchTimeoutId = 0;
|
||||
|
||||
this.state = {
|
||||
channels: props.channels,
|
||||
channels: props.channels.splice(0, General.CHANNELS_CHUNK_SIZE),
|
||||
page: 0,
|
||||
adding: false,
|
||||
next: true,
|
||||
|
|
@ -94,7 +94,7 @@ class MoreChannels extends PureComponent {
|
|||
} else if (requestStatus.status === RequestStatus.STARTED &&
|
||||
nextProps.requestStatus.status === RequestStatus.SUCCESS) {
|
||||
const {page} = this.state;
|
||||
const channels = nextProps.channels.splice(0, (page + 1) * Constants.CHANNELS_CHUNK_SIZE);
|
||||
const channels = nextProps.channels.splice(0, (page + 1) * General.CHANNELS_CHUNK_SIZE);
|
||||
this.setState({channels, showNoResults: true});
|
||||
}
|
||||
}
|
||||
|
|
@ -107,7 +107,7 @@ class MoreChannels extends PureComponent {
|
|||
// set the timeout to 400 cause is the time that the modal takes to open
|
||||
// Somehow interactionManager doesn't care
|
||||
setTimeout(() => {
|
||||
this.props.actions.getMoreChannels(this.props.currentTeamId, 0);
|
||||
this.props.actions.getChannels(this.props.currentTeamId, 0);
|
||||
}, 400);
|
||||
}
|
||||
|
||||
|
|
@ -152,14 +152,14 @@ class MoreChannels extends PureComponent {
|
|||
|
||||
this.searchTimeoutId = setTimeout(() => {
|
||||
this.props.actions.searchMoreChannels(this.props.currentTeamId, term);
|
||||
}, Constants.SEARCH_TIMEOUT_MILLISECONDS);
|
||||
}, General.SEARCH_TIMEOUT_MILLISECONDS);
|
||||
} else {
|
||||
this.cancelSearch();
|
||||
}
|
||||
};
|
||||
|
||||
cancelSearch = () => {
|
||||
this.props.actions.getMoreChannels(this.props.currentTeamId, 0);
|
||||
this.props.actions.getChannels(this.props.currentTeamId, 0);
|
||||
this.setState({
|
||||
term: null,
|
||||
searching: false,
|
||||
|
|
@ -171,12 +171,12 @@ class MoreChannels extends PureComponent {
|
|||
let {page} = this.state;
|
||||
if (this.props.requestStatus.status !== RequestStatus.STARTED && this.state.next && !this.state.searching) {
|
||||
page = page + 1;
|
||||
this.props.actions.getMoreChannels(
|
||||
this.props.actions.getChannels(
|
||||
this.props.currentTeamId,
|
||||
page * Constants.CHANNELS_CHUNK_SIZE,
|
||||
Constants.CHANNELS_CHUNK_SIZE).
|
||||
page,
|
||||
General.CHANNELS_CHUNK_SIZE).
|
||||
then((data) => {
|
||||
if (Object.keys(data).length) {
|
||||
if (data && data.length) {
|
||||
this.setState({
|
||||
page
|
||||
});
|
||||
|
|
@ -225,7 +225,7 @@ class MoreChannels extends PureComponent {
|
|||
};
|
||||
|
||||
onCreateChannel = async () => {
|
||||
this.props.actions.goToCreateChannel(Constants.OPEN_CHANNEL);
|
||||
this.props.actions.goToCreateChannel(General.OPEN_CHANNEL);
|
||||
};
|
||||
|
||||
render() {
|
||||
|
|
|
|||
|
|
@ -17,9 +17,11 @@ import Loading from 'app/components/loading';
|
|||
import MemberList from 'app/components/custom_list';
|
||||
import SearchBar from 'app/components/search_bar';
|
||||
import {createMembersSections, loadingText, renderMemberRow} from 'app/utils/member_list';
|
||||
import {Constants, RequestStatus} from 'mattermost-redux/constants';
|
||||
import {makeStyleSheetFromTheme, changeOpacity} from 'app/utils/theme';
|
||||
|
||||
import {General, RequestStatus} from 'mattermost-redux/constants';
|
||||
import {filterProfiles} from 'mattermost-redux/utils/user_utils';
|
||||
|
||||
class MoreDirectMessages extends PureComponent {
|
||||
static propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
|
|
@ -62,7 +64,7 @@ class MoreDirectMessages extends PureComponent {
|
|||
this.searchTimeoutId = 0;
|
||||
|
||||
this.state = {
|
||||
profiles: props.profiles,
|
||||
profiles: props.profiles.splice(0, General.PROFILE_CHUNK_SIZE),
|
||||
page: 0,
|
||||
adding: false,
|
||||
next: true,
|
||||
|
|
@ -80,15 +82,11 @@ class MoreDirectMessages extends PureComponent {
|
|||
if (requestStatus.status === RequestStatus.STARTED &&
|
||||
nextProps.requestStatus.status === RequestStatus.SUCCESS) {
|
||||
const {page} = this.state;
|
||||
const profiles = nextProps.profiles.splice(0, (page + 1) * Constants.PROFILE_CHUNK_SIZE);
|
||||
const profiles = nextProps.profiles.splice(0, (page + 1) * General.PROFILE_CHUNK_SIZE);
|
||||
this.setState({profiles, showNoResults: true});
|
||||
} else if (this.state.searching &&
|
||||
nextProps.searchRequest.status === RequestStatus.SUCCESS) {
|
||||
const results = nextProps.profiles.filter((p) => {
|
||||
const {term} = this.state;
|
||||
return p.username.toLowerCase().includes(term) || p.email.toLowerCase().includes(term) ||
|
||||
p.first_name.toLowerCase().includes(term) || p.last_name.toLowerCase().includes(term);
|
||||
});
|
||||
const results = filterProfiles(nextProps.profiles, this.state.term);
|
||||
this.setState({profiles: results, showNoResults: true});
|
||||
}
|
||||
}
|
||||
|
|
@ -134,7 +132,7 @@ class MoreDirectMessages extends PureComponent {
|
|||
|
||||
this.searchTimeoutId = setTimeout(() => {
|
||||
this.props.actions.searchProfiles(term);
|
||||
}, Constants.SEARCH_TIMEOUT_MILLISECONDS);
|
||||
}, General.SEARCH_TIMEOUT_MILLISECONDS);
|
||||
} else {
|
||||
this.cancelSearch();
|
||||
}
|
||||
|
|
@ -153,9 +151,9 @@ class MoreDirectMessages extends PureComponent {
|
|||
let {page} = this.state;
|
||||
if (this.props.requestStatus.status !== RequestStatus.STARTED && this.state.next && !this.state.searching) {
|
||||
page = page + 1;
|
||||
this.props.actions.getProfiles(page * Constants.PROFILE_CHUNK_SIZE, Constants.PROFILE_CHUNK_SIZE).
|
||||
this.props.actions.getProfiles(page, General.PROFILE_CHUNK_SIZE).
|
||||
then((data) => {
|
||||
if (Object.keys(data).length) {
|
||||
if (data && data.length) {
|
||||
this.setState({
|
||||
page
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@
|
|||
|
||||
import React, {PropTypes, PureComponent} from 'react';
|
||||
import Orientation from 'react-native-orientation';
|
||||
import Loading from 'app/components/loading';
|
||||
|
||||
import Client from 'mattermost-redux/client';
|
||||
import {RequestStatus} from 'mattermost-redux/constants';
|
||||
import SplashScreen from 'react-native-smart-splash-screen';
|
||||
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
import {RequestStatus} from 'mattermost-redux/constants';
|
||||
|
||||
import Loading from 'app/components/loading';
|
||||
import {stripTrailingSlashes} from 'app/utils/url';
|
||||
|
||||
export default class Root extends PureComponent {
|
||||
static propTypes = {
|
||||
credentials: PropTypes.object,
|
||||
|
|
@ -59,8 +61,8 @@ export default class Root extends PureComponent {
|
|||
// Will probably need to make this optimistic since we
|
||||
// assume that the stored token is good.
|
||||
if (loginRequest.status === RequestStatus.NOT_STARTED) {
|
||||
Client.setToken(credentials.token);
|
||||
Client.setUrl(credentials.url);
|
||||
Client4.setToken(credentials.token);
|
||||
Client4.setUrl(stripTrailingSlashes(credentials.url));
|
||||
|
||||
loadMe().then(goToLoadTeam).catch(goToLoadTeam);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
} from 'react-native';
|
||||
|
||||
import CookieManager from 'react-native-cookies';
|
||||
import Client from 'mattermost-redux/client';
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
|
||||
export default class Saml extends PureComponent {
|
||||
static propTypes = {
|
||||
|
|
@ -40,7 +40,7 @@ export default class Saml extends PureComponent {
|
|||
goToLoadTeam
|
||||
} = this.props.actions;
|
||||
|
||||
Client.setToken(token);
|
||||
Client4.setToken(token);
|
||||
handleSuccessfulLogin().
|
||||
then(setStoreFromLocalData.bind(null, {url: this.props.serverUrl, token})).
|
||||
then(goToLoadTeam);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import {
|
|||
import Button from 'react-native-button';
|
||||
|
||||
import {RequestStatus} from 'mattermost-redux/constants';
|
||||
import Client from 'mattermost-redux/client';
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
|
||||
import ErrorText from 'app/components/error_text';
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
|
|
@ -83,7 +83,7 @@ export default class SelectServer extends PureComponent {
|
|||
Keyboard.dismiss();
|
||||
|
||||
if (isValidUrl(url)) {
|
||||
Client.setUrl(stripTrailingSlashes(url));
|
||||
Client4.setUrl(stripTrailingSlashes(url));
|
||||
await this.props.actions.getPing();
|
||||
} else {
|
||||
error = {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ import {selectPost} from 'mattermost-redux/actions/posts';
|
|||
|
||||
import {makeGetPostsForThread} from 'mattermost-redux/selectors/entities/posts';
|
||||
import {getTheme} from 'app/selectors/preferences';
|
||||
import {getCurrentChannelMembership} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getMyCurrentChannelMembership} from 'mattermost-redux/selectors/entities/channels';
|
||||
|
||||
import navigationSceneConnect from '../navigationSceneConnect';
|
||||
import Thread from './thread';
|
||||
|
|
@ -21,20 +20,12 @@ function makeMapStateToProps() {
|
|||
|
||||
return function mapStateToProps(state, ownProps) {
|
||||
const posts = getPostsForThread(state, ownProps);
|
||||
|
||||
let teamId = state.entities.channels.channels[ownProps.channelId].team_id;
|
||||
if (!teamId) {
|
||||
// We can't make a post without a team id, so get it from the current team
|
||||
teamId = getCurrentTeamId(state);
|
||||
}
|
||||
|
||||
const threadDraft = state.views.thread.drafts[ownProps.rootId];
|
||||
|
||||
return {
|
||||
...ownProps,
|
||||
teamId,
|
||||
channelId: ownProps.channelId,
|
||||
myMember: getCurrentChannelMembership(state),
|
||||
myMember: getMyCurrentChannelMembership(state),
|
||||
rootId: ownProps.rootId,
|
||||
draft: threadDraft.draft,
|
||||
files: threadDraft.files,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ export default class Thread extends PureComponent {
|
|||
handleCommentDraftChanged: PropTypes.func.isRequired,
|
||||
selectPost: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
teamId: PropTypes.string.isRequired,
|
||||
channelId: PropTypes.string.isRequired,
|
||||
myMember: PropTypes.object.isRequired,
|
||||
files: PropTypes.array,
|
||||
|
|
@ -68,7 +67,6 @@ export default class Thread extends PureComponent {
|
|||
rootId={this.props.rootId}
|
||||
value={this.props.draft}
|
||||
files={this.props.files}
|
||||
teamId={this.props.teamId}
|
||||
channelId={this.props.channelId}
|
||||
onChangeText={this.handleDraftChanged}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
View
|
||||
} from 'react-native';
|
||||
|
||||
import {Constants} from 'mattermost-redux/constants';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
import {getCurrentChannel} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getTheme} from 'app/selectors/preferences';
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ import FormattedText from 'app/components/formatted_text';
|
|||
function ThreadTitle(props) {
|
||||
const {currentChannel, theme} = props;
|
||||
let label;
|
||||
if (currentChannel.type === Constants.DM_CHANNEL) {
|
||||
if (currentChannel.type === General.DM_CHANNEL) {
|
||||
label = (
|
||||
<FormattedText
|
||||
id='mobile.routes.thread_dm'
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import {batchActions} from 'redux-batched-actions';
|
||||
import {AsyncStorage} from 'react-native';
|
||||
import configureStore from 'mattermost-redux/store';
|
||||
import {Constants, RequestStatus} from 'mattermost-redux/constants';
|
||||
import {General, RequestStatus} from 'mattermost-redux/constants';
|
||||
import {createBlacklistFilter} from 'redux-persist-transform-filter';
|
||||
import {createTransform, persistStore} from 'redux-persist';
|
||||
|
||||
|
|
@ -20,16 +20,22 @@ function getAppReducer() {
|
|||
const usersSetTransform = [
|
||||
'profilesInChannel',
|
||||
'profilesNotInChannel',
|
||||
'profilesInTeam'
|
||||
'profilesInTeam',
|
||||
'profilesNotInTeam'
|
||||
];
|
||||
|
||||
const teamSetTransform = [
|
||||
'membersInTeam'
|
||||
];
|
||||
|
||||
const channelSetTransform = [
|
||||
'channelsInTeam'
|
||||
];
|
||||
|
||||
const setTransforms = [
|
||||
...usersSetTransform,
|
||||
...teamSetTransform
|
||||
...teamSetTransform,
|
||||
...channelSetTransform
|
||||
];
|
||||
|
||||
export default function configureAppStore(initialState) {
|
||||
|
|
@ -73,7 +79,7 @@ export default function configureAppStore(initialState) {
|
|||
persist: (store, options) => {
|
||||
const persistor = persistStore(store, {storage: AsyncStorage, ...options}, () => {
|
||||
store.dispatch({
|
||||
type: Constants.STORE_REHYDRATION_COMPLETE,
|
||||
type: General.STORE_REHYDRATION_COMPLETE,
|
||||
complete: true
|
||||
});
|
||||
});
|
||||
|
|
@ -90,7 +96,7 @@ export default function configureAppStore(initialState) {
|
|||
|
||||
store.dispatch(batchActions([
|
||||
{
|
||||
type: Constants.OFFLINE_STORE_RESET,
|
||||
type: General.OFFLINE_STORE_RESET,
|
||||
data: initialState
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,5 +7,5 @@ export function isValidUrl(url) {
|
|||
}
|
||||
|
||||
export function stripTrailingSlashes(url) {
|
||||
return url.replace(/\/+$/, '');
|
||||
return url.replace(/\/+$/, '').trim();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,5 +5,5 @@
|
|||
"DefaultTheme": "default",
|
||||
"ShowErrorsList": false,
|
||||
"GooglePlaySenderId": "184930218130",
|
||||
"MinServerVersion": "3.7.0"
|
||||
"MinServerVersion": "3.8.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
"linkColor": "#2f81b7",
|
||||
"buttonBg": "#1dacfc",
|
||||
"buttonColor": "#FFFFFF",
|
||||
"errorTextColor": "#a94442",
|
||||
"mentionHighlightBg": "#fff2bb",
|
||||
"mentionHighlightLink": "#2f81b7",
|
||||
"codeTheme": "github"
|
||||
|
|
@ -41,6 +42,7 @@
|
|||
"linkColor": "#2389d7",
|
||||
"buttonBg": "#23A2FF",
|
||||
"buttonColor": "#FFFFFF",
|
||||
"errorTextColor": "#a94442",
|
||||
"mentionHighlightBg": "#fff2bb",
|
||||
"mentionHighlightLink": "#2f81b7",
|
||||
"codeTheme": "github"
|
||||
|
|
@ -64,6 +66,7 @@
|
|||
"linkColor": "#A4FFEB",
|
||||
"buttonBg": "#4CBBA4",
|
||||
"buttonColor": "#FFFFFF",
|
||||
"errorTextColor": "#ff6461",
|
||||
"mentionHighlightBg": "#984063",
|
||||
"mentionHighlightLink": "#A4FFEB",
|
||||
"codeTheme": "solarized-dark"
|
||||
|
|
@ -87,6 +90,7 @@
|
|||
"linkColor": "#0D93FF",
|
||||
"buttonBg": "#0177e7",
|
||||
"buttonColor": "#FFFFFF",
|
||||
"errorTextColor": "#ff6461",
|
||||
"mentionHighlightBg": "#784098",
|
||||
"mentionHighlightLink": "#A4FFEB",
|
||||
"codeTheme": "monokai"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
"deep-equal": "1.0.1",
|
||||
"harmony-reflect": "1.5.1",
|
||||
"intl": "1.2.5",
|
||||
"mattermost-redux": "mattermost/mattermost-redux#release-3.7",
|
||||
"mattermost-redux": "mattermost/mattermost-redux#master",
|
||||
"react": "15.4.2",
|
||||
"react-intl": "2.2.3",
|
||||
"react-native": "0.42.3",
|
||||
|
|
@ -42,6 +42,7 @@
|
|||
"babel-eslint": "7.2.1",
|
||||
"babel-plugin-module-resolver": "2.7.0",
|
||||
"babel-preset-es2015": "6.24.0",
|
||||
"babel-preset-stage-0": "6.22.0",
|
||||
"babel-preset-react-native": "1.9.1",
|
||||
"babel-register": "6.24.0",
|
||||
"chai": "3.5.0",
|
||||
|
|
|
|||
Loading…
Reference in a new issue