Config to enable when clicking on username to fill input with @mention (#966)

* Config to enable when clicking on username to fill input with @mention

* Rename to experimental and move tenary from componet to variable

* Use getCurrentChannelId in insertToPostDraft action

* Change to work with rebase

* Refactor thread reducer and blacklist currentThreadId

* Review feedback

* Review feedback 2
This commit is contained in:
Chris Duarte
2017-10-26 07:48:52 -07:00
committed by Harrison Healey
parent 54ddec3d46
commit 9ff9b872ba
13 changed files with 464 additions and 270 deletions

View File

@@ -18,6 +18,7 @@ import {savePreferences} from 'mattermost-redux/actions/preferences';
import {getTeamMembersByIds} from 'mattermost-redux/actions/teams';
import {getProfilesInChannel} from 'mattermost-redux/actions/users';
import {General, Preferences} from 'mattermost-redux/constants';
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
import {
getChannelByName,
getDirectChannelName,
@@ -273,16 +274,67 @@ export function handleSelectChannel(channelId) {
};
}
export function handlePostDraftChanged(channelId, postDraft) {
export function handlePostDraftChanged(channelId, draft) {
return async (dispatch, getState) => {
dispatch({
type: ViewTypes.POST_DRAFT_CHANGED,
channelId,
postDraft
draft
}, getState);
};
}
export function handlePostDraftSelectionChanged(channelId, cursorPosition) {
return {
type: ViewTypes.POST_DRAFT_SELECTION_CHANGED,
channelId,
cursorPosition
};
}
export function insertToDraft(value) {
return (dispatch, getState) => {
const state = getState();
const channelId = getCurrentChannelId(state);
const threadId = state.entities.posts.selectedPostId;
let draft;
let cursorPosition;
let action;
if (state.views.thread.drafts[threadId]) {
const threadDraft = state.views.thread.drafts[threadId];
draft = threadDraft.draft;
cursorPosition = threadDraft.cursorPosition;
action = {
type: ViewTypes.COMMENT_DRAFT_CHANGED,
rootId: threadId
};
} else if (state.views.channel.drafts[channelId]) {
const channelDraft = state.views.channel.drafts[channelId];
draft = channelDraft.draft;
cursorPosition = channelDraft.cursorPosition;
action = {
type: ViewTypes.POST_DRAFT_CHANGED,
channelId
};
}
let nextDraft = `${value}`;
if (cursorPosition > 0) {
const beginning = draft.slice(0, cursorPosition);
const end = draft.slice(cursorPosition);
nextDraft = `${beginning}${value}${end}`;
}
if (action && nextDraft !== draft) {
dispatch({
...action,
draft: nextDraft
});
}
};
}
export function toggleDMChannel(otherUserId, visible) {
return async (dispatch, getState) => {
const state = getState();

View File

@@ -12,3 +12,11 @@ export function handleCommentDraftChanged(rootId, draft) {
}, getState);
};
}
export function handleCommentDraftSelectionChanged(rootId, cursorPosition) {
return {
type: ViewTypes.COMMENT_DRAFT_SELECTION_CHANGED,
rootId,
cursorPosition
};
}

View File

@@ -8,6 +8,7 @@ import {addReaction, createPost, deletePost, removePost} from 'mattermost-redux/
import {getPost} from 'mattermost-redux/selectors/entities/posts';
import {getCurrentUserId, getCurrentUserRoles} from 'mattermost-redux/selectors/entities/users';
import {insertToDraft, setPostTooltipVisible} from 'app/actions/views/channel';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import Post from './post';
@@ -62,7 +63,9 @@ function mapDispatchToProps(dispatch) {
addReaction,
createPost,
deletePost,
removePost
removePost,
setPostTooltipVisible,
insertToDraft
}, dispatch)
};
}

View File

