forked from Ivasoft/mattermost-mobile
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:
committed by
Harrison Healey
parent
54ddec3d46
commit
9ff9b872ba
@@ -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();
|
||||
|
||||
@@ -12,3 +12,11 @@ export function handleCommentDraftChanged(rootId, draft) {
|
||||
}, getState);
|
||||
};
|
||||
}
|
||||
|
||||
export function handleCommentDraftSelectionChanged(rootId, cursorPosition) {
|
||||
return {
|
||||
type: ViewTypes.COMMENT_DRAFT_SELECTION_CHANGED,
|
||||
rootId,
|
||||
cursorPosition
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -13,4 +13,4 @@ function mapStateToProps(state) {
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(Code);
|
||||
export default connect(mapStateToProps)(Code);
|
||||
@@ -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": "",
|
||||
|
||||
Reference in New Issue
Block a user