From f0f9993ebd0fe60386d46fb5f51d0a2d82ce972a Mon Sep 17 00:00:00 2001 From: zubiden <19638254+zubiden@users.noreply.github.com> Date: Wed, 8 Oct 2025 12:33:22 +0200 Subject: [PATCH] Web Page: Support media repair (#6321) --- src/api/gramjs/helpers/localDb.ts | 22 ++++++++++-- src/api/gramjs/localDb.ts | 7 +++- src/api/gramjs/methods/client.ts | 35 ++++++++++++++++++- src/assets/localization/fallback.strings | 23 ++++++------ src/components/main/Dialogs.tsx | 10 ++++-- .../modals/webApp/WebAppModalTabContent.tsx | 10 ++++-- src/config.ts | 3 +- src/types/language.d.ts | 3 +- src/util/localization/index.ts | 4 +-- 9 files changed, 91 insertions(+), 26 deletions(-) diff --git a/src/api/gramjs/helpers/localDb.ts b/src/api/gramjs/helpers/localDb.ts index cadf632f6..1dea37f94 100644 --- a/src/api/gramjs/helpers/localDb.ts +++ b/src/api/gramjs/helpers/localDb.ts @@ -25,14 +25,14 @@ export function addMessageToLocalDb(message: GramJs.TypeMessage | GramJs.TypeSpo } } -export function addWebPageMediaToLocalDb(webPage: GramJs.TypeWebPage, context?: MediaRepairContext) { +export function addWebPageMediaToLocalDb(webPage: GramJs.TypeWebPage) { if (webPage instanceof GramJs.WebPage) { if (webPage.document) { - const document = addMessageRepairInfo(webPage.document, context); + const document = addWebPageRepairInfo(webPage.document, webPage); addDocumentToLocalDb(document); } if (webPage.photo) { - const photo = addMessageRepairInfo(webPage.photo, context); + const photo = addWebPageRepairInfo(webPage.photo, webPage); addPhotoToLocalDb(photo); } } @@ -146,6 +146,22 @@ export function addMessageRepairInfo( + media: T, webPage?: GramJs.TypeWebPage, +): T & RepairInfo { + if (!(webPage instanceof GramJs.WebPage)) return media; + if (!(media instanceof GramJs.Document || media instanceof GramJs.Photo || media instanceof GramJs.WebDocument)) { + return media; + } + + const repairableMedia = media as T & RepairInfo; + repairableMedia.localRepairInfo = { + type: 'webPage', + url: webPage.url, + }; + return repairableMedia; +} + export function addChatToLocalDb(chat: GramJs.Chat | GramJs.Channel) { const id = buildApiPeerId(chat.id, chat instanceof GramJs.Chat ? 'chat' : 'channel'); const storedChat = localDb.chats[id]; diff --git a/src/api/gramjs/localDb.ts b/src/api/gramjs/localDb.ts index 3d659f541..4e2170e7e 100644 --- a/src/api/gramjs/localDb.ts +++ b/src/api/gramjs/localDb.ts @@ -18,8 +18,13 @@ export type MessageRepairInfo = { id: number; }; +export type WebPageRepairInfo = { + type: 'webPage'; + url: string; +}; + export type RepairInfo = { - localRepairInfo?: StoryRepairInfo | MessageRepairInfo; + localRepairInfo?: StoryRepairInfo | MessageRepairInfo | WebPageRepairInfo; }; export interface LocalDb { diff --git a/src/api/gramjs/methods/client.ts b/src/api/gramjs/methods/client.ts index bbb6e8fae..b569fe065 100644 --- a/src/api/gramjs/methods/client.ts +++ b/src/api/gramjs/methods/client.ts @@ -21,6 +21,7 @@ import { DEBUG, DEBUG_GRAMJS, IS_TEST, LANG_PACK, UPLOAD_WORKERS, } from '../../../config'; import { pause } from '../../../util/schedulers'; +import { buildWebPage } from '../apiBuilders/messageContent'; import { buildApiMessage, setMessageBuilderCurrentUserId, @@ -28,9 +29,15 @@ import { import { buildApiPeerId } from '../apiBuilders/peers'; import { buildApiStory } from '../apiBuilders/stories'; import { buildApiUser, buildApiUserFullInfo } from '../apiBuilders/users'; -import { buildInputChannelFromLocalDb, buildInputPeerFromLocalDb, getEntityTypeById } from '../gramjsBuilders'; +import { + buildInputChannelFromLocalDb, + buildInputPeerFromLocalDb, + DEFAULT_PRIMITIVES, + getEntityTypeById, +} from '../gramjsBuilders'; import { addStoryToLocalDb, addUserToLocalDb, + addWebPageMediaToLocalDb, } from '../helpers/localDb'; import { isResponseUpdate, log, @@ -522,6 +529,11 @@ export async function repairFileReference({ const result = await repairMessageMedia(localRepairInfo.peerId, localRepairInfo.id); return result; } + + if (localRepairInfo.type === 'webPage') { + const result = await repairWebPageMedia(localRepairInfo.url); + return result; + } } return false; @@ -599,6 +611,27 @@ async function repairStoryMedia(peerId: string, storyId: number) { return true; } +export async function repairWebPageMedia(url: string) { + const result = await invokeRequest(new GramJs.messages.GetWebPage({ + url, + hash: DEFAULT_PRIMITIVES.INT, + }), { + shouldIgnoreErrors: true, + }); + + if (!result?.webpage) return false; + const webPage = buildWebPage(result.webpage); + if (!webPage) return false; + + addWebPageMediaToLocalDb(result.webpage); + sendApiUpdate({ + '@type': 'updateWebPage', + webPage, + }); + + return webPage.webpageType === 'full'; +} + export function setForceHttpTransport(forceHttpTransport: boolean) { client.setForceHttpTransport(forceHttpTransport); } diff --git a/src/assets/localization/fallback.strings b/src/assets/localization/fallback.strings index fe343ff81..ab2005b17 100644 --- a/src/assets/localization/fallback.strings +++ b/src/assets/localization/fallback.strings @@ -574,7 +574,8 @@ "MemberRequestsRequestToJoinDescriptionChannel" = "This channel accepts new subscribers only after they are approved by its admins."; "MemberRequestsRequestToJoinDescriptionGroup" = "This group accepts new members only after they are approved by its admins."; "ShareYouPhoneNumberTitle" = "Share your phone number?"; -"AreYouSureShareMyContactInfoBot" = "The bot will know your phone number. This can be useful for integration with other services."; +"AreYouSureShareMyContactInfoBot" = "The bot will know your phone number. This can be useful for integration with other services.\n\n⚠️ Warning! Never enter your Telegram login codes in mini apps."; +"ContactShare" = "Share Contact"; "OK" = "OK"; "ForwardTo" = "Forward to..."; "AttachGame" = "Game"; @@ -752,7 +753,6 @@ "PollsStopSure" = "Stop"; "PaymentTestInvoice" = "TEST INVOICE"; "PaymentInvoiceNotFound" = "Invoice not found"; -"AttachLiveLocation" = "Live Location"; "NoWordsRecognized" = "No words recognized."; "ViaBot" = "via"; "DiscussChannel" = "channel"; @@ -1117,6 +1117,7 @@ "AttachStory" = "Story"; "AttachInvoice" = "Invoice: {description}"; "AttachLocation" = "Location"; +"AttachLiveLocation" = "Live Location"; "AttachGiveaway" = "Giveaway"; "AttachGiveawayResults" = "Giveaway Results"; "AttachTodo" = "Checklist"; @@ -1478,9 +1479,9 @@ "GiftInfoDescriptionConverted_other" = "You converted this gift to **{amount}** Stars."; "GiftInfoPeerDescriptionOutConverted_one" = "{peer} converted this gift to **{amount}** Star."; "GiftInfoPeerDescriptionOutConverted_other" = "{peer} converted this gift to **{amount}** Stars."; -"GiftInfoDescriptionFreeUpgrade" = "Upgrade this gift for free to turn it to a unique collectible."; +"GiftInfoDescriptionFreeUpgrade" = "Upgrade this gift for free to turn it into a unique collectible."; "GiftInfoDescriptionUpgrade2" = "Upgrade this gift to turn it to a unique collectible."; -"GiftInfoPeerDescriptionFreeUpgradeOut" = "{peer} can turn this gift to a unique collectible"; +"GiftInfoPeerDescriptionFreeUpgradeOut" = "{peer} can turn this gift into a unique collectible"; "GiftInfoDescriptionUpgraded" = "This gift was turned into a unique collectible."; "GiftInfoFrom" = "From"; "GiftInfoDate" = "Date"; @@ -1877,8 +1878,8 @@ "ActionStarGiftChannelText" = "Add this gift to your channel's profile or convert it to {amount}."; "ActionStarGiftDisplaying" = "You added this gift to your profile."; "ActionStarGiftChannelDisplaying" = "This gift is displayed to visitors of your channel."; -"ActionStarGiftUpgradeText" = "{peer} can turn this gift to a unique collectible."; -"ActionStarGiftUpgradeTextYou" = "Tap “Unpack” to turn this gift to a unique collectible."; +"ActionStarGiftUpgradeText" = "{peer} can turn this gift into a unique collectible."; +"ActionStarGiftUpgradeTextYou" = "Tap “Unpack” to turn this gift into a unique collectible."; "ActionStarGiftUpgraded" = "This gift was upgraded."; "ActionStarGiftUnpack" = "Unpack"; "ActionStarGiftLimitedRibbon" = "1 of {total}"; @@ -1973,7 +1974,7 @@ "GiftPremiumDescriptionYourBalance" = "Your balance is **{stars}**. {link}"; "StarsGiftCompleted" = "Gift sent!"; "GiftSent" = "Gift sent!"; -"PrivacyDescriptionMessagesContactsAndPremium" = "You can restrict messages from users who are not in your contacts and don't have Premium."; +"PrivacyDescriptionMessagesContactsAndPremium" = "You can restrict messages from users who are not in your contacts and whom you haven't messaged first."; "PrivacyChargeForMessages" = "Charge for Messages"; "PrivacyDescriptionChargeForMessages" = "Charge a fee for messages from people outside your contacts or those you haven't messaged first."; "RemoveFeeTitle" = "Remove Fee"; @@ -2201,7 +2202,7 @@ "DescriptionAboutTon" = "Offer TON to submit post suggestions to channels on Telegram."; "ButtonTopUpViaFragment" = "Top Up Via Fragment"; "TonModalHint" = "You can top up your TON using Fragment."; -"TonGiftReceived" = "TON Top Up"; +"TonGiftReceived" = "TON Top-Up"; "MediaSpoilerSensitive" = "18+"; "TitleSensitiveModal" = "{years}+ content"; "TextSensitiveModal" = "This media may contain sensitive content suitable only for adults. Do you still want to view it?"; @@ -2215,8 +2216,8 @@ "TitleAgeCheckSuccess" = "Age Check Success"; "ButtonAgeVerification" = "Verify My Age"; "GiftRibbonPremium" = "premium"; -"NotificationGiftsLimit2_one" = "You've already sent **one** of these gifts, and it's the limit."; -"NotificationGiftsLimit2_other" = "You've already sent {count} of these gifts, and it's the limit."; +"NotificationGiftsLimit2_one" = "You already sent **one** of these gifts, which is the limit."; +"NotificationGiftsLimit2_other" = "You already sent **{count}** of these gifts, which is the limit."; "PremiumGiftHeader" = "Premium Gift"; "DescriptionGiftPremiumRequired2_one" = "Subscribe to **Telegram Premium** to send **one** of these gifts and unlock powerful features."; "DescriptionGiftPremiumRequired2_other" = "Subscribe to **Telegram Premium** to send up to **{count}** of these gifts and unlock powerful features."; @@ -2257,7 +2258,7 @@ "PostsSearchTransaction" = "Posts Search"; "AllStoriesCategory" = "All stories"; "TitleRating" = "Rating"; -"RatingReflectsActivity" = "This rating reflects {name}'s activity on Telegram. It is based on:"; +"RatingReflectsActivity" = "This rating reflects **{name}'s** activity on Telegram. It is based on:"; "RatingYourReflectsActivity" = "This rating reflects your activity on Telegram. It is based on:"; "RatingGiftsFromTelegram" = "Gifts from Telegram"; "RatingGiftsFromTelegramDesc" = "100% of the Stars spent on gifts purchased from Telegram."; diff --git a/src/components/main/Dialogs.tsx b/src/components/main/Dialogs.tsx index 5f3a10c06..5ac04835a 100644 --- a/src/components/main/Dialogs.tsx +++ b/src/components/main/Dialogs.tsx @@ -12,7 +12,7 @@ import getReadableErrorText from '../../util/getReadableErrorText'; import renderText from '../common/helpers/renderText'; import useFlag from '../../hooks/useFlag'; -import useOldLang from '../../hooks/useOldLang'; +import useLang from '../../hooks/useLang'; import Button from '../ui/Button'; import Modal from '../ui/Modal'; @@ -29,7 +29,7 @@ const Dialogs: FC = ({ dialogs, currentMessageList }) => { } = getActions(); const [isModalOpen, openModal, closeModal] = useFlag(); - const lang = useOldLang(); + const lang = useLang(); useEffect(() => { if (dialogs.length > 0) { @@ -62,7 +62,11 @@ const Dialogs: FC = ({ dialogs, currentMessageList }) => { title={lang('ShareYouPhoneNumberTitle')} onCloseAnimationEnd={dismissDialog} > - {lang('AreYouSureShareMyContactInfoBot')} + {lang( + 'AreYouSureShareMyContactInfoBot', + undefined, + { withNodes: true, withMarkdown: true, renderTextFilters: ['br', 'emoji'], + })}