@@ -10,6 +10,7 @@ import {
} from 'react-native';
import {injectIntl, intlShape} from 'react-intl';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import {isToolTipShowing} from 'react-native-tooltip';
import PostBody from 'app/components/post_body';
import PostHeader from 'app/components/post_header';
@@ -25,7 +26,7 @@ import EventEmitter from 'mattermost-redux/utils/event_emitter';
import {canDeletePost, canEditPost, isPostEphemeral, isPostPendingOrFailed, isSystemMessage} from 'mattermost-redux/utils/post_utils';
import {isAdmin, isSystemAdmin} from 'mattermost-redux/utils/user_utils';
import {isToolTipShowing} from 'react-native-tooltip';
import Config from 'assets/config';
class Post extends PureComponent {
static propTypes = {
@@ -33,6 +34,7 @@ class Post extends PureComponent {
addReaction: PropTypes.func.isRequired,
createPost: PropTypes.func.isRequired,
deletePost: PropTypes.func.isRequired,
insertToDraft: PropTypes.func.isRequired,
removePost: PropTypes.func.isRequired
}).isRequired,
config: PropTypes.object.isRequired,
@@ -117,6 +119,12 @@ class Post extends PureComponent {
});
};
autofillUserMention = (username) => {
// create a general action that checks for currentThreadId in the state and decides
// whether to insert to root or thread
this.props.actions.insertToDraft(`@${username} `);
}
handleEditDisable = () => {
this.setState({canEdit: false});
};
@@ -330,6 +338,8 @@ class Post extends PureComponent {
const selected = this.state && this.state.selected ? style.selected : null;
const highlighted = highlight ? style.highlight : null;
const onUsernamePress = Config.ExperimentalUsernamePressIsMention ? this.autofillUserMention : this.viewUserProfile;
return (
<View style={[style.container, this.props.style, highlighted, selected]}>
<View style={[style.profilePictureContainer, (isPostPendingOrFailed(post) && style.pendingPost)]}>
@@ -349,7 +359,7 @@ class Post extends PureComponent {
shouldRenderReplyButton={shouldRenderReplyButton}
showFullDate={showFullDate}
onPress={this.handleReply}
onViewUserProfile={this.viewUserProfile}
onUsernamePress={onUsernamePress}
renderReplies={renderReplies}
theme={theme}
/>

View File

@@ -18,7 +18,7 @@ function makeMapStateToProps() {
const {config} = state.entities.general;
const post = getPost(state, ownProps.postId);
const commentedOnUser = getUser(state, ownProps.commentedOnUserId);
const user = getUser(state, post.user_id);
const user = getUser(state, post.user_id) || {};
const teammateNameDisplay = getTeammateNameDisplaySetting(state);
const militaryTime = getBool(state, Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time');
@@ -34,7 +34,8 @@ function makeMapStateToProps() {
isPendingOrFailedPost: isPostPendingOrFailed(post),
isSystemMessage: isSystemMessage(post),
overrideUsername: post.props && post.props.override_username,
theme: getTheme(state)
theme: getTheme(state),
username: user.username
};
};
}

View File

@@ -31,26 +31,30 @@ export default class PostHeader extends PureComponent {
isSystemMessage: PropTypes.bool,
militaryTime: PropTypes.bool,
onPress: PropTypes.func,
onViewUserProfile: PropTypes.func,
onUsernamePress: PropTypes.func,
overrideUsername: PropTypes.string,
renderReplies: PropTypes.bool,
shouldRenderReplyButton: PropTypes.bool,
showFullDate: PropTypes.bool,
theme: PropTypes.object.isRequired
theme: PropTypes.object.isRequired,
username: PropTypes.string.isRequired
};
static defaultProps = {
commentCount: 0,
onPress: emptyFunction,
onViewUserProfile: emptyFunction
onUsernamePress: emptyFunction
};
handleUsernamePress = () => {
this.props.onUsernamePress(this.props.username);
}
getDisplayName = (style) => {
const {
enablePostUsernameOverride,
fromWebHook,
isSystemMessage,
onViewUserProfile,
overrideUsername
} = this.props;
@@ -80,7 +84,7 @@ export default class PostHeader extends PureComponent {
);
} else if (this.props.displayName) {
return (
<TouchableOpacity onPress={onViewUserProfile}>
<TouchableOpacity onPress={this.handleUsernamePress}>
<Text style={style.displayName}>
{this.props.displayName}
</Text>

View File

@@ -11,9 +11,9 @@ import {canUploadFilesOnMobile} from 'mattermost-redux/selectors/entities/genera
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
import {addReactionToLatestPost} from 'app/actions/views/emoji';
import {handlePostDraftChanged} from 'app/actions/views/channel';
import {handlePostDraftChanged, handlePostDraftSelectionChanged} from 'app/actions/views/channel';
import {handleClearFiles, handleRemoveLastFile, handleUploadFiles} from 'app/actions/views/file_upload';
import {handleCommentDraftChanged} from 'app/actions/views/thread';
import {handleCommentDraftChanged, handleCommentDraftSelectionChanged} from 'app/actions/views/thread';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getCurrentChannelDraft, getThreadDraft} from 'app/selectors/views';
@@ -44,7 +44,9 @@ function mapDispatchToProps(dispatch) {
handlePostDraftChanged,
handleRemoveLastFile,
handleUploadFiles,
userTyping
userTyping,
handlePostDraftSelectionChanged,
handleCommentDraftSelectionChanged
}, dispatch)
};
}

View File

@@ -39,7 +39,9 @@ class PostTextbox extends PureComponent {
handleClearFiles: PropTypes.func.isRequired,
handleRemoveLastFile: PropTypes.func.isRequired,
handleUploadFiles: PropTypes.func.isRequired,
userTyping: PropTypes.func.isRequired
userTyping: PropTypes.func.isRequired,
handlePostDraftSelectionChanged: PropTypes.func.isRequired,
handleCommentDraftSelectionChanged: PropTypes.func.isRequired
}).isRequired,
canUploadFiles: PropTypes.bool.isRequired,
channelId: PropTypes.string.isRequired,
@@ -56,7 +58,6 @@ class PostTextbox extends PureComponent {
static defaultProps = {
files: [],
onSelectionChange: () => true,
rootId: '',
value: ''
};
@@ -239,12 +240,6 @@ class PostTextbox extends PureComponent {
actions.userTyping(channelId, rootId);
};
handleSelectionChange = (event) => {
if (this.autocomplete) {
this.autocomplete.handleSelectionChange(event);
}
};
handleContentSizeChange = (event) => {
let contentHeight = event.nativeEvent.layout.height;
if (contentHeight < INITIAL_HEIGHT) {
@@ -319,6 +314,17 @@ class PostTextbox extends PureComponent {
return null;
}
handlePostDraftSelectionChanged = (event) => {
const cursorPosition = event.nativeEvent.selection.end;
if (this.props.rootId) {
this.props.actions.handleCommentDraftSelectionChanged(this.props.rootId, cursorPosition);
} else {
this.props.actions.handlePostDraftSelectionChanged(this.props.channelId, cursorPosition);
}
this.autocomplete.handleSelectionChange(event);
}
render() {
const {
canUploadFiles,
@@ -382,7 +388,7 @@ class PostTextbox extends PureComponent {
ref='input'
value={textValue}
onChangeText={this.handleTextChange}
onSelectionChange={this.handleSelectionChange}
onSelectionChange={this.handlePostDraftSelectionChanged}
placeholder={intl.formatMessage(placeholder)}
placeholderTextColor={changeOpacity('#000', 0.5)}
multiline={true}

View File

@@ -19,6 +19,9 @@ const ViewTypes = keyMirror({
COMMENT_DRAFT_CHANGED: null,
SEARCH_DRAFT_CHANGED: null,
POST_DRAFT_SELECTION_CHANGED: null,
COMMENT_DRAFT_SELECTION_CHANGED: null,
NOTIFICATION_IN_APP: null,
NOTIFICATION_TAPPED: null,

View File

@@ -18,156 +18,199 @@ function displayName(state = '', action) {
}
}
function drafts(state = {}, action) {
switch (action.type) {
case ViewTypes.POST_DRAFT_CHANGED: {
return {
function handlePostDraftChanged(state, action) {
return {
...state,
[action.channelId]: Object.assign({}, state[action.channelId], {draft: action.draft})
};
}
function handlePostDraftSelectionChanged(state, action) {
return {
...state,
[action.channelId]: Object.assign({}, state[action.channelId], {
cursorPosition: action.cursorPosition
})
};
}
function handleSetPostDraft(state, action) {
return {
...state,
[action.channelId]: {
draft: action.draft,
cursorPosition: 0,
files: action.files
}
};
}
function handleSelectChannel(state, action) {
let data = {...state};
if (action.data && !data[action.data]) {
data = {
...state,
[action.channelId]: Object.assign({}, state[action.channelId], {draft: action.postDraft})
};
}
case ViewTypes.SET_POST_DRAFT: {
return {
...state,
[action.channelId]: {
draft: action.postDraft,
files: action.files
[action.data]: {
draft: '',
cursorPosition: 0,
files: []
}
};
}
case ChannelTypes.SELECT_CHANNEL: {
let data = {...state};
if (action.data && !data[action.data]) {
data = {
...state,
[action.data]: {
draft: '',
files: []
}
return data;
}
function handleSetTempUploadFileForPostDraft(state, action) {
if (action.rootId) {
return state;
}
const tempFiles = action.clientIds.map((temp) => ({...temp, loading: true}));
const files = [
...state[action.channelId].files,
...tempFiles
];
return {
...state,
[action.channelId]: Object.assign({}, state[action.channelId], {files})
};
}
function handleRetryUploadFileForPost(state, action) {
if (action.rootId) {
return state;
}
const files = state[action.channelId].files.map((f) => {
if (f.clientId === action.clientId) {
return {
...f,
loading: true,
failed: false
};
}
return data;
return f;
});
return {
...state,
[action.channelId]: Object.assign({}, state[action.channelId], {files})
};
}
function handleReceivedUploadFiles(state, action) {
if (action.rootId || !state[action.channelId].files) {
return state;
}
case ViewTypes.SET_TEMP_UPLOAD_FILES_FOR_POST_DRAFT: {
if (action.rootId) {
return state;
// Reconcile tempFiles with the received uploaded files
const files = state[action.channelId].files.map((tempFile) => {
const file = action.data.find((f) => f.clientId === tempFile.clientId);
if (file) {
return {
...file,
localPath: tempFile.localPath
};
}
const tempFiles = action.clientIds.map((temp) => ({...temp, loading: true}));
const files = [
...state[action.channelId].files,
...tempFiles
];
return tempFile;
});
return {
...state,
[action.channelId]: Object.assign({}, state[action.channelId], {files})
};
return {
...state,
[action.channelId]: Object.assign({}, state[action.channelId], {files})
};
}
function handleUploadFilesFailure(state, action) {
if (action.rootId) {
return state;
}
case ViewTypes.RETRY_UPLOAD_FILE_FOR_POST: {
if (action.rootId) {
return state;
const clientIds = action.clientIds;
const files = state[action.channelId].files.map((tempFile) => {
if (clientIds.includes(tempFile.clientId)) {
return {
...tempFile,
loading: false,
failed: true
};
}
const files = state[action.channelId].files.map((f) => {
if (f.clientId === action.clientId) {
return {
...f,
loading: true,
failed: false
};
}
return tempFile;
});
return f;
});
return {
...state,
[action.channelId]: Object.assign({}, state[action.channelId], {files})
};
}
return {
...state,
[action.channelId]: Object.assign({}, state[action.channelId], {files})
};
function handleClearFilesForPostDraft(state, action) {
if (action.rootId) {
return state;
}
case FileTypes.RECEIVED_UPLOAD_FILES: {
if (action.rootId || !state[action.channelId].files) {
return state;
}
// Reconcile tempFiles with the received uploaded files
const files = state[action.channelId].files.map((tempFile) => {
const file = action.data.find((f) => f.clientId === tempFile.clientId);
if (file) {
return {
...file,
localPath: tempFile.localPath
};
}
return {
...state,
[action.channelId]: Object.assign({}, state[action.channelId], {files: []})
};
}
return tempFile;
});
return {
...state,
[action.channelId]: Object.assign({}, state[action.channelId], {files})
};
function handleRemoveFileFromPostDraft(state, action) {
if (action.rootId) {
return state;
}
case FileTypes.UPLOAD_FILES_FAILURE: {
if (action.rootId) {
return state;
}
const clientIds = action.clientIds;
const files = state[action.channelId].files.map((tempFile) => {
if (clientIds.includes(tempFile.clientId)) {
return {
...tempFile,
loading: false,
failed: true
};
}
const files = state[action.channelId].files.filter((file) => (file.clientId !== action.clientId));
return tempFile;
});
return {
...state,
[action.channelId]: Object.assign({}, state[action.channelId], {files})
};
}
return {
...state,
[action.channelId]: Object.assign({}, state[action.channelId], {files})
};
function handleRemoveLastFileFromPostDraft(state, action) {
if (action.rootId) {
return state;
}
case ViewTypes.CLEAR_FILES_FOR_POST_DRAFT: {
if (action.rootId) {
return state;
}
return {
...state,
[action.channelId]: Object.assign({}, state[action.channelId], {files: []})
};
}
case ViewTypes.REMOVE_FILE_FROM_POST_DRAFT: {
if (action.rootId) {
return state;
}
const files = [...state[action.channelId].files];
files.splice(-1);
const files = state[action.channelId].files.filter((file) => (file.clientId !== action.clientId));
return {
...state,
[action.channelId]: Object.assign({}, state[action.channelId], {files})
};
}
return {
...state,
[action.channelId]: Object.assign({}, state[action.channelId], {files})
};
}
case ViewTypes.REMOVE_LAST_FILE_FROM_POST_DRAFT: {
if (action.rootId) {
return state;
}
const files = [...state[action.channelId].files];
files.splice(-1);
return {
...state,
[action.channelId]: Object.assign({}, state[action.channelId], {files})
};
}
function drafts(state = {}, action) { // eslint-disable-line complexity
switch (action.type) {
case ViewTypes.POST_DRAFT_CHANGED:
return handlePostDraftChanged(state, action);
case ViewTypes.POST_DRAFT_SELECTION_CHANGED:
return handlePostDraftSelectionChanged(state, action);
case ViewTypes.SET_POST_DRAFT:
return handleSetPostDraft(state, action);
case ChannelTypes.SELECT_CHANNEL:
return handleSelectChannel(state, action);
case ViewTypes.SET_TEMP_UPLOAD_FILES_FOR_POST_DRAFT:
return handleSetTempUploadFileForPostDraft(state, action);
case ViewTypes.RETRY_UPLOAD_FILE_FOR_POST:
return handleRetryUploadFileForPost(state, action);
case FileTypes.RECEIVED_UPLOAD_FILES:
return handleReceivedUploadFiles(state, action);
case FileTypes.UPLOAD_FILES_FAILURE:
return handleUploadFilesFailure(state, action);
case ViewTypes.CLEAR_FILES_FOR_POST_DRAFT:
return handleClearFilesForPostDraft(state, action);
case ViewTypes.REMOVE_FILE_FROM_POST_DRAFT:
return handleRemoveFileFromPostDraft(state, action);
case ViewTypes.REMOVE_LAST_FILE_FROM_POST_DRAFT:
return handleRemoveLastFileFromPostDraft(state, action);
default:
return state;
}

View File

@@ -6,155 +6,216 @@ import {FileTypes, PostTypes} from 'mattermost-redux/action_types';
import {ViewTypes} from 'app/constants';
function drafts(state = {}, action) {
switch (action.type) {
case ViewTypes.COMMENT_DRAFT_CHANGED:
return {
function handleCommentDraftChanged(state, action) {
if (!action.rootId) {
return state;
}
return {
...state,
[action.rootId]: Object.assign({}, state[action.rootId], {draft: action.draft})
};
}
function handleSetCommentDraft(state, action) {
if (!action.rootId) {
return state;
}
return {
...state,
[action.rootId]: {
draft: action.draft,
cursorPosition: 0,
files: action.files
}
};
}
function handleCommentDraftSelectionChange(state, action) {
if (!action.rootId) {
return state;
}
return {
...state,
[action.rootId]: Object.assign({}, state[action.rootId], {
cursorPosition: action.cursorPosition
})
};
}
function handleReceivedPostSelected(state, action) {
if (!action.data) {
return state;
}
let data = {...state};
if (!data[action.data]) {
data = {
...state,
[action.rootId]: Object.assign({}, state[action.rootId], {draft: action.draft})
};
case ViewTypes.SET_COMMENT_DRAFT:
return {
...state,
[action.rootId]: {
draft: action.draft,
files: action.files
[action.data]: {
draft: '',
cursorPosition: 0,
files: []
}
};
case PostTypes.RECEIVED_POST_SELECTED: {
let data = {...state};
}
if (!data[action.data]) {
data = {
...state,
[action.data]: {
draft: '',
files: []
}
return data;
}
function handleSetTempUploadFilesForPostDraft(state, action) {
if (!action.rootId) {
return state;
}
const tempFiles = action.clientIds.map((temp) => ({...temp, loading: true}));
const files = [
...state[action.rootId].files,
...tempFiles
];
return {
...state,
[action.rootId]: Object.assign({}, state[action.rootId], {files})
};
}
function handleRetryUploadForPost(state, action) {
if (!action.rootId) {
return state;
}
const files = state[action.rootId].files.map((f) => {
if (f.clientId === action.clientId) {
return {
...f,
loading: true,
failed: false
};
}
return data;
return f;
});
return {
...state,
[action.rootId]: Object.assign({}, state[action.rootId], {files})
};
}
function handleReceiveUploadFiles(state, action) {
if (!action.rootId) {
return state;
}
case ViewTypes.SET_TEMP_UPLOAD_FILES_FOR_POST_DRAFT: {
if (!action.rootId) {
return state;
// Reconcile tempFiles with the received uploaded files
const files = state[action.rootId].files.map((tempFile) => {
const file = action.data.find((f) => f.clientId === tempFile.clientId);
if (file) {
return {
...file,
localPath: tempFile.localPath
};
}
const tempFiles = action.clientIds.map((temp) => ({...temp, loading: true}));
const files = [
...state[action.rootId].files,
...tempFiles
];
return tempFile;
});
return {
...state,
[action.rootId]: Object.assign({}, state[action.rootId], {files})
};
return {
...state,
[action.rootId]: Object.assign({}, state[action.rootId], {files})
};
}
function handleUploadFilesFailure(state, action) {
if (!action.rootId) {
return state;
}
case ViewTypes.RETRY_UPLOAD_FILE_FOR_POST: {
if (!action.rootId) {
return state;
const clientIds = action.clientIds;
const files = state[action.rootId].files.map((tempFile) => {
if (clientIds.includes(tempFile.clientId)) {
return {
...tempFile,
loading: false,
failed: true
};
}
const files = state[action.rootId].files.map((f) => {
if (f.clientId === action.clientId) {
return {
...f,
loading: true,
failed: false
};
}
return tempFile;
});
return f;
});
return {
...state,
[action.rootId]: Object.assign({}, state[action.rootId], {files})
};
}
return {
...state,
[action.rootId]: Object.assign({}, state[action.rootId], {files})
};
function handleClearFilesForPostDraft(state, action) {
if (!action.rootId) {
return state;
}
case FileTypes.RECEIVED_UPLOAD_FILES: {
if (!action.rootId) {
return state;
}
// Reconcile tempFiles with the received uploaded files
const files = state[action.rootId].files.map((tempFile) => {
const file = action.data.find((f) => f.clientId === tempFile.clientId);
if (file) {
return {
...file,
localPath: tempFile.localPath
};
}
return {
...state,
[action.rootId]: Object.assign({}, state[action.rootId], {files: []})
};
}
return tempFile;
});
return {
...state,
[action.rootId]: Object.assign({}, state[action.rootId], {files})
};
function handleRemoveFileFromPostDraft(state, action) {
if (!action.rootId) {
return state;
}
case FileTypes.UPLOAD_FILES_FAILURE: {
if (!action.rootId) {
return state;
}
const clientIds = action.clientIds;
const files = state[action.rootId].files.map((tempFile) => {
if (clientIds.includes(tempFile.clientId)) {
return {
...tempFile,
loading: false,
failed: true
};
}
const files = state[action.rootId].files.filter((file) => (file.clientId !== action.clientId));
return tempFile;
});
return {
...state,
[action.rootId]: Object.assign({}, state[action.rootId], {files})
};
}
return {
...state,
[action.rootId]: Object.assign({}, state[action.rootId], {files})
};
function handleRemoveLastFromPostDraft(state, action) {
if (!action.rootId) {
return state;
}
case ViewTypes.CLEAR_FILES_FOR_POST_DRAFT: {
if (!action.rootId) {
return state;
}
return {
...state,
[action.rootId]: Object.assign({}, state[action.rootId], {files: []})
};
}
case ViewTypes.REMOVE_FILE_FROM_POST_DRAFT: {
if (!action.rootId) {
return state;
}
const files = [...state[action.rootId].files];
files.splice(-1);
const files = state[action.rootId].files.filter((file) => (file.clientId !== action.clientId));
return {
...state,
[action.rootId]: Object.assign({}, state[action.rootId], {files})
};
}
return {
...state,
[action.rootId]: Object.assign({}, state[action.rootId], {files})
};
}
case ViewTypes.REMOVE_LAST_FILE_FROM_POST_DRAFT: {
if (!action.rootId) {
return state;
}
const files = [...state[action.rootId].files];
files.splice(-1);
return {
...state,
[action.rootId]: Object.assign({}, state[action.rootId], {files})
};
}
function drafts(state = {}, action) { // eslint-disable-line complexity
switch (action.type) {
case ViewTypes.COMMENT_DRAFT_CHANGED:
return handleCommentDraftChanged(state, action);
case ViewTypes.SET_COMMENT_DRAFT:
return handleSetCommentDraft(state, action);
case ViewTypes.COMMENT_DRAFT_SELECTION_CHANGED:
return handleCommentDraftSelectionChange(state, action);
case PostTypes.RECEIVED_POST_SELECTED:
return handleReceivedPostSelected(state, action);
case ViewTypes.SET_TEMP_UPLOAD_FILES_FOR_POST_DRAFT:
return handleSetTempUploadFilesForPostDraft(state, action);
case ViewTypes.RETRY_UPLOAD_FILE_FOR_POST:
return handleRetryUploadForPost(state, action);
case FileTypes.RECEIVED_UPLOAD_FILES:
return handleReceiveUploadFiles(state, action);
case FileTypes.UPLOAD_FILES_FAILURE:
return handleUploadFilesFailure(state, action);
case ViewTypes.CLEAR_FILES_FOR_POST_DRAFT:
return handleClearFilesForPostDraft(state, action);
case ViewTypes.REMOVE_FILE_FROM_POST_DRAFT:
return handleRemoveFileFromPostDraft(state, action);
case ViewTypes.REMOVE_LAST_FILE_FROM_POST_DRAFT:
return handleRemoveLastFromPostDraft(state, action);
default:
return state;
}

View File

@@ -13,4 +13,4 @@ function mapStateToProps(state) {
};
}
export default connect(mapStateToProps)(Code);
export default connect(mapStateToProps)(Code);

View File

@@ -11,6 +11,7 @@
"PlatformNoticeURL": "https://about.mattermost.com/platform-notice-txt/",
"MobileNoticeURL": "https://about.mattermost.com/mobile-notice-txt/",
"SegmentApiKey": "3MT7rAoC0OP7yy3ThzqFSAtKzmzqtUPX",
"ExperimentalUsernamePressIsMention": false,
"SentryEnabled": false,
"SentryDsnIos": "",