New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MM-37606 Queue sending message after file uploading finish #23382
base: master
Are you sure you want to change the base?
MM-37606 Queue sending message after file uploading finish #23382
Conversation
E2E tests not automatically triggered, because PR has no approval yet. Please ask a developer to review and then try again to attach the QA label. |
f1967d9
to
f60f2ea
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks again for resubmitting that PR! I continue to appreciate that you're putting in all the work to deal with out moving stuff around here.
My review of this is still taking some time since I want to be extra thorough because this is a very important part of the app, so while I still have some stuff I wanted to check out, I wanted to submit a partial review of the code and avoid being completely silent on it. I'm hoping to have some more time later this week to continue on my review, but I'll try to be responsive on my current comments in the mean time
|
||
export function cancelUploadingFile(clientId: string) { | ||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => { | ||
const state = getState(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you mind inlining these calls to getState
instead of saving it as a variable? It shouldn't matter here too much, but because dispatching an action changes the state, state
is out of date by the time we use it again on line 180, so I think it's a good practice to not reuse the old version of state
there
return; | ||
} | ||
|
||
const pendingPost = Object.values(state.entities.posts.posts).find((post) => post.pending_post_id === pendingPostId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we just use the getPost
selector here? I'm pretty sure pending posts have their ID set to their pending post ID before they've been created
return []; | ||
} | ||
return state.entities.files.filePreviews[pendingPostId] ?? []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This selector isn't memoized, so we can't return new array literals from it. Either we can turn it into a selector factory which we could memoize with createSelector
or I'd suggest having this return undefined and making the components that use it be able to handle the undefined value using defaultProps
or something similar
return post.id === post.pending_post_id && post.file_client_ids !== undefined && post.file_client_ids.length !== 0; | ||
} | ||
|
||
export function isPostDangling(state: GlobalState, post: Post) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I'm reading this right, is a "dangling post" another name for being a reply to a pending post? If so, is that needed for this ticket, or can we remove that part of the PR?
Queuing posts with ongoing file uploads is already pretty complicated, and being able to reply to a pending post feels significantly more complex than that. I don't think Thread objects have any logic yet to support being in a pending state similar to posts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I'm reading this right, is a "dangling post" another name for being a reply to a pending post?
Yes, that's right.
If so, is that needed for this ticket, or can we remove that part of the PR?
I implemented this to fix the following issue.
mattermost/mattermost-webapp#11370 (comment)
I think we need this feature since an alert appears when a user replies to a pending post.
Can we ignore such a case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see. I was under the impression that we didn't allow you to open the RHS to a pending post, but maybe that's not actually the case since, unless there's a lot of lag, it's hard to open the RHS to reply to a post before it's created currently. It'll definitely be easier now if we hold the post while we wait for attachments to upload.
Perhaps, we can either prevent the user from opening a pending post in the RHS or just replying to it? It's not ideal UX, but I feel like it'd be much easier to prevent that case from being possible. That way, we can also focus this effort on just queuing posts with pending file attachments without worrying about solving the existing edge cases caused by being able to open pending posts in the RHS.
I'll see if anyone else here has any ideas, but how would that sound to you?
const aIsPendingOrFailed = isPostPendingOrFailed(a); | ||
const bIsPendingOrFailed = isPostPendingOrFailed(b); | ||
if (aIsPendingOrFailed && !bIsPendingOrFailed) { | ||
if (!aIsUploadingFile && aIsPendingOrFailed && !bIsPendingOrFailed) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this supposed to make queued posts with uploads always appear after regular pending posts in the channel? If so, would you mind separating this case into a separate if-else block? I usually like doing that because it makes it easy to see the priority of different fields when you're comparing two items
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hmhealey
I added this condition to sort posts with uploads by their create_at.
Should I separate if-else block in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm thinking of just breaking that up so that it's like
if (aIsUploadingFile && !bIsUploadingFile) {
// I might have gotten these cases backwards to what you were intending
return -1;
} else if (!aIsUploadingFile && bIsUploadingFile) {
return 1;
}
if (aIsPendingOrFailed && !bIsPendingOrFailed) {
return -1;
} else if (!aIsPendingOrFailed && bIsPendingOrFailed) {
return 1;
}
if (a.create_at > b.create_at) {
return -1;
} else if (a.create_at < b.create_at) {
return 1;
}
return 0;
I find that pattern just makes it a bit easier to read since it spreads out the different cases pretty clearly
const entries = Object.entries(nextState); | ||
for (let entryIndex = 0; entryIndex < entries.length; entryIndex++) { | ||
const entry = entries[entryIndex]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you might be able to rewrite this as a for-of loop and use some destructuring to make it a bit easier to follow since it's not particularly clear what entry
is. I'm thinking something like
for (const [id, uploadsInProgress] of Object.entries(nextState)) {
// ...
}
} | ||
return { | ||
...state, | ||
[entry[0]]: entry[1].filter((f) => f.clientId !== action.clientId), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit more nitpicking here, but would you mind redeclaring entry[0]
and entry[1]
as something like id
and uploadsInProgress
to make it more obvious what's going on here?
} | ||
} | ||
|
||
function fileUploadRequests(state: Record<string, XMLHttpRequest> = {}, action: GenericAction) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this still needed? I can't find where it's being used
return nextState; | ||
} | ||
|
||
case FileTypes.REMOVE_FILE_PREVIEWS: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of needing a separate action for this, could we actually listen for a RECEIVED_NEW_POST
here? If that post's pending_post_id
is set, we can see if that ID is a key in state
and then, if that's found, remove it
START_UPLOADING_FILE: null, | ||
UPDATE_FILE_UPLOAD_PROGRESS: null, | ||
REMOVE_FILE_PREVIEWS: null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More nitpicking, but would you mind renaming these to be written more like STARTED_FILE_UPLOAD
or CANCELED_FILE_UPLOAD
to be a bit more consistent with all of our RECEIVED_*
actions?
Creating a new SpinWick test server using Mattermost Cloud. |
Enterprise Edition Image not available in the 30 minutes timeframe, checking the Team Edition Image and if available will use that. |
hi @sk409 , can you help in resolving conflicts on this PR ? |
I've got it. |
This PR has been automatically labelled "stale" because it hasn't had recent activity. |
@sk409 Are you still working on this? Could you resolve the merge conflicts and the comments? |
@larkox |
@sk409 no rush at all. Take your time. Just checking in case I had to mark it again as up for grabs 😄 |
Summary
Queue sending message when files are being uploaded so that users can send next message.
Migrated from: mattermost/mattermost-webapp#11370
Ticket Link
Fixes #21264
Jira: https://mattermost.atlassian.net/browse/MM-37606
Related Pull Requests
No related PRs.
Screenshots
Release Note