mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
26c03bd265
commit
2253fa1550
@ -11729,3 +11729,28 @@ Sorry for the inconvenience.";
|
||||
"Monetization.Intro.Info.Text_URL" = "https://ton.org";
|
||||
|
||||
"Monetization.Intro.Understood" = "Understood";
|
||||
|
||||
"ReportAd.Title" = "Report Ad";
|
||||
"ReportAd.Help" = "Learn more about [Telegram Ad Policies and Guidelines]().";
|
||||
"ReportAd.Help_URL" = "https://promote.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.";
|
||||
|
||||
"AdsInfo.Title" = "About These Ads";
|
||||
"AdsInfo.Info" = "Telegram Ads are very different from ads on other platforms. Ads such as this one:";
|
||||
"AdsInfo.Respect.Title" = "Respect Your Privacy";
|
||||
"AdsInfo.Respect.Text" = "Ads on Telegram do not use your personal information and are based on the channel in which you see them.";
|
||||
"AdsInfo.Split.Title" = "Help the Channel Creator";
|
||||
"AdsInfo.Split.Text" = "50% of the revenue from Telegram Ads goes to the owner of the channel where they are displayed.";
|
||||
"AdsInfo.Ads.Title" = "Can Be Removed";
|
||||
"AdsInfo.Ads.Text" = "You can turn off ads by subscribing to [Telegram Premium](), and Level 30 channels can remove them for their subscribers.";
|
||||
|
||||
"AdsInfo.Launch.Title" = "Can I Launch an Ad?";
|
||||
"AdsInfo.Launch.Text" = "Anyone can create ads to display in this channel – with minimal budgets. Check out the Telegram Ad Platform for details. [Learn More >]()";
|
||||
"AdsInfo.Launch.Text_URL" = "https://promote.telegram.org";
|
||||
|
||||
"AdsInfo.Understood" = "Understood";
|
||||
|
||||
"Chat.ContextMenu.AboutAd" = "About This Ad";
|
||||
"Chat.ContextMenu.ReportAd" = "Report Ad";
|
||||
"Chat.ContextMenu.RemoveAd" = "Remove Ad";
|
||||
|
@ -106,6 +106,10 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
|
||||
}
|
||||
super.setContentOffset(contentOffset, animated: animated)
|
||||
}
|
||||
|
||||
override func touchesShouldCancel(in view: UIView) -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
public final class View: UIView, UIScrollViewDelegate, ComponentTaggedView {
|
||||
@ -363,14 +367,21 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
|
||||
transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: availableSize), completion: nil)
|
||||
|
||||
let previousContentSize = self.scrollView.contentSize
|
||||
self.scrollView.contentSize = contentSize
|
||||
self.scrollView.contentInset = UIEdgeInsets(top: max(0.0, availableSize.height - contentSize.height) + contentSize.height, left: 0.0, bottom: 0.0, right: 0.0)
|
||||
self.ignoreScrolling = false
|
||||
let updateContentSize = {
|
||||
self.scrollView.contentSize = contentSize
|
||||
self.scrollView.contentInset = UIEdgeInsets(top: max(0.0, availableSize.height - contentSize.height) + contentSize.height, left: 0.0, bottom: 0.0, right: 0.0)
|
||||
}
|
||||
if previousContentSize.height.isZero {
|
||||
updateContentSize()
|
||||
}
|
||||
|
||||
self.ignoreScrolling = false
|
||||
if let currentAvailableSize = self.currentAvailableSize, currentAvailableSize.height != availableSize.height {
|
||||
self.scrollView.contentOffset = CGPoint(x: 0.0, y: -(availableSize.height - contentSize.height))
|
||||
} else if component.followContentSizeChanges, !previousContentSize.height.isZero, previousContentSize != contentSize {
|
||||
transition.setBounds(view: self.scrollView, bounds: CGRect(origin: CGPoint(x: 0.0, y: -(availableSize.height - contentSize.height)), size: availableSize))
|
||||
transition.setBounds(view: self.scrollView, bounds: CGRect(origin: CGPoint(x: 0.0, y: -(availableSize.height - contentSize.height)), size: availableSize), completion: { _ in
|
||||
updateContentSize()
|
||||
})
|
||||
}
|
||||
if self.currentHasInputHeight != previousHasInputHeight {
|
||||
transition.setBounds(view: self.scrollView, bounds: CGRect(origin: CGPoint(x: 0.0, y: -(availableSize.height - contentSize.height)), size: self.scrollView.bounds.size))
|
||||
|
@ -79,6 +79,7 @@ extension TelegramBirthday {
|
||||
|
||||
public enum UpdateBirthdayError {
|
||||
case generic
|
||||
case flood
|
||||
}
|
||||
|
||||
func _internal_updateBirthday(account: Account, birthday: TelegramBirthday?) -> Signal<Never, UpdateBirthdayError> {
|
||||
@ -86,9 +87,13 @@ func _internal_updateBirthday(account: Account, birthday: TelegramBirthday?) ->
|
||||
if let _ = birthday {
|
||||
flags |= (1 << 0)
|
||||
}
|
||||
return account.network.request(Api.functions.account.updateBirthday(flags: flags, birthday: birthday?.apiBirthday))
|
||||
|> mapError { _ -> UpdateBirthdayError in
|
||||
return .generic
|
||||
return account.network.request(Api.functions.account.updateBirthday(flags: flags, birthday: birthday?.apiBirthday), automaticFloodWait: false)
|
||||
|> mapError { error -> UpdateBirthdayError in
|
||||
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
|
||||
return .flood
|
||||
} else {
|
||||
return .generic
|
||||
}
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Never, UpdateBirthdayError> in
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
|
@ -20,16 +20,16 @@ private final class ScrollContent: CombinedComponent {
|
||||
typealias EnvironmentType = (ViewControllerComponentContainer.Environment, ScrollChildEnvironment)
|
||||
|
||||
let context: AccountContext
|
||||
let openMore: () -> Void
|
||||
let openPremium: () -> Void
|
||||
let dismiss: () -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
openMore: @escaping () -> Void,
|
||||
openPremium: @escaping () -> Void,
|
||||
dismiss: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.openMore = openMore
|
||||
self.openPremium = openPremium
|
||||
self.dismiss = dismiss
|
||||
}
|
||||
|
||||
@ -80,7 +80,8 @@ private final class ScrollContent: CombinedComponent {
|
||||
let state = context.state
|
||||
|
||||
let theme = environment.theme
|
||||
// let strings = environment.strings
|
||||
let strings = environment.strings
|
||||
let presentationData = context.component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||
let textSideInset: CGFloat = 30.0 + environment.safeInsets.left
|
||||
@ -95,9 +96,7 @@ private final class ScrollContent: CombinedComponent {
|
||||
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: textFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in
|
||||
return (TelegramTextAttributes.URL, contents)
|
||||
})
|
||||
|
||||
//TODO:localize
|
||||
|
||||
|
||||
let spacing: CGFloat = 16.0
|
||||
var contentSize = CGSize(width: context.availableSize.width, height: 30.0)
|
||||
|
||||
@ -137,7 +136,7 @@ private final class ScrollContent: CombinedComponent {
|
||||
|
||||
let title = title.update(
|
||||
component: BalancedTextComponent(
|
||||
text: .plain(NSAttributedString(string: "About These Ads", font: titleFont, textColor: textColor)),
|
||||
text: .plain(NSAttributedString(string: strings.AdsInfo_Title, font: titleFont, textColor: textColor)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.1
|
||||
@ -153,7 +152,7 @@ private final class ScrollContent: CombinedComponent {
|
||||
|
||||
let text = text.update(
|
||||
component: BalancedTextComponent(
|
||||
text: .plain(NSAttributedString(string: "Telegram Ads are very different from ads on other platforms. Ads such as this one:", font: textFont, textColor: secondaryTextColor)),
|
||||
text: .plain(NSAttributedString(string: strings.AdsInfo_Info, font: textFont, textColor: secondaryTextColor)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.2
|
||||
@ -173,9 +172,9 @@ private final class ScrollContent: CombinedComponent {
|
||||
AnyComponentWithIdentity(
|
||||
id: "respect",
|
||||
component: AnyComponent(ParagraphComponent(
|
||||
title: "Respect Your Privacy",
|
||||
title: strings.AdsInfo_Respect_Title,
|
||||
titleColor: textColor,
|
||||
text: "Ads on Telegram do not use your personal information and are based on the channel in which you see them.",
|
||||
text: strings.AdsInfo_Respect_Text,
|
||||
textColor: secondaryTextColor,
|
||||
accentColor: linkColor,
|
||||
iconName: "Ads/Privacy",
|
||||
@ -187,9 +186,9 @@ private final class ScrollContent: CombinedComponent {
|
||||
AnyComponentWithIdentity(
|
||||
id: "split",
|
||||
component: AnyComponent(ParagraphComponent(
|
||||
title: "Help the Channel Creator",
|
||||
title: strings.AdsInfo_Split_Title,
|
||||
titleColor: textColor,
|
||||
text: "50% of the revenue from Telegram Ads goes to the owner of the channel where they are displayed.",
|
||||
text: strings.AdsInfo_Split_Text,
|
||||
textColor: secondaryTextColor,
|
||||
accentColor: linkColor,
|
||||
iconName: "Ads/Split",
|
||||
@ -201,13 +200,16 @@ private final class ScrollContent: CombinedComponent {
|
||||
AnyComponentWithIdentity(
|
||||
id: "ads",
|
||||
component: AnyComponent(ParagraphComponent(
|
||||
title: "Can Be Removed",
|
||||
title: strings.AdsInfo_Ads_Title,
|
||||
titleColor: textColor,
|
||||
text: "You can turn off ads by subscribing to [Telegram Premium](), and Level 30 channels can remove them for their subscribers.",
|
||||
text: strings.AdsInfo_Ads_Text,
|
||||
textColor: secondaryTextColor,
|
||||
accentColor: linkColor,
|
||||
iconName: "Premium/BoostPerk/NoAds",
|
||||
iconColor: linkColor
|
||||
iconColor: linkColor,
|
||||
action: {
|
||||
component.openPremium()
|
||||
}
|
||||
))
|
||||
)
|
||||
)
|
||||
@ -223,7 +225,7 @@ private final class ScrollContent: CombinedComponent {
|
||||
contentSize.height += list.size.height
|
||||
contentSize.height += spacing - 9.0
|
||||
|
||||
let infoTitleAttributedString = NSMutableAttributedString(string: "Can I Launch an Ad?", font: titleFont, textColor: textColor)
|
||||
let infoTitleAttributedString = NSMutableAttributedString(string: strings.AdsInfo_Launch_Title, font: titleFont, textColor: textColor)
|
||||
let infoTitle = infoTitle.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(infoTitleAttributedString),
|
||||
@ -239,7 +241,7 @@ private final class ScrollContent: CombinedComponent {
|
||||
state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Settings/TextArrowRight"), color: linkColor)!, theme)
|
||||
}
|
||||
|
||||
let infoString = "Anyone can create ads to display in this channel – with minimal budgets. Check out the Telegram Ad Platform for details. [Learn More >]()"
|
||||
let infoString = strings.AdsInfo_Launch_Text
|
||||
let infoAttributedString = parseMarkdownIntoAttributedString(infoString, attributes: markdownAttributes).mutableCopy() as! NSMutableAttributedString
|
||||
if let range = infoAttributedString.string.range(of: ">"), let chevronImage = state.cachedChevronImage?.0 {
|
||||
infoAttributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: infoAttributedString.string))
|
||||
@ -249,7 +251,17 @@ private final class ScrollContent: CombinedComponent {
|
||||
text: .plain(infoAttributedString),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.2
|
||||
lineSpacing: 0.2,
|
||||
highlightAction: { attributes in
|
||||
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
|
||||
return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
},
|
||||
tapAction: { _, _ in
|
||||
component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: strings.AdsInfo_Launch_Text_URL, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 3.5, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
@ -287,7 +299,7 @@ private final class ScrollContent: CombinedComponent {
|
||||
|
||||
let actionButton = actionButton.update(
|
||||
component: SolidRoundedButtonComponent(
|
||||
title: "Understood",
|
||||
title: strings.AdsInfo_Understood,
|
||||
theme: SolidRoundedButtonComponent.Theme(
|
||||
backgroundColor: theme.list.itemCheckColors.fillColor,
|
||||
backgroundColors: [],
|
||||
@ -327,14 +339,14 @@ private final class ContainerComponent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let openMore: () -> Void
|
||||
let openPremium: () -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
openMore: @escaping () -> Void
|
||||
openPremium: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.openMore = openMore
|
||||
self.openPremium = openPremium
|
||||
}
|
||||
|
||||
static func ==(lhs: ContainerComponent, rhs: ContainerComponent) -> Bool {
|
||||
@ -367,7 +379,7 @@ private final class ContainerComponent: CombinedComponent {
|
||||
component: ScrollComponent<EnvironmentType>(
|
||||
content: AnyComponent(ScrollContent(
|
||||
context: context.component.context,
|
||||
openMore: context.component.openMore,
|
||||
openPremium: context.component.openPremium,
|
||||
dismiss: {
|
||||
controller()?.dismiss()
|
||||
}
|
||||
@ -387,49 +399,6 @@ private final class ContainerComponent: CombinedComponent {
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
)
|
||||
// let sheet = sheet.update(
|
||||
// component: SheetComponent<EnvironmentType>(
|
||||
// content: AnyComponent<EnvironmentType>(ScrollContent(
|
||||
// context: context.component.context,
|
||||
// openMore: context.component.openMore,
|
||||
// dismiss: {
|
||||
// animateOut.invoke(Action { _ in
|
||||
// if let controller = controller() {
|
||||
// controller.dismiss(completion: nil)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// )),
|
||||
// backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
||||
// followContentSizeChanges: true,
|
||||
// externalState: sheetExternalState,
|
||||
// animateOut: animateOut
|
||||
// ),
|
||||
// environment: {
|
||||
// environment
|
||||
// SheetComponentEnvironment(
|
||||
// isDisplaying: environment.value.isVisible,
|
||||
// isCentered: environment.metrics.widthClass == .regular,
|
||||
// hasInputHeight: !environment.inputHeight.isZero,
|
||||
// regularMetricsSize: CGSize(width: 430.0, height: 900.0),
|
||||
// dismiss: { animated in
|
||||
// if animated {
|
||||
// animateOut.invoke(Action { _ in
|
||||
// if let controller = controller() {
|
||||
// controller.dismiss(completion: nil)
|
||||
// }
|
||||
// })
|
||||
// } else {
|
||||
// if let controller = controller() {
|
||||
// controller.dismiss(completion: nil)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
// },
|
||||
// availableSize: context.availableSize,
|
||||
// transition: context.transition
|
||||
// )
|
||||
|
||||
context.add(scroll
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
||||
@ -448,11 +417,14 @@ public final class AdsInfoScreen: ViewControllerComponentContainer {
|
||||
) {
|
||||
self.context = context
|
||||
|
||||
var openPremiumImpl: (() -> Void)?
|
||||
super.init(
|
||||
context: context,
|
||||
component: ContainerComponent(
|
||||
context: context,
|
||||
openMore: {}
|
||||
openPremium: {
|
||||
openPremiumImpl?()
|
||||
}
|
||||
),
|
||||
navigationBarAppearance: .none,
|
||||
statusBarStyle: .ignore,
|
||||
@ -460,6 +432,20 @@ public final class AdsInfoScreen: ViewControllerComponentContainer {
|
||||
)
|
||||
|
||||
self.navigationPresentation = .modal
|
||||
|
||||
openPremiumImpl = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
let navigationController = self.navigationController
|
||||
self.dismiss()
|
||||
|
||||
Queue.mainQueue().after(0.3) {
|
||||
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .ads, forceDark: false, dismissed: nil)
|
||||
navigationController?.pushViewController(controller, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
@ -475,6 +461,7 @@ private final class ParagraphComponent: CombinedComponent {
|
||||
let accentColor: UIColor
|
||||
let iconName: String
|
||||
let iconColor: UIColor
|
||||
let action: () -> Void
|
||||
|
||||
public init(
|
||||
title: String,
|
||||
@ -483,7 +470,8 @@ private final class ParagraphComponent: CombinedComponent {
|
||||
textColor: UIColor,
|
||||
accentColor: UIColor,
|
||||
iconName: String,
|
||||
iconColor: UIColor
|
||||
iconColor: UIColor,
|
||||
action: @escaping () -> Void = {}
|
||||
) {
|
||||
self.title = title
|
||||
self.titleColor = titleColor
|
||||
@ -492,6 +480,7 @@ private final class ParagraphComponent: CombinedComponent {
|
||||
self.accentColor = accentColor
|
||||
self.iconName = iconName
|
||||
self.iconColor = iconColor
|
||||
self.action = action
|
||||
}
|
||||
|
||||
static func ==(lhs: ParagraphComponent, rhs: ParagraphComponent) -> Bool {
|
||||
@ -557,8 +546,8 @@ private final class ParagraphComponent: CombinedComponent {
|
||||
body: MarkdownAttributeSet(font: textFont, textColor: textColor),
|
||||
bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor),
|
||||
link: MarkdownAttributeSet(font: textFont, textColor: accentColor),
|
||||
linkAttribute: { _ in
|
||||
return nil
|
||||
linkAttribute: { contents in
|
||||
return (TelegramTextAttributes.URL, contents)
|
||||
}
|
||||
)
|
||||
|
||||
@ -567,7 +556,17 @@ private final class ParagraphComponent: CombinedComponent {
|
||||
text: .markdown(text: component.text, attributes: markdownAttributes),
|
||||
horizontalAlignment: .natural,
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.2
|
||||
lineSpacing: 0.2,
|
||||
highlightAction: { attributes in
|
||||
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
|
||||
return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
},
|
||||
tapAction: { _, _ in
|
||||
component.action()
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - leftInset - rightInset, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
|
@ -17,6 +17,12 @@ import ItemListUI
|
||||
import UndoUI
|
||||
import AccountContext
|
||||
|
||||
private enum ReportResult {
|
||||
case reported
|
||||
case hidden
|
||||
case premiumRequired
|
||||
}
|
||||
|
||||
private final class SheetPageContent: CombinedComponent {
|
||||
struct Item: Equatable {
|
||||
let title: String
|
||||
@ -83,7 +89,7 @@ private final class SheetPageContent: CombinedComponent {
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let theme = presentationData.theme
|
||||
// let strings = environment.strings
|
||||
let strings = presentationData.strings
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
|
||||
@ -108,12 +114,12 @@ private final class SheetPageContent: CombinedComponent {
|
||||
|
||||
let backContents: AnyComponent<Empty>
|
||||
if component.title == nil {
|
||||
backContents = AnyComponent(Text(text: "Cancel", font: Font.regular(17.0), color: theme.list.itemAccentColor))
|
||||
backContents = AnyComponent(Text(text: strings.Common_Cancel, font: Font.regular(17.0), color: theme.list.itemAccentColor))
|
||||
} else {
|
||||
backContents = AnyComponent(
|
||||
HStack([
|
||||
AnyComponentWithIdentity(id: "arrow", component: AnyComponent(Image(image: backArrowImage, contentMode: .center))),
|
||||
AnyComponentWithIdentity(id: "label", component: AnyComponent(Text(text: "Back", font: Font.regular(17.0), color: theme.list.itemAccentColor)))
|
||||
AnyComponentWithIdentity(id: "label", component: AnyComponent(Text(text: strings.Common_Back, font: Font.regular(17.0), color: theme.list.itemAccentColor)))
|
||||
], spacing: 6.0)
|
||||
)
|
||||
}
|
||||
@ -132,7 +138,7 @@ private final class SheetPageContent: CombinedComponent {
|
||||
)
|
||||
|
||||
let title = title.update(
|
||||
component: Text(text: "Report Ad", font: Font.semibold(17.0), color: theme.list.itemPrimaryTextColor),
|
||||
component: Text(text: strings.ReportAd_Title, font: Font.semibold(17.0), color: theme.list.itemPrimaryTextColor),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
@ -193,17 +199,18 @@ private final class SheetPageContent: CombinedComponent {
|
||||
)),
|
||||
footer: AnyComponent(MultilineTextComponent(
|
||||
text: .markdown(
|
||||
text: "Learn more about [Telegram Ad Policies and Guidelines]().",
|
||||
text: strings.ReportAd_Help,
|
||||
attributes: MarkdownAttributes(
|
||||
body: MarkdownAttributeSet(font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), textColor: theme.list.freeTextColor),
|
||||
bold: MarkdownAttributeSet(font: Font.semibold(presentationData.listsFontSize.itemListBaseHeaderFontSize), textColor: theme.list.freeTextColor),
|
||||
link: MarkdownAttributeSet(font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), textColor: theme.list.itemAccentColor),
|
||||
linkAttribute: { _ in
|
||||
return nil
|
||||
linkAttribute: { contents in
|
||||
return (TelegramTextAttributes.URL, contents)
|
||||
}
|
||||
)
|
||||
),
|
||||
maximumNumberOfLines: 0,
|
||||
highlightColor: theme.list.itemAccentColor.withAlphaComponent(0.2),
|
||||
highlightAction: { attributes in
|
||||
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
|
||||
return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)
|
||||
@ -212,7 +219,7 @@ private final class SheetPageContent: CombinedComponent {
|
||||
}
|
||||
},
|
||||
tapAction: { _, _ in
|
||||
// component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: strings.Monetization_Intro_Info_Text_URL, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
|
||||
component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: strings.ReportAd_Help_URL, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
|
||||
}
|
||||
)),
|
||||
items: items
|
||||
@ -242,7 +249,7 @@ private final class SheetContent: CombinedComponent {
|
||||
let options: [ReportAdMessageResult.Option]
|
||||
let pts: Int
|
||||
let openMore: () -> Void
|
||||
let complete: () -> Void
|
||||
let complete: (ReportResult) -> Void
|
||||
let dismiss: () -> Void
|
||||
let update: (Transition) -> Void
|
||||
|
||||
@ -254,7 +261,7 @@ private final class SheetContent: CombinedComponent {
|
||||
options: [ReportAdMessageResult.Option],
|
||||
pts: Int,
|
||||
openMore: @escaping () -> Void,
|
||||
complete: @escaping () -> Void,
|
||||
complete: @escaping (ReportResult) -> Void,
|
||||
dismiss: @escaping () -> Void,
|
||||
update: @escaping (Transition) -> Void
|
||||
) {
|
||||
@ -330,9 +337,13 @@ private final class SheetContent: CombinedComponent {
|
||||
state?.pushedOptions = (item.title, title, options)
|
||||
state?.updated(transition: .spring(duration: 0.45))
|
||||
case .adsHidden:
|
||||
complete()
|
||||
complete(.hidden)
|
||||
case .reported:
|
||||
complete()
|
||||
complete(.reported)
|
||||
}
|
||||
}, error: { error in
|
||||
if case .premiumRequired = error {
|
||||
complete(.premiumRequired)
|
||||
}
|
||||
})
|
||||
)
|
||||
@ -377,7 +388,13 @@ private final class SheetContent: CombinedComponent {
|
||||
|
||||
var contentSize = CGSize(width: context.availableSize.width, height: 0.0)
|
||||
let navigation = navigation.update(
|
||||
component: NavigationStackComponent(items: items),
|
||||
component: NavigationStackComponent(
|
||||
items: items,
|
||||
requestPop: { [weak state] in
|
||||
state?.pushedOptions = nil
|
||||
update(.spring(duration: 0.45))
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: context.availableSize.height),
|
||||
transition: context.transition
|
||||
)
|
||||
@ -402,7 +419,7 @@ private final class SheetContainerComponent: CombinedComponent {
|
||||
let title: String
|
||||
let options: [ReportAdMessageResult.Option]
|
||||
let openMore: () -> Void
|
||||
let complete: () -> Void
|
||||
let complete: (ReportResult) -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
@ -411,7 +428,7 @@ private final class SheetContainerComponent: CombinedComponent {
|
||||
title: String,
|
||||
options: [ReportAdMessageResult.Option],
|
||||
openMore: @escaping () -> Void,
|
||||
complete: @escaping () -> Void
|
||||
complete: @escaping (ReportResult) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.peerId = peerId
|
||||
@ -552,7 +569,7 @@ public final class AdsReportScreen: ViewControllerComponentContainer {
|
||||
) {
|
||||
self.context = context
|
||||
|
||||
var completeImpl: (() -> Void)?
|
||||
var completeImpl: ((ReportResult) -> Void)?
|
||||
super.init(
|
||||
context: context,
|
||||
component: SheetContainerComponent(
|
||||
@ -562,8 +579,8 @@ public final class AdsReportScreen: ViewControllerComponentContainer {
|
||||
title: title,
|
||||
options: options,
|
||||
openMore: {},
|
||||
complete: {
|
||||
completeImpl?()
|
||||
complete: { hidden in
|
||||
completeImpl?(hidden)
|
||||
}
|
||||
),
|
||||
navigationBarAppearance: .none,
|
||||
@ -573,7 +590,7 @@ public final class AdsReportScreen: ViewControllerComponentContainer {
|
||||
|
||||
self.navigationPresentation = .flatModal
|
||||
|
||||
completeImpl = { [weak self] in
|
||||
completeImpl = { [weak self] result in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
@ -581,11 +598,32 @@ public final class AdsReportScreen: ViewControllerComponentContainer {
|
||||
self.dismissAnimated()
|
||||
|
||||
Queue.mainQueue().after(0.4, {
|
||||
//TODO:localize
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
(navigationController?.viewControllers.last as? ViewController)?.present(UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: nil, text: "We will review this ad to ensure it matches our [Ad Policies and Guidelines]().", cancel: nil, destructive: false), elevatedLayout: false, action: { _ in
|
||||
return true
|
||||
}), in: .current)
|
||||
switch result {
|
||||
case .reported, .hidden:
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let text: String
|
||||
if case .reported = result {
|
||||
text = presentationData.strings.ReportAd_Reported
|
||||
} else {
|
||||
text = presentationData.strings.ReportAd_Hidden
|
||||
}
|
||||
(navigationController?.viewControllers.last as? ViewController)?.present(UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: nil, text: text, cancel: nil, destructive: false), elevatedLayout: false, action: { action in
|
||||
if case .info = action, case .reported = result {
|
||||
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: presentationData.strings.ReportAd_Help_URL, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
|
||||
}
|
||||
return true
|
||||
}), in: .current)
|
||||
case .premiumRequired:
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .noAds, action: {
|
||||
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .ads, forceDark: false, dismissed: nil)
|
||||
replaceImpl?(controller)
|
||||
})
|
||||
replaceImpl = { [weak controller] c in
|
||||
controller?.replace(with: c)
|
||||
}
|
||||
navigationController?.pushViewController(controller, animated: true)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -609,84 +647,89 @@ public final class AdsReportScreen: ViewControllerComponentContainer {
|
||||
|
||||
|
||||
|
||||
//private final class NavigationContainer: UIView, UIGestureRecognizerDelegate {
|
||||
// var requestUpdate: ((Transition) -> Void)?
|
||||
// var requestPop: (() -> Void)?
|
||||
// var transitionFraction: CGFloat = 0.0
|
||||
//
|
||||
// private var panRecognizer: InteractiveTransitionGestureRecognizer?
|
||||
//
|
||||
// var isNavigationEnabled: Bool = false {
|
||||
// didSet {
|
||||
// self.panRecognizer?.isEnabled = self.isNavigationEnabled
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override init() {
|
||||
// super.init()
|
||||
//
|
||||
// self.clipsToBounds = true
|
||||
//
|
||||
// let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] point in
|
||||
// guard let strongSelf = self else {
|
||||
// return []
|
||||
// }
|
||||
// let _ = strongSelf
|
||||
// return [.right]
|
||||
// })
|
||||
// panRecognizer.delegate = self
|
||||
// self.view.addGestureRecognizer(panRecognizer)
|
||||
// self.panRecognizer = panRecognizer
|
||||
// }
|
||||
//
|
||||
// func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
// return false
|
||||
// }
|
||||
//
|
||||
// func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
// if let _ = otherGestureRecognizer as? InteractiveTransitionGestureRecognizer {
|
||||
// return false
|
||||
// }
|
||||
// if let _ = otherGestureRecognizer as? UIPanGestureRecognizer {
|
||||
// return true
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
//
|
||||
// @objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||
// switch recognizer.state {
|
||||
// case .began:
|
||||
// self.transitionFraction = 0.0
|
||||
// case .changed:
|
||||
// let distanceFactor: CGFloat = recognizer.translation(in: self.view).x / self.bounds.width
|
||||
// let transitionFraction = max(0.0, min(1.0, distanceFactor))
|
||||
// if self.transitionFraction != transitionFraction {
|
||||
// self.transitionFraction = transitionFraction
|
||||
// self.requestUpdate?(.immediate)
|
||||
// }
|
||||
// case .ended, .cancelled:
|
||||
// let distanceFactor: CGFloat = recognizer.translation(in: self.view).x / self.bounds.width
|
||||
// let transitionFraction = max(0.0, min(1.0, distanceFactor))
|
||||
// if transitionFraction > 0.2 {
|
||||
// self.transitionFraction = 0.0
|
||||
// self.requestPop?()
|
||||
// } else {
|
||||
// self.transitionFraction = 0.0
|
||||
// self.requestUpdate?(.spring(duration: 0.45))
|
||||
// }
|
||||
// default:
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
private final class NavigationContainer: UIView, UIGestureRecognizerDelegate {
|
||||
var requestUpdate: ((Transition) -> Void)?
|
||||
var requestPop: (() -> Void)?
|
||||
var transitionFraction: CGFloat = 0.0
|
||||
|
||||
private var panRecognizer: InteractiveTransitionGestureRecognizer?
|
||||
|
||||
var isNavigationEnabled: Bool = false {
|
||||
didSet {
|
||||
self.panRecognizer?.isEnabled = self.isNavigationEnabled
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
super.init(frame: .zero)
|
||||
|
||||
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] point in
|
||||
guard let strongSelf = self else {
|
||||
return []
|
||||
}
|
||||
let _ = strongSelf
|
||||
return [.right]
|
||||
})
|
||||
panRecognizer.delegate = self
|
||||
self.addGestureRecognizer(panRecognizer)
|
||||
self.panRecognizer = panRecognizer
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
if let _ = otherGestureRecognizer as? InteractiveTransitionGestureRecognizer {
|
||||
return false
|
||||
}
|
||||
if let _ = otherGestureRecognizer as? UIPanGestureRecognizer {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
self.transitionFraction = 0.0
|
||||
case .changed:
|
||||
let distanceFactor: CGFloat = recognizer.translation(in: self).x / self.bounds.width
|
||||
let transitionFraction = max(0.0, min(1.0, distanceFactor))
|
||||
if self.transitionFraction != transitionFraction {
|
||||
self.transitionFraction = transitionFraction
|
||||
self.requestUpdate?(.immediate)
|
||||
}
|
||||
case .ended, .cancelled:
|
||||
let distanceFactor: CGFloat = recognizer.translation(in: self).x / self.bounds.width
|
||||
let transitionFraction = max(0.0, min(1.0, distanceFactor))
|
||||
if transitionFraction > 0.2 {
|
||||
self.transitionFraction = 0.0
|
||||
self.requestPop?()
|
||||
} else {
|
||||
self.transitionFraction = 0.0
|
||||
self.requestUpdate?(.spring(duration: 0.45))
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class NavigationStackComponent: Component {
|
||||
public let items: [AnyComponentWithIdentity<Empty>]
|
||||
public let requestPop: () -> Void
|
||||
|
||||
public init(
|
||||
items: [AnyComponentWithIdentity<Empty>]
|
||||
items: [AnyComponentWithIdentity<Empty>],
|
||||
requestPop: @escaping () -> Void
|
||||
) {
|
||||
self.items = items
|
||||
self.requestPop = requestPop
|
||||
}
|
||||
|
||||
public static func ==(lhs: NavigationStackComponent, rhs: NavigationStackComponent) -> Bool {
|
||||
@ -716,11 +759,29 @@ final class NavigationStackComponent: Component {
|
||||
|
||||
public final class View: UIView {
|
||||
private var itemViews: [AnyHashable: ItemView] = [:]
|
||||
private let navigationContainer = NavigationContainer()
|
||||
|
||||
private var component: NavigationStackComponent?
|
||||
private var state: EmptyComponentState?
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.addSubview(self.navigationContainer)
|
||||
|
||||
self.navigationContainer.requestUpdate = { [weak self] transition in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.state?.updated(transition: transition)
|
||||
}
|
||||
|
||||
self.navigationContainer.requestPop = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.component?.requestPop()
|
||||
}
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
@ -729,9 +790,11 @@ final class NavigationStackComponent: Component {
|
||||
|
||||
func update(component: NavigationStackComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
var contentHeight: CGFloat = 0.0
|
||||
|
||||
let navigationTransitionFraction = self.navigationContainer.transitionFraction
|
||||
self.navigationContainer.isNavigationEnabled = component.items.count > 1
|
||||
|
||||
var validItemIds: [AnyHashable] = []
|
||||
struct ReadyItem {
|
||||
var index: Int
|
||||
@ -762,7 +825,6 @@ final class NavigationStackComponent: Component {
|
||||
} else {
|
||||
itemTransition = itemTransition.withAnimation(.none)
|
||||
itemView = ItemView()
|
||||
itemView.clipsToBounds = true
|
||||
self.itemViews[itemId] = itemView
|
||||
itemView.contents.parentState = state
|
||||
}
|
||||
@ -781,15 +843,32 @@ final class NavigationStackComponent: Component {
|
||||
itemTransition: itemTransition,
|
||||
itemSize: itemSize
|
||||
))
|
||||
|
||||
if i == component.items.count - 1 {
|
||||
contentHeight = itemSize.height
|
||||
}
|
||||
}
|
||||
|
||||
for readyItem in readyItems.sorted(by: { $0.index < $1.index }) {
|
||||
let isLast = readyItem.index == readyItems.count - 1
|
||||
let itemFrame = CGRect(origin: CGPoint(x: isLast ? 0.0 : -readyItem.itemSize.width / 3.0, y: 0.0), size: readyItem.itemSize)
|
||||
let sortedItems = readyItems.sorted(by: { $0.index < $1.index })
|
||||
for readyItem in sortedItems {
|
||||
let transitionFraction: CGFloat
|
||||
let alphaTransitionFraction: CGFloat
|
||||
if readyItem.index == readyItems.count - 1 {
|
||||
transitionFraction = navigationTransitionFraction
|
||||
alphaTransitionFraction = 1.0
|
||||
} else if readyItem.index == readyItems.count - 2 {
|
||||
transitionFraction = navigationTransitionFraction - 1.0
|
||||
alphaTransitionFraction = navigationTransitionFraction
|
||||
} else {
|
||||
transitionFraction = 0.0
|
||||
alphaTransitionFraction = 0.0
|
||||
}
|
||||
|
||||
let transitionOffset: CGFloat
|
||||
if readyItem.index == readyItems.count - 1 {
|
||||
transitionOffset = readyItem.itemSize.width * transitionFraction
|
||||
} else {
|
||||
transitionOffset = readyItem.itemSize.width / 3.0 * transitionFraction
|
||||
}
|
||||
|
||||
let itemFrame = CGRect(origin: CGPoint(x: transitionOffset, y: 0.0), size: readyItem.itemSize)
|
||||
|
||||
let itemBounds = CGRect(origin: .zero, size: itemFrame.size)
|
||||
if let itemComponentView = readyItem.itemView.contents.view {
|
||||
var isAdded = false
|
||||
@ -797,12 +876,12 @@ final class NavigationStackComponent: Component {
|
||||
isAdded = true
|
||||
|
||||
readyItem.itemView.insertSubview(itemComponentView, at: 0)
|
||||
self.addSubview(readyItem.itemView)
|
||||
self.navigationContainer.addSubview(readyItem.itemView)
|
||||
}
|
||||
readyItem.itemTransition.setFrame(view: readyItem.itemView, frame: itemFrame)
|
||||
readyItem.itemTransition.setFrame(view: itemComponentView, frame: itemBounds)
|
||||
readyItem.itemTransition.setFrame(view: readyItem.itemView.dimView, frame: itemBounds)
|
||||
readyItem.itemTransition.setAlpha(view: readyItem.itemView.dimView, alpha: isLast ? 0.0 : 1.0)
|
||||
readyItem.itemTransition.setAlpha(view: readyItem.itemView.dimView, alpha: 1.0 - alphaTransitionFraction)
|
||||
|
||||
if readyItem.index > 0 && isAdded {
|
||||
transition.animatePosition(view: itemComponentView, from: CGPoint(x: itemFrame.width, y: 0.0), to: .zero, additive: true, completion: nil)
|
||||
@ -810,6 +889,15 @@ final class NavigationStackComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
let lastHeight = sortedItems.last?.itemSize.height ?? 0.0
|
||||
let previousHeight: CGFloat
|
||||
if sortedItems.count > 1 {
|
||||
previousHeight = sortedItems[sortedItems.count - 2].itemSize.height
|
||||
} else {
|
||||
previousHeight = lastHeight
|
||||
}
|
||||
let contentHeight = lastHeight * (1.0 - navigationTransitionFraction) + previousHeight * navigationTransitionFraction
|
||||
|
||||
var removedItemIds: [AnyHashable] = []
|
||||
for (id, _) in self.itemViews {
|
||||
if !validItemIds.contains(id) {
|
||||
@ -833,7 +921,10 @@ final class NavigationStackComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
return CGSize(width: availableSize.width, height: contentHeight)
|
||||
let contentSize = CGSize(width: availableSize.width, height: contentHeight)
|
||||
self.navigationContainer.frame = CGRect(origin: .zero, size: contentSize)
|
||||
|
||||
return contentSize
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,6 @@ import DebugSettingsUI
|
||||
import ChatPresentationInterfaceState
|
||||
import Pasteboard
|
||||
import SettingsUI
|
||||
import PremiumUI
|
||||
import TextNodeWithEntities
|
||||
import ChatControllerInteraction
|
||||
import ChatMessageItemCommon
|
||||
@ -477,21 +476,18 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
|
||||
var actions: [ContextMenuItem] = []
|
||||
|
||||
if "".isEmpty {
|
||||
// if adAttribute.canReport {
|
||||
//TODO:localize
|
||||
|
||||
actions.append(.action(ContextMenuActionItem(text: "About This Ad", textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in
|
||||
if adAttribute.canReport {
|
||||
actions.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_ContextMenu_AboutAd, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.actionSheet.primaryTextColor)
|
||||
}, iconSource: nil, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
controllerInteraction.navigationController()?.pushViewController(AdsInfoScreen(context: context))
|
||||
})))
|
||||
|
||||
actions.append(.action(ContextMenuActionItem(text: "Report Ad", textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in
|
||||
actions.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_ContextMenu_ReportAd, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.actionSheet.primaryTextColor)
|
||||
}, iconSource: nil, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
f(.default)
|
||||
|
||||
let _ = (context.engine.messages.reportAdMessage(peerId: message.id.peerId, opaqueId: adAttribute.opaqueId, option: nil)
|
||||
|> deliverOnMainQueue).start(next: { result in
|
||||
@ -511,13 +507,13 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
|
||||
actions.append(.separator)
|
||||
|
||||
actions.append(.action(ContextMenuActionItem(text: "Remove Ad", textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in
|
||||
actions.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_ContextMenu_RemoveAd, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.primaryTextColor)
|
||||
}, iconSource: nil, action: { c, _ in
|
||||
c.dismiss(completion: {
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let controller = PremiumDemoScreen(context: context, subject: .noAds, action: {
|
||||
let controller = PremiumIntroScreen(context: context, source: .ads)
|
||||
let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .noAds, action: {
|
||||
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .ads, forceDark: false, dismissed: nil)
|
||||
replaceImpl?(controller)
|
||||
})
|
||||
replaceImpl = { [weak controller] c in
|
||||
@ -585,8 +581,8 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
}, iconSource: nil, action: { c, _ in
|
||||
c.dismiss(completion: {
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let controller = PremiumDemoScreen(context: context, subject: .noAds, action: {
|
||||
let controller = PremiumIntroScreen(context: context, source: .ads)
|
||||
let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .noAds, action: {
|
||||
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .ads, forceDark: false, dismissed: nil)
|
||||
replaceImpl?(controller)
|
||||
})
|
||||
replaceImpl = { [weak controller] c in
|
||||
@ -1083,15 +1079,14 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
}, action: { _, f in
|
||||
let context = context
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let controller = PremiumDemoScreen(context: context, subject: .fasterDownload, action: {
|
||||
let controller = PremiumIntroScreen(context: context, source: .fasterDownload)
|
||||
let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .fasterDownload, action: {
|
||||
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .fasterDownload, forceDark: false, dismissed: nil)
|
||||
replaceImpl?(controller)
|
||||
})
|
||||
replaceImpl = { [weak controller] c in
|
||||
controller?.replace(with: c)
|
||||
}
|
||||
controllerInteraction.navigationController()?.pushViewController(controller)
|
||||
|
||||
f(.dismissWithoutContent)
|
||||
})))
|
||||
actions.append(.separator)
|
||||
@ -1626,7 +1621,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
}
|
||||
controllerInteraction.presentControllerInCurrent(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: presentationData.strings.Premium_MaxSavedGifsTitle("\(limit)").string, text: text, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
||||
if case .info = action {
|
||||
let controller = PremiumIntroScreen(context: context, source: .savedGifs)
|
||||
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .savedGifs, forceDark: false, dismissed: nil)
|
||||
controllerInteraction.navigationController()?.pushViewController(controller)
|
||||
return true
|
||||
}
|
||||
|
@ -279,6 +279,10 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
displayUndo = false
|
||||
}
|
||||
self.originalRemainingSeconds = 5
|
||||
|
||||
if text.contains("](") {
|
||||
isUserInteractionEnabled = true
|
||||
}
|
||||
case let .linkCopied(text):
|
||||
self.avatarNode = nil
|
||||
self.iconNode = nil
|
||||
|
Loading…
x
Reference in New Issue
Block a user