Merge branches 'master' and 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2024-03-29 12:46:59 +04:00
commit 74c22e8c18
9 changed files with 160 additions and 26 deletions

View File

@ -11613,9 +11613,9 @@ Sorry for the inconvenience.";
"CollectibleItemInfo.StoreName" = "Fragment"; "CollectibleItemInfo.StoreName" = "Fragment";
"CollectibleItemInfo.UsernameTitle" = "%@ is a collectible username that belongs to"; "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.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.ButtonOpenInfo" = "Learn More";
"CollectibleItemInfo.ButtonCopyUsername" = "Copy Link"; "CollectibleItemInfo.ButtonCopyUsername" = "Copy Link";
"CollectibleItemInfo.ButtonCopyPhone" = "Copy Phone Number"; "CollectibleItemInfo.ButtonCopyPhone" = "Copy Phone Number";
@ -11660,7 +11660,7 @@ Sorry for the inconvenience.";
"ChatList.BirthdaySingleTitle" = "It's %@'s **birthday** today! 🎂"; "ChatList.BirthdaySingleTitle" = "It's %@'s **birthday** today! 🎂";
"ChatList.BirthdaySingleText" = "Gift them Telegram Premium."; "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.BirthdayMultipleTitle_any" = "%@ contacts have **birthdays** today! 🎂";
"ChatList.BirthdayMultipleText" = "Gift them Telegram Premium."; "ChatList.BirthdayMultipleText" = "Gift them Telegram Premium.";
@ -11689,7 +11689,7 @@ Sorry for the inconvenience.";
"Monetization.BalanceTitle" = "AVAILABLE BALANCE"; "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.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.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.BalanceInfo_URL" = "https://telegram.org";
"Monetization.BalanceWithdraw" = "Withdraw via Fragment"; "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.Withdrawal.Text" = "You can withdraw your TON any time.";
"Monetization.Intro.Info.Title" = "What's #TON?"; "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.Info.Text_URL" = "https://ton.org";
"Monetization.Intro.Understood" = "Understood"; "Monetization.Intro.Understood" = "Understood";
"ReportAd.Title" = "Report Ad"; "ReportAd.Title" = "Report Ad";
"ReportAd.Help" = "Learn more about our [Ad Policies and Guidelines]()."; "ReportAd.Help" = "Learn more about [Telegram Ad Policies and Guidelines]().";
"ReportAd.Help_URL" = "https://promote.telegram.org/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.Reported" = "We will review this ad to ensure it matches our [Ad Policies and Guidelines]().";
"ReportAd.Hidden" = "Ads are hidden now."; "ReportAd.Hidden" = "Ads are hidden now.";

View File

@ -175,16 +175,16 @@ class BarsComponentController: GeneralChartComponentController {
calculatingRange: horizontalRange, calculatingRange: horizontalRange,
addBounds: true) { addBounds: true) {
let (range, labels) = verticalLimitsLabels(verticalRange: range, secondary: false) 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.setup(verticalLimitsLabels: labels, animated: animated)
} // }
verticalScalesRenderer.setVisible(true, animated: animated) verticalScalesRenderer.setVisible(true, animated: animated)
if let secondaryScalesRenderer = self.secondaryScalesRenderer { if let secondaryScalesRenderer = self.secondaryScalesRenderer {
let (range, labels) = verticalLimitsLabels(verticalRange: range, secondary: true) let (_, labels) = verticalLimitsLabels(verticalRange: range, secondary: true)
if secondaryScalesRenderer.verticalRange.end != range { // if secondaryScalesRenderer.verticalRange.end != range {
secondaryScalesRenderer.setup(verticalLimitsLabels: labels, animated: animated) secondaryScalesRenderer.setup(verticalLimitsLabels: labels, animated: animated)
} // }
secondaryScalesRenderer.setVisible(true, animated: animated) secondaryScalesRenderer.setVisible(true, animated: animated)
} }

View File

@ -9,6 +9,8 @@
import Foundation import Foundation
#if os(macOS) #if os(macOS)
import Cocoa import Cocoa
typealias UIColor = NSColor
#else #else
import UIKit import UIKit
#endif #endif

View File

