Various improvements

This commit is contained in:
Ilya Laktyushin 2026-06-02 13:31:23 +02:00
parent b15b6ebf50
commit 11a4dbf0a0
10 changed files with 120 additions and 114 deletions

View file

@ -16342,3 +16342,16 @@ Error: %8$@";
"MediaPicker.SetNewGroupPhoto" = "Set new group photo";
"MediaPicker.SetNewChannelPhoto" = "Set new channel photo";
"Attachment.Link" = "Link";
"Checkout.PaymentMethod.Proceed" = "Proceed";
"Checkout.ShippingMethod.Proceed" = "Proceed";
"OpenLinkConfirmation.Title" = "Open link?";
"OpenLinkConfirmation.Open" = "Open";
"Chat.ContextMenu.OpenInBrowser" = "Open in Browser";
"Chat.ContextMenu.OpenInApp" = "Open In-App";
"CreatePoll.Link.Description" = "Attach a link to this option.";

View file

@ -244,8 +244,7 @@ private final class AttachButtonComponent: CombinedComponent {
name = strings.Attachment_Audio
imageName = "Chat/Attach Menu/Audio"
case .link:
//TODO:localize
name = "Link"
name = strings.Attachment_Link
imageName = "Chat/Attach Menu/Link"
case let .app(bot):
botPeer = bot.peer
@ -2187,8 +2186,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate, ASGestureRecog
case .audio:
accessibilityTitle = self.presentationData.strings.Attachment_Audio
case .link:
//TODO:localize
accessibilityTitle = "Link"
accessibilityTitle = self.presentationData.strings.Attachment_Link
case let .app(bot):
accessibilityTitle = bot.shortName
case .standalone:

View file

@ -328,9 +328,9 @@ private final class BotCheckoutPaymentMethodScreenComponent: Component {
self.component = component
self.state = state
let environmentValue = environment[ViewControllerComponentContainer.Environment.self].value
let controller = environmentValue.controller
let theme = environmentValue.theme.withModalBlocksBackground()
let environment = environment[ViewControllerComponentContainer.Environment.self].value
let controller = environment.controller
let theme = environment.theme.withModalBlocksBackground()
let dismiss: (Bool, (() -> Void)?) -> Void = { [weak self] animated, completion in
guard let self, !self.isDismissing else {
@ -379,7 +379,7 @@ private final class BotCheckoutPaymentMethodScreenComponent: Component {
)),
titleItem: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(
string: environmentValue.strings.Checkout_PaymentMethod,
string: environment.strings.Checkout_PaymentMethod,
font: Font.semibold(17.0),
textColor: theme.list.itemPrimaryTextColor
)),
@ -402,7 +402,6 @@ private final class BotCheckoutPaymentMethodScreenComponent: Component {
}
)
),
//TODO:localize
bottomItem: AnyComponent(ButtonComponent(
background: ButtonComponent.Background(
style: .glass,
@ -413,7 +412,7 @@ private final class BotCheckoutPaymentMethodScreenComponent: Component {
content: AnyComponentWithIdentity(
id: AnyHashable("proceed"),
component: AnyComponent(ButtonTextContentComponent(
text: "Proceed",
text: environment.strings.Checkout_PaymentMethod_Proceed,
badge: 0,
textColor: theme.list.itemCheckColors.foregroundColor,
badgeBackground: theme.list.itemCheckColors.foregroundColor,
@ -440,16 +439,16 @@ private final class BotCheckoutPaymentMethodScreenComponent: Component {
animateOut: self.animateOut
)),
environment: {
environmentValue
environment
ResizableSheetComponentEnvironment(
theme: theme,
statusBarHeight: environmentValue.statusBarHeight,
safeInsets: environmentValue.safeInsets,
statusBarHeight: environment.statusBarHeight,
safeInsets: environment.safeInsets,
inputHeight: 0.0,
metrics: environmentValue.metrics,
deviceMetrics: environmentValue.deviceMetrics,
isDisplaying: environmentValue.isVisible,
isCentered: environmentValue.metrics.widthClass == .regular,
metrics: environment.metrics,
deviceMetrics: environment.deviceMetrics,
isDisplaying: environment.isVisible,
isCentered: environment.metrics.widthClass == .regular,
screenSize: availableSize,
regularMetricsSize: nil,
dismiss: { animated in

View file

@ -225,9 +225,9 @@ private final class BotCheckoutShippingOptionScreenComponent: Component {
self.component = component
self.state = state
let environmentValue = environment[ViewControllerComponentContainer.Environment.self].value
let controller = environmentValue.controller
let theme = environmentValue.theme.withModalBlocksBackground()
let environment = environment[ViewControllerComponentContainer.Environment.self].value
let controller = environment.controller
let theme = environment.theme.withModalBlocksBackground()
let dismiss: (Bool) -> Void = { [weak self] animated in
guard let self, !self.isDismissing else {
@ -268,7 +268,7 @@ private final class BotCheckoutShippingOptionScreenComponent: Component {
)),
titleItem: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(
string: environmentValue.strings.Checkout_ShippingMethod,
string: environment.strings.Checkout_ShippingMethod,
font: Font.semibold(17.0),
textColor: theme.list.itemPrimaryTextColor
)),
@ -291,7 +291,6 @@ private final class BotCheckoutShippingOptionScreenComponent: Component {
}
)
),
//TODO:localize
bottomItem: AnyComponent(ButtonComponent(
background: ButtonComponent.Background(
style: .glass,
@ -302,7 +301,7 @@ private final class BotCheckoutShippingOptionScreenComponent: Component {
content: AnyComponentWithIdentity(
id: AnyHashable("proceed"),
component: AnyComponent(ButtonTextContentComponent(
text: "Proceed",
text: environment.strings.Checkout_ShippingMethod_Proceed,
badge: 0,
textColor: theme.list.itemCheckColors.foregroundColor,
badgeBackground: theme.list.itemCheckColors.foregroundColor,
@ -323,16 +322,16 @@ private final class BotCheckoutShippingOptionScreenComponent: Component {
animateOut: self.animateOut
)),
environment: {
environmentValue
environment
ResizableSheetComponentEnvironment(
theme: theme,
statusBarHeight: environmentValue.statusBarHeight,
safeInsets: environmentValue.safeInsets,
statusBarHeight: environment.statusBarHeight,
safeInsets: environment.safeInsets,
inputHeight: 0.0,
metrics: environmentValue.metrics,
deviceMetrics: environmentValue.deviceMetrics,
isDisplaying: environmentValue.isVisible,
isCentered: environmentValue.metrics.widthClass == .regular,
metrics: environment.metrics,
deviceMetrics: environment.deviceMetrics,
isDisplaying: environment.isVisible,
isCentered: environment.metrics.widthClass == .regular,
screenSize: availableSize,
regularMetricsSize: nil,
dismiss: { animated in

View file

@ -5900,8 +5900,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
}
actions.append(.init(title: self.presentationData.strings.Common_Cancel))
//TODO:localize
let title: String = "Delete Chat"
let title: String = self.presentationData.strings.ChatList_DeleteChat
var text: String
if mainPeer.id == self.context.account.peerId {
text = self.presentationData.strings.ChatList_DeleteSavedMessagesConfirmation
@ -5948,56 +5947,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
],
actions: actions
)
// let actionSheet = ActionSheetController(presentationData: self.presentationData)
// var items: [ActionSheetItem] = []
//
// items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: mainPeer, chatPeer: chatPeer, action: .delete, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder))
//
// if joined || mainPeer.isDeleted {
// items.append(ActionSheetButtonItem(title: self.presentationData.strings.Common_Delete, color: .destructive, action: { [weak self, weak actionSheet] in
// actionSheet?.dismissAnimated()
// self?.schedulePeerChatRemoval(peer: peer, type: .forEveryone, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: {
// removed()
// })
// completion(true)
// }))
// } else {
// items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak self, weak actionSheet] in
// actionSheet?.dismissAnimated()
// self?.schedulePeerChatRemoval(peer: peer, type: .forLocalPeer, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: {
// removed()
// })
// completion(true)
// }))
// items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).string, color: .destructive, action: { [weak self, weak actionSheet] in
// actionSheet?.dismissAnimated()
// guard let strongSelf = self else {
// return
// }
// strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationText, actions: [
// TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
// completion(false)
// }),
// TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationAction, action: {
// self?.schedulePeerChatRemoval(peer: peer, type: .forEveryone, deleteGloballyIfPossible: deleteGloballyIfPossible, completion: {
// removed()
// })
// completion(true)
// })
// ], parseMarkdown: true), in: .window(.root))
// }))
// }
// actionSheet.setItemGroups([
// ActionSheetItemGroup(items: items),
// ActionSheetItemGroup(items: [
// ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
// actionSheet?.dismissAnimated()
// completion(false)
// })
// ])
// ])
self.present(alertScreen, in: .window(.root))
} else if peer.peerId == self.context.account.peerId {
self.present(textAlertController(context: self.context, title: self.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: self.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [

View file

@ -737,6 +737,14 @@ NSString *suffix = @"";
[platform isEqualToString:@"iPad16,6"])
return @"iPad Pro 12.9 inch (7th gen)";
if ([platform isEqualToString:@"iPad16,8"] ||
[platform isEqualToString:@"iPad16,9"])
return @"iPad Air 11 inch (8th gen)";
if ([platform isEqualToString:@"iPad16,10"] ||
[platform isEqualToString:@"iPad16,11"])
return @"iPad Air 13 inch (8th gen)";
if ([platform hasPrefix:@"iPhone"])
return @"Unknown iPhone";
if ([platform hasPrefix:@"iPod"])

View file

@ -37,6 +37,7 @@ import EdgeEffect
import LocationUI
import CountrySelectionUI
import ShareWithPeersScreen
import ChatTextLinkEditUI
public final class ComposedPoll {
public struct Text {
@ -918,6 +919,11 @@ final class ComposePollScreenComponent: Component {
self.deactivateInput()
if !replace, case .pollOption = subject, let webpage = self.attachedMedia(for: subject)?.media.media as? TelegramMediaWebpage, let link = webpage.content.url {
self.openAttachedLinkMedia(subject: subject, link: link)
return
}
guard replace || !self.openAttachMediaContextMenu(subject: subject) else {
return
}
@ -967,6 +973,38 @@ final class ComposePollScreenComponent: Component {
})
}
private func openAttachedLinkMedia(subject: MediaAttachSubject, link: String) {
guard let component = self.component else {
return
}
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
let controller = chatTextLinkEditController(
context: component.context,
updatedPresentationData: (presentationData, .never()),
text: presentationData.strings.CreatePoll_Link_Description,
link: link,
preview: true,
apply: { [weak self] link, webpage in
guard let self, let link else {
return
}
if link.isEmpty {
self.setAttachedMedia(nil, for: subject)
self.state?.updated(transition: .easeInOut(duration: 0.2))
return
}
let attachedMedia = AttachedMedia(media: .standalone(media: webpage ?? makePollAttachmentLinkWebpage(link: link)))
self.setAttachedMedia(attachedMedia, for: subject)
self.uploadAttachedMediaIfNeeded(attachedMedia)
self.state?.updated(transition: .easeInOut(duration: 0.2))
}
)
(self.environment?.controller() as? ComposePollScreen)?.parentController()?.present(controller, in: .window(.root))
}
private func uploadAttachedMediaIfNeeded(_ media: AttachedMedia) {
guard let component = self.component, media.requiresUpload, media.uploadDisposable == nil else {
return

View file

@ -23,6 +23,34 @@ public enum PollAttachmentSubject {
case option
}
func makePollAttachmentLinkWebpage(link: String) -> TelegramMediaWebpage {
let mediaId = Int64.random(in: Int64.min ... Int64.max)
return TelegramMediaWebpage(
webpageId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: mediaId),
content: .Loaded(TelegramMediaWebpageLoadedContent(
url: link,
displayUrl: link,
hash: 0,
type: nil,
websiteName: nil,
title: nil,
text: nil,
embedUrl: nil,
embedType: nil,
embedSize: nil,
duration: nil,
author: nil,
isMediaLargeByDefault: nil,
imageIsVideoCover: false,
image: nil,
file: nil,
story: nil,
attributes: [],
instantPage: nil
))
)
}
public func presentPollAttachmentScreen(
context: AccountContext,
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?,
@ -236,11 +264,11 @@ public func presentPollAttachmentScreen(
case .link:
attachmentController?.dismiss(animated: true)
//TODO:localize
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
let controller = chatTextLinkEditController(
context: context,
updatedPresentationData: updatedPresentationData,
text: "Attach a link to this option.",
text: presentationData.strings.CreatePoll_Link_Description,
link: nil,
preview: true,
apply: { link, webpage in
@ -251,31 +279,7 @@ public func presentPollAttachmentScreen(
completion(.standalone(media: webpage))
return
}
let mediaId = Int64.random(in: Int64.min ... Int64.max)
let webPage = TelegramMediaWebpage(
webpageId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: mediaId),
content: .Loaded(TelegramMediaWebpageLoadedContent(
url: link,
displayUrl: link,
hash: 0,
type: nil,
websiteName: nil,
title: nil,
text: nil,
embedUrl: nil,
embedType: nil,
embedSize: nil,
duration: nil,
author: nil,
isMediaLargeByDefault: nil,
imageIsVideoCover: false,
image: nil,
file: nil,
story: nil,
attributes: [],
instantPage: nil
)))
completion(.standalone(media: webPage))
completion(.standalone(media: makePollAttachmentLinkWebpage(link: link)))
}
)
present(controller, false)

View file

@ -125,9 +125,8 @@ public func openUserGeneratedUrl(
let disposable = MetaDisposable()
let checkState = concealedAlertOption.map { _ in AlertCheckComponent.ExternalState() }
//TODO:localize
var content: [AnyComponentWithIdentity<AlertComponentEnvironment>] = []
content.append(AnyComponentWithIdentity(id: "title", component: AnyComponent(AlertTitleComponent(title: "Open link?"))))
content.append(AnyComponentWithIdentity(id: "title", component: AnyComponent(AlertTitleComponent(title: presentationData.strings.OpenLinkConfirmation_Title))))
content.append(AnyComponentWithIdentity(id: "text", component: AnyComponent(AlertTextComponent(
content: .plain(displayUrl),
alignment: .center,
@ -158,7 +157,7 @@ public func openUserGeneratedUrl(
content: content,
actions: [
.init(title: presentationData.strings.Common_Cancel),
.init(title: "Open", type: .default, action: {
.init(title: presentationData.strings.OpenLinkConfirmation_Open, type: .default, action: {
if checkState?.value == true {
concealedAlertOption?.action()
}

View file

@ -250,8 +250,7 @@ extension ChatControllerImpl {
}))
if let openMode {
//TODO:localize
let reverseText = openMode.shouldOpenInApp ? "Open in Browser" : "Open In-App"
let reverseText = openMode.shouldOpenInApp ? self.presentationData.strings.Chat_ContextMenu_OpenInBrowser : self.presentationData.strings.Chat_ContextMenu_OpenInApp
items.append(ActionSheetButtonItem(title: reverseText, color: .accent, action: { [weak self, weak actionSheet] in
actionSheet?.dismissAnimated()
guard let self else {