diff --git a/src/assets/localization/fallback.strings b/src/assets/localization/fallback.strings index 865740426..172201b03 100644 --- a/src/assets/localization/fallback.strings +++ b/src/assets/localization/fallback.strings @@ -2679,6 +2679,7 @@ "SettingsDataClearMediaDone" = "Media cache cleared"; "LeaveGroupTitle" = "Leave {group}?"; "LeaveGroupDescription" = "If you leave, **{nextOwner}** will become the new owner of **{group}** in 1 week."; +"LeaveBasicGroupDescription" = "If you leave, **{nextOwner}** will immediately become the new owner of **{group}**."; "LeaveGroupAppointOwner" = "Appoint Another Owner"; "LeaveGroupAdmins" = "GROUP ADMINS"; "LeaveGroupMembers" = "GROUP MEMBERS"; diff --git a/src/components/common/DeleteChatModal.tsx b/src/components/common/DeleteChatModal.tsx index 08cdfee6a..89c3ec638 100644 --- a/src/components/common/DeleteChatModal.tsx +++ b/src/components/common/DeleteChatModal.tsx @@ -63,6 +63,7 @@ const DeleteChatModal = ({ }: OwnProps & StateProps) => { const { leaveChannel, + leaveBasicGroup, deleteHistory, deleteSavedHistory, deleteChannel, @@ -114,8 +115,8 @@ const DeleteChatModal = ({ leaveChannel({ chatId: chat.id }); onClose(); } else if (isBasicGroup && chat.isCreator) { - deleteHistory({ chatId: chat.id, shouldDeleteForAll: false }); - deleteChatUser({ chatId: chat.id, userId: currentUserId! }); + leaveBasicGroup({ chatId: chat.id }); + onClose(); } else { handleDeleteChat(); } diff --git a/src/components/modals/leaveGroup/LeaveGroupModal.tsx b/src/components/modals/leaveGroup/LeaveGroupModal.tsx index 82c8aac1f..99e67697a 100644 --- a/src/components/modals/leaveGroup/LeaveGroupModal.tsx +++ b/src/components/modals/leaveGroup/LeaveGroupModal.tsx @@ -6,6 +6,7 @@ import { getActions, withGlobal } from '../../../global'; import type { ApiChat, ApiChatFullInfo, ApiPeer } from '../../../api/types'; import type { GlobalState, TabState } from '../../../global/types'; +import { isChatBasicGroup } from '../../../global/helpers'; import { getPeerTitle } from '../../../global/helpers/peers'; import { selectChat, selectChatFullInfo, selectPeer } from '../../../global/selectors'; @@ -46,7 +47,7 @@ const LeaveGroupModal = ({ chatFullInfo, }: OwnProps & StateProps) => { const { - closeLeaveGroupModal, leaveChannel, loadMoreMembers, loadFullChat, + closeLeaveGroupModal, leaveChannel, leaveBasicGroup, loadMoreMembers, loadFullChat, transferChatOwnership, verifyTransferOwnership, openTwoFaCheckModal, } = getActions(); const lang = useLang(); @@ -58,6 +59,7 @@ const LeaveGroupModal = ({ const isOpen = Boolean(modal); const renderingChat = useCurrentOrPrev(chat); + const isBasicGroup = renderingChat && isChatBasicGroup(renderingChat); const renderingCurrentUser = useCurrentOrPrev(currentUser); useEffect(() => { @@ -171,15 +173,17 @@ const LeaveGroupModal = ({ const chatId = modal?.chatId; if (!chatId) return; + const leaveAction = isBasicGroup ? leaveBasicGroup : leaveChannel; + if (isOwnerChanged && newOwnerId) { transferChatOwnership({ chatId, userId: newOwnerId, password, - onSuccess: () => leaveChannel({ chatId, shouldSkipOwnershipCheck: true }), + onSuccess: () => leaveAction({ chatId, shouldSkipOwnershipCheck: true }), }); } else { - leaveChannel({ chatId, shouldSkipOwnershipCheck: true }); + leaveAction({ chatId, shouldSkipOwnershipCheck: true }); } closeLeaveGroupModal(); }); @@ -220,7 +224,7 @@ const LeaveGroupModal = ({ )}

{lang('LeaveGroupTitle', { group: chatTitle })}

- {lang('LeaveGroupDescription', { + {lang(isBasicGroup ? 'LeaveBasicGroupDescription' : 'LeaveGroupDescription', { nextOwner: newOwnerName, group: chatTitle, }, { diff --git a/src/global/actions/api/chats.ts b/src/global/actions/api/chats.ts index 1c97620be..5b1f0d4a6 100644 --- a/src/global/actions/api/chats.ts +++ b/src/global/actions/api/chats.ts @@ -57,7 +57,7 @@ import { isUserBot, } from '../../helpers'; import { - addActionHandler, getGlobal, setGlobal, + addActionHandler, getActions, getGlobal, setGlobal, } from '../../index'; import { addChatListIds, @@ -888,6 +888,56 @@ addActionHandler('deleteChat', (global, actions, payload): ActionReturnType => { void callApi('deleteChat', { chatId: chat.id }); }); +async function checkFutureCreatorAndOpenModal( + chat: ApiChat, + shouldSkipOwnershipCheck: boolean | undefined, + tabId: number, +): Promise { + if (shouldSkipOwnershipCheck || !chat.isCreator) { + return false; + } + + const futureCreator = await callApi('fetchFutureCreatorAfterLeave', { chat }); + if (!futureCreator) { + return false; + } + + const global = getGlobal(); + const hasPassword = global.settings.byKey.hasPassword; + const actions = getActions(); + if (!hasPassword) { + actions.openTwoFaCheckModal({ tabId }); + return true; + } + + actions.openLeaveGroupModal({ chatId: chat.id, nextOwnerId: futureCreator.id, tabId }); + return true; +} + +function cleanupAfterLeave(chatId: string, tabId: number) { + let global = getGlobal(); + global = leaveChat(global, chatId); + setGlobal(global); + + if (selectCurrentMessageList(global, tabId)?.chatId === chatId) { + getActions().openChat({ id: undefined, tabId }); + } + + global = getGlobal(); + const chatMessages = selectChatMessages(global, chatId); + if (!chatMessages) { + return; + } + + const localMessageIds = Object.keys(chatMessages).map(Number).filter(isLocalMessageId); + if (!localMessageIds.length) { + return; + } + + global = deleteChatMessages(global, chatId, localMessageIds); + setGlobal(global); +} + addActionHandler('leaveChannel', async (global, actions, payload): Promise => { const { chatId, shouldSkipOwnershipCheck, tabId = getCurrentTabId() } = payload; const chat = selectChat(global, chatId); @@ -895,35 +945,39 @@ addActionHandler('leaveChannel', async (global, actions, payload): Promise return; } - if (!shouldSkipOwnershipCheck && chat.isCreator) { - const futureCreator = await callApi('fetchFutureCreatorAfterLeave', { chat }); - if (futureCreator) { - global = getGlobal(); - const hasPassword = global.settings.byKey.hasPassword; - if (!hasPassword) { - actions.openTwoFaCheckModal({ tabId }); - return; - } - - actions.openLeaveGroupModal({ chatId, nextOwnerId: futureCreator.id, tabId }); - return; - } + const isModalOpen = await checkFutureCreatorAndOpenModal(chat, shouldSkipOwnershipCheck, tabId); + if (isModalOpen) { + return; } global = getGlobal(); - global = leaveChat(global, chatId); - setGlobal(global); - - if (selectCurrentMessageList(global, tabId)?.chatId === chatId) { - actions.openChat({ id: undefined, tabId }); - } - await callApi('leaveChannel', { chat }); + + cleanupAfterLeave(chatId, tabId); +}); + +addActionHandler('leaveBasicGroup', async (global, actions, payload): Promise => { + const { chatId, shouldSkipOwnershipCheck, tabId = getCurrentTabId() } = payload; + const chat = selectChat(global, chatId); + const currentUserId = global.currentUserId; + if (!chat || !currentUserId) { + return; + } + + const isModalOpen = await checkFutureCreatorAndOpenModal(chat, shouldSkipOwnershipCheck, tabId); + if (isModalOpen) { + return; + } + + actions.deleteHistory({ chatId, shouldDeleteForAll: false, tabId }); + global = getGlobal(); - const chatMessages = selectChatMessages(global, chatId); - const localMessageIds = Object.keys(chatMessages).map(Number).filter(isLocalMessageId); - global = deleteChatMessages(global, chatId, localMessageIds); - setGlobal(global); + const user = selectUser(global, currentUserId); + if (user) { + await callApi('deleteChatUser', { chat, user, shouldRevokeHistory: false }); + } + + cleanupAfterLeave(chatId, tabId); }); addActionHandler('verifyTransferOwnership', async (global, actions, payload): Promise => { @@ -973,8 +1027,7 @@ addActionHandler('transferChatOwnership', async (global, actions, payload): Prom const chat = selectChat(global, chatId); const user = selectUser(global, userId); - - if (!chat || !user) { + if (!chat || !user?.accessHash) { return; } diff --git a/src/global/types/actions.ts b/src/global/types/actions.ts index fb8af6fd8..d2e2c76aa 100644 --- a/src/global/types/actions.ts +++ b/src/global/types/actions.ts @@ -405,6 +405,10 @@ export interface ActionPayloads { chatId: string; shouldSkipOwnershipCheck?: boolean; } & WithTabId; + leaveBasicGroup: { + chatId: string; + shouldSkipOwnershipCheck?: boolean; + } & WithTabId; deleteChannel: { chatId: string } & WithTabId; toggleChatPinned: { id: string; diff --git a/src/types/language.d.ts b/src/types/language.d.ts index 6bad2cca3..83a2d6e3e 100644 --- a/src/types/language.d.ts +++ b/src/types/language.d.ts @@ -3493,6 +3493,10 @@ export interface LangPairWithVariables { 'nextOwner': V; 'group': V; }; + 'LeaveBasicGroupDescription': { + 'nextOwner': V; + 'group': V; + }; 'LeaveGroupJoinedDate': { 'date': V; };