@ -0,0 +1,5 @@
import Foundation
import SwiftSignalKit
import AVFoundation
import UIKit

View File

@ -365,7 +365,7 @@ public final class RevenueStatsTransactionsContext {
} }
} }
public enum RequestRevenueWithdrawalError { public enum RequestRevenueWithdrawalError : Equatable {
case generic case generic
case twoStepAuthMissing case twoStepAuthMissing
case twoStepAuthTooFresh(Int32) case twoStepAuthTooFresh(Int32)

View File

@ -53,6 +53,7 @@ swift_library(
"//submodules/TelegramUI/Components/ListItemSwipeOptionContainer", "//submodules/TelegramUI/Components/ListItemSwipeOptionContainer",
"//submodules/UndoUI", "//submodules/UndoUI",
"//submodules/ShareController", "//submodules/ShareController",
"//submodules/ContextUI",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -21,6 +21,7 @@ final class BusinessLinkListItemComponent: Component {
let action: () -> Void let action: () -> Void
let deleteAction: () -> Void let deleteAction: () -> Void
let shareAction: () -> Void let shareAction: () -> Void
let contextAction: ((ContextExtractedContentContainingView, ContextGesture) -> Void)?
init( init(
context: AccountContext, context: AccountContext,
@ -29,7 +30,8 @@ final class BusinessLinkListItemComponent: Component {
link: TelegramBusinessChatLinks.Link, link: TelegramBusinessChatLinks.Link,
action: @escaping () -> Void, action: @escaping () -> Void,
deleteAction: @escaping () -> Void, deleteAction: @escaping () -> Void,
shareAction: @escaping () -> Void shareAction: @escaping () -> Void,
contextAction: ((ContextExtractedContentContainingView, ContextGesture) -> Void)?
) { ) {
self.context = context self.context = context
self.theme = theme self.theme = theme
@ -38,6 +40,7 @@ final class BusinessLinkListItemComponent: Component {
self.action = action self.action = action
self.deleteAction = deleteAction self.deleteAction = deleteAction
self.shareAction = shareAction self.shareAction = shareAction
self.contextAction = contextAction
} }
static func ==(lhs: BusinessLinkListItemComponent, rhs: BusinessLinkListItemComponent) -> Bool { static func ==(lhs: BusinessLinkListItemComponent, rhs: BusinessLinkListItemComponent) -> Bool {
@ -56,7 +59,8 @@ final class BusinessLinkListItemComponent: Component {
return true return true
} }
final class View: UIView, ListSectionComponent.ChildView { final class View: ContextControllerSourceView, ListSectionComponent.ChildView {
private let extractedContainerView: ContextExtractedContentContainingView
private let containerButton: HighlightTrackingButton private let containerButton: HighlightTrackingButton
private let swipeOptionContainer: ListItemSwipeOptionContainer private let swipeOptionContainer: ListItemSwipeOptionContainer
@ -71,7 +75,10 @@ final class BusinessLinkListItemComponent: Component {
var customUpdateIsHighlighted: ((Bool) -> Void)? var customUpdateIsHighlighted: ((Bool) -> Void)?
private(set) var separatorInset: CGFloat = 0.0 private(set) var separatorInset: CGFloat = 0.0
private var isExtractedToContextMenu: Bool = false
override init(frame: CGRect) { override init(frame: CGRect) {
self.extractedContainerView = ContextExtractedContentContainingView()
self.containerButton = HighlightTrackingButton() self.containerButton = HighlightTrackingButton()
self.containerButton.layer.anchorPoint = CGPoint() self.containerButton.layer.anchorPoint = CGPoint()
self.containerButton.isExclusiveTouch = true self.containerButton.isExclusiveTouch = true
@ -80,6 +87,11 @@ final class BusinessLinkListItemComponent: Component {
super.init(frame: frame) 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.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
self.containerButton.internalHighligthedChanged = { [weak self] isHighlighted in self.containerButton.internalHighligthedChanged = { [weak self] isHighlighted in
guard let self else { guard let self else {
@ -108,9 +120,38 @@ final class BusinessLinkListItemComponent: Component {
} }
} }
self.addSubview(self.swipeOptionContainer)
self.swipeOptionContainer.addSubview(self.containerButton) 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) { required init?(coder: NSCoder) {
@ -128,13 +169,21 @@ final class BusinessLinkListItemComponent: Component {
self.component = component self.component = component
self.componentState = state self.componentState = state
let leftInset: CGFloat = 0.0
let leftContentInset: CGFloat = 62.0 let leftContentInset: CGFloat = 62.0
let rightInset: CGFloat = 8.0 var rightInset: CGFloat = 8.0
let topInset: CGFloat = 9.0 let topInset: CGFloat = 9.0
let bottomInset: CGFloat = 9.0 let bottomInset: CGFloat = 9.0
let titleViewCountSpacing: CGFloat = 4.0 let titleViewCountSpacing: CGFloat = 4.0
let titleTextSpacing: 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 let viewCountText: String
if component.link.viewCount == 0 { if component.link.viewCount == 0 {
viewCountText = component.strings.Business_Links_ItemNoClicks viewCountText = component.strings.Business_Links_ItemNoClicks
@ -149,7 +198,7 @@ final class BusinessLinkListItemComponent: Component {
environment: {}, environment: {},
containerSize: CGSize(width: 100.0, height: 100.0) 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 let viewCountView = self.viewCount.view {
if viewCountView.superview == nil { if viewCountView.superview == nil {
viewCountView.isUserInteractionEnabled = false 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)) text: .plain(NSAttributedString(string: component.link.title ?? component.link.url, font: Font.regular(16.0), textColor: component.theme.list.itemPrimaryTextColor))
)), )),
environment: {}, 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 let titleView = self.title.view {
if titleView.superview == nil { if titleView.superview == nil {
titleView.isUserInteractionEnabled = false titleView.isUserInteractionEnabled = false
@ -213,7 +262,7 @@ final class BusinessLinkListItemComponent: Component {
adjustQuoteFontSize: false, adjustQuoteFontSize: false,
cachedMessageSyntaxHighlight: nil 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( let _ = textApply(TextNodeWithEntities.Arguments(
context: component.context, context: component.context,
cache: component.context.animationCache, cache: component.context.animationCache,
@ -222,7 +271,7 @@ final class BusinessLinkListItemComponent: Component {
attemptSynchronous: true attemptSynchronous: true
)) ))
let textSize = textLayout.size 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 { if self.text.textNode.view.superview == nil {
self.text.textNode.view.isUserInteractionEnabled = false self.text.textNode.view.isUserInteractionEnabled = false
self.containerButton.addSubview(self.text.textNode.view) self.containerButton.addSubview(self.text.textNode.view)
@ -237,18 +286,25 @@ final class BusinessLinkListItemComponent: Component {
self.iconView.isUserInteractionEnabled = false self.iconView.isUserInteractionEnabled = false
self.containerButton.addSubview(self.iconView) 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) transition.setFrame(view: self.iconView, frame: iconFrame)
} }
let swipeOptionContainerFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size) let swipeOptionContainerFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size)
transition.setFrame(view: self.swipeOptionContainer, frame: swipeOptionContainerFrame) transition.setFrame(view: self.swipeOptionContainer, frame: swipeOptionContainerFrame)
transition.setPosition(view: self.containerButton, position: CGPoint()) 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.setBounds(view: self.containerButton, bounds: CGRect(origin: self.containerButton.bounds.origin, size: size))
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) 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] = [] var rightOptions: [ListItemSwipeOptionContainer.Option] = []
rightOptions = [ rightOptions = [
ListItemSwipeOptionContainer.Option( ListItemSwipeOptionContainer.Option(

View File

@ -18,6 +18,7 @@ import BundleIconComponent
import TextFormat import TextFormat
import UndoUI import UndoUI
import ShareController import ShareController
import ContextUI
final class BusinessLinksSetupScreenComponent: Component { final class BusinessLinksSetupScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -535,6 +536,54 @@ final class BusinessLinksSetupScreenComponent: Component {
return return
} }
self.openShareLink(url: link.url) 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) 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)
}
}

View File

@ -1,4 +1,5 @@
{ {
"app": "10.9.2",
"app": "10.10", "app": "10.10",
"bazel": "7.0.2", "bazel": "7.0.2",
"xcode": "15.2", "xcode": "15.2",