Chat: Fix incorrect draft display (#6192)
This commit is contained in:
parent
77449ad407
commit
b2d58b6cfd
23
CLAUDE.md
23
CLAUDE.md
@ -26,7 +26,7 @@ You are an expert in TypeScript, JavaScript, HTML, SCSS and Teact with deep expe
|
||||
- Use [buildClassName.ts](mdc:src/util/buildClassName.ts) to merge multiple class names.
|
||||
- **Always extract styles to files** - avoid inline styles unless absolutely necessary.
|
||||
- **If file already imports styles**, check where they come from and add new styles there - don't create new style files.
|
||||
- Use rem units for all measurements.
|
||||
- Prefer rem units for all measurements. Exceptions are possible, but usually rare.
|
||||
|
||||
- **Code Style:**
|
||||
- Early returns.
|
||||
@ -34,19 +34,20 @@ You are an expert in TypeScript, JavaScript, HTML, SCSS and Teact with deep expe
|
||||
- Functions should start with a verb (e.q. `openModal`, `closeDialog`, `handleClick`).
|
||||
- Prefer checking required parameter before calling a function, avoid making it optinal and checking at the beginning of the function.
|
||||
- Only leave comments for complex logic.
|
||||
- Do not use `null`. There's linter rule to enforce it.
|
||||
- **IMPORTANT: Avoid conditional spread operators** - TypeScript doesn't check if spread fields match the target type.
|
||||
```typescript
|
||||
// ❌ BAD - No type checking
|
||||
{ ...condition && { field: value } }
|
||||
|
||||
|
||||
// ✅ GOOD - Full type checking
|
||||
{ field: condition ? value : undefined }
|
||||
```
|
||||
- **IMPORTANT: Use string templates for inline styles** - Always use template literals for style prop:
|
||||
- **IMPORTANT: Use string templates for inline styles** - Always use template literals for style prop. Teact does not support object:
|
||||
```typescript
|
||||
// ✅ CORRECT
|
||||
style={`transform: translateX(${value}%)`}
|
||||
|
||||
|
||||
// ❌ WRONG
|
||||
style={{ transform: `translateX(${value}%)` }}
|
||||
style={{ '--custom-prop': value } as React.CSSProperties}
|
||||
@ -56,7 +57,7 @@ You are an expert in TypeScript, JavaScript, HTML, SCSS and Teact with deep expe
|
||||
// ✅ CORRECT
|
||||
font-weight: var(--font-weight-medium);
|
||||
font-weight: var(--font-weight-bold);
|
||||
|
||||
|
||||
// ❌ WRONG
|
||||
font-weight: 600;
|
||||
font-weight: bold;
|
||||
@ -65,7 +66,7 @@ You are an expert in TypeScript, JavaScript, HTML, SCSS and Teact with deep expe
|
||||
- **Localization & Text Rules:**
|
||||
- **ALWAYS** use `lang()` for all text content - never hardcode strings.
|
||||
- `lang()` can accept parameters: `lang('Key', { param: value })`.
|
||||
- Add new translations to end of `src/assets/localization/fallback.strings`.
|
||||
- Add new translations to `src/assets/localization/fallback.strings`.
|
||||
|
||||
- **After your solution:**
|
||||
1. Critique it—identify any shortcomings.
|
||||
@ -158,8 +159,8 @@ addActionHandler('loadUser', async (global, actions, { userId }) => {
|
||||
### 1. Basics & Imports
|
||||
|
||||
* All components use JSX and render with Teact.
|
||||
* **Always** import React from teact library, for JSX compatibility reasons. Only import from `'react'` when you need React **types** that are not provided in Teact.
|
||||
* Built-in hooks live in `src/lib/teact/teact`. Import them from there.
|
||||
* Only import from `'react'` when you need React **types** that are not provided in Teact.
|
||||
* Built-in hooks live in Teact library. Import them from there.
|
||||
|
||||
### 2. Props & Types
|
||||
|
||||
@ -225,12 +226,12 @@ const MAX_ITEMS = 10
|
||||
const Component = ({ id, className, stateValue, onClick }: OwnProps & StateProps) => {
|
||||
const { someAction } = getActions(); // Should always be first, if actions are used
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const ref = useRef<HTMLDivElement>();
|
||||
|
||||
const [color, setColor] = useState('#FF00FF');
|
||||
const [isOpen, open, close] = useFlag();
|
||||
|
||||
const lang = useLang();
|
||||
const lang = useLang(); // Somewhere near the top, after state definition
|
||||
|
||||
const handleClick = useLastCallback(() => {
|
||||
if (!ref.current) return;
|
||||
@ -415,4 +416,4 @@ lang('MarkdownKey', undefined, { withNodes: true, withMarkdown: true });
|
||||
* Flags: `lang.isRtl`, `lang.code`, `lang.rawCode`
|
||||
|
||||
**7. Beyond React**
|
||||
Use `getTranslationFn()` to grab the same `lang` function in non-component code. Discouraged, use object syntax.
|
||||
Use `getTranslationFn()` to grab the same `lang` function in non-component code. Discouraged, use object syntax.
|
||||
|
||||
@ -321,6 +321,7 @@
|
||||
"ArchivedChats" = "Archived Chats";
|
||||
"FilterAddTo" = "Add to folder";
|
||||
"Draft" = "Draft";
|
||||
"ChatDraftPrefix" = "Draft:";
|
||||
"FilterAllChatsShort" = "All";
|
||||
"FilterAllChats" = "All Chats";
|
||||
"CreateNewContact" = "Create New Contact";
|
||||
@ -2024,6 +2025,7 @@
|
||||
"NotificationMessageNotSupportedInFrozenAccount" = "This action is not available";
|
||||
"NotificationGiftIsSale" = "{gift} is now for sale!";
|
||||
"NotificationGiftIsUnlist" = "{gift} is removed from sale.";
|
||||
"NotificationMessageTextHidden" = "New message";
|
||||
"GiftRibbonSale" = "sale";
|
||||
"ButtonBuyGift" = "Buy for {stars}";
|
||||
"GiftInfoBuyGift" = "{user} is selling this gift and you can buy it.";
|
||||
|
||||
@ -260,10 +260,9 @@
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.draft {
|
||||
&::after {
|
||||
content: ": ";
|
||||
}
|
||||
margin-inline-end: 0.5ch;
|
||||
}
|
||||
|
||||
.colon, .chat-prefix-icon {
|
||||
@ -271,9 +270,8 @@
|
||||
}
|
||||
|
||||
.chat-prefix-icon {
|
||||
transform: translateY(1px);
|
||||
display: inline-block;
|
||||
font-size: 0.875rem;
|
||||
align-self: center;
|
||||
color: var(--color-list-icon);
|
||||
}
|
||||
|
||||
|
||||
@ -25,8 +25,8 @@ import { ChatAnimationTypes } from './useChatAnimationType';
|
||||
import useMessageMediaHash from '../../../../hooks/media/useMessageMediaHash';
|
||||
import useThumbnail from '../../../../hooks/media/useThumbnail';
|
||||
import useEnsureStory from '../../../../hooks/useEnsureStory';
|
||||
import useLang from '../../../../hooks/useLang';
|
||||
import useMedia from '../../../../hooks/useMedia';
|
||||
import useOldLang from '../../../../hooks/useOldLang';
|
||||
|
||||
import ChatForumLastMessage from '../../../common/ChatForumLastMessage';
|
||||
import Icon from '../../../common/icons/Icon';
|
||||
@ -73,7 +73,7 @@ export default function useChatListEntry({
|
||||
withInterfaceAnimations?: boolean;
|
||||
noForumTitle?: boolean;
|
||||
}) {
|
||||
const oldLang = useOldLang();
|
||||
const lang = useLang();
|
||||
const ref = useRef<HTMLDivElement>();
|
||||
|
||||
const storyData = lastMessage?.content.storyData;
|
||||
@ -105,8 +105,8 @@ export default function useChatListEntry({
|
||||
|
||||
if (canDisplayDraft) {
|
||||
return (
|
||||
<p className="last-message" dir={oldLang.isRtl ? 'auto' : 'ltr'}>
|
||||
<span className="draft">{oldLang('Draft')}</span>
|
||||
<p className="last-message" dir={lang.isRtl ? 'auto' : 'ltr'}>
|
||||
<span className="draft">{lang('ChatDraftPrefix')}</span>
|
||||
<span className="last-message-summary" dir="auto">
|
||||
{renderTextWithEntities({
|
||||
text: draft.text?.text || '',
|
||||
@ -124,11 +124,11 @@ export default function useChatListEntry({
|
||||
}
|
||||
|
||||
const senderName = lastMessageSender
|
||||
? getMessageSenderName(oldLang, chatId, lastMessageSender)
|
||||
? getMessageSenderName(lang, chatId, lastMessageSender)
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<p className="last-message shared-canvas-container" dir={oldLang.isRtl ? 'auto' : 'ltr'}>
|
||||
<p className="last-message shared-canvas-container" dir={lang.isRtl ? 'auto' : 'ltr'}>
|
||||
{senderName && (
|
||||
<>
|
||||
<span className="sender-name">{renderText(senderName)}</span>
|
||||
@ -143,7 +143,7 @@ export default function useChatListEntry({
|
||||
</p>
|
||||
);
|
||||
}, [
|
||||
chat, chatId, draft, isRoundVideo, isTopic, oldLang, lastMessage, lastMessageSender, lastMessageTopic,
|
||||
chat, chatId, draft, isRoundVideo, isTopic, lang, lastMessage, lastMessageSender, lastMessageTopic,
|
||||
mediaBlobUrl, mediaThumbnail, observeIntersection, typingStatus, isSavedDialog, isPreview,
|
||||
]);
|
||||
|
||||
|
||||
@ -6,8 +6,8 @@ import { getMessageSenderName } from '../../../global/helpers/peers';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import renderText from '../../common/helpers/renderText';
|
||||
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
import useOldLang from '../../../hooks/useOldLang';
|
||||
|
||||
import Avatar from '../../common/Avatar';
|
||||
import FullNameTitle from '../../common/FullNameTitle';
|
||||
@ -39,7 +39,7 @@ const MiddleSearchResult = ({
|
||||
className,
|
||||
onClick,
|
||||
}: OwnProps) => {
|
||||
const lang = useOldLang();
|
||||
const lang = useLang();
|
||||
const hiddenForwardTitle = message.forwardInfo?.hiddenUserName;
|
||||
|
||||
const peer = shouldShowChat ? messageChat : senderPeer;
|
||||
|
||||
@ -3183,19 +3183,21 @@ async function loadChats(
|
||||
);
|
||||
}
|
||||
|
||||
const idsToUpdateDraft = isFullDraftSync ? result.chatIds : Object.keys(result.draftsById);
|
||||
idsToUpdateDraft.forEach((chatId) => {
|
||||
const draft = result.draftsById[chatId];
|
||||
const thread = selectThread(global, chatId, MAIN_THREAD_ID);
|
||||
if (listType === 'active' || listType === 'archived') {
|
||||
const idsToUpdateDraft = isFullDraftSync ? result.chatIds : Object.keys(result.draftsById);
|
||||
idsToUpdateDraft.forEach((chatId) => {
|
||||
const draft = result.draftsById[chatId];
|
||||
const thread = selectThread(global, chatId, MAIN_THREAD_ID);
|
||||
|
||||
if (!draft && !thread) return;
|
||||
if (!draft && !thread) return;
|
||||
|
||||
if (!selectDraft(global, chatId, MAIN_THREAD_ID)?.isLocal) {
|
||||
global = replaceThreadParam(
|
||||
global, chatId, MAIN_THREAD_ID, 'draft', draft,
|
||||
);
|
||||
}
|
||||
});
|
||||
if (!selectDraft(global, chatId, MAIN_THREAD_ID)?.isLocal) {
|
||||
global = replaceThreadParam(
|
||||
global, chatId, MAIN_THREAD_ID, 'draft', draft,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ((chatIds.length === 0 || chatIds.length === result.totalChatCount) && !global.chats.isFullyLoaded[listType]) {
|
||||
global = {
|
||||
|
||||
@ -111,7 +111,7 @@ export function getPeerFullTitle(lang: OldLangFn | LangFn, peer: ApiPeer | Custo
|
||||
return isApiPeerUser(peer) ? getUserFullName(peer) : getChatTitle(lang, peer);
|
||||
}
|
||||
|
||||
export function getMessageSenderName(lang: OldLangFn, chatId: string, sender: ApiPeer) {
|
||||
export function getMessageSenderName(lang: LangFn, chatId: string, sender: ApiPeer) {
|
||||
// Hide sender name for private chats
|
||||
if (isUserId(chatId)) return undefined;
|
||||
|
||||
|
||||
2
src/types/language.d.ts
vendored
2
src/types/language.d.ts
vendored
@ -279,6 +279,7 @@ export interface LangPair {
|
||||
'ArchivedChats': undefined;
|
||||
'FilterAddTo': undefined;
|
||||
'Draft': undefined;
|
||||
'ChatDraftPrefix': undefined;
|
||||
'FilterAllChatsShort': undefined;
|
||||
'FilterAllChats': undefined;
|
||||
'CreateNewContact': undefined;
|
||||
@ -1545,6 +1546,7 @@ export interface LangPair {
|
||||
'ActionPaidMessagePriceFreeYou': undefined;
|
||||
'NotificationTitleNotSupportedInFrozenAccount': undefined;
|
||||
'NotificationMessageNotSupportedInFrozenAccount': undefined;
|
||||
'NotificationMessageTextHidden': undefined;
|
||||
'GiftRibbonSale': undefined;
|
||||
'StarsGiftBought': undefined;
|
||||
'GiftSellTitle': undefined;
|
||||
|
||||
@ -31,6 +31,7 @@ import { callApi } from '../api/gramjs';
|
||||
import { IS_ELECTRON, IS_SERVICE_WORKER_SUPPORTED, IS_TOUCH_ENV } from './browser/windowEnvironment';
|
||||
import jsxToHtml from './element/jsxToHtml';
|
||||
import { buildCollectionByKey } from './iteratees';
|
||||
import { getTranslationFn } from './localization';
|
||||
import * as mediaLoader from './mediaLoader';
|
||||
import { oldTranslate } from './oldLangProvider';
|
||||
import { debounce } from './schedulers';
|
||||
@ -313,7 +314,7 @@ function getNotificationContent(chat: ApiChat, message: ApiMessage, reaction?: A
|
||||
!isScreenLocked
|
||||
&& getShouldShowMessagePreview(chat, selectNotifyDefaults(global), selectNotifyException(global, chat.id))
|
||||
) {
|
||||
const senderName = sender ? getMessageSenderName(oldTranslate, chat.id, sender) : undefined;
|
||||
const senderName = sender ? getMessageSenderName(getTranslationFn(), chat.id, sender) : undefined;
|
||||
let summary = jsxToHtml(<span><MessageSummary message={message} /></span>)[0].textContent || '';
|
||||
|
||||
if (hasReaction) {
|
||||
@ -323,7 +324,7 @@ function getNotificationContent(chat: ApiChat, message: ApiMessage, reaction?: A
|
||||
|
||||
body = senderName ? `${senderName}: ${summary}` : summary;
|
||||
} else {
|
||||
body = 'New message';
|
||||
body = getTranslationFn()('NotificationMessageTextHidden');
|
||||
}
|
||||
|
||||
let title = isScreenLocked ? APP_NAME : getChatTitle(oldTranslate, chat, isSelf);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user