Forum: Saving the state of the "View as Messages" option (#4091)

This commit is contained in:
Alexander Zinchuk 2023-12-12 12:34:38 +01:00
parent 2edd680802
commit 93aa65cd72
19 changed files with 120 additions and 16 deletions

View File

@ -96,7 +96,7 @@ export function buildApiChatFromDialog(
const {
peer, folderId, unreadMark, unreadCount, unreadMentionsCount, unreadReactionsCount,
notifySettings: { silent, muteUntil },
readOutboxMaxId, readInboxMaxId, draft,
readOutboxMaxId, readInboxMaxId, draft, viewForumAsMessages,
} = dialog;
const isMuted = silent || (typeof muteUntil === 'number' && getServerTime() < muteUntil);
@ -114,6 +114,7 @@ export function buildApiChatFromDialog(
muteUntil,
...(unreadMark && { hasUnreadMark: true }),
...(draft instanceof GramJs.DraftMessage && { draftDate: draft.date }),
...(viewForumAsMessages && { isForumAsMessages: true }),
...buildApiChatFieldsFromPeerEntity(peerEntity),
};
}

View File

@ -81,6 +81,7 @@ type FullChatData = {
userStatusesById: { [userId: string]: ApiUserStatus };
groupCall?: Partial<ApiGroupCall>;
membersCount?: number;
isForumAsMessages?: true;
};
let onUpdate: OnApiUpdate;
@ -483,6 +484,7 @@ async function getFullChannelInfo(
participantsHidden,
translationsDisabled,
storiesPinnedAvailable,
viewForumAsMessages,
} = result.fullChat;
if (chatPhoto instanceof GramJs.Photo) {
@ -572,6 +574,7 @@ async function getFullChannelInfo(
connectionState: 'disconnected',
} : undefined,
membersCount: participantsCount,
...(viewForumAsMessages && { isForumAsMessages: true }),
};
}
@ -1880,6 +1883,18 @@ export function togglePeerTranslations({
}));
}
export function setViewForumAsMessages({ chat, isEnabled }: { chat: ApiChat; isEnabled: boolean }) {
const { id, accessHash } = chat;
const channel = buildInputEntity(id, accessHash);
return invokeRequest(new GramJs.channels.ToggleViewForumAsMessages({
channel: channel as GramJs.InputChannel,
enabled: Boolean(isEnabled),
}), {
shouldReturnTrue: true,
});
}
function handleUserPrivacyRestrictedUpdates(updates: GramJs.TypeUpdates) {
if (!(updates instanceof GramJs.Updates) && !(updates instanceof GramJs.UpdatesCombined)) {
return undefined;

View File

@ -23,7 +23,7 @@ export {
getChatByPhoneNumber, toggleJoinToSend, toggleJoinRequest, fetchTopics, deleteTopic, togglePinnedTopic,
editTopic, toggleForum, fetchTopicById, createTopic, toggleParticipantsHidden, checkChatlistInvite,
joinChatlistInvite, createChalistInvite, editChatlistInvite, deleteChatlistInvite, fetchChatlistInvites,
fetchLeaveChatlistSuggestions, leaveChatlist, togglePeerTranslations,
fetchLeaveChatlistSuggestions, leaveChatlist, togglePeerTranslations, setViewForumAsMessages,
} from './chats';
export {

View File

@ -1131,6 +1131,12 @@ export function updater(update: Update) {
location: update.location,
isUnconfirmed: update.unconfirmed,
});
} else if (update instanceof GramJs.UpdateChannelViewForumAsMessages) {
onUpdate({
'@type': 'updateViewForumAsMessages',
chatId: buildApiPeerId(update.channelId, 'channel'),
isEnabled: update.enabled ? true : undefined,
});
} else if (DEBUG) {
const params = typeof update === 'object' && 'className' in update ? update.className : update;
log('UNEXPECTED UPDATE', params);

View File

@ -44,6 +44,7 @@ export interface ApiChat {
fakeType?: ApiFakeType;
color?: ApiPeerColor;
isForum?: boolean;
isForumAsMessages?: true;
topics?: Record<number, ApiTopic>;
listedTopicIds?: number[];
topicsCount?: number;

View File

@ -609,6 +609,12 @@ export type ApiUpdateTopics = {
chatId: string;
};
export type ApiUpdateViewForumAsMessages = {
'@type': 'updateViewForumAsMessages';
chatId: string;
isEnabled?: true;
};
export type ApiUpdateMessageTranslations = {
'@type': 'updateMessageTranslations';
chatId: string;
@ -706,7 +712,8 @@ export type ApiUpdate = (
ApiUpdatePinnedTopicsOrder | ApiUpdateTopic | ApiUpdateTopics | ApiUpdateRecentEmojiStatuses |
ApiUpdateRecentReactions | ApiUpdateStory | ApiUpdateReadStories | ApiUpdateDeleteStory | ApiUpdateSentStoryReaction |
ApiRequestReconnectApi | ApiRequestSync | ApiUpdateFetchingDifference | ApiUpdateChannelMessages |
ApiUpdateStealthMode | ApiUpdateAttachMenuBots | ApiUpdateNewAuthorization | ApiUpdateGroupInvitePrivacyForbidden
ApiUpdateStealthMode | ApiUpdateAttachMenuBots | ApiUpdateNewAuthorization | ApiUpdateGroupInvitePrivacyForbidden |
ApiUpdateViewForumAsMessages
);
export type OnApiUpdate = (update: ApiUpdate) => void;

View File

@ -62,7 +62,7 @@ const ChatForumLastMessage: FC<OwnProps> = ({
handleClick: handleOpenTopicClick,
handleMouseDown: handleOpenTopicMouseDown,
} = useFastClick((e: React.MouseEvent<HTMLDivElement>) => {
if (lastActiveTopic.unreadCount === 0) return;
if (lastActiveTopic.unreadCount === 0 || chat.isForumAsMessages) return;
e.stopPropagation();
e.preventDefault();

View File

@ -22,6 +22,7 @@ import type {
ApiReaction,
ApiStealthMode,
ApiSticker,
ApiTopic,
ApiUser,
ApiVideo,
} from '../../api/types';
@ -30,6 +31,7 @@ import type {
MessageListType, TabState,
} from '../../global/types';
import type { IAnchorPosition, InlineBotSettings, ISettings } from '../../types';
import { MAIN_THREAD_ID } from '../../api/types';
import {
BASE_EMOJI_KEYWORD_LANG,
@ -75,6 +77,7 @@ import {
selectScheduledIds,
selectTabState,
selectTheme,
selectTopicFromMessage,
selectUser,
selectUserFullInfo,
} from '../../global/selectors';
@ -184,6 +187,7 @@ type StateProps =
editingMessage?: ApiMessage;
chat?: ApiChat;
draft?: ApiDraft;
replyToTopic?: ApiTopic;
currentMessageList?: MessageList;
isChatWithBot?: boolean;
isChatWithSelf?: boolean;
@ -282,6 +286,7 @@ const Composer: FC<OwnProps & StateProps> = ({
messageListType,
draft,
chat,
replyToTopic,
isForCurrentMessageList,
isCurrentUserPremium,
canSendVoiceByPrivacy,
@ -1292,6 +1297,11 @@ const Composer: FC<OwnProps & StateProps> = ({
|| isCustomSendMenuOpen || Boolean(activeVoiceRecording) || attachments.length > 0 || isInputHasFocus;
const isReactionSelectorOpen = isComposerHasFocus && !isReactionPickerOpen && isInStoryViewer && !isAttachMenuOpen
&& !isSymbolMenuOpen;
const placeholderForForumAsMessages = chat?.isForum && chat?.isForumAsMessages && threadId === MAIN_THREAD_ID
? (replyToTopic
? lang('Chat.InputPlaceholderReplyInTopic', replyToTopic.title)
: lang('Message.Placeholder.MessageInGeneral'))
: undefined;
useEffect(() => {
if (isComposerHasFocus) {
@ -1680,7 +1690,7 @@ const Composer: FC<OwnProps & StateProps> = ({
activeVoiceRecording && windowWidth <= SCREEN_WIDTH_TO_HIDE_PLACEHOLDER
? ''
: (!isComposerBlocked
? (botKeyboardPlaceholder || inputPlaceholder || lang('Message'))
? (botKeyboardPlaceholder || inputPlaceholder || lang(placeholderForForumAsMessages || 'Message'))
: lang('Chat.PlaceholderTextNotAllowed'))
}
timedPlaceholderDate={timedPlaceholderDate}
@ -1932,13 +1942,20 @@ export default memo(withGlobal<OwnProps>(
const story = storyId && selectPeerStory(global, chatId, storyId);
const sentStoryReaction = story && 'sentReaction' in story ? story.sentReaction : undefined;
const draft = selectDraft(global, chatId, threadId);
const replyToMessage = draft?.replyInfo
? selectChatMessage(global, chatId, draft.replyInfo.replyToMsgId)
: undefined;
const replyToTopic = chat?.isForum && chat.isForumAsMessages && threadId === MAIN_THREAD_ID && replyToMessage
? selectTopicFromMessage(global, replyToMessage)
: undefined;
return {
availableReactions: type === 'story' ? global.availableReactions : undefined,
topReactions: type === 'story' ? global.topReactions : undefined,
isOnActiveTab: !tabState.isBlurred,
editingMessage: selectEditingMessage(global, chatId, threadId, messageListType),
draft: selectDraft(global, chatId, threadId),
draft,
chat,
isChatWithBot,
isChatWithSelf,
@ -1996,6 +2013,7 @@ export default memo(withGlobal<OwnProps>(
shouldCollectDebugLogs: global.settings.byKey.shouldCollectDebugLogs,
sentStoryReaction,
stealthMode: global.stories.stealthMode,
replyToTopic,
};
},
)(Composer));

View File

@ -173,12 +173,13 @@ const EmbeddedMessage: FC<OwnProps> = ({
}
const isChatSender = senderChat?.id === sender?.id;
const isReplyToQuote = isInComposer && Boolean(replyInfo && 'quoteText' in replyInfo && replyInfo?.quoteText);
return (
<>
{!isChatSender && (
<span className="embedded-sender">
{renderText(isInComposer ? lang('ReplyToQuote', senderTitle) : senderTitle)}
{renderText(isReplyToQuote ? lang('ReplyToQuote', senderTitle) : senderTitle)}
</span>
)}
{icon && <Icon name={icon} className="embedded-chat-icon" />}

View File

@ -147,7 +147,7 @@ const Chat: FC<OwnProps & StateProps> = ({
const [shouldRenderChatFolderModal, markRenderChatFolderModal, unmarkRenderChatFolderModal] = useFlag();
const [shouldRenderReportModal, markRenderReportModal, unmarkRenderReportModal] = useFlag();
const { lastMessage, isForum } = chat || {};
const { lastMessage, isForum, isForumAsMessages } = chat || {};
const { renderSubtitle, ref } = useChatListEntry({
chat,
@ -169,17 +169,23 @@ const Chat: FC<OwnProps & StateProps> = ({
const getIsForumPanelClosed = useSelectorSignal(selectIsForumPanelClosed);
const handleClick = useLastCallback(() => {
const noForumTopicPanel = isMobile && isForumAsMessages;
if (isForum) {
if (isForumPanelOpen) {
closeForumPanel(undefined, { forceOnHeavyAnimation: true });
} else {
openForumPanel({ chatId }, { forceOnHeavyAnimation: true });
}
return;
return;
} else {
if (!noForumTopicPanel) {
openForumPanel({ chatId }, { forceOnHeavyAnimation: true });
}
if (!isForumAsMessages) return;
}
}
openChat({ id: chatId, shouldReplaceHistory: true }, { forceOnHeavyAnimation: true });
openChat({ id: chatId, noForumTopicPanel, shouldReplaceHistory: true }, { forceOnHeavyAnimation: true });
if (isSelected && canScrollDown) {
focusLastMessage();

View File

@ -92,7 +92,12 @@ const Topic: FC<OwnProps & StateProps> = ({
draft,
wasTopicOpened,
}) => {
const { openThread, deleteTopic, focusLastMessage } = getActions();
const {
openThread,
deleteTopic,
focusLastMessage,
setViewForumAsMessages,
} = getActions();
const lang = useLang();
@ -141,6 +146,7 @@ const Topic: FC<OwnProps & StateProps> = ({
const handleOpenTopic = useLastCallback(() => {
openThread({ chatId, threadId: topic.id, shouldReplaceHistory: true });
setViewForumAsMessages({ chatId, isEnabled: false });
if (canScrollDown) {
focusLastMessage();

View File

@ -130,6 +130,7 @@ const HeaderActions: FC<OwnProps & StateProps> = ({
openChatLanguageModal,
setSettingOption,
unblockUser,
setViewForumAsMessages,
} = getActions();
// eslint-disable-next-line no-null/no-null
const menuButtonRef = useRef<HTMLButtonElement>(null);
@ -209,6 +210,7 @@ const HeaderActions: FC<OwnProps & StateProps> = ({
const handleAsMessagesClick = useLastCallback(() => {
openChat({ id: chatId });
setViewForumAsMessages({ chatId, isEnabled: true });
});
function handleRequestCall() {

View File

@ -104,6 +104,7 @@ type StateProps = {
isMuted?: boolean;
isTopic?: boolean;
isForum?: boolean;
isForumAsMessages?: true;
canAddContact?: boolean;
canReportChat?: boolean;
canDeleteChat?: boolean;
@ -133,6 +134,7 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
withForumActions,
isTopic,
isForum,
isForumAsMessages,
isChatInfoShown,
canStartBot,
canSubscribe,
@ -189,6 +191,7 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
togglePeerTranslations,
blockUser,
unblockUser,
setViewForumAsMessages,
} = getActions();
const { isMobile } = useAppLayout();
@ -280,6 +283,7 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
const handleViewAsTopicsClick = useLastCallback(() => {
openChat({ id: undefined });
setViewForumAsMessages({ chatId, isEnabled: false });
closeMenu();
});
@ -469,7 +473,7 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
<div className="right-badge">{pendingJoinRequests}</div>
</MenuItem>
)}
{withForumActions && !isTopic && (
{withForumActions && !isTopic && !isForumAsMessages && (
<MenuItem
icon="message"
onClick={handleOpenAsMessages}
@ -696,6 +700,7 @@ export default memo(withGlobal<OwnProps>(
isPrivate,
isTopic: chat?.isForum && !isMainThread,
isForum: chat?.isForum,
isForumAsMessages: chat?.isForumAsMessages,
canAddContact,
canReportChat,
canDeleteChat: getCanDeleteChat(chat),

View File

@ -327,6 +327,7 @@ export default memo(withGlobal<OwnProps>(
const draft = selectDraft(global, chatId, threadId);
const replyInfo = draft?.replyInfo;
let message: ApiMessage | undefined;
if (replyInfo && !shouldForceShowEditing) {
message = selectChatMessage(global, replyInfo.replyToPeerId || chatId, replyInfo.replyToMsgId);

View File

@ -2503,6 +2503,20 @@ addActionHandler('togglePeerTranslations', async (global, actions, payload): Pro
setGlobal(global);
});
addActionHandler('setViewForumAsMessages', (global, actions, payload): ActionReturnType => {
const { chatId, isEnabled } = payload;
const chat = selectChat(global, chatId);
if (!chat?.isForum || chat.isForumAsMessages === isEnabled) {
return;
}
global = updateChat(global, chatId, { isForumAsMessages: isEnabled || undefined });
setGlobal(global);
void callApi('setViewForumAsMessages', { chat, isEnabled });
});
async function loadChats(
listType: 'active' | 'archived',
offsetId?: string,
@ -2638,7 +2652,7 @@ export async function loadFullChat<T extends GlobalState>(
}
const {
users, userStatusesById, fullInfo, groupCall, membersCount,
users, userStatusesById, fullInfo, groupCall, membersCount, isForumAsMessages,
} = result;
global = getGlobal();
@ -2664,6 +2678,9 @@ export async function loadFullChat<T extends GlobalState>(
if (membersCount !== undefined) {
global = updateChat(global, chat.id, { membersCount });
}
if (chat.isForum) {
global = updateChat(global, chat.id, { isForumAsMessages });
}
global = replaceChatFullInfo(global, chat.id, fullInfo);
setGlobal(global);

View File

@ -469,6 +469,18 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
return undefined;
}
case 'updateViewForumAsMessages': {
const { chatId, isEnabled } = update;
const chat = selectChat(global, chatId);
if (!chat?.isForum) return undefined;
global = updateChat(global, chatId, {
isForumAsMessages: isEnabled,
});
setGlobal(global);
}
}
return undefined;

View File

@ -2819,6 +2819,10 @@ export interface ActionPayloads {
isMuted?: boolean;
muteUntil?: number;
};
setViewForumAsMessages: {
chatId: string;
isEnabled: boolean;
};
openCreateTopicPanel: {
chatId: string;

View File

@ -1442,6 +1442,7 @@ channels.updatePinnedForumTopic#6c2d9026 channel:InputChannel topic_id:int pinne
channels.deleteTopicHistory#34435f2d channel:InputChannel top_msg_id:int = messages.AffectedHistory;
channels.toggleParticipantsHidden#6a6e7854 channel:InputChannel enabled:Bool = Updates;
channels.clickSponsoredMessage#18afbc93 channel:InputChannel random_id:bytes = Bool;
channels.toggleViewForumAsMessages#9738bb15 channel:InputChannel enabled:Bool = Updates;
bots.canSendMessage#1359f4e6 bot:InputUser = Bool;
bots.allowSendMessage#f132e3ef bot:InputUser = Updates;
bots.invokeWebViewCustomMethod#87fc5e7 bot:InputUser custom_method:string params:DataJSON = DataJSON;

View File

@ -289,6 +289,7 @@
"channels.deleteTopicHistory",
"channels.toggleParticipantsHidden",
"channels.clickSponsoredMessage",
"channels.toggleViewForumAsMessages",
"photos.uploadContactProfilePhoto",
"messages.getMessagesViews",
"chatlists.exportChatlistInvite",