Messages: Implement non-сontact Info (#5708)
Co-authored-by: zubiden <19638254+zubiden@users.noreply.github.com>
This commit is contained in:
parent
d4c7fdb7ed
commit
dc61b12f9b
@ -13,7 +13,6 @@ import type {
|
|||||||
ApiChatlistInvite,
|
ApiChatlistInvite,
|
||||||
ApiChatMember,
|
ApiChatMember,
|
||||||
ApiChatReactions,
|
ApiChatReactions,
|
||||||
ApiChatSettings,
|
|
||||||
ApiExportedInvite,
|
ApiExportedInvite,
|
||||||
ApiMissingInvitedUser,
|
ApiMissingInvitedUser,
|
||||||
ApiRestrictionReason,
|
ApiRestrictionReason,
|
||||||
@ -517,20 +516,6 @@ export function buildChatInviteImporter(importer: GramJs.ChatInviteImporter): Ap
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildApiChatSettings({
|
|
||||||
autoarchived,
|
|
||||||
reportSpam,
|
|
||||||
addContact,
|
|
||||||
blockContact,
|
|
||||||
}: GramJs.PeerSettings): ApiChatSettings {
|
|
||||||
return {
|
|
||||||
isAutoArchived: Boolean(autoarchived),
|
|
||||||
canReportSpam: Boolean(reportSpam),
|
|
||||||
canAddContact: Boolean(addContact),
|
|
||||||
canBlockContact: Boolean(blockContact),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildApiChatReactions(chatReactions?: GramJs.TypeChatReactions): ApiChatReactions | undefined {
|
export function buildApiChatReactions(chatReactions?: GramJs.TypeChatReactions): ApiChatReactions | undefined {
|
||||||
if (chatReactions instanceof GramJs.ChatReactionsAll) {
|
if (chatReactions instanceof GramJs.ChatReactionsAll) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { Api as GramJs } from '../../../lib/gramjs';
|
|||||||
|
|
||||||
import type {
|
import type {
|
||||||
ApiBirthday,
|
ApiBirthday,
|
||||||
|
ApiPeerSettings,
|
||||||
ApiUser,
|
ApiUser,
|
||||||
ApiUserFullInfo,
|
ApiUserFullInfo,
|
||||||
ApiUserStatus,
|
ApiUserStatus,
|
||||||
@ -24,7 +25,7 @@ export function buildApiUserFullInfo(mtpUserFull: GramJs.users.UserFull): ApiUse
|
|||||||
fallbackPhoto, personalPhoto, translationsDisabled, storiesPinnedAvailable,
|
fallbackPhoto, personalPhoto, translationsDisabled, storiesPinnedAvailable,
|
||||||
contactRequirePremium, businessWorkHours, businessLocation, businessIntro,
|
contactRequirePremium, businessWorkHours, businessLocation, businessIntro,
|
||||||
birthday, personalChannelId, personalChannelMessage, sponsoredEnabled, stargiftsCount, botVerification,
|
birthday, personalChannelId, personalChannelMessage, sponsoredEnabled, stargiftsCount, botVerification,
|
||||||
botCanManageEmojiStatus,
|
botCanManageEmojiStatus, settings,
|
||||||
},
|
},
|
||||||
users,
|
users,
|
||||||
} = mtpUserFull;
|
} = mtpUserFull;
|
||||||
@ -55,6 +56,29 @@ export function buildApiUserFullInfo(mtpUserFull: GramJs.users.UserFull): ApiUse
|
|||||||
starGiftCount: stargiftsCount,
|
starGiftCount: stargiftsCount,
|
||||||
isBotCanManageEmojiStatus: botCanManageEmojiStatus,
|
isBotCanManageEmojiStatus: botCanManageEmojiStatus,
|
||||||
hasScheduledMessages: hasScheduled,
|
hasScheduledMessages: hasScheduled,
|
||||||
|
settings: buildApiPeerSettings(settings),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildApiPeerSettings({
|
||||||
|
autoarchived,
|
||||||
|
reportSpam,
|
||||||
|
addContact,
|
||||||
|
blockContact,
|
||||||
|
registrationMonth,
|
||||||
|
phoneCountry,
|
||||||
|
nameChangeDate,
|
||||||
|
photoChangeDate,
|
||||||
|
}: GramJs.PeerSettings): ApiPeerSettings {
|
||||||
|
return {
|
||||||
|
isAutoArchived: Boolean(autoarchived),
|
||||||
|
canReportSpam: Boolean(reportSpam),
|
||||||
|
canAddContact: Boolean(addContact),
|
||||||
|
canBlockContact: Boolean(blockContact),
|
||||||
|
registrationMonth,
|
||||||
|
phoneCountry,
|
||||||
|
nameChangeDate,
|
||||||
|
photoChangeDate,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -44,7 +44,6 @@ import {
|
|||||||
buildApiChatlistExportedInvite,
|
buildApiChatlistExportedInvite,
|
||||||
buildApiChatlistInvite,
|
buildApiChatlistInvite,
|
||||||
buildApiChatReactions,
|
buildApiChatReactions,
|
||||||
buildApiChatSettings,
|
|
||||||
buildApiMissingInvitedUser,
|
buildApiMissingInvitedUser,
|
||||||
buildApiTopic,
|
buildApiTopic,
|
||||||
buildChatMember,
|
buildChatMember,
|
||||||
@ -56,7 +55,7 @@ import { buildApiMessage, buildMessageDraft } from '../apiBuilders/messages';
|
|||||||
import { buildApiPeerNotifySettings } from '../apiBuilders/misc';
|
import { buildApiPeerNotifySettings } from '../apiBuilders/misc';
|
||||||
import { buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
|
import { buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
|
||||||
import { buildStickerSet } from '../apiBuilders/symbols';
|
import { buildStickerSet } from '../apiBuilders/symbols';
|
||||||
import { buildApiUser, buildApiUserStatuses } from '../apiBuilders/users';
|
import { buildApiPeerSettings, buildApiUser, buildApiUserStatuses } from '../apiBuilders/users';
|
||||||
import {
|
import {
|
||||||
buildChatAdminRights,
|
buildChatAdminRights,
|
||||||
buildChatBannedRights,
|
buildChatBannedRights,
|
||||||
@ -354,8 +353,8 @@ export function fetchFullChat(chat: ApiChat) {
|
|||||||
: getFullChatInfo(id);
|
: getFullChatInfo(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchChatSettings(chat: ApiChat) {
|
export async function fetchPeerSettings(peer: ApiPeer) {
|
||||||
const { id, accessHash } = chat;
|
const { id, accessHash } = peer;
|
||||||
|
|
||||||
const result = await invokeRequest(new GramJs.messages.GetPeerSettings({
|
const result = await invokeRequest(new GramJs.messages.GetPeerSettings({
|
||||||
peer: buildInputPeer(id, accessHash),
|
peer: buildInputPeer(id, accessHash),
|
||||||
@ -368,7 +367,7 @@ export async function fetchChatSettings(chat: ApiChat) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
settings: buildApiChatSettings(result.settings),
|
settings: buildApiPeerSettings(result.settings),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Api as GramJs } from '../../../lib/gramjs';
|
import { Api as GramJs } from '../../../lib/gramjs';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ApiChat, ApiError, ApiUser, ApiUsername,
|
ApiChat, ApiError, ApiPeer, ApiUser, ApiUsername,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
import { ACCEPTABLE_USERNAME_ERRORS } from '../../../config';
|
import { ACCEPTABLE_USERNAME_ERRORS } from '../../../config';
|
||||||
@ -280,8 +280,8 @@ export function hideAllChatJoinRequests({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hideChatReportPane(chat: ApiChat) {
|
export function hidePeerSettingsBar(peer: ApiPeer) {
|
||||||
const { id, accessHash } = chat;
|
const { id, accessHash } = peer;
|
||||||
|
|
||||||
return invokeRequest(new GramJs.messages.HidePeerSettingsBar({
|
return invokeRequest(new GramJs.messages.HidePeerSettingsBar({
|
||||||
peer: buildInputPeer(id, accessHash),
|
peer: buildInputPeer(id, accessHash),
|
||||||
|
|||||||
@ -22,7 +22,6 @@ import {
|
|||||||
import {
|
import {
|
||||||
buildApiChatFolder,
|
buildApiChatFolder,
|
||||||
buildApiChatFromPreview,
|
buildApiChatFromPreview,
|
||||||
buildApiChatSettings,
|
|
||||||
buildChatMember,
|
buildChatMember,
|
||||||
buildChatMembers,
|
buildChatMembers,
|
||||||
buildChatTypingStatus,
|
buildChatTypingStatus,
|
||||||
@ -61,7 +60,7 @@ import {
|
|||||||
import { buildApiStealthMode, buildApiStory } from '../apiBuilders/stories';
|
import { buildApiStealthMode, buildApiStory } from '../apiBuilders/stories';
|
||||||
import { buildApiEmojiInteraction, buildStickerSet } from '../apiBuilders/symbols';
|
import { buildApiEmojiInteraction, buildStickerSet } from '../apiBuilders/symbols';
|
||||||
import {
|
import {
|
||||||
buildApiUser,
|
buildApiPeerSettings,
|
||||||
buildApiUserStatus,
|
buildApiUserStatus,
|
||||||
} from '../apiBuilders/users';
|
} from '../apiBuilders/users';
|
||||||
import {
|
import {
|
||||||
@ -776,42 +775,14 @@ export function updater(update: Update) {
|
|||||||
user: { phoneNumber: phone },
|
user: { phoneNumber: phone },
|
||||||
});
|
});
|
||||||
} else if (update instanceof GramJs.UpdatePeerSettings) {
|
} else if (update instanceof GramJs.UpdatePeerSettings) {
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
const { peer, settings } = update;
|
||||||
const { _entities, settings } = update;
|
const peerId = getApiChatIdFromMtpPeer(peer);
|
||||||
if (!_entities) {
|
const apiSettings = buildApiPeerSettings(settings);
|
||||||
return;
|
sendApiUpdate({
|
||||||
}
|
'@type': 'updatePeerSettings',
|
||||||
|
id: peerId,
|
||||||
if (_entities?.length) {
|
settings: apiSettings,
|
||||||
_entities
|
});
|
||||||
.filter((e) => e instanceof GramJs.User && !e.contact)
|
|
||||||
.forEach((user) => {
|
|
||||||
sendApiUpdate({
|
|
||||||
'@type': 'deleteContact',
|
|
||||||
id: buildApiPeerId(user.id, 'user'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
_entities
|
|
||||||
.filter((e) => e instanceof GramJs.User && e.contact)
|
|
||||||
.map(buildApiUser)
|
|
||||||
.forEach((user) => {
|
|
||||||
if (!user) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sendApiUpdate({
|
|
||||||
'@type': 'updateUser',
|
|
||||||
id: user.id,
|
|
||||||
user: {
|
|
||||||
...user,
|
|
||||||
...(settings && { settings: buildApiChatSettings(settings) }),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Settings
|
|
||||||
} else if (update instanceof GramJs.UpdateNotifySettings) {
|
} else if (update instanceof GramJs.UpdateNotifySettings) {
|
||||||
const {
|
const {
|
||||||
notifySettings,
|
notifySettings,
|
||||||
|
|||||||
@ -68,9 +68,6 @@ export interface ApiChat {
|
|||||||
accessHash?: string;
|
accessHash?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Obtained from GetChatSettings
|
|
||||||
settings?: ApiChatSettings;
|
|
||||||
|
|
||||||
joinRequests?: ApiChatInviteImporter[];
|
joinRequests?: ApiChatInviteImporter[];
|
||||||
isJoinToSend?: boolean;
|
isJoinToSend?: boolean;
|
||||||
isJoinRequest?: boolean;
|
isJoinRequest?: boolean;
|
||||||
@ -230,11 +227,15 @@ export interface ApiChatFolder {
|
|||||||
hasMyInvites?: true;
|
hasMyInvites?: true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiChatSettings {
|
export interface ApiPeerSettings {
|
||||||
isAutoArchived?: boolean;
|
isAutoArchived?: boolean;
|
||||||
canReportSpam?: boolean;
|
canReportSpam?: boolean;
|
||||||
canAddContact?: boolean;
|
canAddContact?: boolean;
|
||||||
canBlockContact?: boolean;
|
canBlockContact?: boolean;
|
||||||
|
registrationMonth?: string;
|
||||||
|
phoneCountry?: string;
|
||||||
|
nameChangeDate?: number;
|
||||||
|
photoChangeDate?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiSendAsPeerId {
|
export interface ApiSendAsPeerId {
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import type {
|
|||||||
ApiChatFullInfo,
|
ApiChatFullInfo,
|
||||||
ApiChatMember,
|
ApiChatMember,
|
||||||
ApiDraft,
|
ApiDraft,
|
||||||
|
ApiPeerSettings,
|
||||||
ApiTypingStatus,
|
ApiTypingStatus,
|
||||||
} from './chats';
|
} from './chats';
|
||||||
import type {
|
import type {
|
||||||
@ -403,6 +404,12 @@ export type ApiDeleteContact = {
|
|||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ApiUpdatePeerSettings = {
|
||||||
|
'@type': 'updatePeerSettings';
|
||||||
|
id: string;
|
||||||
|
settings: ApiPeerSettings;
|
||||||
|
};
|
||||||
|
|
||||||
export type ApiUpdateUser = {
|
export type ApiUpdateUser = {
|
||||||
'@type': 'updateUser';
|
'@type': 'updateUser';
|
||||||
id: string;
|
id: string;
|
||||||
@ -822,7 +829,7 @@ export type ApiUpdate = (
|
|||||||
ApiUpdateDeleteMessages | ApiUpdateMessagePoll | ApiUpdateMessagePollVote | ApiUpdateDeleteHistory |
|
ApiUpdateDeleteMessages | ApiUpdateMessagePoll | ApiUpdateMessagePollVote | ApiUpdateDeleteHistory |
|
||||||
ApiDeleteParticipantHistory | ApiUpdateMessageSendSucceeded | ApiUpdateMessageSendFailed |
|
ApiDeleteParticipantHistory | ApiUpdateMessageSendSucceeded | ApiUpdateMessageSendFailed |
|
||||||
ApiUpdateServiceNotification | ApiDeleteContact | ApiUpdateUser | ApiUpdateUserStatus |
|
ApiUpdateServiceNotification | ApiDeleteContact | ApiUpdateUser | ApiUpdateUserStatus |
|
||||||
ApiUpdateUserFullInfo | ApiUpdateVideoProcessingPending |
|
ApiUpdateUserFullInfo | ApiUpdateVideoProcessingPending | ApiUpdatePeerSettings |
|
||||||
ApiUpdateAvatar | ApiUpdateMessageImage | ApiUpdateDraftMessage |
|
ApiUpdateAvatar | ApiUpdateMessageImage | ApiUpdateDraftMessage |
|
||||||
ApiUpdateError | ApiUpdateResetContacts | ApiUpdateStartEmojiInteraction |
|
ApiUpdateError | ApiUpdateResetContacts | ApiUpdateStartEmojiInteraction |
|
||||||
ApiUpdateFavoriteStickers | ApiUpdateStickerSet | ApiUpdateStickerSets | ApiUpdateStickerSetsOrder |
|
ApiUpdateFavoriteStickers | ApiUpdateStickerSet | ApiUpdateStickerSets | ApiUpdateStickerSetsOrder |
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { API_CHAT_TYPES } from '../../config';
|
import type { API_CHAT_TYPES } from '../../config';
|
||||||
import type { ApiBotInfo } from './bots';
|
import type { ApiBotInfo } from './bots';
|
||||||
import type { ApiBusinessIntro, ApiBusinessLocation, ApiBusinessWorkHours } from './business';
|
import type { ApiBusinessIntro, ApiBusinessLocation, ApiBusinessWorkHours } from './business';
|
||||||
import type { ApiPeerColor } from './chats';
|
import type { ApiPeerColor, ApiPeerSettings } from './chats';
|
||||||
import type { ApiDocument, ApiPhoto } from './messages';
|
import type { ApiDocument, ApiPhoto } from './messages';
|
||||||
import type { ApiBotVerification } from './misc';
|
import type { ApiBotVerification } from './misc';
|
||||||
import type { ApiSavedStarGift } from './stars';
|
import type { ApiSavedStarGift } from './stars';
|
||||||
@ -65,6 +65,7 @@ export interface ApiUserFullInfo {
|
|||||||
isBotAccessEmojiGranted?: boolean;
|
isBotAccessEmojiGranted?: boolean;
|
||||||
hasScheduledMessages?: boolean;
|
hasScheduledMessages?: boolean;
|
||||||
botVerification?: ApiBotVerification;
|
botVerification?: ApiBotVerification;
|
||||||
|
settings?: ApiPeerSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ApiFakeType = 'fake' | 'scam';
|
export type ApiFakeType = 'fake' | 'scam';
|
||||||
|
|||||||
@ -252,6 +252,10 @@
|
|||||||
"ChatListDeleteChatConfirmation" = "Are you sure you want to delete the chat\nwith {chat}?";
|
"ChatListDeleteChatConfirmation" = "Are you sure you want to delete the chat\nwith {chat}?";
|
||||||
"DeleteAndStop" = "Delete and Block";
|
"DeleteAndStop" = "Delete and Block";
|
||||||
"ChatListDeleteForEveryone" = "Delete for me and {user}";
|
"ChatListDeleteForEveryone" = "Delete for me and {user}";
|
||||||
|
"ChatNonContactUserSubtitle" = "Not a contact";
|
||||||
|
"ChatNonContactUserGroups" = "Shared Groups";
|
||||||
|
"ContactInfoRegistration" = "Registration";
|
||||||
|
"ContactInfoNotVerified" = "Not an official account";
|
||||||
"DeleteForAll" = "Delete for all members";
|
"DeleteForAll" = "Delete for all members";
|
||||||
"DeleteSingleMessagesTitle" = "Delete message";
|
"DeleteSingleMessagesTitle" = "Delete message";
|
||||||
"AreYouSureDeleteSingleMessage" = "Are you sure you want to delete this message?";
|
"AreYouSureDeleteSingleMessage" = "Are you sure you want to delete this message?";
|
||||||
@ -1117,6 +1121,7 @@
|
|||||||
"WeekdaySaturday" = "Saturday";
|
"WeekdaySaturday" = "Saturday";
|
||||||
"WeekdaySunday" = "Sunday";
|
"WeekdaySunday" = "Sunday";
|
||||||
"WeekdayToday" = "Today";
|
"WeekdayToday" = "Today";
|
||||||
|
"Today" = "today";
|
||||||
"WeekdayYesterday" = "Yesterday";
|
"WeekdayYesterday" = "Yesterday";
|
||||||
"User" = "User";
|
"User" = "User";
|
||||||
"SecretChat" = "Secret Chat";
|
"SecretChat" = "Secret Chat";
|
||||||
@ -1193,6 +1198,14 @@
|
|||||||
"Seconds_other" = "{count} seconds";
|
"Seconds_other" = "{count} seconds";
|
||||||
"Minutes_one" = "{count} minute";
|
"Minutes_one" = "{count} minute";
|
||||||
"Minutes_other" = "{count} minutes";
|
"Minutes_other" = "{count} minutes";
|
||||||
|
"JustNowAgo" = "just now";
|
||||||
|
"MinutesAgo_one" = "1 minute ago";
|
||||||
|
"MinutesAgo_other" = "{count} minutes ago";
|
||||||
|
"HoursAgo_one" = "1 hour ago";
|
||||||
|
"HoursAgo_other" = "{count} hours ago";
|
||||||
|
"DaysAgo_one" = "1 day ago";
|
||||||
|
"DaysAgo_other" = "{count} days ago";
|
||||||
|
"AtDateAgo" = "on {date}";
|
||||||
"AudioPause" = "Pause audio";
|
"AudioPause" = "Pause audio";
|
||||||
"AudioPlay" = "Play audio";
|
"AudioPlay" = "Play audio";
|
||||||
"ToggleUserNotifications" = "Toggle user notifications";
|
"ToggleUserNotifications" = "Toggle user notifications";
|
||||||
@ -1419,6 +1432,8 @@
|
|||||||
"GiftInfoConvertDescription2" = "This action cannot be undone. This will permanently destroy the gift.";
|
"GiftInfoConvertDescription2" = "This action cannot be undone. This will permanently destroy the gift.";
|
||||||
"GiftInfoConvertDescriptionPeriod_one" = "Conversion is available for the next **{count} days**.";
|
"GiftInfoConvertDescriptionPeriod_one" = "Conversion is available for the next **{count} days**.";
|
||||||
"GiftInfoConvertDescriptionPeriod_other" = "Conversion is available for the next **{count} days**.";
|
"GiftInfoConvertDescriptionPeriod_other" = "Conversion is available for the next **{count} days**.";
|
||||||
|
"UserUpdatedName" = "{user} updated name {time}";
|
||||||
|
"UserUpdatedPhoto" = "{user} updated photo {time}";
|
||||||
"GiftInfoSaved" = "This gift is visible on your profile. {link}";
|
"GiftInfoSaved" = "This gift is visible on your profile. {link}";
|
||||||
"GiftInfoHidden" = "This gift is hidden. Only you can see it. {link}";
|
"GiftInfoHidden" = "This gift is hidden. Only you can see it. {link}";
|
||||||
"GiftInfoChannelSaved" = "This gift is visible in your channel's profile. {link}";
|
"GiftInfoChannelSaved" = "This gift is visible in your channel's profile. {link}";
|
||||||
@ -1487,6 +1502,8 @@
|
|||||||
"GiftWithdrawSubmit" = "Open Fragment";
|
"GiftWithdrawSubmit" = "Open Fragment";
|
||||||
"GiftWithdrawWait_one" = "In {days} day, you'll be able to send this collectible to any TON blockchain address outside Telegram for sale or auction.";
|
"GiftWithdrawWait_one" = "In {days} day, you'll be able to send this collectible to any TON blockchain address outside Telegram for sale or auction.";
|
||||||
"GiftWithdrawWait_other" = "In {days} days, you'll be able to send this collectible to any TON blockchain address outside Telegram for sale or auction.";
|
"GiftWithdrawWait_other" = "In {days} days, you'll be able to send this collectible to any TON blockchain address outside Telegram for sale or auction.";
|
||||||
|
"ChatGroups_one" = "{count} group";
|
||||||
|
"ChatGroups_other" = "{count} groups";
|
||||||
"StarsAmount" = "⭐️{amount}";
|
"StarsAmount" = "⭐️{amount}";
|
||||||
"StarsAmountText_one" = "{amount} Star";
|
"StarsAmountText_one" = "{amount} Star";
|
||||||
"StarsAmountText_other" = "{amount} Stars";
|
"StarsAmountText_other" = "{amount} Stars";
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import { preloadImage } from '../../util/files';
|
|||||||
import preloadFonts from '../../util/fonts';
|
import preloadFonts from '../../util/fonts';
|
||||||
import { pick } from '../../util/iteratees';
|
import { pick } from '../../util/iteratees';
|
||||||
import { oldSetLanguage } from '../../util/oldLangProvider';
|
import { oldSetLanguage } from '../../util/oldLangProvider';
|
||||||
import { formatPhoneNumber, getCountryCodesByIso, getCountryFromPhoneNumber } from '../../util/phoneNumber';
|
import { formatPhoneNumber, getCountryCodeByIso, getCountryFromPhoneNumber } from '../../util/phoneNumber';
|
||||||
import { IS_SAFARI, IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
import { IS_SAFARI, IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||||
import { getSuggestedLanguage } from './helpers/getSuggestedLanguage';
|
import { getSuggestedLanguage } from './helpers/getSuggestedLanguage';
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ const AuthPhoneNumber: FC<StateProps> = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (authNearestCountry && phoneCodeList && !country && !isTouched) {
|
if (authNearestCountry && phoneCodeList && !country && !isTouched) {
|
||||||
setCountry(getCountryCodesByIso(phoneCodeList, authNearestCountry)[0]);
|
setCountry(getCountryCodeByIso(phoneCodeList, authNearestCountry));
|
||||||
}
|
}
|
||||||
}, [country, authNearestCountry, isTouched, phoneCodeList]);
|
}, [country, authNearestCountry, isTouched, phoneCodeList]);
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,10 @@ import { addExtraClass, removeExtraClass } from '../../lib/teact/teact-dom';
|
|||||||
import { getActions, getGlobal, withGlobal } from '../../global';
|
import { getActions, getGlobal, withGlobal } from '../../global';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ApiChatFullInfo, ApiMessage, ApiRestrictionReason, ApiTopic,
|
ApiChatFullInfo,
|
||||||
|
ApiMessage,
|
||||||
|
ApiRestrictionReason,
|
||||||
|
ApiTopic,
|
||||||
} from '../../api/types';
|
} from '../../api/types';
|
||||||
import type { OnIntersectPinnedMessage } from './hooks/usePinnedMessage';
|
import type { OnIntersectPinnedMessage } from './hooks/usePinnedMessage';
|
||||||
import { MAIN_THREAD_ID } from '../../api/types';
|
import { MAIN_THREAD_ID } from '../../api/types';
|
||||||
@ -74,7 +77,7 @@ import useStickyDates from './hooks/useStickyDates';
|
|||||||
|
|
||||||
import Loading from '../ui/Loading';
|
import Loading from '../ui/Loading';
|
||||||
import ContactGreeting from './ContactGreeting';
|
import ContactGreeting from './ContactGreeting';
|
||||||
import MessageListBotInfo from './MessageListBotInfo';
|
import MessageListAccountInfo from './MessageListAccountInfo';
|
||||||
import MessageListContent from './MessageListContent';
|
import MessageListContent from './MessageListContent';
|
||||||
import NoMessages from './NoMessages';
|
import NoMessages from './NoMessages';
|
||||||
import PremiumRequiredMessage from './PremiumRequiredMessage';
|
import PremiumRequiredMessage from './PremiumRequiredMessage';
|
||||||
@ -106,6 +109,9 @@ type StateProps = {
|
|||||||
isCreator?: boolean;
|
isCreator?: boolean;
|
||||||
isChannelWithAvatars?: boolean;
|
isChannelWithAvatars?: boolean;
|
||||||
isBot?: boolean;
|
isBot?: boolean;
|
||||||
|
isNonContact?: boolean;
|
||||||
|
nameChangeDate?: number;
|
||||||
|
photoChangeDate?: number;
|
||||||
isSynced?: boolean;
|
isSynced?: boolean;
|
||||||
messageIds?: number[];
|
messageIds?: number[];
|
||||||
messagesById?: Record<number, ApiMessage>;
|
messagesById?: Record<number, ApiMessage>;
|
||||||
@ -159,6 +165,9 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
|||||||
isAnonymousForwards,
|
isAnonymousForwards,
|
||||||
isCreator,
|
isCreator,
|
||||||
isBot,
|
isBot,
|
||||||
|
isNonContact,
|
||||||
|
nameChangeDate,
|
||||||
|
photoChangeDate,
|
||||||
messageIds,
|
messageIds,
|
||||||
messagesById,
|
messagesById,
|
||||||
firstUnreadId,
|
firstUnreadId,
|
||||||
@ -682,8 +691,8 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
) : isContactRequirePremium && !hasMessages ? (
|
) : isContactRequirePremium && !hasMessages ? (
|
||||||
<PremiumRequiredMessage userId={chatId} />
|
<PremiumRequiredMessage userId={chatId} />
|
||||||
) : isBot && !hasMessages ? (
|
) : (isBot || isNonContact) && !hasMessages ? (
|
||||||
<MessageListBotInfo chatId={chatId} />
|
<MessageListAccountInfo chatId={chatId} />
|
||||||
) : shouldRenderGreeting ? (
|
) : shouldRenderGreeting ? (
|
||||||
<ContactGreeting key={chatId} userId={chatId} />
|
<ContactGreeting key={chatId} userId={chatId} />
|
||||||
) : messageIds && (!messageGroups || isGroupChatJustCreated || isEmptyTopic) ? (
|
) : messageIds && (!messageGroups || isGroupChatJustCreated || isEmptyTopic) ? (
|
||||||
@ -718,7 +727,9 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
|||||||
isReady={isReady}
|
isReady={isReady}
|
||||||
hasLinkedChat={hasLinkedChat}
|
hasLinkedChat={hasLinkedChat}
|
||||||
isSchedule={messageGroups ? type === 'scheduled' : false}
|
isSchedule={messageGroups ? type === 'scheduled' : false}
|
||||||
shouldRenderBotInfo={isBot}
|
shouldRenderAccountInfo={isBot || isNonContact}
|
||||||
|
nameChangeDate={nameChangeDate}
|
||||||
|
photoChangeDate={photoChangeDate}
|
||||||
noAppearanceAnimation={!messageGroups || !shouldAnimateAppearanceRef.current}
|
noAppearanceAnimation={!messageGroups || !shouldAnimateAppearanceRef.current}
|
||||||
onScrollDownToggle={onScrollDownToggle}
|
onScrollDownToggle={onScrollDownToggle}
|
||||||
onNotchToggle={onNotchToggle}
|
onNotchToggle={onNotchToggle}
|
||||||
@ -735,6 +746,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
(global, { chatId, threadId, type }): StateProps => {
|
(global, { chatId, threadId, type }): StateProps => {
|
||||||
const currentUserId = global.currentUserId!;
|
const currentUserId = global.currentUserId!;
|
||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
|
const userFullInfo = selectUserFullInfo(global, chatId);
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
return { currentUserId };
|
return { currentUserId };
|
||||||
}
|
}
|
||||||
@ -763,6 +775,9 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const chatBot = selectBot(global, chatId);
|
const chatBot = selectBot(global, chatId);
|
||||||
|
const isNonContact = Boolean(userFullInfo?.settings?.canAddContact);
|
||||||
|
const nameChangeDate = userFullInfo?.settings?.nameChangeDate;
|
||||||
|
const photoChangeDate = userFullInfo?.settings?.photoChangeDate;
|
||||||
|
|
||||||
const topic = selectTopic(global, chatId, threadId);
|
const topic = selectTopic(global, chatId, threadId);
|
||||||
const chatFullInfo = !isUserId(chatId) ? selectChatFullInfo(global, chatId) : undefined;
|
const chatFullInfo = !isUserId(chatId) ? selectChatFullInfo(global, chatId) : undefined;
|
||||||
@ -784,6 +799,9 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
isSystemBotChat: isSystemBot(chatId),
|
isSystemBotChat: isSystemBot(chatId),
|
||||||
isAnonymousForwards: isAnonymousForwardsChat(chatId),
|
isAnonymousForwards: isAnonymousForwardsChat(chatId),
|
||||||
isBot: Boolean(chatBot),
|
isBot: Boolean(chatBot),
|
||||||
|
isNonContact,
|
||||||
|
nameChangeDate,
|
||||||
|
photoChangeDate,
|
||||||
isSynced: global.isSynced,
|
isSynced: global.isSynced,
|
||||||
messageIds,
|
messageIds,
|
||||||
messagesById,
|
messagesById,
|
||||||
|
|||||||
109
src/components/middle/MessageListAccountInfo.module.scss
Normal file
109
src/components/middle/MessageListAccountInfo.module.scss
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
.root {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0 0.6875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chatInfo {
|
||||||
|
max-width: min(80%, 25rem);
|
||||||
|
min-width: 15.8125rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
font-size: calc(var(--message-text-size, 1rem) - 0.0625rem);
|
||||||
|
border-radius: var(--border-radius-messages);
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chatBackground {
|
||||||
|
background-color: var(--action-message-bg);
|
||||||
|
color: white;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
margin: 5rem 0 2rem;
|
||||||
|
contain: layout; // Force new stacking context for text blending
|
||||||
|
}
|
||||||
|
|
||||||
|
.botBackground {
|
||||||
|
background-color: var(--color-background);
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bot-info-description {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
text-wrap: pretty;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bot-info-title {
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chatInfoTitle {
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
margin: 0 0 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chatInfoSubtitle {
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chatDescription {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.country {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.emoji-small) {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
margin-top: -0.1875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chatNotVerified {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verifiedTitle {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.linkInfo:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-inline-start: -0.1875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textColor {
|
||||||
|
color: white;
|
||||||
|
mix-blend-mode: overlay;
|
||||||
|
}
|
||||||
293
src/components/middle/MessageListAccountInfo.tsx
Normal file
293
src/components/middle/MessageListAccountInfo.tsx
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
import type { FC } from '../../lib/teact/teact';
|
||||||
|
import React, {
|
||||||
|
memo,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
} from '../../lib/teact/teact';
|
||||||
|
import { getActions, getGlobal, withGlobal } from '../../global';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ApiBotInfo, ApiChat, ApiCountryCode, ApiUserCommonChats, ApiUserFullInfo,
|
||||||
|
} from '../../api/types';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getBotCoverMediaHash,
|
||||||
|
getChatTitle,
|
||||||
|
getPhotoFullDimensions,
|
||||||
|
getVideoDimensions,
|
||||||
|
getVideoMediaHash,
|
||||||
|
isChatWithVerificationCodesBot,
|
||||||
|
} from '../../global/helpers';
|
||||||
|
import {
|
||||||
|
selectBot, selectChat, selectPeer, selectUserCommonChats, selectUserFullInfo,
|
||||||
|
} from '../../global/selectors';
|
||||||
|
import buildClassName from '../../util/buildClassName';
|
||||||
|
import buildStyle from '../../util/buildStyle';
|
||||||
|
import { formatPastDatetime, formatRegistrationMonth } from '../../util/dates/dateFormat';
|
||||||
|
import { isoToEmoji } from '../../util/emoji/emoji';
|
||||||
|
import { getCountryCodeByIso } from '../../util/phoneNumber';
|
||||||
|
import stopEvent from '../../util/stopEvent';
|
||||||
|
import renderText from '../common/helpers/renderText';
|
||||||
|
|
||||||
|
import useEffectOnce from '../../hooks/useEffectOnce';
|
||||||
|
import useLang from '../../hooks/useLang';
|
||||||
|
import useLastCallback from '../../hooks/useLastCallback';
|
||||||
|
import useMedia from '../../hooks/useMedia';
|
||||||
|
import useOldLang from '../../hooks/useOldLang';
|
||||||
|
import useShowTransition from '../../hooks/useShowTransition';
|
||||||
|
|
||||||
|
import AvatarList from '../common/AvatarList';
|
||||||
|
import Icon from '../common/icons/Icon';
|
||||||
|
import MiniTable, { type TableEntry } from '../common/MiniTable';
|
||||||
|
import Link from '../ui/Link';
|
||||||
|
import OptimizedVideo from '../ui/OptimizedVideo';
|
||||||
|
import Skeleton from '../ui/placeholder/Skeleton';
|
||||||
|
|
||||||
|
import styles from './MessageListAccountInfo.module.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
chatId: string;
|
||||||
|
isInMessageList?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type StateProps = {
|
||||||
|
chat?: ApiChat;
|
||||||
|
botInfo?: ApiBotInfo;
|
||||||
|
isLoadingFullUser?: boolean;
|
||||||
|
phoneCodeList?: ApiCountryCode[];
|
||||||
|
commonChats?: ApiUserCommonChats;
|
||||||
|
userFullInfo?: ApiUserFullInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
const MessageListAccountInfo: FC<OwnProps & StateProps> = ({
|
||||||
|
chat,
|
||||||
|
chatId,
|
||||||
|
botInfo,
|
||||||
|
isLoadingFullUser,
|
||||||
|
isInMessageList,
|
||||||
|
phoneCodeList,
|
||||||
|
commonChats,
|
||||||
|
userFullInfo,
|
||||||
|
}) => {
|
||||||
|
const { loadCommonChats, openChatWithInfo } = getActions();
|
||||||
|
const oldLang = useOldLang();
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
const {
|
||||||
|
phoneCountry,
|
||||||
|
registrationMonth,
|
||||||
|
nameChangeDate,
|
||||||
|
photoChangeDate,
|
||||||
|
} = userFullInfo?.settings || {};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadCommonChats({ userId: chatId });
|
||||||
|
}, [chatId]);
|
||||||
|
|
||||||
|
const country = useMemo(() => {
|
||||||
|
if (!phoneCodeList || !phoneCountry) return undefined;
|
||||||
|
return getCountryCodeByIso(phoneCodeList, phoneCountry);
|
||||||
|
}, [phoneCodeList, phoneCountry]);
|
||||||
|
|
||||||
|
const botInfoPhotoUrl = useMedia(botInfo?.photo ? getBotCoverMediaHash(botInfo.photo) : undefined);
|
||||||
|
const botInfoGifUrl = useMedia(botInfo?.gif ? getVideoMediaHash(botInfo.gif, 'full') : undefined);
|
||||||
|
const botInfoDimensions = botInfo?.photo ? getPhotoFullDimensions(botInfo.photo) : botInfo?.gif
|
||||||
|
? getVideoDimensions(botInfo.gif) : undefined;
|
||||||
|
const isBotInfoEmpty = botInfo && !botInfo.description && !botInfo.gif && !botInfo.photo;
|
||||||
|
const isChatInfoEmpty = !country || !registrationMonth;
|
||||||
|
|
||||||
|
const isVerifyCodes = isChatWithVerificationCodesBot(chatId);
|
||||||
|
|
||||||
|
const { width, height } = botInfoDimensions || {};
|
||||||
|
|
||||||
|
const handleClick = useLastCallback((e: React.SyntheticEvent<any>) => {
|
||||||
|
stopEvent(e);
|
||||||
|
openChatWithInfo({
|
||||||
|
id: chatId, shouldReplaceHistory: true, profileTab: 'commonChats', forceScrollProfileTab: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const securityNameInfo = nameChangeDate && chat ? (
|
||||||
|
<div className="local-action-message" key="security-name-message">
|
||||||
|
<span>{lang('UserUpdatedName', {
|
||||||
|
user: chat.title,
|
||||||
|
time: formatPastDatetime(lang, nameChangeDate),
|
||||||
|
}, { withNodes: true, withMarkdown: true })}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : undefined;
|
||||||
|
|
||||||
|
const securityPhotoInfo = photoChangeDate && chat ? (
|
||||||
|
<div className="local-action-message" key="security-photo-message">
|
||||||
|
<span>{lang('UserUpdatedPhoto', {
|
||||||
|
user: chat.title,
|
||||||
|
time: formatPastDatetime(lang, photoChangeDate),
|
||||||
|
}, { withNodes: true, withMarkdown: true })}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : undefined;
|
||||||
|
|
||||||
|
const tableData = useMemo((): TableEntry[] => {
|
||||||
|
const entries: TableEntry[] = [];
|
||||||
|
if (country) {
|
||||||
|
entries.push([
|
||||||
|
oldLang('PrivacyPhone'),
|
||||||
|
<span className={styles.chatDescription}>
|
||||||
|
<span className={styles.country}>
|
||||||
|
{renderText(isoToEmoji(country?.iso2))}
|
||||||
|
</span>
|
||||||
|
{country?.defaultName}
|
||||||
|
</span>,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if (registrationMonth) {
|
||||||
|
entries.push([
|
||||||
|
lang('ContactInfoRegistration'),
|
||||||
|
formatRegistrationMonth(lang.code, registrationMonth),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if (userFullInfo?.commonChatsCount) {
|
||||||
|
const global = getGlobal();
|
||||||
|
const peers = commonChats?.ids.slice(0, 3).map((id) => selectPeer(global, id)!).filter(Boolean);
|
||||||
|
entries.push([
|
||||||
|
lang('ChatNonContactUserGroups'),
|
||||||
|
<Link className={styles.link} onClick={handleClick}>
|
||||||
|
<span className={styles.linkInfo}>
|
||||||
|
{lang('ChatGroups', {
|
||||||
|
count: userFullInfo.commonChatsCount,
|
||||||
|
}, {
|
||||||
|
pluralValue: userFullInfo.commonChatsCount,
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
{Boolean(peers?.length) && <AvatarList size="micro" peers={peers} />}
|
||||||
|
<Icon name="next" className={styles.icon} />
|
||||||
|
</Link>,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return entries;
|
||||||
|
}, [lang, oldLang, country, registrationMonth, commonChats, userFullInfo]);
|
||||||
|
|
||||||
|
const isEmptyOrLoading = (isBotInfoEmpty && isChatInfoEmpty) || isLoadingFullUser;
|
||||||
|
|
||||||
|
const isFirstRenderRef = useRef(true);
|
||||||
|
const {
|
||||||
|
shouldRender,
|
||||||
|
ref,
|
||||||
|
} = useShowTransition({
|
||||||
|
isOpen: !isEmptyOrLoading && isInMessageList,
|
||||||
|
withShouldRender: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffectOnce(() => {
|
||||||
|
isFirstRenderRef.current = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!shouldRender) return undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={ref} className={buildClassName(styles.root, 'empty')}>
|
||||||
|
{isLoadingFullUser && isChatInfoEmpty && <span>{oldLang('Loading')}</span>}
|
||||||
|
{(isBotInfoEmpty && isChatInfoEmpty) && !isLoadingFullUser && <span>{oldLang('NoMessages')}</span>}
|
||||||
|
{botInfo && (
|
||||||
|
<div
|
||||||
|
className={buildClassName(styles.chatInfo, styles.botBackground)}
|
||||||
|
style={buildStyle(
|
||||||
|
width ? `width: ${width}px` : undefined,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{botInfoPhotoUrl && (
|
||||||
|
<img
|
||||||
|
className={styles.media}
|
||||||
|
src={botInfoPhotoUrl}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
alt="Bot info"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{botInfoGifUrl && (
|
||||||
|
<OptimizedVideo
|
||||||
|
canPlay
|
||||||
|
className={styles.media}
|
||||||
|
src={botInfoGifUrl}
|
||||||
|
loop
|
||||||
|
disablePictureInPicture
|
||||||
|
muted
|
||||||
|
playsInline
|
||||||
|
style={buildStyle(Boolean(width) && `width: ${width}px`, Boolean(height) && `height: ${height}px`)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{botInfoDimensions && !botInfoPhotoUrl && !botInfoGifUrl && (
|
||||||
|
<Skeleton
|
||||||
|
className={styles.media}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
forceAspectRatio
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isVerifyCodes && (
|
||||||
|
<div className={styles.botInfoDescription}>
|
||||||
|
{oldLang('VerifyChatInfo')}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!isVerifyCodes && botInfo.description && (
|
||||||
|
<div className={styles.botInfoDescription}>
|
||||||
|
<p className={styles.botInfoTitle}>{oldLang('BotInfoTitle')}</p>
|
||||||
|
{renderText(botInfo.description, ['br', 'emoji', 'links'])}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!isChatInfoEmpty && chat && (
|
||||||
|
<div
|
||||||
|
className={buildClassName(styles.chatInfo, styles.chatBackground)}
|
||||||
|
>
|
||||||
|
<h3 className={styles.chatInfoTitle}>{renderText(getChatTitle(lang, chat))}</h3>
|
||||||
|
<p className={buildClassName(styles.chatInfoSubtitle, styles.textColor)}>
|
||||||
|
{lang('ChatNonContactUserSubtitle')}
|
||||||
|
</p>
|
||||||
|
<MiniTable keyClassName={styles.textColor} data={tableData} />
|
||||||
|
{!chat?.isVerified && (
|
||||||
|
<div className={buildClassName(styles.chatNotVerified, styles.textColor)}>
|
||||||
|
<Icon name="info-filled" />
|
||||||
|
<p className={styles.verifiedTitle}>{lang('ContactInfoNotVerified')}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{securityNameInfo}
|
||||||
|
{securityPhotoInfo}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(withGlobal<OwnProps>(
|
||||||
|
(global, { chatId }) => {
|
||||||
|
const {
|
||||||
|
countryList: { phoneCodes: phoneCodeList },
|
||||||
|
} = global;
|
||||||
|
const chat = selectChat(global, chatId);
|
||||||
|
const userFullInfo = selectUserFullInfo(global, chatId);
|
||||||
|
const commonChats = selectUserCommonChats(global, chatId);
|
||||||
|
const chatBot = selectBot(global, chatId);
|
||||||
|
|
||||||
|
let isLoadingFullUser = false;
|
||||||
|
let botInfo;
|
||||||
|
if (chatBot) {
|
||||||
|
if (userFullInfo) {
|
||||||
|
botInfo = userFullInfo.botInfo;
|
||||||
|
} else {
|
||||||
|
isLoadingFullUser = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
chat,
|
||||||
|
userFullInfo,
|
||||||
|
botInfo,
|
||||||
|
isLoadingFullUser,
|
||||||
|
phoneCodeList,
|
||||||
|
commonChats,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
)(MessageListAccountInfo));
|
||||||
@ -1,35 +0,0 @@
|
|||||||
.root {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bot-info {
|
|
||||||
max-width: min(80%, 25rem);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
background-color: var(--color-background);
|
|
||||||
color: var(--color-text);
|
|
||||||
font-size: calc(var(--message-text-size, 1rem) - 0.0625rem);
|
|
||||||
border-radius: var(--border-radius-messages);
|
|
||||||
overflow: hidden;
|
|
||||||
text-align: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bot-info-description {
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
text-wrap: pretty;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bot-info-title {
|
|
||||||
font-weight: var(--font-weight-medium);
|
|
||||||
margin-bottom: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media {
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto !important;
|
|
||||||
}
|
|
||||||
@ -1,134 +0,0 @@
|
|||||||
import type { FC } from '../../lib/teact/teact';
|
|
||||||
import React, { memo } from '../../lib/teact/teact';
|
|
||||||
import { withGlobal } from '../../global';
|
|
||||||
|
|
||||||
import type { ApiBotInfo } from '../../api/types';
|
|
||||||
|
|
||||||
import {
|
|
||||||
getBotCoverMediaHash,
|
|
||||||
getPhotoFullDimensions,
|
|
||||||
getVideoDimensions,
|
|
||||||
getVideoMediaHash,
|
|
||||||
isChatWithVerificationCodesBot,
|
|
||||||
} from '../../global/helpers';
|
|
||||||
import { selectBot, selectUserFullInfo } from '../../global/selectors';
|
|
||||||
import buildClassName from '../../util/buildClassName';
|
|
||||||
import buildStyle from '../../util/buildStyle';
|
|
||||||
import renderText from '../common/helpers/renderText';
|
|
||||||
|
|
||||||
import useMedia from '../../hooks/useMedia';
|
|
||||||
import useOldLang from '../../hooks/useOldLang';
|
|
||||||
|
|
||||||
import OptimizedVideo from '../ui/OptimizedVideo';
|
|
||||||
import Skeleton from '../ui/placeholder/Skeleton';
|
|
||||||
|
|
||||||
import styles from './MessageListBotInfo.module.scss';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
chatId: string;
|
|
||||||
isInMessageList?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type StateProps = {
|
|
||||||
botInfo?: ApiBotInfo;
|
|
||||||
isLoadingBotInfo?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
const MessageListBotInfo: FC<OwnProps & StateProps> = ({
|
|
||||||
chatId,
|
|
||||||
botInfo,
|
|
||||||
isLoadingBotInfo,
|
|
||||||
isInMessageList,
|
|
||||||
}) => {
|
|
||||||
const lang = useOldLang();
|
|
||||||
|
|
||||||
const botInfoPhotoUrl = useMedia(botInfo?.photo ? getBotCoverMediaHash(botInfo.photo) : undefined);
|
|
||||||
const botInfoGifUrl = useMedia(botInfo?.gif ? getVideoMediaHash(botInfo.gif, 'full') : undefined);
|
|
||||||
const botInfoDimensions = botInfo?.photo ? getPhotoFullDimensions(botInfo.photo) : botInfo?.gif
|
|
||||||
? getVideoDimensions(botInfo.gif) : undefined;
|
|
||||||
const isBotInfoEmpty = botInfo && !botInfo.description && !botInfo.gif && !botInfo.photo;
|
|
||||||
|
|
||||||
const isVerifyCodes = isChatWithVerificationCodesBot(chatId);
|
|
||||||
|
|
||||||
const { width, height } = botInfoDimensions || {};
|
|
||||||
|
|
||||||
const isEmptyOrLoading = isBotInfoEmpty || isLoadingBotInfo;
|
|
||||||
|
|
||||||
if (isEmptyOrLoading && isInMessageList) return undefined;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={buildClassName(styles.root, 'empty')}>
|
|
||||||
{isLoadingBotInfo && <span>{lang('Loading')}</span>}
|
|
||||||
{isBotInfoEmpty && !isLoadingBotInfo && <span>{lang('NoMessages')}</span>}
|
|
||||||
{botInfo && (
|
|
||||||
<div
|
|
||||||
className={styles.botInfo}
|
|
||||||
style={buildStyle(
|
|
||||||
width ? `width: ${width}px` : undefined,
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{botInfoPhotoUrl && (
|
|
||||||
<img
|
|
||||||
className={styles.media}
|
|
||||||
src={botInfoPhotoUrl}
|
|
||||||
width={width}
|
|
||||||
height={height}
|
|
||||||
alt="Bot info"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{botInfoGifUrl && (
|
|
||||||
<OptimizedVideo
|
|
||||||
canPlay
|
|
||||||
className={styles.media}
|
|
||||||
src={botInfoGifUrl}
|
|
||||||
loop
|
|
||||||
disablePictureInPicture
|
|
||||||
muted
|
|
||||||
playsInline
|
|
||||||
style={buildStyle(Boolean(width) && `width: ${width}px`, Boolean(height) && `height: ${height}px`)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{botInfoDimensions && !botInfoPhotoUrl && !botInfoGifUrl && (
|
|
||||||
<Skeleton
|
|
||||||
className={styles.media}
|
|
||||||
width={width}
|
|
||||||
height={height}
|
|
||||||
forceAspectRatio
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isVerifyCodes && (
|
|
||||||
<div className={styles.botInfoDescription}>
|
|
||||||
{lang('VerifyChatInfo')}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{!isVerifyCodes && botInfo.description && (
|
|
||||||
<div className={styles.botInfoDescription}>
|
|
||||||
<p className={styles.botInfoTitle}>{lang('BotInfoTitle')}</p>
|
|
||||||
{renderText(botInfo.description, ['br', 'emoji', 'links'])}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(withGlobal<OwnProps>(
|
|
||||||
(global, { chatId }) => {
|
|
||||||
const chatBot = selectBot(global, chatId);
|
|
||||||
let isLoadingBotInfo = false;
|
|
||||||
let botInfo;
|
|
||||||
if (chatBot) {
|
|
||||||
const chatBotFullInfo = selectUserFullInfo(global, chatBot.id);
|
|
||||||
if (chatBotFullInfo) {
|
|
||||||
botInfo = chatBotFullInfo.botInfo;
|
|
||||||
} else {
|
|
||||||
isLoadingBotInfo = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
botInfo,
|
|
||||||
isLoadingBotInfo,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
)(MessageListBotInfo));
|
|
||||||
@ -33,7 +33,7 @@ import ActionMessage from './message/ActionMessage';
|
|||||||
import Message from './message/Message';
|
import Message from './message/Message';
|
||||||
import SenderGroupContainer from './message/SenderGroupContainer';
|
import SenderGroupContainer from './message/SenderGroupContainer';
|
||||||
import SponsoredMessage from './message/SponsoredMessage';
|
import SponsoredMessage from './message/SponsoredMessage';
|
||||||
import MessageListBotInfo from './MessageListBotInfo';
|
import MessageListAccountInfo from './MessageListAccountInfo';
|
||||||
|
|
||||||
interface OwnProps {
|
interface OwnProps {
|
||||||
canShowAds?: boolean;
|
canShowAds?: boolean;
|
||||||
@ -57,7 +57,9 @@ interface OwnProps {
|
|||||||
isReady: boolean;
|
isReady: boolean;
|
||||||
hasLinkedChat: boolean | undefined;
|
hasLinkedChat: boolean | undefined;
|
||||||
isSchedule: boolean;
|
isSchedule: boolean;
|
||||||
shouldRenderBotInfo?: boolean;
|
shouldRenderAccountInfo?: boolean;
|
||||||
|
nameChangeDate?: number;
|
||||||
|
photoChangeDate?: number;
|
||||||
noAppearanceAnimation: boolean;
|
noAppearanceAnimation: boolean;
|
||||||
isSavedDialog?: boolean;
|
isSavedDialog?: boolean;
|
||||||
onScrollDownToggle: BooleanToVoidFunction;
|
onScrollDownToggle: BooleanToVoidFunction;
|
||||||
@ -89,7 +91,9 @@ const MessageListContent: FC<OwnProps> = ({
|
|||||||
isReady,
|
isReady,
|
||||||
hasLinkedChat,
|
hasLinkedChat,
|
||||||
isSchedule,
|
isSchedule,
|
||||||
shouldRenderBotInfo,
|
shouldRenderAccountInfo,
|
||||||
|
nameChangeDate,
|
||||||
|
photoChangeDate,
|
||||||
noAppearanceAnimation,
|
noAppearanceAnimation,
|
||||||
isSavedDialog,
|
isSavedDialog,
|
||||||
onScrollDownToggle,
|
onScrollDownToggle,
|
||||||
@ -126,11 +130,11 @@ const MessageListContent: FC<OwnProps> = ({
|
|||||||
isReady,
|
isReady,
|
||||||
);
|
);
|
||||||
|
|
||||||
const lang = useOldLang();
|
const oldLang = useOldLang();
|
||||||
|
|
||||||
const unreadDivider = (
|
const unreadDivider = (
|
||||||
<div className={buildClassName(UNREAD_DIVIDER_CLASS, 'local-action-message')} key="unread-messages">
|
<div className={buildClassName(UNREAD_DIVIDER_CLASS, 'local-action-message')} key="unread-messages">
|
||||||
<span>{lang('UnreadMessages')}</span>
|
<span>{oldLang('UnreadMessages')}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
const messageCountToAnimate = noAppearanceAnimation ? 0 : messageGroups.reduce((acc, messageGroup) => {
|
const messageCountToAnimate = noAppearanceAnimation ? 0 : messageGroups.reduce((acc, messageGroup) => {
|
||||||
@ -251,7 +255,7 @@ const MessageListContent: FC<OwnProps> = ({
|
|||||||
/>,
|
/>,
|
||||||
message.id === threadId && (
|
message.id === threadId && (
|
||||||
<div className="local-action-message" key="discussion-started">
|
<div className="local-action-message" key="discussion-started">
|
||||||
<span>{lang(isEmptyThread
|
<span>{oldLang(isEmptyThread
|
||||||
? (isComments ? 'NoComments' : 'NoReplies') : 'DiscussionStarted')}
|
? (isComments ? 'NoComments' : 'NoReplies') : 'DiscussionStarted')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -301,7 +305,8 @@ const MessageListContent: FC<OwnProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={buildClassName('message-date-group', dateGroupIndex === 0 && 'first-message-date-group')}
|
className={buildClassName('message-date-group', !(nameChangeDate || photoChangeDate)
|
||||||
|
&& dateGroupIndex === 0 && 'first-message-date-group')}
|
||||||
key={dateGroup.datetime}
|
key={dateGroup.datetime}
|
||||||
onMouseDown={preventMessageInputBlur}
|
onMouseDown={preventMessageInputBlur}
|
||||||
teactFastList
|
teactFastList
|
||||||
@ -314,12 +319,12 @@ const MessageListContent: FC<OwnProps> = ({
|
|||||||
>
|
>
|
||||||
<span dir="auto">
|
<span dir="auto">
|
||||||
{isSchedule && dateGroup.originalDate === SCHEDULED_WHEN_ONLINE && (
|
{isSchedule && dateGroup.originalDate === SCHEDULED_WHEN_ONLINE && (
|
||||||
lang('MessageScheduledUntilOnline')
|
oldLang('MessageScheduledUntilOnline')
|
||||||
)}
|
)}
|
||||||
{isSchedule && dateGroup.originalDate !== SCHEDULED_WHEN_ONLINE && (
|
{isSchedule && dateGroup.originalDate !== SCHEDULED_WHEN_ONLINE && (
|
||||||
lang('MessageScheduledOn', formatHumanDate(lang, dateGroup.datetime, undefined, true))
|
oldLang('MessageScheduledOn', formatHumanDate(oldLang, dateGroup.datetime, undefined, true))
|
||||||
)}
|
)}
|
||||||
{!isSchedule && formatHumanDate(lang, dateGroup.datetime)}
|
{!isSchedule && formatHumanDate(oldLang, dateGroup.datetime)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{senderGroups.flat()}
|
{senderGroups.flat()}
|
||||||
@ -330,7 +335,8 @@ const MessageListContent: FC<OwnProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="messages-container" teactFastList>
|
<div className="messages-container" teactFastList>
|
||||||
{withHistoryTriggers && <div ref={backwardsTriggerRef} key="backwards-trigger" className="backwards-trigger" />}
|
{withHistoryTriggers && <div ref={backwardsTriggerRef} key="backwards-trigger" className="backwards-trigger" />}
|
||||||
{shouldRenderBotInfo && <MessageListBotInfo isInMessageList key={`bot_info_${chatId}`} chatId={chatId} />}
|
{shouldRenderAccountInfo
|
||||||
|
&& <MessageListAccountInfo isInMessageList key={`account_info_${chatId}`} chatId={chatId} />}
|
||||||
{dateGroups.flat()}
|
{dateGroups.flat()}
|
||||||
{withHistoryTriggers && (
|
{withHistoryTriggers && (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -138,7 +138,7 @@ type StateProps = {
|
|||||||
shouldSkipHistoryAnimations?: boolean;
|
shouldSkipHistoryAnimations?: boolean;
|
||||||
currentTransitionKey: number;
|
currentTransitionKey: number;
|
||||||
isChannel?: boolean;
|
isChannel?: boolean;
|
||||||
areChatSettingsLoaded?: boolean;
|
arePeerSettingsLoaded?: boolean;
|
||||||
canSubscribe?: boolean;
|
canSubscribe?: boolean;
|
||||||
canStartBot?: boolean;
|
canStartBot?: boolean;
|
||||||
canRestartBot?: boolean;
|
canRestartBot?: boolean;
|
||||||
@ -198,7 +198,7 @@ function MiddleColumn({
|
|||||||
shouldSkipHistoryAnimations,
|
shouldSkipHistoryAnimations,
|
||||||
currentTransitionKey,
|
currentTransitionKey,
|
||||||
isChannel,
|
isChannel,
|
||||||
areChatSettingsLoaded,
|
arePeerSettingsLoaded,
|
||||||
canSubscribe,
|
canSubscribe,
|
||||||
canStartBot,
|
canStartBot,
|
||||||
canRestartBot,
|
canRestartBot,
|
||||||
@ -219,7 +219,7 @@ function MiddleColumn({
|
|||||||
openPreviousChat,
|
openPreviousChat,
|
||||||
unpinAllMessages,
|
unpinAllMessages,
|
||||||
loadUser,
|
loadUser,
|
||||||
loadChatSettings,
|
loadPeerSettings,
|
||||||
exitMessageSelectMode,
|
exitMessageSelectMode,
|
||||||
joinChannel,
|
joinChannel,
|
||||||
sendBotCommand,
|
sendBotCommand,
|
||||||
@ -339,10 +339,10 @@ function MiddleColumn({
|
|||||||
}, [chatId, isPrivate, loadUser]);
|
}, [chatId, isPrivate, loadUser]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!areChatSettingsLoaded) {
|
if (!arePeerSettingsLoaded) {
|
||||||
loadChatSettings({ chatId: chatId! });
|
loadPeerSettings({ peerId: chatId! });
|
||||||
}
|
}
|
||||||
}, [chatId, isPrivate, areChatSettingsLoaded]);
|
}, [chatId, isPrivate, arePeerSettingsLoaded]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (chatId && shouldLoadFullChat && isReady) {
|
if (chatId && shouldLoadFullChat && isReady) {
|
||||||
@ -762,6 +762,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const bot = selectBot(global, chatId);
|
const bot = selectBot(global, chatId);
|
||||||
const pinnedIds = selectPinnedIds(global, chatId, threadId);
|
const pinnedIds = selectPinnedIds(global, chatId, threadId);
|
||||||
const chatFullInfo = chatId ? selectChatFullInfo(global, chatId) : undefined;
|
const chatFullInfo = chatId ? selectChatFullInfo(global, chatId) : undefined;
|
||||||
|
const userFullInfo = chatId ? selectUserFullInfo(global, chatId) : undefined;
|
||||||
|
|
||||||
const threadInfo = selectThreadInfo(global, chatId, threadId);
|
const threadInfo = selectThreadInfo(global, chatId, threadId);
|
||||||
const isMessageThread = Boolean(!threadInfo?.isCommentsInfo && threadInfo?.fromChannelId);
|
const isMessageThread = Boolean(!threadInfo?.isCommentsInfo && threadInfo?.fromChannelId);
|
||||||
@ -809,7 +810,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
chat,
|
chat,
|
||||||
draftReplyInfo,
|
draftReplyInfo,
|
||||||
isPrivate,
|
isPrivate,
|
||||||
areChatSettingsLoaded: Boolean(chat?.settings),
|
arePeerSettingsLoaded: Boolean(userFullInfo?.settings),
|
||||||
isComments: isMessageThread,
|
isComments: isMessageThread,
|
||||||
canPost:
|
canPost:
|
||||||
!isPinnedMessageList
|
!isPinnedMessageList
|
||||||
|
|||||||
@ -4,12 +4,13 @@ import React, {
|
|||||||
import { setExtraStyles } from '../../lib/teact/teact-dom';
|
import { setExtraStyles } from '../../lib/teact/teact-dom';
|
||||||
import { withGlobal } from '../../global';
|
import { withGlobal } from '../../global';
|
||||||
|
|
||||||
|
import type { ApiChat, ApiUserFullInfo } from '../../api/types';
|
||||||
import type { MessageListType, ThreadId } from '../../types';
|
import type { MessageListType, ThreadId } from '../../types';
|
||||||
import type { Signal } from '../../util/signals';
|
import type { Signal } from '../../util/signals';
|
||||||
import { type ApiChat, MAIN_THREAD_ID } from '../../api/types';
|
import { MAIN_THREAD_ID } from '../../api/types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
selectChat, selectChatMessage, selectCurrentMiddleSearch, selectTabState,
|
selectChat, selectChatMessage, selectCurrentMiddleSearch, selectTabState, selectUserFullInfo,
|
||||||
} from '../../global/selectors';
|
} from '../../global/selectors';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ type OwnProps = {
|
|||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
chat?: ApiChat;
|
chat?: ApiChat;
|
||||||
|
userFullInfo?: ApiUserFullInfo;
|
||||||
isAudioPlayerRendered?: boolean;
|
isAudioPlayerRendered?: boolean;
|
||||||
isMiddleSearchOpen?: boolean;
|
isMiddleSearchOpen?: boolean;
|
||||||
};
|
};
|
||||||
@ -52,13 +54,14 @@ const MiddleHeaderPanes = ({
|
|||||||
threadId,
|
threadId,
|
||||||
messageListType,
|
messageListType,
|
||||||
chat,
|
chat,
|
||||||
|
userFullInfo,
|
||||||
getCurrentPinnedIndex,
|
getCurrentPinnedIndex,
|
||||||
getLoadingPinnedId,
|
getLoadingPinnedId,
|
||||||
isAudioPlayerRendered,
|
isAudioPlayerRendered,
|
||||||
isMiddleSearchOpen,
|
isMiddleSearchOpen,
|
||||||
onFocusPinnedMessage,
|
onFocusPinnedMessage,
|
||||||
}: OwnProps & StateProps) => {
|
}: OwnProps & StateProps) => {
|
||||||
const { settings } = chat || {};
|
const { settings } = userFullInfo || {};
|
||||||
|
|
||||||
const { isDesktop } = useAppLayout();
|
const { isDesktop } = useAppLayout();
|
||||||
const [getAudioPlayerState, setAudioPlayerState] = useSignal<PaneState>(FALLBACK_PANE_STATE);
|
const [getAudioPlayerState, setAudioPlayerState] = useSignal<PaneState>(FALLBACK_PANE_STATE);
|
||||||
@ -163,6 +166,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
}): StateProps => {
|
}): StateProps => {
|
||||||
const { audioPlayer } = selectTabState(global);
|
const { audioPlayer } = selectTabState(global);
|
||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
|
const userFullInfo = selectUserFullInfo(global, chatId);
|
||||||
|
|
||||||
const { chatId: audioChatId, messageId: audioMessageId } = audioPlayer;
|
const { chatId: audioChatId, messageId: audioMessageId } = audioPlayer;
|
||||||
const audioMessage = audioChatId && audioMessageId
|
const audioMessage = audioChatId && audioMessageId
|
||||||
@ -173,6 +177,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
chat,
|
chat,
|
||||||
|
userFullInfo,
|
||||||
isAudioPlayerRendered: Boolean(audioMessage),
|
isAudioPlayerRendered: Boolean(audioMessage),
|
||||||
isMiddleSearchOpen,
|
isMiddleSearchOpen,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import {
|
|||||||
isGeoLiveExpired,
|
isGeoLiveExpired,
|
||||||
} from '../../../global/helpers';
|
} from '../../../global/helpers';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import { formatCountdownShort, formatLastUpdated } from '../../../util/dates/dateFormat';
|
import { formatCountdownShort, formatLocationLastUpdate } from '../../../util/dates/dateFormat';
|
||||||
import {
|
import {
|
||||||
getMetersPerPixel, getVenueColor, getVenueIconUrl,
|
getMetersPerPixel, getVenueColor, getVenueIconUrl,
|
||||||
} from '../../../util/map';
|
} from '../../../util/map';
|
||||||
@ -161,7 +161,7 @@ const Location: FC<OwnProps> = ({
|
|||||||
<div className="location-info">
|
<div className="location-info">
|
||||||
<div className="location-info-title">{lang('AttachLiveLocation')}</div>
|
<div className="location-info-title">{lang('AttachLiveLocation')}</div>
|
||||||
<div className="location-info-subtitle">
|
<div className="location-info-subtitle">
|
||||||
{formatLastUpdated(lang, serverTime, message.editDate)}
|
{formatLocationLastUpdate(lang, serverTime, message.editDate)}
|
||||||
</div>
|
</div>
|
||||||
{!isExpired && (
|
{!isExpired && (
|
||||||
<div className="geo-countdown" ref={countdownRef}>
|
<div className="geo-countdown" ref={countdownRef}>
|
||||||
|
|||||||
@ -59,7 +59,7 @@ const ChatReportPane: FC<OwnProps & StateProps> = ({
|
|||||||
deleteChatUser,
|
deleteChatUser,
|
||||||
deleteHistory,
|
deleteHistory,
|
||||||
toggleChatArchived,
|
toggleChatArchived,
|
||||||
hideChatReportPane,
|
hidePeerSettingsBar,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const lang = useOldLang();
|
const lang = useOldLang();
|
||||||
@ -96,7 +96,7 @@ const ChatReportPane: FC<OwnProps & StateProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleCloseReportPane = useLastCallback(() => {
|
const handleCloseReportPane = useLastCallback(() => {
|
||||||
hideChatReportPane({ chatId });
|
hidePeerSettingsBar({ peerId: chatId });
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleChatReportSpam = useLastCallback(() => {
|
const handleChatReportSpam = useLastCallback(() => {
|
||||||
|
|||||||
@ -24,10 +24,11 @@ import {
|
|||||||
selectPerformanceSettingsValue,
|
selectPerformanceSettingsValue,
|
||||||
selectTabState,
|
selectTabState,
|
||||||
selectUser,
|
selectUser,
|
||||||
|
selectUserFullInfo,
|
||||||
} from '../../global/selectors';
|
} from '../../global/selectors';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import captureKeyboardListeners from '../../util/captureKeyboardListeners';
|
import captureKeyboardListeners from '../../util/captureKeyboardListeners';
|
||||||
import { formatMediaDuration, formatRelativeTime } from '../../util/dates/dateFormat';
|
import { formatMediaDuration, formatRelativePastTime } from '../../util/dates/dateFormat';
|
||||||
import download from '../../util/download';
|
import download from '../../util/download';
|
||||||
import { round } from '../../util/math';
|
import { round } from '../../util/math';
|
||||||
import { getServerTime } from '../../util/serverTime';
|
import { getServerTime } from '../../util/serverTime';
|
||||||
@ -94,7 +95,7 @@ interface StateProps {
|
|||||||
storyChangelogUserId?: string;
|
storyChangelogUserId?: string;
|
||||||
viewersExpirePeriod: number;
|
viewersExpirePeriod: number;
|
||||||
isChatExist?: boolean;
|
isChatExist?: boolean;
|
||||||
areChatSettingsLoaded?: boolean;
|
arePeerSettingsLoaded?: boolean;
|
||||||
isCurrentUserPremium?: boolean;
|
isCurrentUserPremium?: boolean;
|
||||||
stealthMode: ApiStealthMode;
|
stealthMode: ApiStealthMode;
|
||||||
withHeaderAnimation?: boolean;
|
withHeaderAnimation?: boolean;
|
||||||
@ -122,7 +123,7 @@ function Story({
|
|||||||
storyChangelogUserId,
|
storyChangelogUserId,
|
||||||
viewersExpirePeriod,
|
viewersExpirePeriod,
|
||||||
isChatExist,
|
isChatExist,
|
||||||
areChatSettingsLoaded,
|
arePeerSettingsLoaded,
|
||||||
getIsAnimating,
|
getIsAnimating,
|
||||||
isCurrentUserPremium,
|
isCurrentUserPremium,
|
||||||
stealthMode,
|
stealthMode,
|
||||||
@ -143,7 +144,7 @@ function Story({
|
|||||||
openChat,
|
openChat,
|
||||||
showNotification,
|
showNotification,
|
||||||
openStoryPrivacyEditor,
|
openStoryPrivacyEditor,
|
||||||
loadChatSettings,
|
loadPeerSettings,
|
||||||
fetchChat,
|
fetchChat,
|
||||||
loadStoryViews,
|
loadStoryViews,
|
||||||
toggleStealthModal,
|
toggleStealthModal,
|
||||||
@ -279,10 +280,10 @@ function Story({
|
|||||||
}
|
}
|
||||||
}, [isChatExist, peerId]);
|
}, [isChatExist, peerId]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isChatExist && !areChatSettingsLoaded) {
|
if (isChatExist && !arePeerSettingsLoaded) {
|
||||||
loadChatSettings({ chatId: peerId });
|
loadPeerSettings({ peerId });
|
||||||
}
|
}
|
||||||
}, [areChatSettingsLoaded, isChatExist, peerId]);
|
}, [arePeerSettingsLoaded, isChatExist, peerId]);
|
||||||
|
|
||||||
const handlePauseStory = useLastCallback(() => {
|
const handlePauseStory = useLastCallback(() => {
|
||||||
if (isVideo) {
|
if (isVideo) {
|
||||||
@ -672,7 +673,7 @@ function Story({
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{story && 'date' in story && (
|
{story && 'date' in story && (
|
||||||
<span className={styles.storyMeta}>{formatRelativeTime(lang, serverTime, story.date)}</span>
|
<span className={styles.storyMeta}>{formatRelativePastTime(lang, serverTime, story.date)}</span>
|
||||||
)}
|
)}
|
||||||
{isLoadedStory && story.isEdited && (
|
{isLoadedStory && story.isEdited && (
|
||||||
<span className={styles.storyMeta}>{lang('Story.HeaderEdited')}</span>
|
<span className={styles.storyMeta}>{lang('Story.HeaderEdited')}</span>
|
||||||
@ -906,6 +907,7 @@ export default memo(withGlobal<OwnProps>((global, {
|
|||||||
const { appConfig } = global;
|
const { appConfig } = global;
|
||||||
const user = selectUser(global, peerId);
|
const user = selectUser(global, peerId);
|
||||||
const chat = selectChat(global, peerId);
|
const chat = selectChat(global, peerId);
|
||||||
|
const userFullInfo = selectUserFullInfo(global, peerId);
|
||||||
const tabState = selectTabState(global);
|
const tabState = selectTabState(global);
|
||||||
const {
|
const {
|
||||||
storyViewer: {
|
storyViewer: {
|
||||||
@ -951,7 +953,7 @@ export default memo(withGlobal<OwnProps>((global, {
|
|||||||
storyChangelogUserId: appConfig!.storyChangelogUserId,
|
storyChangelogUserId: appConfig!.storyChangelogUserId,
|
||||||
viewersExpirePeriod: appConfig!.storyExpirePeriod + appConfig!.storyViewersExpirePeriod,
|
viewersExpirePeriod: appConfig!.storyExpirePeriod + appConfig!.storyViewersExpirePeriod,
|
||||||
isChatExist: Boolean(chat),
|
isChatExist: Boolean(chat),
|
||||||
areChatSettingsLoaded: Boolean(chat?.settings),
|
arePeerSettingsLoaded: Boolean(userFullInfo?.settings),
|
||||||
stealthMode: global.stories.stealthMode,
|
stealthMode: global.stories.stealthMode,
|
||||||
withHeaderAnimation,
|
withHeaderAnimation,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2130,21 +2130,6 @@ addActionHandler('fetchChat', (global, actions, payload): ActionReturnType => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('loadChatSettings', async (global, actions, payload): Promise<void> => {
|
|
||||||
const { chatId } = payload;
|
|
||||||
const chat = selectChat(global, chatId);
|
|
||||||
if (!chat) return;
|
|
||||||
|
|
||||||
const result = await callApi('fetchChatSettings', chat);
|
|
||||||
if (!result) return;
|
|
||||||
|
|
||||||
const { settings } = result;
|
|
||||||
|
|
||||||
global = getGlobal();
|
|
||||||
global = updateChat(global, chat.id, { settings });
|
|
||||||
setGlobal(global);
|
|
||||||
});
|
|
||||||
|
|
||||||
addActionHandler('toggleJoinToSend', async (global, actions, payload): Promise<void> => {
|
addActionHandler('toggleJoinToSend', async (global, actions, payload): Promise<void> => {
|
||||||
const { chatId, isEnabled } = payload;
|
const { chatId, isEnabled } = payload;
|
||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { callApi } from '../../../api/gramjs';
|
|||||||
import { getUserFirstOrLastName } from '../../helpers';
|
import { getUserFirstOrLastName } from '../../helpers';
|
||||||
import { addActionHandler, getGlobal, setGlobal } from '../../index';
|
import { addActionHandler, getGlobal, setGlobal } from '../../index';
|
||||||
import {
|
import {
|
||||||
updateChat, updateChatFullInfo, updateManagement, updateManagementProgress,
|
updateChat, updateChatFullInfo, updateManagement, updateManagementProgress, updateUserFullInfo,
|
||||||
} from '../../reducers';
|
} from '../../reducers';
|
||||||
import {
|
import {
|
||||||
selectChat, selectCurrentMessageList, selectTabState, selectUser,
|
selectChat, selectCurrentMessageList, selectTabState, selectUser,
|
||||||
@ -394,16 +394,16 @@ addActionHandler('hideAllChatJoinRequests', async (global, actions, payload): Pr
|
|||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('hideChatReportPane', async (global, actions, payload): Promise<void> => {
|
addActionHandler('hidePeerSettingsBar', async (global, actions, payload): Promise<void> => {
|
||||||
const { chatId } = payload!;
|
const { peerId } = payload!;
|
||||||
const chat = selectChat(global, chatId);
|
const user = selectUser(global, peerId);
|
||||||
if (!chat) return;
|
if (!user) return;
|
||||||
|
|
||||||
const result = await callApi('hideChatReportPane', chat);
|
const result = await callApi('hidePeerSettingsBar', user);
|
||||||
if (!result) return;
|
if (!result) return;
|
||||||
|
|
||||||
global = getGlobal();
|
global = getGlobal();
|
||||||
global = updateChat(global, chatId, {
|
global = updateUserFullInfo(global, peerId, {
|
||||||
settings: undefined,
|
settings: undefined,
|
||||||
});
|
});
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
|
|||||||
@ -217,7 +217,7 @@ addActionHandler('updateContact', async (global, actions, payload): Promise<void
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
actions.loadChatSettings({ chatId: userId });
|
actions.loadPeerSettings({ peerId: userId });
|
||||||
actions.loadPeerStories({ peerId: userId });
|
actions.loadPeerStories({ peerId: userId });
|
||||||
|
|
||||||
global = getGlobal();
|
global = getGlobal();
|
||||||
@ -506,6 +506,30 @@ addActionHandler('openSuggestedStatusModal', async (global, actions, payload): P
|
|||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('loadPeerSettings', async (global, actions, payload): Promise<void> => {
|
||||||
|
const { peerId } = payload;
|
||||||
|
|
||||||
|
const userFullInfo = selectUserFullInfo(global, peerId);
|
||||||
|
if (!userFullInfo) {
|
||||||
|
actions.loadFullUser({ userId: peerId });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = selectUser(global, peerId);
|
||||||
|
if (!user) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await callApi('fetchPeerSettings', user);
|
||||||
|
if (!result) return;
|
||||||
|
|
||||||
|
const { settings } = result;
|
||||||
|
|
||||||
|
global = getGlobal();
|
||||||
|
global = updateUserFullInfo(global, peerId, { settings });
|
||||||
|
setGlobal(global);
|
||||||
|
});
|
||||||
|
|
||||||
addActionHandler('markBotVerificationInfoShown', (global, actions, payload): ActionReturnType => {
|
addActionHandler('markBotVerificationInfoShown', (global, actions, payload): ActionReturnType => {
|
||||||
const { peerId } = payload;
|
const { peerId } = payload;
|
||||||
|
|
||||||
|
|||||||
@ -106,6 +106,21 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'updatePeerSettings': {
|
||||||
|
const { id, settings } = update;
|
||||||
|
|
||||||
|
const targetUserFullInfo = selectUserFullInfo(global, id);
|
||||||
|
if (!targetUserFullInfo?.botInfo) {
|
||||||
|
actions.loadFullUser({ userId: id });
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
global = updateUserFullInfo(global, id, {
|
||||||
|
settings,
|
||||||
|
});
|
||||||
|
return global;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@ -14,7 +14,6 @@ import { getCurrentTabId } from '../../util/establishMultitabRole';
|
|||||||
import { omit, omitUndefined, unique } from '../../util/iteratees';
|
import { omit, omitUndefined, unique } from '../../util/iteratees';
|
||||||
import { MEMO_EMPTY_ARRAY } from '../../util/memo';
|
import { MEMO_EMPTY_ARRAY } from '../../util/memo';
|
||||||
import { selectTabState } from '../selectors';
|
import { selectTabState } from '../selectors';
|
||||||
import { updateChat } from './chats';
|
|
||||||
import { updateTabState } from './tabs';
|
import { updateTabState } from './tabs';
|
||||||
|
|
||||||
export function replaceUsers<T extends GlobalState>(global: T, newById: Record<string, ApiUser>): T {
|
export function replaceUsers<T extends GlobalState>(global: T, newById: Record<string, ApiUser>): T {
|
||||||
@ -179,7 +178,7 @@ export function deleteContact<T extends GlobalState>(global: T, userId: string):
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return updateChat(global, userId, {
|
return updateUserFullInfo(global, userId, {
|
||||||
settings: undefined,
|
settings: undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -918,8 +918,8 @@ export interface ActionPayloads {
|
|||||||
offsetUserId?: string;
|
offsetUserId?: string;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
hideChatReportPane: {
|
hidePeerSettingsBar: {
|
||||||
chatId: string;
|
peerId: string;
|
||||||
};
|
};
|
||||||
toggleManagement: ({
|
toggleManagement: ({
|
||||||
force?: boolean;
|
force?: boolean;
|
||||||
@ -1032,8 +1032,8 @@ export interface ActionPayloads {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Chats
|
// Chats
|
||||||
loadChatSettings: {
|
loadPeerSettings: {
|
||||||
chatId: string;
|
peerId: string;
|
||||||
};
|
};
|
||||||
fetchChat: {
|
fetchChat: {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
|
|||||||
@ -58,6 +58,8 @@ $color-message-reaction-own-hover: #b5e0a4;
|
|||||||
$color-message-reaction-chosen-hover: #1a82ea;
|
$color-message-reaction-chosen-hover: #1a82ea;
|
||||||
$color-message-reaction-chosen-hover-own: #3f9d4b;
|
$color-message-reaction-chosen-hover-own: #3f9d4b;
|
||||||
|
|
||||||
|
$color-message-non-contact: #cceebf;
|
||||||
|
|
||||||
$color-message-story-mention-from: #4ef390;
|
$color-message-story-mention-from: #4ef390;
|
||||||
$color-message-story-mention-to: #74bcff;
|
$color-message-story-mention-to: #74bcff;
|
||||||
|
|
||||||
@ -151,6 +153,8 @@ $color-message-story-mention-to: #74bcff;
|
|||||||
--color-message-reaction-chosen-hover: $color-message-reaction-chosen-hover;
|
--color-message-reaction-chosen-hover: $color-message-reaction-chosen-hover;
|
||||||
--color-message-reaction-chosen-hover-own: $color-message-reaction-chosen-hover-own;
|
--color-message-reaction-chosen-hover-own: $color-message-reaction-chosen-hover-own;
|
||||||
|
|
||||||
|
--color-message-non-contact: $color-message-non-contact;
|
||||||
|
|
||||||
--color-message-story-mention-from: $color-message-story-mention-from;
|
--color-message-story-mention-from: $color-message-story-mention-from;
|
||||||
--color-message-story-mention-to: $color-message-story-mention-to;
|
--color-message-story-mention-to: $color-message-story-mention-to;
|
||||||
|
|
||||||
|
|||||||
@ -400,6 +400,7 @@ body:not(.is-ios) {
|
|||||||
--color-message-reaction-hover-own: rgb(91, 82, 155);
|
--color-message-reaction-hover-own: rgb(91, 82, 155);
|
||||||
--color-message-reaction-chosen-hover: rgb(120, 100, 221);
|
--color-message-reaction-chosen-hover: rgb(120, 100, 221);
|
||||||
--color-message-reaction-chosen-hover-own: rgb(245, 245, 245);
|
--color-message-reaction-chosen-hover-own: rgb(245, 245, 245);
|
||||||
|
--color-message-non-contact: rgb(204, 238, 191);
|
||||||
--color-voice-transcribe-button: rgb(42, 42, 60);
|
--color-voice-transcribe-button: rgb(42, 42, 60);
|
||||||
--color-voice-transcribe-button-own: rgb(131, 115, 211);
|
--color-voice-transcribe-button-own: rgb(131, 115, 211);
|
||||||
--color-topic-blue: rgb(111, 249, 240);
|
--color-topic-blue: rgb(111, 249, 240);
|
||||||
|
|||||||
@ -57,6 +57,7 @@
|
|||||||
"--color-message-reaction-hover-own": ["#b5e0a4", "#5B529B"],
|
"--color-message-reaction-hover-own": ["#b5e0a4", "#5B529B"],
|
||||||
"--color-message-reaction-chosen-hover": ["#1a82ea", "#7864dd"],
|
"--color-message-reaction-chosen-hover": ["#1a82ea", "#7864dd"],
|
||||||
"--color-message-reaction-chosen-hover-own": ["#3f9d4b", "#f5f5f5"],
|
"--color-message-reaction-chosen-hover-own": ["#3f9d4b", "#f5f5f5"],
|
||||||
|
"--color-message-non-contact": ["#cceebf", "#AAAAAA"],
|
||||||
"--color-voice-transcribe-button": ["#e8f3ff", "#2a2a3c"],
|
"--color-voice-transcribe-button": ["#e8f3ff", "#2a2a3c"],
|
||||||
"--color-voice-transcribe-button-own": ["#cceebf", "#8373d3"],
|
"--color-voice-transcribe-button-own": ["#cceebf", "#8373d3"],
|
||||||
"--color-topic-blue": ["#2F7772", "#6ff9f0"],
|
"--color-topic-blue": ["#2F7772", "#6ff9f0"],
|
||||||
|
|||||||
29
src/types/language.d.ts
vendored
29
src/types/language.d.ts
vendored
@ -224,6 +224,10 @@ export interface LangPair {
|
|||||||
'WalletAddressCopied': undefined;
|
'WalletAddressCopied': undefined;
|
||||||
'Copy': undefined;
|
'Copy': undefined;
|
||||||
'DeleteAndStop': undefined;
|
'DeleteAndStop': undefined;
|
||||||
|
'ChatNonContactUserSubtitle': undefined;
|
||||||
|
'ChatNonContactUserGroups': undefined;
|
||||||
|
'ContactInfoRegistration': undefined;
|
||||||
|
'ContactInfoNotVerified': undefined;
|
||||||
'DeleteForAll': undefined;
|
'DeleteForAll': undefined;
|
||||||
'DeleteSingleMessagesTitle': undefined;
|
'DeleteSingleMessagesTitle': undefined;
|
||||||
'AreYouSureDeleteSingleMessage': undefined;
|
'AreYouSureDeleteSingleMessage': undefined;
|
||||||
@ -961,6 +965,7 @@ export interface LangPair {
|
|||||||
'WeekdaySaturday': undefined;
|
'WeekdaySaturday': undefined;
|
||||||
'WeekdaySunday': undefined;
|
'WeekdaySunday': undefined;
|
||||||
'WeekdayToday': undefined;
|
'WeekdayToday': undefined;
|
||||||
|
'Today': undefined;
|
||||||
'WeekdayYesterday': undefined;
|
'WeekdayYesterday': undefined;
|
||||||
'User': undefined;
|
'User': undefined;
|
||||||
'SecretChat': undefined;
|
'SecretChat': undefined;
|
||||||
@ -1010,6 +1015,7 @@ export interface LangPair {
|
|||||||
'VoipIncoming': undefined;
|
'VoipIncoming': undefined;
|
||||||
'LiveLocationUpdatedJustNow': undefined;
|
'LiveLocationUpdatedJustNow': undefined;
|
||||||
'RightNow': undefined;
|
'RightNow': undefined;
|
||||||
|
'JustNowAgo': undefined;
|
||||||
'AudioPause': undefined;
|
'AudioPause': undefined;
|
||||||
'AudioPlay': undefined;
|
'AudioPlay': undefined;
|
||||||
'ToggleUserNotifications': undefined;
|
'ToggleUserNotifications': undefined;
|
||||||
@ -1721,6 +1727,9 @@ export interface LangPairWithVariables<V extends unknown = LangVariable> {
|
|||||||
'LiveLocationUpdatedTodayAt': {
|
'LiveLocationUpdatedTodayAt': {
|
||||||
'time': V;
|
'time': V;
|
||||||
};
|
};
|
||||||
|
'AtDateAgo': {
|
||||||
|
'date': V;
|
||||||
|
};
|
||||||
'MediaViewDownloading': {
|
'MediaViewDownloading': {
|
||||||
'count': V;
|
'count': V;
|
||||||
};
|
};
|
||||||
@ -1792,6 +1801,14 @@ export interface LangPairWithVariables<V extends unknown = LangVariable> {
|
|||||||
'peer': V;
|
'peer': V;
|
||||||
'amount': V;
|
'amount': V;
|
||||||
};
|
};
|
||||||
|
'UserUpdatedName': {
|
||||||
|
'user': V;
|
||||||
|
'time': V;
|
||||||
|
};
|
||||||
|
'UserUpdatedPhoto': {
|
||||||
|
'user': V;
|
||||||
|
'time': V;
|
||||||
|
};
|
||||||
'GiftInfoSaved': {
|
'GiftInfoSaved': {
|
||||||
'link': V;
|
'link': V;
|
||||||
};
|
};
|
||||||
@ -2394,6 +2411,15 @@ export interface LangPairPluralWithVariables<V extends unknown = LangVariable> {
|
|||||||
'Minutes': {
|
'Minutes': {
|
||||||
'count': V;
|
'count': V;
|
||||||
};
|
};
|
||||||
|
'MinutesAgo': {
|
||||||
|
'count': V;
|
||||||
|
};
|
||||||
|
'HoursAgo': {
|
||||||
|
'count': V;
|
||||||
|
};
|
||||||
|
'DaysAgo': {
|
||||||
|
'count': V;
|
||||||
|
};
|
||||||
'PreviewForwardedMessage': {
|
'PreviewForwardedMessage': {
|
||||||
'count': V;
|
'count': V;
|
||||||
};
|
};
|
||||||
@ -2436,6 +2462,9 @@ export interface LangPairPluralWithVariables<V extends unknown = LangVariable> {
|
|||||||
'GiftWithdrawWait': {
|
'GiftWithdrawWait': {
|
||||||
'days': V;
|
'days': V;
|
||||||
};
|
};
|
||||||
|
'ChatGroups': {
|
||||||
|
'count': V;
|
||||||
|
};
|
||||||
'StarsAmountText': {
|
'StarsAmountText': {
|
||||||
'amount': V;
|
'amount': V;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,8 +2,9 @@ import type { OldLangFn } from '../../hooks/useOldLang';
|
|||||||
import type { TimeFormat } from '../../types';
|
import type { TimeFormat } from '../../types';
|
||||||
import type { LangFn } from '../localization';
|
import type { LangFn } from '../localization';
|
||||||
|
|
||||||
|
import { getServerTime } from '../serverTime';
|
||||||
import withCache from '../withCache';
|
import withCache from '../withCache';
|
||||||
import { getDays } from './units';
|
import { getDays, getHours, getMinutes } from './units';
|
||||||
|
|
||||||
const WEEKDAYS_FULL = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
const WEEKDAYS_FULL = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
||||||
const MONTHS_FULL = [
|
const MONTHS_FULL = [
|
||||||
@ -78,11 +79,11 @@ export function formatPastTimeShort(lang: OldLangFn, datetime: number | Date, al
|
|||||||
return alwaysShowTime ? lang('FullDateTimeFormat', [formattedDate, time]) : formattedDate;
|
return alwaysShowTime ? lang('FullDateTimeFormat', [formattedDate, time]) : formattedDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatFullDate(lang: OldLangFn, datetime: number | Date) {
|
export function formatFullDate(lang: OldLangFn | LangFn, datetime: number | Date) {
|
||||||
return formatDateToString(datetime, lang.code, false, 'numeric');
|
return formatDateToString(datetime, lang.code, false, 'numeric');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatMonthAndYear(lang: OldLangFn, date: Date, isShort = false) {
|
export function formatMonthAndYear(lang: OldLangFn | LangFn, date: Date, isShort = false) {
|
||||||
return formatDateToString(date, lang.code, false, isShort ? 'short' : 'long', true);
|
return formatDateToString(date, lang.code, false, isShort ? 'short' : 'long', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +123,7 @@ export function formatCountdownShort(lang: OldLangFn, msLeft: number): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatLastUpdated(lang: OldLangFn, currentTime: number, lastUpdated = currentTime) {
|
export function formatLocationLastUpdate(lang: OldLangFn, currentTime: number, lastUpdated = currentTime) {
|
||||||
const seconds = currentTime - lastUpdated;
|
const seconds = currentTime - lastUpdated;
|
||||||
if (seconds < 60) {
|
if (seconds < 60) {
|
||||||
return lang('LiveLocationUpdated.JustNow');
|
return lang('LiveLocationUpdated.JustNow');
|
||||||
@ -133,7 +134,7 @@ export function formatLastUpdated(lang: OldLangFn, currentTime: number, lastUpda
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatRelativeTime(lang: OldLangFn, currentTime: number, lastUpdated = currentTime) {
|
export function formatRelativePastTime(lang: OldLangFn, currentTime: number, lastUpdated = currentTime) {
|
||||||
const seconds = currentTime - lastUpdated;
|
const seconds = currentTime - lastUpdated;
|
||||||
|
|
||||||
if (seconds < 60) {
|
if (seconds < 60) {
|
||||||
@ -160,6 +161,31 @@ export function formatRelativeTime(lang: OldLangFn, currentTime: number, lastUpd
|
|||||||
return lang('Time.AtDate', formatFullDate(lang, lastUpdatedDate));
|
return lang('Time.AtDate', formatFullDate(lang, lastUpdatedDate));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatPastDatetime(lang: LangFn, pastTime: number, currentTime = getServerTime()) {
|
||||||
|
const seconds = currentTime - pastTime;
|
||||||
|
const minutes = getMinutes(seconds);
|
||||||
|
const hours = getHours(seconds);
|
||||||
|
const days = getDays(seconds);
|
||||||
|
|
||||||
|
if (seconds < 60) {
|
||||||
|
return lang('JustNowAgo');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minutes < 60) {
|
||||||
|
return lang('MinutesAgo', { count: minutes }, { pluralValue: minutes });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hours < 24) {
|
||||||
|
return lang('HoursAgo', { count: hours }, { pluralValue: hours });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (days < 28) {
|
||||||
|
return lang('DaysAgo', { count: days }, { pluralValue: days });
|
||||||
|
}
|
||||||
|
|
||||||
|
return lang('AtDateAgo', { date: formatFullDate(lang, pastTime) });
|
||||||
|
}
|
||||||
|
|
||||||
type DurationType = 'Seconds' | 'Minutes' | 'Hours' | 'Days' | 'Weeks';
|
type DurationType = 'Seconds' | 'Minutes' | 'Hours' | 'Days' | 'Weeks';
|
||||||
|
|
||||||
export function formatTimeDuration(lang: OldLangFn, duration: number, showLast = 2) {
|
export function formatTimeDuration(lang: OldLangFn, duration: number, showLast = 2) {
|
||||||
@ -449,3 +475,10 @@ function lowerFirst(str: string) {
|
|||||||
function upperFirst(str: string) {
|
function upperFirst(str: string) {
|
||||||
return `${str[0].toUpperCase()}${str.slice(1)}`;
|
return `${str[0].toUpperCase()}${str.slice(1)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatRegistrationMonth(lang: string, dateString: string) {
|
||||||
|
const [month, year] = dateString.split('.');
|
||||||
|
const date = new Date(`${year}-${month}`);
|
||||||
|
|
||||||
|
return new Intl.DateTimeFormat(lang, { month: 'long', year: 'numeric' }).format(date);
|
||||||
|
}
|
||||||
|
|||||||
@ -429,7 +429,7 @@ function processTranslationAdvanced(
|
|||||||
if (value === undefined) return result;
|
if (value === undefined) return result;
|
||||||
|
|
||||||
const preparedValue = Number.isFinite(value) ? formatters!.number.format(value as number) : value;
|
const preparedValue = Number.isFinite(value) ? formatters!.number.format(value as number) : value;
|
||||||
return replaceInStringsWithTeact(result, `{${key}}`, preparedValue);
|
return replaceInStringsWithTeact(result, `{${key}}`, renderText(preparedValue));
|
||||||
}, [part] as TeactNode[]);
|
}, [part] as TeactNode[]);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -440,7 +440,7 @@ function processTranslationAdvanced(
|
|||||||
if (value === undefined) return result;
|
if (value === undefined) return result;
|
||||||
|
|
||||||
const preparedValue = Number.isFinite(value) ? formatters!.number.format(value as number) : value;
|
const preparedValue = Number.isFinite(value) ? formatters!.number.format(value as number) : value;
|
||||||
return replaceInStringsWithTeact(result, `{${key}}`, preparedValue);
|
return replaceInStringsWithTeact(result, `{${key}}`, renderText(preparedValue));
|
||||||
}, tempResult);
|
}, tempResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,8 +3,8 @@ import type { ApiCountryCode } from '../api/types';
|
|||||||
const PATTERN_PLACEHOLDER = 'X';
|
const PATTERN_PLACEHOLDER = 'X';
|
||||||
const DEFAULT_PATTERN = 'XXX XXX XXX XXX';
|
const DEFAULT_PATTERN = 'XXX XXX XXX XXX';
|
||||||
|
|
||||||
export function getCountryCodesByIso(phoneCodeList: ApiCountryCode[], iso: string) {
|
export function getCountryCodeByIso(phoneCodeList: ApiCountryCode[], iso: string) {
|
||||||
return phoneCodeList.filter((country) => country.iso2 === iso);
|
return phoneCodeList.find((country) => country.iso2 === iso);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCountryFromPhoneNumber(phoneCodeList: ApiCountryCode[], input = '') {
|
export function getCountryFromPhoneNumber(phoneCodeList: ApiCountryCode[], input = '') {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user