Manage Discussion: Fix creating new linked chat in channels (#5958)
Co-authored-by: zubiden <19638254+zubiden@users.noreply.github.com>
This commit is contained in:
parent
9bdd213f8f
commit
f2d14ca78f
@ -347,14 +347,27 @@ export async function fetchSavedChats({
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchFullChat(chat: ApiChat) {
|
||||
const fullChatRequestDedupe = new Map<string, Promise<FullChatData | undefined>>();
|
||||
export async function fetchFullChat(chat: ApiChat) {
|
||||
const { id } = chat;
|
||||
|
||||
if (fullChatRequestDedupe.has(id)) {
|
||||
return fullChatRequestDedupe.get(id);
|
||||
}
|
||||
|
||||
const type = getEntityTypeById(chat.id);
|
||||
|
||||
return type === 'channel'
|
||||
const promise = type === 'channel'
|
||||
? getFullChannelInfo(chat)
|
||||
: getFullChatInfo(id);
|
||||
|
||||
fullChatRequestDedupe.set(id, promise);
|
||||
|
||||
promise.finally(() => {
|
||||
fullChatRequestDedupe.delete(id);
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
export async function fetchPeerSettings(peer: ApiPeer) {
|
||||
@ -801,14 +814,15 @@ export function updateTopicMutedState({
|
||||
}
|
||||
|
||||
export async function createChannel({
|
||||
title, about = '', users,
|
||||
title, about = '', users, isBroadcast, isMegagroup,
|
||||
}: {
|
||||
title: string; about?: string; users?: ApiUser[];
|
||||
title: string; about?: string; users?: ApiUser[]; isBroadcast?: true; isMegagroup?: true;
|
||||
}) {
|
||||
const result = await invokeRequest(new GramJs.channels.CreateChannel({
|
||||
broadcast: true,
|
||||
broadcast: isBroadcast,
|
||||
title,
|
||||
about,
|
||||
megagroup: isMegagroup,
|
||||
}), {
|
||||
shouldThrow: true,
|
||||
});
|
||||
|
||||
@ -1083,6 +1083,7 @@
|
||||
"ChannelPersmissionDeniedSendMessagesForever" = "The admins of this group have restricted your ability to send messages.";
|
||||
"ChannelPersmissionDeniedSendMessagesDefaultRestrictedText" = "Sending messages is not allowed in this group.";
|
||||
"Chats" = "Chats";
|
||||
"NewDiscussionChatTitle" = "{name} Chat";
|
||||
"FilterBots" = "Bots";
|
||||
"FilterContacts" = "Contacts";
|
||||
"FilterNonContacts" = "Non-Contacts";
|
||||
@ -1750,6 +1751,7 @@
|
||||
"ActionChangedTitleYou" = "You changed group name to «{title}»";
|
||||
"ActionChangedTitleChannel" = "Channel name was changed to «{title}»";
|
||||
"ActionCreatedChat" = "{from} created the group «{title}»";
|
||||
"ActionCreatedChatYou" = "You created the group «{title}»";
|
||||
"ActionCreatedChannel" = "Channel created";
|
||||
"ActionGameScore_one" = "{from} scored {count} in {game}";
|
||||
"ActionGameScore_other" = "{from} scored {count} in {game}";
|
||||
|
||||
@ -76,7 +76,6 @@ const ContactList: FC<OwnProps & StateProps> = ({
|
||||
<ListItem
|
||||
key={id}
|
||||
className="chat-item-clickable contact-list-item"
|
||||
|
||||
onClick={() => handleClick(id)}
|
||||
>
|
||||
<PrivateChatInfo
|
||||
|
||||
@ -132,6 +132,7 @@ const NewChatStep2: FC<OwnProps & StateProps> = ({
|
||||
about,
|
||||
photo,
|
||||
memberIds,
|
||||
isChannel: true,
|
||||
});
|
||||
}, [title, createChannel, about, photo, memberIds, channelTitleEmptyError]);
|
||||
|
||||
|
||||
@ -182,6 +182,9 @@ const RightColumn: FC<OwnProps & StateProps> = ({
|
||||
setSelectedChatMemberId(undefined);
|
||||
setIsPromotedByCurrentUser(undefined);
|
||||
break;
|
||||
case ManagementScreens.NewDiscussionGroup:
|
||||
setManagementScreen(ManagementScreens.Discussion);
|
||||
break;
|
||||
case ManagementScreens.ChatAdminRights:
|
||||
case ManagementScreens.ChatNewAdminRights:
|
||||
case ManagementScreens.GroupAddAdmins:
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
.title {
|
||||
margin-bottom: 0;
|
||||
margin-left: 1.375rem;
|
||||
margin-left: 1.1875rem;
|
||||
font-size: 1.25rem;
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
@ -136,6 +136,7 @@ enum HeaderContent {
|
||||
CreateTopic,
|
||||
EditTopic,
|
||||
SavedDialogs,
|
||||
NewDiscussionGroup,
|
||||
}
|
||||
|
||||
const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
@ -318,6 +319,8 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
HeaderContent.ManageInviteInfo
|
||||
) : managementScreen === ManagementScreens.JoinRequests ? (
|
||||
HeaderContent.ManageJoinRequests
|
||||
) : managementScreen === ManagementScreens.NewDiscussionGroup ? (
|
||||
HeaderContent.NewDiscussionGroup
|
||||
) : undefined // Never reached
|
||||
) : isStatistics ? (
|
||||
HeaderContent.Statistics
|
||||
@ -588,6 +591,8 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
)}
|
||||
</>
|
||||
);
|
||||
case HeaderContent.NewDiscussionGroup:
|
||||
return <h3 className="title">{oldLang('NewGroup')}</h3>;
|
||||
default:
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -15,6 +15,7 @@ import renderText from '../../common/helpers/renderText';
|
||||
|
||||
import useFlag from '../../../hooks/useFlag';
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
import useLastCallback from '../../../hooks/useLastCallback.ts';
|
||||
import useOldLang from '../../../hooks/useOldLang';
|
||||
|
||||
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
||||
@ -198,6 +199,10 @@ const ManageDiscussion: FC<OwnProps & StateProps> = ({
|
||||
);
|
||||
}
|
||||
|
||||
const handleNewGroupClick = useLastCallback(() => {
|
||||
onScreenSelect(ManagementScreens.NewDiscussionGroup);
|
||||
});
|
||||
|
||||
function renderDiscussionGroups() {
|
||||
return (
|
||||
<div>
|
||||
@ -208,8 +213,10 @@ const ManageDiscussion: FC<OwnProps & StateProps> = ({
|
||||
key="create-group"
|
||||
icon="group"
|
||||
ripple
|
||||
className="create-item"
|
||||
withPrimaryColor
|
||||
teactOrderKey={0}
|
||||
disabled
|
||||
onClick={handleNewGroupClick}
|
||||
>
|
||||
{lang('DiscussionCreateGroup')}
|
||||
</ListItem>
|
||||
@ -219,7 +226,6 @@ const ManageDiscussion: FC<OwnProps & StateProps> = ({
|
||||
key={id}
|
||||
teactOrderKey={i + 1}
|
||||
className="chat-item-clickable scroll-item"
|
||||
|
||||
onClick={() => {
|
||||
onDiscussionClick(id);
|
||||
}}
|
||||
|
||||
@ -294,7 +294,7 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
||||
</div>
|
||||
)}
|
||||
<div className="section" teactFastList>
|
||||
<ListItem icon="add" withPrimaryColor key="create" className="create-link" onClick={handleCreateNewClick}>
|
||||
<ListItem icon="add" withPrimaryColor key="create" className="create-item" onClick={handleCreateNewClick}>
|
||||
{oldLang('CreateNewLink')}
|
||||
</ListItem>
|
||||
{(!temporalInvites || !temporalInvites.length) && <NothingFound text="No links found" key="nothing" />}
|
||||
|
||||
@ -162,6 +162,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.create-item {
|
||||
.icon-group {
|
||||
margin-inline-start: 0.1875rem;
|
||||
margin-inline-end: 1.1875rem;
|
||||
}
|
||||
}
|
||||
|
||||
.Spinner {
|
||||
margin: 2rem auto;
|
||||
}
|
||||
@ -172,7 +179,7 @@
|
||||
}
|
||||
|
||||
.ManageInvites {
|
||||
.create-link {
|
||||
.create-item {
|
||||
margin-bottom: 0.5rem;
|
||||
.icon-add {
|
||||
margin-inline-start: 0.1875rem;
|
||||
|
||||
@ -25,6 +25,7 @@ import ManageInvites from './ManageInvites';
|
||||
import ManageJoinRequests from './ManageJoinRequests';
|
||||
import ManageReactions from './ManageReactions';
|
||||
import ManageUser from './ManageUser';
|
||||
import NewDiscussionGroup from './NewDiscussionGroup.tsx';
|
||||
|
||||
export type OwnProps = {
|
||||
chatId: string;
|
||||
@ -202,6 +203,16 @@ const Management: FC<OwnProps & StateProps> = ({
|
||||
/>
|
||||
);
|
||||
|
||||
case ManagementScreens.NewDiscussionGroup:
|
||||
return (
|
||||
<NewDiscussionGroup
|
||||
chatId={chatId}
|
||||
onScreenSelect={onScreenSelect}
|
||||
isActive={isActive}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
|
||||
case ManagementScreens.ChatNewAdminRights:
|
||||
case ManagementScreens.ChatAdminRights:
|
||||
return (
|
||||
|
||||
142
src/components/right/management/NewDiscussionGroup.tsx
Normal file
142
src/components/right/management/NewDiscussionGroup.tsx
Normal file
@ -0,0 +1,142 @@
|
||||
import type { FC } from '../../../lib/teact/teact.ts';
|
||||
import { useState } from '../../../lib/teact/teact.ts';
|
||||
import React, { memo } from '../../../lib/teact/teact.ts';
|
||||
|
||||
import type { ApiChat } from '../../../api/types/index.ts';
|
||||
import type { ManagementScreens } from '../../../types/index.ts';
|
||||
import { ChatCreationProgress } from '../../../types/index.ts';
|
||||
|
||||
import { getActions, withGlobal } from '../../../global/index.ts';
|
||||
import { selectChat, selectTabState } from '../../../global/selectors/index.ts';
|
||||
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack.ts';
|
||||
import useLang from '../../../hooks/useLang.ts';
|
||||
import useLastCallback from '../../../hooks/useLastCallback.ts';
|
||||
|
||||
import Icon from '../../common/icons/Icon.tsx';
|
||||
import AvatarEditable from '../../ui/AvatarEditable.tsx';
|
||||
import FloatingActionButton from '../../ui/FloatingActionButton.tsx';
|
||||
import InputText from '../../ui/InputText.tsx';
|
||||
import Spinner from '../../ui/Spinner.tsx';
|
||||
|
||||
type OwnProps = {
|
||||
chatId: string;
|
||||
isActive: boolean;
|
||||
onScreenSelect: (screen: ManagementScreens) => void;
|
||||
onClose: NoneToVoidFunction;
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
chat?: ApiChat;
|
||||
creationProgress?: ChatCreationProgress;
|
||||
creationError?: string;
|
||||
};
|
||||
|
||||
const NewDiscussionGroup: FC<OwnProps & StateProps> = ({
|
||||
chat,
|
||||
onClose,
|
||||
isActive,
|
||||
creationProgress,
|
||||
creationError,
|
||||
}) => {
|
||||
const { createChannel } = getActions();
|
||||
const lang = useLang();
|
||||
|
||||
useHistoryBack({
|
||||
isActive,
|
||||
onBack: onClose,
|
||||
});
|
||||
|
||||
const [title, setTitle] = useState(lang('NewDiscussionChatTitle', { name: chat?.title }));
|
||||
const [photo, setPhoto] = useState<File | undefined>();
|
||||
const [error, setError] = useState<string | undefined>();
|
||||
|
||||
const isLoading = creationProgress === ChatCreationProgress.InProgress;
|
||||
|
||||
const handleTitleChange = useLastCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { value } = e.currentTarget;
|
||||
const newValue = value.trimStart();
|
||||
|
||||
setTitle(newValue);
|
||||
|
||||
if (newValue !== value) {
|
||||
e.currentTarget.value = newValue;
|
||||
}
|
||||
});
|
||||
|
||||
const renderedError = (creationError && lang('NewChatTitleEmptyError')) || (
|
||||
error !== lang('NewChatTitleEmptyError') && error !== lang('NewChannelTitleEmptyError')
|
||||
? error
|
||||
: undefined
|
||||
);
|
||||
|
||||
const handleCreateGroup = useLastCallback(() => {
|
||||
if (!title.length) {
|
||||
setError(lang('NewChatTitleEmptyError'));
|
||||
return;
|
||||
}
|
||||
if (!chat) return;
|
||||
|
||||
createChannel({
|
||||
discussionChannelId: chat.id,
|
||||
title,
|
||||
photo,
|
||||
isSuperGroup: true,
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="Management">
|
||||
<div className="panel-content custom-scroll">
|
||||
<div className="NewChat">
|
||||
<div className="NewChat-inner step-2">
|
||||
<AvatarEditable
|
||||
onChange={setPhoto}
|
||||
title={lang('AddPhoto')}
|
||||
/>
|
||||
<InputText
|
||||
value={title}
|
||||
onChange={handleTitleChange}
|
||||
label={lang('GroupName')}
|
||||
error={error === lang('NewChatTitleEmptyError')
|
||||
|| error === lang('NewChannelTitleEmptyError') ? error : undefined}
|
||||
/>
|
||||
|
||||
{renderedError && (
|
||||
<p className="error">{renderedError}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<FloatingActionButton
|
||||
isShown={title.length !== 0}
|
||||
onClick={handleCreateGroup}
|
||||
disabled={isLoading}
|
||||
ariaLabel={lang('DiscussionCreateGroup')}
|
||||
>
|
||||
{isLoading ? (
|
||||
<Spinner color="white" />
|
||||
) : (
|
||||
<Icon name="arrow-right" />
|
||||
)}
|
||||
</FloatingActionButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global, { chatId }): StateProps => {
|
||||
const {
|
||||
progress: creationProgress,
|
||||
error: creationError,
|
||||
} = selectTabState(global).chatCreation || {};
|
||||
const chat = selectChat(global, chatId);
|
||||
|
||||
return {
|
||||
chat,
|
||||
creationProgress,
|
||||
creationError,
|
||||
};
|
||||
},
|
||||
)(NewDiscussionGroup));
|
||||
@ -674,11 +674,12 @@ addActionHandler('updateTopicMutedState', (global, actions, payload): ActionRetu
|
||||
|
||||
addActionHandler('createChannel', async (global, actions, payload): Promise<void> => {
|
||||
const {
|
||||
title, about, photo, memberIds, tabId = getCurrentTabId(),
|
||||
title, about, photo, memberIds, discussionChannelId, tabId = getCurrentTabId(),
|
||||
} = payload;
|
||||
const isChannel = 'isChannel' in payload ? payload.isChannel : undefined;
|
||||
const isSuperGroup = 'isSuperGroup' in payload ? payload.isSuperGroup : undefined;
|
||||
|
||||
const users = (memberIds)
|
||||
.map((id) => selectUser(global, id))
|
||||
const users = memberIds?.map((id) => selectUser(global, id))
|
||||
.filter(Boolean);
|
||||
|
||||
global = updateTabState(global, {
|
||||
@ -691,7 +692,13 @@ addActionHandler('createChannel', async (global, actions, payload): Promise<void
|
||||
let createdChannel: ApiChat | undefined;
|
||||
let missingInvitedUsers: ApiMissingInvitedUser[] | undefined;
|
||||
try {
|
||||
const result = await callApi('createChannel', { title, about, users });
|
||||
const result = await callApi('createChannel', {
|
||||
title,
|
||||
about,
|
||||
users,
|
||||
isBroadcast: isChannel,
|
||||
isMegagroup: isSuperGroup,
|
||||
});
|
||||
createdChannel = result?.channel;
|
||||
missingInvitedUsers = result?.missingUsers;
|
||||
} catch (error) {
|
||||
@ -727,6 +734,13 @@ addActionHandler('createChannel', async (global, actions, payload): Promise<void
|
||||
},
|
||||
}, tabId);
|
||||
setGlobal(global);
|
||||
if (discussionChannelId && channelId) {
|
||||
actions.linkDiscussionGroup({
|
||||
channelId: discussionChannelId,
|
||||
chatId: channelId,
|
||||
tabId,
|
||||
});
|
||||
}
|
||||
actions.openChat({ id: channelId, shouldReplaceHistory: true, tabId });
|
||||
|
||||
if (missingInvitedUsers) {
|
||||
|
||||
@ -685,8 +685,11 @@ export interface ActionPayloads {
|
||||
title: string;
|
||||
about?: string;
|
||||
photo?: File;
|
||||
memberIds: string[];
|
||||
} & WithTabId;
|
||||
memberIds?: string[];
|
||||
discussionChannelId?: string;
|
||||
} & (
|
||||
{ isChannel: true } | { isSuperGroup: true }
|
||||
) & WithTabId;
|
||||
createGroupChat: {
|
||||
title: string;
|
||||
memberIds: string[];
|
||||
|
||||
@ -331,6 +331,7 @@ export enum RightColumnContent {
|
||||
CreateTopic,
|
||||
EditTopic,
|
||||
MonetizationStatistics,
|
||||
NewGroup,
|
||||
}
|
||||
|
||||
export type MediaViewerMedia = ApiPhoto | ApiVideo | ApiDocument;
|
||||
@ -502,6 +503,7 @@ export enum ManagementScreens {
|
||||
Reactions,
|
||||
InviteInfo,
|
||||
JoinRequests,
|
||||
NewDiscussionGroup,
|
||||
}
|
||||
|
||||
export type ManagementType = 'user' | 'group' | 'channel' | 'bot';
|
||||
|
||||
3
src/types/language.d.ts
vendored
3
src/types/language.d.ts
vendored
@ -2483,6 +2483,9 @@ export interface LangPairWithVariables<V = LangVariable> {
|
||||
'ComposerTitleForwardFrom': {
|
||||
'users': V;
|
||||
};
|
||||
'NewDiscussionChatTitle': {
|
||||
'name': V;
|
||||
}
|
||||
}
|
||||
|
||||
export interface LangPairPlural {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user