Skip to content
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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

sk409
Copy link

@sk409 sk409 commented May 13, 2023

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

スクリーンショット 2023-05-13 15 28 24(2)

Release Note

Message can be uploaded when files are being uploaded.

@mm-cloud-bot mm-cloud-bot added the release-note Denotes a PR that will be considered when it comes time to generate release notes. label May 13, 2023
@mattermost-build
Copy link
Contributor

Hello @sk409,

Thanks for your pull request! A Core Committer will review your pull request soon. For code contributions, you can learn more about the review process here.

@M-ZubairAhmed M-ZubairAhmed added 2: Dev Review Requires review by a developer 3: QA Review Requires review by a QA tester. May occur at the same time as Dev Review labels May 13, 2023
@mattermost-build
Copy link
Contributor

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.

@sk409 sk409 force-pushed the MM-37606_Queue-sending-message-after-file-upload-finish branch from f1967d9 to f60f2ea Compare May 21, 2023 06:26
Copy link
Member

@hmhealey hmhealey left a 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();
Copy link
Member

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);
Copy link
Member

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

Comment on lines +66 to +68
return [];
}
return state.entities.files.filePreviews[pendingPostId] ?? [];
Copy link
Member

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) {
Copy link
Member

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

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hmhealey

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?

Copy link
Member

@hmhealey hmhealey May 29, 2023

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) {
Copy link
Member

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

Copy link
Author

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?

Copy link
Member

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

Comment on lines +201 to +203
const entries = Object.entries(nextState);
for (let entryIndex = 0; entryIndex < entries.length; entryIndex++) {
const entry = entries[entryIndex];
Copy link
Member

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),
Copy link
Member

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) {
Copy link
Member

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: {
Copy link
Member

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

Comment on lines +18 to +20
START_UPLOADING_FILE: null,
UPDATE_FILE_UPLOAD_PROGRESS: null,
REMOVE_FILE_PREVIEWS: null,
Copy link
Member

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?

@marianunez marianunez added the Setup Cloud Test Server Setup an on-prem test server label May 24, 2023
@mm-cloud-bot
Copy link

Creating a new SpinWick test server using Mattermost Cloud.

@mm-cloud-bot
Copy link

Enterprise Edition Image not available in the 30 minutes timeframe, checking the Team Edition Image and if available will use that.

@yasserfaraazkhan
Copy link
Contributor

hi @sk409 , can you help in resolving conflicts on this PR ?

@sk409
Copy link
Author

sk409 commented May 28, 2023

@hmhealey

My review of this is still taking some time since...

I've got it.

@mattermost-build
Copy link
Contributor

This PR has been automatically labelled "stale" because it hasn't had recent activity.
A core team member will check in on the status of the PR to help with questions.
Thank you for your contribution!

@calebroseland calebroseland removed their request for review July 11, 2023 16:15
@yasserfaraazkhan yasserfaraazkhan removed their request for review July 25, 2023 07:16
@yasserfaraazkhan yasserfaraazkhan removed the 3: QA Review Requires review by a QA tester. May occur at the same time as Dev Review label Jul 25, 2023
@larkox
Copy link
Contributor

larkox commented Aug 8, 2023

@sk409 Are you still working on this? Could you resolve the merge conflicts and the comments?

@sk409
Copy link
Author

sk409 commented Aug 9, 2023

@larkox
I'm still working on this issue, but haven't had much time lately.
I'm thinking of completing it by the end of this month, but if it seems like it will take longer than that, please unassign me.

@larkox
Copy link
Contributor

larkox commented Aug 10, 2023

@sk409 no rush at all. Take your time. Just checking in case I had to mark it again as up for grabs 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2: Dev Review Requires review by a developer Contributor Lifecycle/1:stale release-note Denotes a PR that will be considered when it comes time to generate release notes. Setup Cloud Test Server Setup an on-prem test server
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Queue sending message after file upload finishes
8 participants