FIX: leaving a group channel should destroy membership (#24631)

In other kind of channels we will only unfollow but for group channels we don't want people to keep appearing in members list.

This commit also creates appropriate services:
- `Chat::LeaveChannel`
- `Chat::UnfollowChannel`

And dedicated endpoint for unfollow: `DELETE /chat/api/channels/:id/memberships/me/follows`
This commit is contained in:
Joffrey JAFFEUX
2023-11-29 17:48:14 +01:00
committed by GitHub
parent 11636f8736
commit 384a8b17a1
18 changed files with 409 additions and 66 deletions

View File

@ -131,7 +131,7 @@ export default class ChatChannelRow extends Component {
}
get leaveDirectMessageLabel() {
return I18n.t("chat.direct_messages.leave");
return I18n.t("chat.direct_messages.close");
}
get leaveChannelLabel() {

View File

@ -29,12 +29,14 @@ const NOTIFICATION_LEVELS = [
export default class ChatAboutScreen extends Component {
@service chatApi;
@service chatGuardian;
@service chatChannelsManager;
@service currentUser;
@service siteSettings;
@service dialog;
@service modal;
@service site;
@service toasts;
@service router;
notificationLevels = NOTIFICATION_LEVELS;
@ -309,6 +311,12 @@ export default class ChatAboutScreen extends Component {
});
}
@action
onLeaveChannel(channel) {
this.chatChannelsManager.remove(channel);
return this.router.transitionTo("chat");
}
@action
onEditChannelDescription() {
return this.modal.show(ChatModalEditChannelDescription, {
@ -573,6 +581,7 @@ export default class ChatAboutScreen extends Component {
<:action>
<ToggleChannelMembershipButton
@channel={{@channel}}
@onLeave={{this.onLeaveChannel}}
@options={{hash
joinClass="btn-primary"
leaveClass="btn-danger"

View File

@ -6,10 +6,13 @@ import DButton from "discourse/components/d-button";
import concatClass from "discourse/helpers/concat-class";
import { popupAjaxError } from "discourse/lib/ajax-error";
import I18n from "discourse-i18n";
export default class ToggleChannelMembershipButton extends Component {
@service chat;
@service chatApi;
@tracked isLoading = false;
onToggle = null;
options = {};
constructor() {
@ -54,7 +57,7 @@ export default class ToggleChannelMembershipButton extends Component {
return this.chat
.followChannel(this.args.channel)
.then(() => {
this.onToggle?.();
this.args.onJoin?.(this.args.channel);
})
.catch(popupAjaxError)
.finally(() => {
@ -67,22 +70,22 @@ export default class ToggleChannelMembershipButton extends Component {
}
@action
onLeaveChannel() {
async onLeaveChannel() {
this.isLoading = true;
return this.chat
.unfollowChannel(this.args.channel)
.then(() => {
this.onToggle?.();
})
.catch(popupAjaxError)
.finally(() => {
if (this.isDestroying || this.isDestroyed) {
return;
}
try {
if (this.args.channel.chatable.group) {
await this.chatApi.leaveChannel(this.args.channel.id);
} else {
await this.chat.unfollowChannel(this.args.channel);
}
this.isLoading = false;
});
this.args.onLeave?.(this.args.channel);
} catch (error) {
popupAjaxError(error);
} finally {
this.isLoading = false;
}
}
<template>

View File

@ -211,7 +211,7 @@ export default {
suffixCSSClass = "urgent";
hoverType = "icon";
hoverValue = "times";
hoverTitle = I18n.t("chat.direct_messages.leave");
hoverTitle = I18n.t("chat.direct_messages.close");
constructor({ channel, chatService, currentUser }) {
super(...arguments);

View File

@ -34,6 +34,13 @@ export default function withChatChannel(extendedClass) {
let { channelTitle } = this.paramsFor(this.routeName);
if (channelTitle && channelTitle !== model.slugifiedTitle) {
if (this.routeName === "chat.channel.info") {
return this.router.replaceWith(
"chat.channel.info",
...model.routeModels
);
}
const messageId = this.paramsFor("chat.channel.near-message").messageId;
const threadId = this.paramsFor("chat.channel.thread").threadId;

View File

@ -293,9 +293,19 @@ export default class ChatApi extends Service {
* @returns {Promise}
*/
unfollowChannel(channelId) {
return this.#deleteRequest(`/channels/${channelId}/memberships/me`).then(
(result) => UserChatChannelMembership.create(result.membership)
);
return this.#deleteRequest(
`/channels/${channelId}/memberships/me/follows`
).then((result) => UserChatChannelMembership.create(result.membership));
}
/**
* Destroys the membership of current user on a channel.
*
* @param {number} channelId - The ID of the channel.
* @returns {Promise}
*/
leaveChannel(channelId) {
return this.#deleteRequest(`/channels/${channelId}/memberships/me`);
}
/**

View File

@ -77,13 +77,15 @@ export default class ChatChannelsManager extends Service {
}
async unfollow(model) {
this.chatSubscriptionsManager.stopChannelSubscription(model);
return this.chatApi.unfollowChannel(model.id).then((membership) => {
model.currentUserMembership = membership;
try {
this.chatSubscriptionsManager.stopChannelSubscription(model);
model.currentUserMembership = await this.chatApi.unfollowChannel(
model.id
);
return model;
});
} catch (error) {
popupAjaxError(error);
}
}
@debounce(300)