diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 063078f82b..f183c2c7a4 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -11613,9 +11613,9 @@ Sorry for the inconvenience."; "CollectibleItemInfo.StoreName" = "Fragment"; "CollectibleItemInfo.UsernameTitle" = "%@ is a collectible username that belongs to"; -"CollectibleItemInfo.UsernameText" = "The %1$@ username was acquired on %2$@ on %3$@ for %4$@ (%5$@)."; +"CollectibleItemInfo.UsernameText" = "The username %1$@ was acquired on %2$@ on %3$@ for %4$@ (%5$@)."; "CollectibleItemInfo.PhoneTitle" = "%@ is a collectible phone number that belongs to"; -"CollectibleItemInfo.PhoneText" = "The %1$@ phone number was acquired on %2$@ on %3$@ for %4$@ (%5$@)."; +"CollectibleItemInfo.PhoneText" = "The phone number %1$@ was acquired on %2$@ on %3$@ for %4$@ (%5$@)."; "CollectibleItemInfo.ButtonOpenInfo" = "Learn More"; "CollectibleItemInfo.ButtonCopyUsername" = "Copy Link"; "CollectibleItemInfo.ButtonCopyPhone" = "Copy Phone Number"; @@ -11660,7 +11660,7 @@ Sorry for the inconvenience."; "ChatList.BirthdaySingleTitle" = "It's %@'s **birthday** today! 🎂"; "ChatList.BirthdaySingleText" = "Gift them Telegram Premium."; -"ChatList.BirthdayMultipleTitle_1" = "%@ contact have **birthday** today! 🎂"; +"ChatList.BirthdayMultipleTitle_1" = "%@ contact has a **birthday** today! 🎂"; "ChatList.BirthdayMultipleTitle_any" = "%@ contacts have **birthdays** today! 🎂"; "ChatList.BirthdayMultipleText" = "Gift them Telegram Premium."; @@ -11689,7 +11689,7 @@ Sorry for the inconvenience."; "Monetization.BalanceTitle" = "AVAILABLE BALANCE"; "Monetization.Balance.ZeroInfo" = "You will be able to collect rewards using Fragment, a third-party platform used by advertisers to pay for ads. [Learn More >]()"; "Monetization.Balance.AvailableInfo" = "You can collect your reward using Fragment, a third-party platform used by advertisers to pay for ads. [Learn More >]()"; -"Monetization.Balance.ComingLaterInfo" = "In the coming weeks you will be able to collect your reward using Fragment, a third-party platform used by advertisers to pay ads. [Learn More >]()"; +"Monetization.Balance.ComingLaterInfo" = "In the coming weeks, you will be able to collect rewards using Fragment, a third-party platform used by advertisers to pay for ads. [Learn More >]()"; "Monetization.BalanceInfo_URL" = "https://telegram.org"; "Monetization.BalanceWithdraw" = "Withdraw via Fragment"; @@ -11733,14 +11733,14 @@ Sorry for the inconvenience."; "Monetization.Intro.Withdrawal.Text" = "You can withdraw your TON any time."; "Monetization.Intro.Info.Title" = "What's #TON?"; -"Monetization.Intro.Info.Text" = "TON is a blockchain platform and cryptocurrency that Telegram uses for its high speed and low commisions on transactions. [Learn More >]()"; +"Monetization.Intro.Info.Text" = "TON is a blockchain platform and cryptocurrency that Telegram uses for its high speed and commissions on transactions. [Learn More >]()"; "Monetization.Intro.Info.Text_URL" = "https://ton.org"; "Monetization.Intro.Understood" = "Understood"; "ReportAd.Title" = "Report Ad"; -"ReportAd.Help" = "Learn more about our [Ad Policies and Guidelines]()."; -"ReportAd.Help_URL" = "https://promote.telegram.org/guidelines"; +"ReportAd.Help" = "Learn more about [Telegram Ad Policies and Guidelines]()."; +"ReportAd.Help_URL" = "https://ads.telegram.org/guidelines"; "ReportAd.Reported" = "We will review this ad to ensure it matches our [Ad Policies and Guidelines]()."; "ReportAd.Hidden" = "Ads are hidden now."; diff --git a/submodules/GraphCore/Sources/Charts/Controllers/Stacked Bars/BarsComponentController.swift b/submodules/GraphCore/Sources/Charts/Controllers/Stacked Bars/BarsComponentController.swift index 16018ce224..227bb0d9d9 100644 --- a/submodules/GraphCore/Sources/Charts/Controllers/Stacked Bars/BarsComponentController.swift +++ b/submodules/GraphCore/Sources/Charts/Controllers/Stacked Bars/BarsComponentController.swift @@ -175,16 +175,16 @@ class BarsComponentController: GeneralChartComponentController { calculatingRange: horizontalRange, addBounds: true) { let (range, labels) = verticalLimitsLabels(verticalRange: range, secondary: false) - if verticalScalesRenderer.verticalRange.end != range { + // if verticalScalesRenderer.verticalRange.end != range { verticalScalesRenderer.setup(verticalLimitsLabels: labels, animated: animated) - } + // } verticalScalesRenderer.setVisible(true, animated: animated) if let secondaryScalesRenderer = self.secondaryScalesRenderer { - let (range, labels) = verticalLimitsLabels(verticalRange: range, secondary: true) - if secondaryScalesRenderer.verticalRange.end != range { + let (_, labels) = verticalLimitsLabels(verticalRange: range, secondary: true) + // if secondaryScalesRenderer.verticalRange.end != range { secondaryScalesRenderer.setup(verticalLimitsLabels: labels, animated: animated) - } + // } secondaryScalesRenderer.setVisible(true, animated: animated) } diff --git a/submodules/GraphCore/Sources/Charts/Renderes/VerticalScalesRenderer.swift b/submodules/GraphCore/Sources/Charts/Renderes/VerticalScalesRenderer.swift index ae1edc2d6e..9de54a1a34 100644 --- a/submodules/GraphCore/Sources/Charts/Renderes/VerticalScalesRenderer.swift +++ b/submodules/GraphCore/Sources/Charts/Renderes/VerticalScalesRenderer.swift @@ -9,6 +9,8 @@ import Foundation #if os(macOS) import Cocoa + +typealias UIColor = NSColor #else import UIKit #endif diff --git a/submodules/TelegramAudio/Sources/AudioSessionManager.swift b/submodules/TelegramAudio/Sources/AudioSessionManager.swift new file mode 100644 index 0000000000..60a0553708 --- /dev/null +++ b/submodules/TelegramAudio/Sources/AudioSessionManager.swift @@ -0,0 +1,5 @@ +import Foundation +import SwiftSignalKit +import AVFoundation +import UIKit + diff --git a/submodules/TelegramCore/Sources/Statistics/RevenueStatistics.swift b/submodules/TelegramCore/Sources/Statistics/RevenueStatistics.swift index 7c15126a48..7b2a799e4d 100644 --- a/submodules/TelegramCore/Sources/Statistics/RevenueStatistics.swift +++ b/submodules/TelegramCore/Sources/Statistics/RevenueStatistics.swift @@ -365,7 +365,7 @@ public final class RevenueStatsTransactionsContext { } } -public enum RequestRevenueWithdrawalError { +public enum RequestRevenueWithdrawalError : Equatable { case generic case twoStepAuthMissing case twoStepAuthTooFresh(Int32) diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/BUILD b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/BUILD index 82b3cd5127..5ebf49bb3c 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/BUILD +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/BUILD @@ -53,6 +53,7 @@ swift_library( "//submodules/TelegramUI/Components/ListItemSwipeOptionContainer", "//submodules/UndoUI", "//submodules/ShareController", + "//submodules/ContextUI", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/BusinessLinkListItemComponent.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/BusinessLinkListItemComponent.swift index 69d7275f75..892f133f75 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/BusinessLinkListItemComponent.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/BusinessLinkListItemComponent.swift @@ -21,6 +21,7 @@ final class BusinessLinkListItemComponent: Component { let action: () -> Void let deleteAction: () -> Void let shareAction: () -> Void + let contextAction: ((ContextExtractedContentContainingView, ContextGesture) -> Void)? init( context: AccountContext, @@ -29,7 +30,8 @@ final class BusinessLinkListItemComponent: Component { link: TelegramBusinessChatLinks.Link, action: @escaping () -> Void, deleteAction: @escaping () -> Void, - shareAction: @escaping () -> Void + shareAction: @escaping () -> Void, + contextAction: ((ContextExtractedContentContainingView, ContextGesture) -> Void)? ) { self.context = context self.theme = theme @@ -38,6 +40,7 @@ final class BusinessLinkListItemComponent: Component { self.action = action self.deleteAction = deleteAction self.shareAction = shareAction + self.contextAction = contextAction } static func ==(lhs: BusinessLinkListItemComponent, rhs: BusinessLinkListItemComponent) -> Bool { @@ -56,7 +59,8 @@ final class BusinessLinkListItemComponent: Component { return true } - final class View: UIView, ListSectionComponent.ChildView { + final class View: ContextControllerSourceView, ListSectionComponent.ChildView { + private let extractedContainerView: ContextExtractedContentContainingView private let containerButton: HighlightTrackingButton private let swipeOptionContainer: ListItemSwipeOptionContainer @@ -71,7 +75,10 @@ final class BusinessLinkListItemComponent: Component { var customUpdateIsHighlighted: ((Bool) -> Void)? private(set) var separatorInset: CGFloat = 0.0 + private var isExtractedToContextMenu: Bool = false + override init(frame: CGRect) { + self.extractedContainerView = ContextExtractedContentContainingView() self.containerButton = HighlightTrackingButton() self.containerButton.layer.anchorPoint = CGPoint() self.containerButton.isExclusiveTouch = true @@ -80,6 +87,11 @@ final class BusinessLinkListItemComponent: Component { super.init(frame: frame) + self.addSubview(self.extractedContainerView) + self.targetViewForActivationProgress = self.extractedContainerView.contentView + + self.extractedContainerView.contentView.addSubview(self.swipeOptionContainer) + self.containerButton.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) self.containerButton.internalHighligthedChanged = { [weak self] isHighlighted in guard let self else { @@ -108,9 +120,38 @@ final class BusinessLinkListItemComponent: Component { } } - self.addSubview(self.swipeOptionContainer) - self.swipeOptionContainer.addSubview(self.containerButton) + + self.extractedContainerView.isExtractedToContextPreviewUpdated = { [weak self] value in + guard let self, let component = self.component else { + return + } + self.containerButton.clipsToBounds = value + self.containerButton.backgroundColor = value ? component.theme.list.itemBlocksBackgroundColor : nil + self.containerButton.layer.cornerRadius = value ? 10.0 : 0.0 + } + self.extractedContainerView.willUpdateIsExtractedToContextPreview = { [weak self] value, transition in + guard let self else { + return + } + self.isExtractedToContextMenu = value + + let mappedTransition: Transition + if value { + mappedTransition = Transition(transition) + } else { + mappedTransition = Transition(animation: .curve(duration: 0.2, curve: .easeInOut)) + } + self.componentState?.updated(transition: mappedTransition) + } + + self.activated = { [weak self] gesture, _ in + guard let self, let component = self.component else { + gesture.cancel() + return + } + component.contextAction?(self.extractedContainerView, gesture) + } } required init?(coder: NSCoder) { @@ -128,13 +169,21 @@ final class BusinessLinkListItemComponent: Component { self.component = component self.componentState = state + let leftInset: CGFloat = 0.0 let leftContentInset: CGFloat = 62.0 - let rightInset: CGFloat = 8.0 + var rightInset: CGFloat = 8.0 let topInset: CGFloat = 9.0 let bottomInset: CGFloat = 9.0 let titleViewCountSpacing: CGFloat = 4.0 let titleTextSpacing: CGFloat = 4.0 + var innerInsets = UIEdgeInsets() + if self.isExtractedToContextMenu { + rightInset += 2.0 + innerInsets.left += 2.0 + innerInsets.right += 2.0 + } + let viewCountText: String if component.link.viewCount == 0 { viewCountText = component.strings.Business_Links_ItemNoClicks @@ -149,7 +198,7 @@ final class BusinessLinkListItemComponent: Component { environment: {}, containerSize: CGSize(width: 100.0, height: 100.0) ) - let viewCountFrame = CGRect(origin: CGPoint(x: availableSize.width - rightInset - viewCountSize.width, y: topInset + 2.0), size: viewCountSize) + let viewCountFrame = CGRect(origin: CGPoint(x: availableSize.width - rightInset - innerInsets.left - viewCountSize.width, y: topInset + 2.0), size: viewCountSize) if let viewCountView = self.viewCount.view { if viewCountView.superview == nil { viewCountView.isUserInteractionEnabled = false @@ -166,9 +215,9 @@ final class BusinessLinkListItemComponent: Component { text: .plain(NSAttributedString(string: component.link.title ?? component.link.url, font: Font.regular(16.0), textColor: component.theme.list.itemPrimaryTextColor)) )), environment: {}, - containerSize: CGSize(width: availableSize.width - leftContentInset - rightInset - viewCountSize.width - titleViewCountSpacing, height: 100.0) + containerSize: CGSize(width: availableSize.width - leftInset - leftContentInset - rightInset - viewCountSize.width - titleViewCountSpacing, height: 100.0) ) - let titleFrame = CGRect(origin: CGPoint(x: leftContentInset, y: topInset), size: titleSize) + let titleFrame = CGRect(origin: CGPoint(x: leftInset + leftContentInset, y: topInset), size: titleSize) if let titleView = self.title.view { if titleView.superview == nil { titleView.isUserInteractionEnabled = false @@ -213,7 +262,7 @@ final class BusinessLinkListItemComponent: Component { adjustQuoteFontSize: false, cachedMessageSyntaxHighlight: nil ) - let (textLayout, textApply) = asyncLayout(TextNodeLayoutArguments(attributedString: textString, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: availableSize.width - leftContentInset - rightInset, height: 100.0))) + let (textLayout, textApply) = asyncLayout(TextNodeLayoutArguments(attributedString: textString, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: availableSize.width - leftContentInset - leftInset - rightInset, height: 100.0))) let _ = textApply(TextNodeWithEntities.Arguments( context: component.context, cache: component.context.animationCache, @@ -222,7 +271,7 @@ final class BusinessLinkListItemComponent: Component { attemptSynchronous: true )) let textSize = textLayout.size - let textFrame = CGRect(origin: CGPoint(x: leftContentInset, y: titleFrame.maxY + titleTextSpacing), size: textLayout.size) + let textFrame = CGRect(origin: CGPoint(x: leftInset + leftContentInset, y: titleFrame.maxY + titleTextSpacing), size: textLayout.size) if self.text.textNode.view.superview == nil { self.text.textNode.view.isUserInteractionEnabled = false self.containerButton.addSubview(self.text.textNode.view) @@ -237,18 +286,25 @@ final class BusinessLinkListItemComponent: Component { self.iconView.isUserInteractionEnabled = false self.containerButton.addSubview(self.iconView) } - let iconFrame = CGRect(origin: CGPoint(x: floor((leftContentInset - image.size.width) * 0.5), y: floor((size.height - image.size.height) * 0.5)), size: image.size) + let iconFrame = CGRect(origin: CGPoint(x: leftInset + floor((leftContentInset - image.size.width) * 0.5), y: floor((size.height - image.size.height) * 0.5)), size: image.size) transition.setFrame(view: self.iconView, frame: iconFrame) } let swipeOptionContainerFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size) transition.setFrame(view: self.swipeOptionContainer, frame: swipeOptionContainerFrame) - transition.setPosition(view: self.containerButton, position: CGPoint()) - transition.setBounds(view: self.containerButton, bounds: CGRect(origin: self.containerButton.bounds.origin, size: size)) + let containerButtonFrame = CGRect(origin: CGPoint(x: innerInsets.left, y: innerInsets.top), size: CGSize(width: size.width - innerInsets.left - innerInsets.right, height: size.height - innerInsets.top - innerInsets.bottom)) + + transition.setPosition(view: self.containerButton, position: containerButtonFrame.origin) + transition.setBounds(view: self.containerButton, bounds: CGRect(origin: self.containerButton.bounds.origin, size: containerButtonFrame.size)) self.swipeOptionContainer.updateLayout(size: swipeOptionContainerFrame.size, leftInset: 0.0, rightInset: 0.0) + let resultBounds = CGRect(origin: CGPoint(), size: size) + transition.setFrame(view: self.extractedContainerView, frame: resultBounds) + transition.setFrame(view: self.extractedContainerView.contentView, frame: resultBounds) + self.extractedContainerView.contentRect = resultBounds + var rightOptions: [ListItemSwipeOptionContainer.Option] = [] rightOptions = [ ListItemSwipeOptionContainer.Option( diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/BusinessLinksSetupScreen.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/BusinessLinksSetupScreen.swift index d4c9ce3968..66c7fd76f8 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/BusinessLinksSetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/BusinessLinksSetupScreen.swift @@ -18,6 +18,7 @@ import BundleIconComponent import TextFormat import UndoUI import ShareController +import ContextUI final class BusinessLinksSetupScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -535,6 +536,54 @@ final class BusinessLinksSetupScreenComponent: Component { return } self.openShareLink(url: link.url) + }, + contextAction: { [weak self] sourceView, gesture in + guard let self, let component = self.component else { + return + } + + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + + var itemList: [ContextMenuItem] = [] + itemList.append(.action(ContextMenuActionItem( + text: presentationData.strings.Business_Links_ItemActionShare, + textColor: .primary, + icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor) }, + action: { [weak self] c, _ in + c.dismiss(completion: { + guard let self else { + return + } + self.openShareLink(url: link.url) + }) + }) + )) + itemList.append(.action(ContextMenuActionItem( + text: presentationData.strings.Common_Delete, + textColor: .destructive, + icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, + action: { [weak self] c, _ in + c.dismiss(completion: { + guard let self else { + return + } + self.openDeleteLink(url: link.url) + }) + }) + )) + let items = ContextController.Items(content: .list(itemList)) + + let controller = ContextController( + presentationData: presentationData, + source: .extracted(BusineesLinkListContextExtractedContentSource(contentView: sourceView)), items: .single(items), recognizer: nil, gesture: gesture) + + self.environment?.controller()?.forEachController({ controller in + if let controller = controller as? UndoOverlayController { + controller.dismiss() + } + return true + }) + self.environment?.controller()?.presentInGlobalOverlay(controller) } )))) } @@ -708,3 +757,23 @@ public final class BusinessLinksSetupScreen: ViewControllerComponentContainer { super.containerLayoutUpdated(layout, transition: transition) } } + +private final class BusineesLinkListContextExtractedContentSource: ContextExtractedContentSource { + let keepInPlace: Bool = false + let ignoreContentTouches: Bool = false + let blurBackground: Bool = true + + private let contentView: ContextExtractedContentContainingView + + init(contentView: ContextExtractedContentContainingView) { + self.contentView = contentView + } + + func takeView() -> ContextControllerTakeViewInfo? { + return ContextControllerTakeViewInfo(containingItem: .view(self.contentView), contentAreaInScreenSpace: UIScreen.main.bounds) + } + + func putBack() -> ContextControllerPutBackViewInfo? { + return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds) + } +} diff --git a/versions.json b/versions.json index b61681ad79..a1bd5122fb 100644 --- a/versions.json +++ b/versions.json @@ -1,4 +1,5 @@ { + "app": "10.9.2", "app": "10.10", "bazel": "7.0.2", "xcode": "15.2",