Various improvements

This commit is contained in:
Ilya Laktyushin 2024-08-11 06:32:41 +02:00
parent c92ce1c1dc
commit 83c7fdd228
36 changed files with 395 additions and 178 deletions

View File

@ -9197,6 +9197,8 @@ Sorry for the inconvenience.";
"WebApp.LaunchMoreInfo" = "More about this bot"; "WebApp.LaunchMoreInfo" = "More about this bot";
"WebApp.LaunchConfirmation" = "To launch this web app, you will connect to its website."; "WebApp.LaunchConfirmation" = "To launch this web app, you will connect to its website.";
"WebApp.LaunchTermsConfirmation" = "By launching this mini app, you agree to the [Terms of Service for Mini Apps]().";
"WebApp.LaunchTermsConfirmation_URL" = "https://telegram.org/tos/mini-apps";
"WebApp.LaunchOpenApp" = "Open App"; "WebApp.LaunchOpenApp" = "Open App";
"WallpaperPreview.PreviewInNightMode" = "Preview this wallpaper in night mode."; "WallpaperPreview.PreviewInNightMode" = "Preview this wallpaper in night mode.";
@ -12517,6 +12519,7 @@ Sorry for the inconvenience.";
"Notification.StarsGift.Title_any" = "%@ Stars"; "Notification.StarsGift.Title_any" = "%@ Stars";
"Notification.StarsGift.Subtitle" = "Use Stars to unlock content and services on Telegram."; "Notification.StarsGift.Subtitle" = "Use Stars to unlock content and services on Telegram.";
"Notification.StarsGift.SubtitleYou" = "With Stars, %@ will be able to unlock content and services on Telegram."; "Notification.StarsGift.SubtitleYou" = "With Stars, %@ will be able to unlock content and services on Telegram.";
"Notification.StarsGift.UnknownUser" = "Unknown user";
"Bot.Settings" = "Bot Settings"; "Bot.Settings" = "Bot Settings";
@ -12736,3 +12739,32 @@ Sorry for the inconvenience.";
"Stars.Transfer.Subscribe.Successful.Text" = "%1$@ transferred to %2$@."; "Stars.Transfer.Subscribe.Successful.Text" = "%1$@ transferred to %2$@.";
"Gallery.Ad" = "Ad"; "Gallery.Ad" = "Ad";
"Chat.SensitiveContent" = "18+ Content";
"Settings.SensitiveContent" = "Show 18+ Content";
"Settings.SensitiveContentInfo" = "Do not hide media that contain content suitable only for adults.";
"SensitiveContent.Title" = "18+ Content";
"SensitiveContent.Text" = "This media may contain sensitive content suitable only for adults.\nDo you still want to view it?";
"SensitiveContent.ShowAlways" = "Always show 18+ media";
"SensitiveContent.ViewAnyway" = "View Anyway";
"SensitiveContent.SettingsInfo" = "You can update the visibility of sensitive media in [Data and Storage > Show 18+ Content]().";
"Notification.Refund" = "You received a refund of {amount} from {name}";
"InviteLink.SubscriptionFee.Title" = "SUBSCRIPTION FEE";
"InviteLink.SubscriptionFee.PerMonth" = "%@ / month";
"InviteLink.SubscriptionFee.NoOneJoined" = "No one joined yet";
"InviteLink.SubscriptionFee.ApproximateIncome" = "You get approximately %@ monthly";
"InviteLink.PerMonth" = "per month";
"InviteLink.Create.Fee" = "Require Monthly Fee";
"InviteLink.Create.FeePerMonth" = "%@ / month";
"InviteLink.Create.FeePlaceholder" = "Stars amount per month";
"InviteLink.Create.FeeInfo" = "Charge a subscription fee from people joining your channel via this link. [Learn More >]()";
"InviteLink.Create.FeeEditInfo" = "If you need to change the subscription fee, create a new invite link with a different price.";
"InviteLink.Create.RequestApprovalFeeUnavailable" = "You can't enable admin approval for links that require a monthly fee.";
"WebApp.PrivacyPolicy_URL" = "https://telegram.org/privacy-tpa";

View File

@ -996,6 +996,8 @@ public protocol SharedAccountContext: AnyObject {
func makeProxySettingsController(sharedContext: SharedAccountContext, account: UnauthorizedAccount) -> ViewController func makeProxySettingsController(sharedContext: SharedAccountContext, account: UnauthorizedAccount) -> ViewController
func makeDataAndStorageController(context: AccountContext, sensitiveContent: Bool) -> ViewController
func makeInstalledStickerPacksController(context: AccountContext, mode: InstalledStickerPacksControllerMode, forceTheme: PresentationTheme?) -> ViewController func makeInstalledStickerPacksController(context: AccountContext, mode: InstalledStickerPacksControllerMode, forceTheme: PresentationTheme?) -> ViewController
func makeChannelStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: EnginePeer.Id, boosts: Bool, boostStatus: ChannelBoostStatus?) -> ViewController func makeChannelStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: EnginePeer.Id, boosts: Bool, boostStatus: ChannelBoostStatus?) -> ViewController

View File

@ -62,6 +62,7 @@ public final class ChatMessageItemAssociatedData: Equatable {
public let deviceContactsNumbers: Set<String> public let deviceContactsNumbers: Set<String>
public let isStandalone: Bool public let isStandalone: Bool
public let isInline: Bool public let isInline: Bool
public let showSensitiveContent: Bool
public init( public init(
automaticDownloadPeerType: MediaAutoDownloadPeerType, automaticDownloadPeerType: MediaAutoDownloadPeerType,
@ -94,7 +95,8 @@ public final class ChatMessageItemAssociatedData: Equatable {
chatThemes: [TelegramTheme] = [], chatThemes: [TelegramTheme] = [],
deviceContactsNumbers: Set<String> = Set(), deviceContactsNumbers: Set<String> = Set(),
isStandalone: Bool = false, isStandalone: Bool = false,
isInline: Bool = false isInline: Bool = false,
showSensitiveContent: Bool = false
) { ) {
self.automaticDownloadPeerType = automaticDownloadPeerType self.automaticDownloadPeerType = automaticDownloadPeerType
self.automaticDownloadPeerId = automaticDownloadPeerId self.automaticDownloadPeerId = automaticDownloadPeerId
@ -127,6 +129,7 @@ public final class ChatMessageItemAssociatedData: Equatable {
self.deviceContactsNumbers = deviceContactsNumbers self.deviceContactsNumbers = deviceContactsNumbers
self.isStandalone = isStandalone self.isStandalone = isStandalone
self.isInline = isInline self.isInline = isInline
self.showSensitiveContent = showSensitiveContent
} }
public static func == (lhs: ChatMessageItemAssociatedData, rhs: ChatMessageItemAssociatedData) -> Bool { public static func == (lhs: ChatMessageItemAssociatedData, rhs: ChatMessageItemAssociatedData) -> Bool {
@ -217,6 +220,9 @@ public final class ChatMessageItemAssociatedData: Equatable {
if lhs.isInline != rhs.isInline { if lhs.isInline != rhs.isInline {
return false return false
} }
if lhs.showSensitiveContent != rhs.showSensitiveContent {
return false
}
return true return true
} }
} }

View File

@ -105,6 +105,7 @@ public class MediaDustNode: ASDisplayNode {
private var staticNode: ASImageNode? private var staticNode: ASImageNode?
private var staticParams: CGSize? private var staticParams: CGSize?
public var revealOnTap = true
public var isRevealed = false public var isRevealed = false
private var isExploding = false private var isExploding = false
@ -218,6 +219,10 @@ public class MediaDustNode: ASDisplayNode {
self.tapped() self.tapped()
guard self.revealOnTap else {
return
}
self.isRevealed = true self.isRevealed = true
if self.enableAnimations { if self.enableAnimations {

View File

@ -489,20 +489,19 @@ private func inviteLinkEditControllerEntries(invite: ExportedInvitation?, state:
let isEditingEnabled = invite?.pricing == nil let isEditingEnabled = invite?.pricing == nil
let isSubscription = state.subscriptionEnabled let isSubscription = state.subscriptionEnabled
if !isGroup { if !isGroup {
//TODO:localize entries.append(.subscriptionFeeToggle(presentationData.theme, presentationData.strings.InviteLink_Create_Fee, state.subscriptionEnabled, isEditingEnabled))
entries.append(.subscriptionFeeToggle(presentationData.theme, "Require Monthly Fee", state.subscriptionEnabled, isEditingEnabled))
if state.subscriptionEnabled { if state.subscriptionEnabled {
var label: String = "" var label: String = ""
if let subscriptionFee = state.subscriptionFee, subscriptionFee > 0, let starsState { if let subscriptionFee = state.subscriptionFee, subscriptionFee > 0, let starsState {
label = "\(formatTonUsdValue(subscriptionFee, divide: false, rate: starsState.usdRate, dateTimeFormat: presentationData.dateTimeFormat)) / month" label = presentationData.strings.InviteLink_Create_FeePerMonth("\(formatTonUsdValue(subscriptionFee, divide: false, rate: starsState.usdRate, dateTimeFormat: presentationData.dateTimeFormat))").string
} }
entries.append(.subscriptionFee(presentationData.theme, "Stars amount per month", isEditingEnabled, state.subscriptionFee, label, configuration.maxFee)) entries.append(.subscriptionFee(presentationData.theme, presentationData.strings.InviteLink_Create_FeePlaceholder, isEditingEnabled, state.subscriptionFee, label, configuration.maxFee))
} }
let infoText: String let infoText: String
if let _ = invite, state.subscriptionEnabled { if let _ = invite, state.subscriptionEnabled {
infoText = "If you need to change the subscription fee, create a new invite link with a different price." infoText = presentationData.strings.InviteLink_Create_FeeEditInfo
} else { } else {
infoText = "Charge a subscription fee from people joining your channel via this link. [Learn More >]()" infoText = presentationData.strings.InviteLink_Create_FeeInfo
} }
entries.append(.subscriptionFeeInfo(presentationData.theme, infoText)) entries.append(.subscriptionFeeInfo(presentationData.theme, infoText))
} }
@ -511,7 +510,7 @@ private func inviteLinkEditControllerEntries(invite: ExportedInvitation?, state:
entries.append(.requestApproval(presentationData.theme, presentationData.strings.InviteLink_Create_RequestApproval, state.requestApproval, isEditingEnabled && !isSubscription)) entries.append(.requestApproval(presentationData.theme, presentationData.strings.InviteLink_Create_RequestApproval, state.requestApproval, isEditingEnabled && !isSubscription))
var requestApprovalInfoText = presentationData.strings.InviteLink_Create_RequestApprovalOffInfoChannel var requestApprovalInfoText = presentationData.strings.InviteLink_Create_RequestApprovalOffInfoChannel
if isSubscription { if isSubscription {
requestApprovalInfoText = "You can't enable admin approval for links that require a monthly fee." requestApprovalInfoText = presentationData.strings.InviteLink_Create_RequestApprovalFeeUnavailable
} else { } else {
if state.requestApproval { if state.requestApproval {
requestApprovalInfoText = isGroup ? presentationData.strings.InviteLink_Create_RequestApprovalOnInfoGroup : presentationData.strings.InviteLink_Create_RequestApprovalOnInfoChannel requestApprovalInfoText = isGroup ? presentationData.strings.InviteLink_Create_RequestApprovalOnInfoGroup : presentationData.strings.InviteLink_Create_RequestApprovalOnInfoChannel

View File

@ -310,10 +310,9 @@ private enum InviteLinkViewEntry: Comparable, Identifiable {
let label: ItemListPeerItemLabel let label: ItemListPeerItemLabel
if let pricing { if let pricing {
//TODO:localize
let text = NSMutableAttributedString() let text = NSMutableAttributedString()
text.append(NSAttributedString(string: "⭐️\(pricing.amount)\n", font: Font.semibold(17.0), textColor: presentationData.theme.list.itemPrimaryTextColor)) text.append(NSAttributedString(string: "⭐️\(pricing.amount)\n", font: Font.semibold(17.0), textColor: presentationData.theme.list.itemPrimaryTextColor))
text.append(NSAttributedString(string: "per month", font: Font.regular(13.0), textColor: presentationData.theme.list.itemSecondaryTextColor)) text.append(NSAttributedString(string: presentationData.strings.InviteLink_PerMonth, font: Font.regular(13.0), textColor: presentationData.theme.list.itemSecondaryTextColor))
if let range = text.string.range(of: "⭐️") { if let range = text.string.range(of: "⭐️") {
text.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: text.string)) text.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: text.string))
text.addAttribute(NSAttributedString.Key.font, value: Font.semibold(15.0), range: NSRange(range, in: text.string)) text.addAttribute(NSAttributedString.Key.font, value: Font.semibold(15.0), range: NSRange(range, in: text.string))
@ -841,15 +840,14 @@ public final class InviteLinkViewController: ViewController {
entries.append(.link(presentationData.theme, invite)) entries.append(.link(presentationData.theme, invite))
if let pricing = invite.pricing { if let pricing = invite.pricing {
//TODO:localize entries.append(.subscriptionHeader(presentationData.theme, presentationData.strings.InviteLink_SubscriptionFee_Title.uppercased()))
entries.append(.subscriptionHeader(presentationData.theme, "SUBSCRIPTION FEE")) var title = presentationData.strings.InviteLink_SubscriptionFee_PerMonth("\(pricing.amount)").string
var title = "⭐️\(pricing.amount) / month" var subtitle = presentationData.strings.InviteLink_SubscriptionFee_NoOneJoined
var subtitle = "No one joined yet"
if state.count > 0 { if state.count > 0 {
title += " x \(state.count)" title += " x \(state.count)"
if let usdRate { if let usdRate {
let usdValue = formatTonUsdValue(pricing.amount * Int64(state.count), divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat) let usdValue = formatTonUsdValue(pricing.amount * Int64(state.count), divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat)
subtitle = "You get approximately \(usdValue) monthly" subtitle = presentationData.strings.InviteLink_SubscriptionFee_ApproximateIncome(usdValue).string
} else { } else {
subtitle = "" subtitle = ""
} }

View File

@ -404,10 +404,9 @@ public class ItemListInviteLinkItemNode: ListViewItemNode, ItemListItemNode {
} }
if let subscriptionPricing { if let subscriptionPricing {
//TODO:localize
let text = NSMutableAttributedString() let text = NSMutableAttributedString()
text.append(NSAttributedString(string: "⭐️\(subscriptionPricing.amount)\n", font: Font.semibold(17.0), textColor: item.presentationData.theme.list.itemPrimaryTextColor)) text.append(NSAttributedString(string: "⭐️\(subscriptionPricing.amount)\n", font: Font.semibold(17.0), textColor: item.presentationData.theme.list.itemPrimaryTextColor))
text.append(NSAttributedString(string: "per month", font: Font.regular(13.0), textColor: item.presentationData.theme.list.itemSecondaryTextColor)) text.append(NSAttributedString(string: item.presentationData.strings.InviteLink_PerMonth, font: Font.regular(13.0), textColor: item.presentationData.theme.list.itemSecondaryTextColor))
if let range = text.string.range(of: "⭐️") { if let range = text.string.range(of: "⭐️") {
text.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: text.string)) text.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: text.string))
text.addAttribute(NSAttributedString.Key.font, value: Font.semibold(15.0), range: NSRange(range, in: text.string)) text.addAttribute(NSAttributedString.Key.font, value: Font.semibold(15.0), range: NSRange(range, in: text.string))

View File

@ -20,6 +20,9 @@ public extension Message {
public extension RestrictedContentMessageAttribute { public extension RestrictedContentMessageAttribute {
func platformText(platform: String, contentSettings: ContentSettings) -> String? { func platformText(platform: String, contentSettings: ContentSettings) -> String? {
for rule in self.rules { for rule in self.rules {
if rule.isSensitive {
continue
}
if rule.platform == "all" || rule.platform == "ios" || contentSettings.addContentRestrictionReasons.contains(rule.platform) { if rule.platform == "all" || rule.platform == "ios" || contentSettings.addContentRestrictionReasons.contains(rule.platform) {
if !contentSettings.ignoreContentRestrictionReasons.contains(rule.reason) { if !contentSettings.ignoreContentRestrictionReasons.contains(rule.reason) {
return rule.text return rule.text

View File

@ -232,6 +232,12 @@ public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13
}, initialValues: [:], queue: queue) }, initialValues: [:], queue: queue)
} }
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>, _ s12: Signal<T12, E>, _ s13: Signal<T13, E>, _ s14: Signal<T14, E>, _ s15: Signal<T15, E>, _ s16: Signal<T16, E>, _ s17: Signal<T17, E>, _ s18: Signal<T18, E>, _ s19: Signal<T19, E>, _ s20: Signal<T20, E>, _ s21: Signal<T21, E>, _ s22: Signal<T22, E>, _ s23: Signal<T23, E>, _ s24: Signal<T24, E>, _ s25: Signal<T25, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11), signalOfAny(s12), signalOfAny(s13), signalOfAny(s14), signalOfAny(s15), signalOfAny(s16), signalOfAny(s17), signalOfAny(s18), signalOfAny(s19), signalOfAny(s20), signalOfAny(s21), signalOfAny(s22), signalOfAny(s23), signalOfAny(s24), signalOfAny(s25)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11, values[11] as! T12, values[12] as! T13, values[13] as! T14, values[14] as! T15, values[15] as! T16, values[16] as! T17, values[17] as! T18, values[18] as! T19, values[19] as! T20, values[20] as! T21, values[21] as! T22, values[22] as! T23, values[23] as! T24, values[24] as! T25)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T, E>(queue: Queue? = nil, _ signals: [Signal<T, E>]) -> Signal<[T], E> { public func combineLatest<T, E>(queue: Queue? = nil, _ signals: [Signal<T, E>]) -> Signal<[T], E> {
if signals.count == 0 { if signals.count == 0 {
return single([T](), E.self) return single([T](), E.self)

View File

@ -34,9 +34,26 @@ private final class DataAndStorageControllerArguments {
let toggleDownloadInBackground: (Bool) -> Void let toggleDownloadInBackground: (Bool) -> Void
let openBrowserSelection: () -> Void let openBrowserSelection: () -> Void
let openIntents: () -> Void let openIntents: () -> Void
let toggleEnableSensitiveContent: (Bool) -> Void let toggleSensitiveContent: (Bool) -> Void
let toggleOtherSensitiveContent: (Bool) -> Void
init(openStorageUsage: @escaping () -> Void, openNetworkUsage: @escaping () -> Void, openProxy: @escaping () -> Void, openAutomaticDownloadConnectionType: @escaping (AutomaticDownloadConnectionType) -> Void, resetAutomaticDownload: @escaping () -> Void, toggleVoiceUseLessData: @escaping (Bool) -> Void, openSaveIncoming: @escaping (AutomaticSaveIncomingPeerType) -> Void, toggleSaveEditedPhotos: @escaping (Bool) -> Void, togglePauseMusicOnRecording: @escaping (Bool) -> Void, toggleRaiseToListen: @escaping (Bool) -> Void, toggleDownloadInBackground: @escaping (Bool) -> Void, openBrowserSelection: @escaping () -> Void, openIntents: @escaping () -> Void, toggleEnableSensitiveContent: @escaping (Bool) -> Void) { init(
openStorageUsage: @escaping () -> Void,
openNetworkUsage: @escaping () -> Void,
openProxy: @escaping () -> Void,
openAutomaticDownloadConnectionType: @escaping (AutomaticDownloadConnectionType) -> Void,
resetAutomaticDownload: @escaping () -> Void,
toggleVoiceUseLessData: @escaping (Bool) -> Void,
openSaveIncoming: @escaping (AutomaticSaveIncomingPeerType) -> Void,
toggleSaveEditedPhotos: @escaping (Bool) -> Void,
togglePauseMusicOnRecording: @escaping (Bool) -> Void,
toggleRaiseToListen: @escaping (Bool) -> Void,
toggleDownloadInBackground: @escaping (Bool) -> Void,
openBrowserSelection: @escaping () -> Void,
openIntents: @escaping () -> Void,
toggleSensitiveContent: @escaping (Bool) -> Void,
toggleOtherSensitiveContent: @escaping (Bool) -> Void
) {
self.openStorageUsage = openStorageUsage self.openStorageUsage = openStorageUsage
self.openNetworkUsage = openNetworkUsage self.openNetworkUsage = openNetworkUsage
self.openProxy = openProxy self.openProxy = openProxy
@ -50,7 +67,8 @@ private final class DataAndStorageControllerArguments {
self.toggleDownloadInBackground = toggleDownloadInBackground self.toggleDownloadInBackground = toggleDownloadInBackground
self.openBrowserSelection = openBrowserSelection self.openBrowserSelection = openBrowserSelection
self.openIntents = openIntents self.openIntents = openIntents
self.toggleEnableSensitiveContent = toggleEnableSensitiveContent self.toggleSensitiveContent = toggleSensitiveContent
self.toggleOtherSensitiveContent = toggleOtherSensitiveContent
} }
} }
@ -62,7 +80,8 @@ private enum DataAndStorageSection: Int32 {
case voiceCalls case voiceCalls
case other case other
case connection case connection
case enableSensitiveContent case sensitiveContent
case otherSensitiveContent
} }
public enum DataAndStorageEntryTag: ItemListItemTag, Equatable { public enum DataAndStorageEntryTag: ItemListItemTag, Equatable {
@ -72,6 +91,7 @@ public enum DataAndStorageEntryTag: ItemListItemTag, Equatable {
case pauseMusicOnRecording case pauseMusicOnRecording
case raiseToListen case raiseToListen
case autoSave(AutomaticSaveIncomingPeerType) case autoSave(AutomaticSaveIncomingPeerType)
case sensitiveContent
public func isEqual(to other: ItemListItemTag) -> Bool { public func isEqual(to other: ItemListItemTag) -> Bool {
if let other = other as? DataAndStorageEntryTag, self == other { if let other = other as? DataAndStorageEntryTag, self == other {
@ -107,9 +127,12 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
case raiseToListen(PresentationTheme, String, Bool) case raiseToListen(PresentationTheme, String, Bool)
case raiseToListenInfo(PresentationTheme, String) case raiseToListenInfo(PresentationTheme, String)
case sensitiveContent(String, Bool)
case sensitiveContentInfo(String)
case connectionHeader(PresentationTheme, String) case connectionHeader(PresentationTheme, String)
case connectionProxy(PresentationTheme, String, String) case connectionProxy(PresentationTheme, String, String)
case enableSensitiveContent(String, Bool) case otherSensitiveContent(String, Bool)
var section: ItemListSectionId { var section: ItemListSectionId {
switch self { switch self {
@ -125,10 +148,12 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
return DataAndStorageSection.voiceCalls.rawValue return DataAndStorageSection.voiceCalls.rawValue
case .otherHeader, .openLinksIn, .shareSheet, .saveEditedPhotos, .pauseMusicOnRecording, .raiseToListen, .raiseToListenInfo: case .otherHeader, .openLinksIn, .shareSheet, .saveEditedPhotos, .pauseMusicOnRecording, .raiseToListen, .raiseToListenInfo:
return DataAndStorageSection.other.rawValue return DataAndStorageSection.other.rawValue
case .sensitiveContent, .sensitiveContentInfo:
return DataAndStorageSection.sensitiveContent.rawValue
case .connectionHeader, .connectionProxy: case .connectionHeader, .connectionProxy:
return DataAndStorageSection.connection.rawValue return DataAndStorageSection.connection.rawValue
case .enableSensitiveContent: case .otherSensitiveContent:
return DataAndStorageSection.enableSensitiveContent.rawValue return DataAndStorageSection.otherSensitiveContent.rawValue
} }
} }
@ -174,12 +199,16 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
return 34 return 34
case .raiseToListenInfo: case .raiseToListenInfo:
return 35 return 35
case .connectionHeader: case .sensitiveContent:
return 36 return 36
case .connectionProxy: case .sensitiveContentInfo:
return 37 return 37
case .enableSensitiveContent: case .connectionHeader:
return 38 return 38
case .connectionProxy:
return 39
case .otherSensitiveContent:
return 40
} }
} }
@ -293,6 +322,18 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
} else { } else {
return false return false
} }
case let .sensitiveContent(text, value):
if case .sensitiveContent(text, value) = rhs {
return true
} else {
return false
}
case let .sensitiveContentInfo(text):
if case .sensitiveContentInfo(text) = rhs {
return true
} else {
return false
}
case let .downloadInBackground(lhsTheme, lhsText, lhsValue): case let .downloadInBackground(lhsTheme, lhsText, lhsValue):
if case let .downloadInBackground(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { if case let .downloadInBackground(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true return true
@ -317,8 +358,8 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
} else { } else {
return false return false
} }
case let .enableSensitiveContent(text, value): case let .otherSensitiveContent(text, value):
if case .enableSensitiveContent(text, value) = rhs { if case .otherSensitiveContent(text, value) = rhs {
return true return true
} else { } else {
return false return false
@ -408,6 +449,12 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
}, tag: DataAndStorageEntryTag.raiseToListen) }, tag: DataAndStorageEntryTag.raiseToListen)
case let .raiseToListenInfo(_, text): case let .raiseToListenInfo(_, text):
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section) return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section)
case let .sensitiveContent(text, value):
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.toggleSensitiveContent(value)
}, tag: DataAndStorageEntryTag.sensitiveContent)
case let .sensitiveContentInfo(text):
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section)
case let .downloadInBackground(_, text, value): case let .downloadInBackground(_, text, value):
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.toggleDownloadInBackground(value) arguments.toggleDownloadInBackground(value)
@ -420,9 +467,9 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, action: { return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, action: {
arguments.openProxy() arguments.openProxy()
}) })
case let .enableSensitiveContent(text, value): case let .otherSensitiveContent(text, value):
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.toggleEnableSensitiveContent(value) arguments.toggleOtherSensitiveContent(value)
}, tag: nil) }, tag: nil)
} }
} }
@ -588,7 +635,7 @@ private func autosaveLabelAndValue(presentationData: PresentationData, settings:
return (label, value) return (label, value)
} }
private func dataAndStorageControllerEntries(state: DataAndStorageControllerState, data: DataAndStorageData, presentationData: PresentationData, defaultWebBrowser: String, contentSettingsConfiguration: ContentSettingsConfiguration?, networkUsage: Int64, storageUsage: Int64, mediaAutoSaveSettings: MediaAutoSaveSettings, autosaveExceptionPeers: [EnginePeer.Id: EnginePeer?]) -> [DataAndStorageEntry] { private func dataAndStorageControllerEntries(state: DataAndStorageControllerState, data: DataAndStorageData, presentationData: PresentationData, defaultWebBrowser: String, contentSettingsConfiguration: ContentSettingsConfiguration?, networkUsage: Int64, storageUsage: Int64, mediaAutoSaveSettings: MediaAutoSaveSettings, autosaveExceptionPeers: [EnginePeer.Id: EnginePeer?], mediaSettings: MediaDisplaySettings) -> [DataAndStorageEntry] {
var entries: [DataAndStorageEntry] = [] var entries: [DataAndStorageEntry] = []
entries.append(.storageUsage(presentationData.theme, presentationData.strings.ChatSettings_Cache, dataSizeString(storageUsage, formatting: DataSizeStringFormatting(presentationData: presentationData)))) entries.append(.storageUsage(presentationData.theme, presentationData.strings.ChatSettings_Cache, dataSizeString(storageUsage, formatting: DataSizeStringFormatting(presentationData: presentationData))))
@ -627,6 +674,9 @@ private func dataAndStorageControllerEntries(state: DataAndStorageControllerStat
entries.append(.raiseToListen(presentationData.theme, presentationData.strings.Settings_RaiseToListen, data.mediaInputSettings.enableRaiseToSpeak)) entries.append(.raiseToListen(presentationData.theme, presentationData.strings.Settings_RaiseToListen, data.mediaInputSettings.enableRaiseToSpeak))
entries.append(.raiseToListenInfo(presentationData.theme, presentationData.strings.Settings_RaiseToListenInfo)) entries.append(.raiseToListenInfo(presentationData.theme, presentationData.strings.Settings_RaiseToListenInfo))
entries.append(.sensitiveContent(presentationData.strings.Settings_SensitiveContent, mediaSettings.showSensitiveContent))
entries.append(.sensitiveContentInfo(presentationData.strings.Settings_SensitiveContentInfo))
let proxyValue: String let proxyValue: String
if let proxySettings = data.proxySettings, let activeServer = proxySettings.activeServer, proxySettings.enabled { if let proxySettings = data.proxySettings, let activeServer = proxySettings.activeServer, proxySettings.enabled {
switch activeServer.connection { switch activeServer.connection {
@ -643,7 +693,7 @@ private func dataAndStorageControllerEntries(state: DataAndStorageControllerStat
#if DEBUG #if DEBUG
if let contentSettingsConfiguration = contentSettingsConfiguration, contentSettingsConfiguration.canAdjustSensitiveContent { if let contentSettingsConfiguration = contentSettingsConfiguration, contentSettingsConfiguration.canAdjustSensitiveContent {
entries.append(.enableSensitiveContent("Display Sensitive Content", contentSettingsConfiguration.sensitiveContentEnabled)) entries.append(.otherSensitiveContent("Display Sensitive Content", contentSettingsConfiguration.sensitiveContentEnabled))
} }
#endif #endif
@ -872,7 +922,11 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da
}, openIntents: { }, openIntents: {
let controller = intentsSettingsController(context: context) let controller = intentsSettingsController(context: context)
pushControllerImpl?(controller) pushControllerImpl?(controller)
}, toggleEnableSensitiveContent: { value in }, toggleSensitiveContent: { value in
let _ = updateMediaDisplaySettingsInteractively(accountManager: context.sharedContext.accountManager, {
$0.withUpdatedShowSensitiveContent(value)
}).startStandalone()
}, toggleOtherSensitiveContent: { value in
let _ = (contentSettingsConfiguration.get() let _ = (contentSettingsConfiguration.get()
|> take(1) |> take(1)
|> deliverOnMainQueue).start(next: { [weak contentSettingsConfiguration] settings in |> deliverOnMainQueue).start(next: { [weak contentSettingsConfiguration] settings in
@ -905,7 +959,7 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da
context.sharedContext.presentationData, context.sharedContext.presentationData,
statePromise.get(), statePromise.get(),
dataAndStorageDataPromise.get(), dataAndStorageDataPromise.get(),
context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.webBrowserSettings]), context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.webBrowserSettings, ApplicationSpecificSharedDataKeys.mediaDisplaySettings]),
contentSettingsConfiguration.get(), contentSettingsConfiguration.get(),
preferences, preferences,
usageSignal, usageSignal,
@ -913,6 +967,8 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da
) )
|> map { presentationData, state, dataAndStorageData, sharedData, contentSettingsConfiguration, mediaAutoSaveSettings, usageSignal, autosaveExceptionPeers -> (ItemListControllerState, (ItemListNodeState, Any)) in |> map { presentationData, state, dataAndStorageData, sharedData, contentSettingsConfiguration, mediaAutoSaveSettings, usageSignal, autosaveExceptionPeers -> (ItemListControllerState, (ItemListNodeState, Any)) in
let webBrowserSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.webBrowserSettings]?.get(WebBrowserSettings.self) ?? WebBrowserSettings.defaultSettings let webBrowserSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.webBrowserSettings]?.get(WebBrowserSettings.self) ?? WebBrowserSettings.defaultSettings
let mediaSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.mediaDisplaySettings]?.get(MediaDisplaySettings.self) ?? MediaDisplaySettings.defaultSettings
let options = availableOpenInOptions(context: context, item: .url(url: "https://telegram.org")) let options = availableOpenInOptions(context: context, item: .url(url: "https://telegram.org"))
let defaultWebBrowser: String let defaultWebBrowser: String
if let option = options.first(where: { $0.identifier == webBrowserSettings.defaultWebBrowser }) { if let option = options.first(where: { $0.identifier == webBrowserSettings.defaultWebBrowser }) {
@ -924,7 +980,7 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da
} }
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.ChatSettings_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false) let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.ChatSettings_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: dataAndStorageControllerEntries(state: state, data: dataAndStorageData, presentationData: presentationData, defaultWebBrowser: defaultWebBrowser, contentSettingsConfiguration: contentSettingsConfiguration, networkUsage: usageSignal.network, storageUsage: usageSignal.storage, mediaAutoSaveSettings: mediaAutoSaveSettings, autosaveExceptionPeers: autosaveExceptionPeers), style: .blocks, ensureVisibleItemTag: focusOnItemTag, emptyStateItem: nil, animateChanges: false) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: dataAndStorageControllerEntries(state: state, data: dataAndStorageData, presentationData: presentationData, defaultWebBrowser: defaultWebBrowser, contentSettingsConfiguration: contentSettingsConfiguration, networkUsage: usageSignal.network, storageUsage: usageSignal.storage, mediaAutoSaveSettings: mediaAutoSaveSettings, autosaveExceptionPeers: autosaveExceptionPeers, mediaSettings: mediaSettings), style: .blocks, ensureVisibleItemTag: focusOnItemTag, emptyStateItem: nil, animateChanges: false)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))
} |> afterDisposed { } |> afterDisposed {

View File

@ -275,7 +275,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1038136962] = { return Api.EncryptedFile.parse_encryptedFileEmpty($0) } dict[-1038136962] = { return Api.EncryptedFile.parse_encryptedFileEmpty($0) }
dict[-317144808] = { return Api.EncryptedMessage.parse_encryptedMessage($0) } dict[-317144808] = { return Api.EncryptedMessage.parse_encryptedMessage($0) }
dict[594758406] = { return Api.EncryptedMessage.parse_encryptedMessageService($0) } dict[594758406] = { return Api.EncryptedMessage.parse_encryptedMessageService($0) }
dict[-1812799720] = { return Api.ExportedChatInvite.parse_chatInviteExported($0) } dict[-1574126186] = { return Api.ExportedChatInvite.parse_chatInviteExported($0) }
dict[-317687113] = { return Api.ExportedChatInvite.parse_chatInvitePublicJoinRequests($0) } dict[-317687113] = { return Api.ExportedChatInvite.parse_chatInvitePublicJoinRequests($0) }
dict[206668204] = { return Api.ExportedChatlistInvite.parse_exportedChatlistInvite($0) } dict[206668204] = { return Api.ExportedChatlistInvite.parse_exportedChatlistInvite($0) }
dict[1103040667] = { return Api.ExportedContactToken.parse_exportedContactToken($0) } dict[1103040667] = { return Api.ExportedContactToken.parse_exportedContactToken($0) }
@ -803,6 +803,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1929860175] = { return Api.RequestedPeer.parse_requestedPeerChat($0) } dict[1929860175] = { return Api.RequestedPeer.parse_requestedPeerChat($0) }
dict[-701500310] = { return Api.RequestedPeer.parse_requestedPeerUser($0) } dict[-701500310] = { return Api.RequestedPeer.parse_requestedPeerUser($0) }
dict[-797791052] = { return Api.RestrictionReason.parse_restrictionReason($0) } dict[-797791052] = { return Api.RestrictionReason.parse_restrictionReason($0) }
dict[2007669447] = { return Api.RestrictionReason.parse_restrictionReasonSensitive($0) }
dict[894777186] = { return Api.RichText.parse_textAnchor($0) } dict[894777186] = { return Api.RichText.parse_textAnchor($0) }
dict[1730456516] = { return Api.RichText.parse_textBold($0) } dict[1730456516] = { return Api.RichText.parse_textBold($0) }
dict[2120376535] = { return Api.RichText.parse_textConcat($0) } dict[2120376535] = { return Api.RichText.parse_textConcat($0) }

View File

@ -397,6 +397,7 @@ public extension Api {
public extension Api { public extension Api {
enum RestrictionReason: TypeConstructorDescription { enum RestrictionReason: TypeConstructorDescription {
case restrictionReason(platform: String, reason: String, text: String) case restrictionReason(platform: String, reason: String, text: String)
case restrictionReasonSensitive(platform: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
@ -408,6 +409,12 @@ public extension Api {
serializeString(reason, buffer: buffer, boxed: false) serializeString(reason, buffer: buffer, boxed: false)
serializeString(text, buffer: buffer, boxed: false) serializeString(text, buffer: buffer, boxed: false)
break break
case .restrictionReasonSensitive(let platform):
if boxed {
buffer.appendInt32(2007669447)
}
serializeString(platform, buffer: buffer, boxed: false)
break
} }
} }
@ -415,6 +422,8 @@ public extension Api {
switch self { switch self {
case .restrictionReason(let platform, let reason, let text): case .restrictionReason(let platform, let reason, let text):
return ("restrictionReason", [("platform", platform as Any), ("reason", reason as Any), ("text", text as Any)]) return ("restrictionReason", [("platform", platform as Any), ("reason", reason as Any), ("text", text as Any)])
case .restrictionReasonSensitive(let platform):
return ("restrictionReasonSensitive", [("platform", platform as Any)])
} }
} }
@ -435,6 +444,17 @@ public extension Api {
return nil return nil
} }
} }
public static func parse_restrictionReasonSensitive(_ reader: BufferReader) -> RestrictionReason? {
var _1: String?
_1 = parseString(reader)
let _c1 = _1 != nil
if _c1 {
return Api.RestrictionReason.restrictionReasonSensitive(platform: _1!)
}
else {
return nil
}
}
} }
} }

View File

@ -7935,14 +7935,15 @@ public extension Api.functions.messages {
} }
} }
public extension Api.functions.messages { public extension Api.functions.messages {
static func sendPaidReaction(peer: Api.InputPeer, msgId: Int32, count: Int32, randomId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) { static func sendPaidReaction(flags: Int32, peer: Api.InputPeer, msgId: Int32, count: Int32, randomId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer() let buffer = Buffer()
buffer.appendInt32(508941107) buffer.appendInt32(633929278)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true) peer.serialize(buffer, true)
serializeInt32(msgId, buffer: buffer, boxed: false) serializeInt32(msgId, buffer: buffer, boxed: false)
serializeInt32(count, buffer: buffer, boxed: false) serializeInt32(count, buffer: buffer, boxed: false)
serializeInt64(randomId, buffer: buffer, boxed: false) serializeInt64(randomId, buffer: buffer, boxed: false)
return (FunctionDescription(name: "messages.sendPaidReaction", parameters: [("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("count", String(describing: count)), ("randomId", String(describing: randomId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in return (FunctionDescription(name: "messages.sendPaidReaction", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("count", String(describing: count)), ("randomId", String(describing: randomId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer) let reader = BufferReader(buffer)
var result: Api.Updates? var result: Api.Updates?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {

View File

@ -1012,14 +1012,14 @@ public extension Api {
} }
public extension Api { public extension Api {
enum ExportedChatInvite: TypeConstructorDescription { enum ExportedChatInvite: TypeConstructorDescription {
case chatInviteExported(flags: Int32, link: String, adminId: Int64, date: Int32, startDate: Int32?, expireDate: Int32?, usageLimit: Int32?, usage: Int32?, requested: Int32?, title: String?, subscriptionPricing: Api.StarsSubscriptionPricing?) case chatInviteExported(flags: Int32, link: String, adminId: Int64, date: Int32, startDate: Int32?, expireDate: Int32?, usageLimit: Int32?, usage: Int32?, requested: Int32?, subscriptionExpired: Int32?, title: String?, subscriptionPricing: Api.StarsSubscriptionPricing?)
case chatInvitePublicJoinRequests case chatInvitePublicJoinRequests
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
case .chatInviteExported(let flags, let link, let adminId, let date, let startDate, let expireDate, let usageLimit, let usage, let requested, let title, let subscriptionPricing): case .chatInviteExported(let flags, let link, let adminId, let date, let startDate, let expireDate, let usageLimit, let usage, let requested, let subscriptionExpired, let title, let subscriptionPricing):
if boxed { if boxed {
buffer.appendInt32(-1812799720) buffer.appendInt32(-1574126186)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(link, buffer: buffer, boxed: false) serializeString(link, buffer: buffer, boxed: false)
@ -1030,6 +1030,7 @@ public extension Api {
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(usageLimit!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 2) != 0 {serializeInt32(usageLimit!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(usage!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 3) != 0 {serializeInt32(usage!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 7) != 0 {serializeInt32(requested!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 7) != 0 {serializeInt32(requested!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(subscriptionExpired!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 8) != 0 {serializeString(title!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 8) != 0 {serializeString(title!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 9) != 0 {subscriptionPricing!.serialize(buffer, true)} if Int(flags) & Int(1 << 9) != 0 {subscriptionPricing!.serialize(buffer, true)}
break break
@ -1044,8 +1045,8 @@ public extension Api {
public func descriptionFields() -> (String, [(String, Any)]) { public func descriptionFields() -> (String, [(String, Any)]) {
switch self { switch self {
case .chatInviteExported(let flags, let link, let adminId, let date, let startDate, let expireDate, let usageLimit, let usage, let requested, let title, let subscriptionPricing): case .chatInviteExported(let flags, let link, let adminId, let date, let startDate, let expireDate, let usageLimit, let usage, let requested, let subscriptionExpired, let title, let subscriptionPricing):
return ("chatInviteExported", [("flags", flags as Any), ("link", link as Any), ("adminId", adminId as Any), ("date", date as Any), ("startDate", startDate as Any), ("expireDate", expireDate as Any), ("usageLimit", usageLimit as Any), ("usage", usage as Any), ("requested", requested as Any), ("title", title as Any), ("subscriptionPricing", subscriptionPricing as Any)]) return ("chatInviteExported", [("flags", flags as Any), ("link", link as Any), ("adminId", adminId as Any), ("date", date as Any), ("startDate", startDate as Any), ("expireDate", expireDate as Any), ("usageLimit", usageLimit as Any), ("usage", usage as Any), ("requested", requested as Any), ("subscriptionExpired", subscriptionExpired as Any), ("title", title as Any), ("subscriptionPricing", subscriptionPricing as Any)])
case .chatInvitePublicJoinRequests: case .chatInvitePublicJoinRequests:
return ("chatInvitePublicJoinRequests", []) return ("chatInvitePublicJoinRequests", [])
} }
@ -1070,11 +1071,13 @@ public extension Api {
if Int(_1!) & Int(1 << 3) != 0 {_8 = reader.readInt32() } if Int(_1!) & Int(1 << 3) != 0 {_8 = reader.readInt32() }
var _9: Int32? var _9: Int32?
if Int(_1!) & Int(1 << 7) != 0 {_9 = reader.readInt32() } if Int(_1!) & Int(1 << 7) != 0 {_9 = reader.readInt32() }
var _10: String? var _10: Int32?
if Int(_1!) & Int(1 << 8) != 0 {_10 = parseString(reader) } if Int(_1!) & Int(1 << 10) != 0 {_10 = reader.readInt32() }
var _11: Api.StarsSubscriptionPricing? var _11: String?
if Int(_1!) & Int(1 << 8) != 0 {_11 = parseString(reader) }
var _12: Api.StarsSubscriptionPricing?
if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() { if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() {
_11 = Api.parse(reader, signature: signature) as? Api.StarsSubscriptionPricing _12 = Api.parse(reader, signature: signature) as? Api.StarsSubscriptionPricing
} } } }
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
@ -1085,10 +1088,11 @@ public extension Api {
let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil
let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil
let _c9 = (Int(_1!) & Int(1 << 7) == 0) || _9 != nil let _c9 = (Int(_1!) & Int(1 << 7) == 0) || _9 != nil
let _c10 = (Int(_1!) & Int(1 << 8) == 0) || _10 != nil let _c10 = (Int(_1!) & Int(1 << 10) == 0) || _10 != nil
let _c11 = (Int(_1!) & Int(1 << 9) == 0) || _11 != nil let _c11 = (Int(_1!) & Int(1 << 8) == 0) || _11 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { let _c12 = (Int(_1!) & Int(1 << 9) == 0) || _12 != nil
return Api.ExportedChatInvite.chatInviteExported(flags: _1!, link: _2!, adminId: _3!, date: _4!, startDate: _5, expireDate: _6, usageLimit: _7, usage: _8, requested: _9, title: _10, subscriptionPricing: _11) if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 {
return Api.ExportedChatInvite.chatInviteExported(flags: _1!, link: _2!, adminId: _3!, date: _4!, startDate: _5, expireDate: _6, usageLimit: _7, usage: _8, requested: _9, subscriptionExpired: _10, title: _11, subscriptionPricing: _12)
} }
else { else {
return nil return nil

View File

@ -6,7 +6,8 @@ import TelegramApi
extension ExportedInvitation { extension ExportedInvitation {
init(apiExportedInvite: Api.ExportedChatInvite) { init(apiExportedInvite: Api.ExportedChatInvite) {
switch apiExportedInvite { switch apiExportedInvite {
case let .chatInviteExported(flags, link, adminId, date, startDate, expireDate, usageLimit, usage, requested, title, pricing): case let .chatInviteExported(flags, link, adminId, date, startDate, expireDate, usageLimit, usage, requested, subscriptionExpired, title, pricing):
let _ = subscriptionExpired
self = .link(link: link, title: title, isPermanent: (flags & (1 << 5)) != 0, requestApproval: (flags & (1 << 6)) != 0, isRevoked: (flags & (1 << 0)) != 0, adminId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(adminId)), date: date, startDate: startDate, expireDate: expireDate, usageLimit: usageLimit, count: usage, requestedCount: requested, pricing: pricing.flatMap { StarsSubscriptionPricing(apiStarsSubscriptionPricing: $0) }) self = .link(link: link, title: title, isPermanent: (flags & (1 << 5)) != 0, requestApproval: (flags & (1 << 6)) != 0, isRevoked: (flags & (1 << 0)) != 0, adminId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(adminId)), date: date, startDate: startDate, expireDate: expireDate, usageLimit: usageLimit, count: usage, requestedCount: requested, pricing: pricing.flatMap { StarsSubscriptionPricing(apiStarsSubscriptionPricing: $0) })
case .chatInvitePublicJoinRequests: case .chatInvitePublicJoinRequests:
self = .publicJoinRequest self = .publicJoinRequest

View File

@ -8,6 +8,8 @@ extension RestrictionRule {
switch apiReason { switch apiReason {
case let .restrictionReason(platform, reason, text): case let .restrictionReason(platform, reason, text):
self.init(platform: platform, reason: reason, text: text) self.init(platform: platform, reason: reason, text: text)
case let .restrictionReasonSensitive(platform):
self.init(platform: platform)
} }
} }
} }

View File

@ -341,7 +341,7 @@ private func requestSendStarsReaction(postbox: Postbox, network: Network, stateM
let timestampPart = UInt64(UInt32(bitPattern: Int32(Date().timeIntervalSince1970))) let timestampPart = UInt64(UInt32(bitPattern: Int32(Date().timeIntervalSince1970)))
let randomId = (timestampPart << 32) | randomPartId let randomId = (timestampPart << 32) | randomPartId
let signal: Signal<Never, RequestUpdateMessageReactionError> = network.request(Api.functions.messages.sendPaidReaction(peer: inputPeer, msgId: messageId.id, count: count, randomId: Int64(bitPattern: randomId))) let signal: Signal<Never, RequestUpdateMessageReactionError> = network.request(Api.functions.messages.sendPaidReaction(flags: 0, peer: inputPeer, msgId: messageId.id, count: count, randomId: Int64(bitPattern: randomId)))
|> mapError { _ -> RequestUpdateMessageReactionError in |> mapError { _ -> RequestUpdateMessageReactionError in
return .generic return .generic
} }

View File

@ -4,23 +4,34 @@ public final class RestrictionRule: PostboxCoding, Equatable {
public let platform: String public let platform: String
public let reason: String public let reason: String
public let text: String public let text: String
public let isSensitive: Bool
public init(platform: String, reason: String, text: String) { public init(platform: String, reason: String, text: String) {
self.platform = platform self.platform = platform
self.reason = reason self.reason = reason
self.text = text self.text = text
self.isSensitive = false
}
public init(platform: String) {
self.platform = platform
self.reason = ""
self.text = ""
self.isSensitive = true
} }
public init(decoder: PostboxDecoder) { public init(decoder: PostboxDecoder) {
self.platform = decoder.decodeStringForKey("p", orElse: "all") self.platform = decoder.decodeStringForKey("p", orElse: "all")
self.reason = decoder.decodeStringForKey("r", orElse: "") self.reason = decoder.decodeStringForKey("r", orElse: "")
self.text = decoder.decodeStringForKey("t", orElse: "") self.text = decoder.decodeStringForKey("t", orElse: "")
self.isSensitive = decoder.decodeBoolForKey("s", orElse: false)
} }
public func encode(_ encoder: PostboxEncoder) { public func encode(_ encoder: PostboxEncoder) {
encoder.encodeString(self.platform, forKey: "p") encoder.encodeString(self.platform, forKey: "p")
encoder.encodeString(self.reason, forKey: "r") encoder.encodeString(self.reason, forKey: "r")
encoder.encodeString(self.text, forKey: "t") encoder.encodeString(self.text, forKey: "t")
encoder.encodeBool(self.isSensitive, forKey: "s")
} }
public static func ==(lhs: RestrictionRule, rhs: RestrictionRule) -> Bool { public static func ==(lhs: RestrictionRule, rhs: RestrictionRule) -> Bool {
@ -33,6 +44,9 @@ public final class RestrictionRule: PostboxCoding, Equatable {
if lhs.text != rhs.text { if lhs.text != rhs.text {
return false return false
} }
if lhs.isSensitive != rhs.isSensitive {
return false
}
return true return true
} }
} }

View File

@ -381,7 +381,15 @@ public extension Message {
} }
} }
func isAgeRestricted() -> Bool { func isSensitiveContent(platform: String) -> Bool {
if let rule = self.restrictedContentAttribute?.rules.first(where: { $0.isSensitive }) {
if rule.platform == "all" || rule.platform == platform {
return true
}
}
if let peer = self.peers[self.id.peerId], peer.hasSensitiveContent(platform: platform) {
return true
}
return false return false
} }
} }

View File

@ -30,6 +30,9 @@ public extension Peer {
if let restrictionInfo = restrictionInfo { if let restrictionInfo = restrictionInfo {
for rule in restrictionInfo.rules { for rule in restrictionInfo.rules {
if rule.isSensitive {
continue
}
if rule.platform == "all" || rule.platform == platform || contentSettings.addContentRestrictionReasons.contains(rule.platform) { if rule.platform == "all" || rule.platform == platform || contentSettings.addContentRestrictionReasons.contains(rule.platform) {
if !contentSettings.ignoreContentRestrictionReasons.contains(rule.reason) { if !contentSettings.ignoreContentRestrictionReasons.contains(rule.reason) {
return rule.text return rule.text
@ -220,6 +223,25 @@ public extension Peer {
} }
} }
func hasSensitiveContent(platform: String) -> Bool {
var restrictionInfo: PeerAccessRestrictionInfo?
switch self {
case let user as TelegramUser:
restrictionInfo = user.restrictionInfo
case let channel as TelegramChannel:
restrictionInfo = channel.restrictionInfo
default:
break
}
if let restrictionInfo, let rule = restrictionInfo.rules.first(where: { $0.isSensitive }) {
if rule.platform == "all" || rule.platform == platform {
return true
}
}
return false
}
var isForum: Bool { var isForum: Bool {
if let channel = self as? TelegramChannel { if let channel = self as? TelegramChannel {
return channel.flags.contains(.isForum) return channel.flags.contains(.isForum)

View File

@ -751,11 +751,10 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
if message.author?.id == accountPeerId { if message.author?.id == accountPeerId {
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_SentYou(price)._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes]) attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_SentYou(price)._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes])
} else { } else {
//TODO:localize
var authorName = compactAuthorName var authorName = compactAuthorName
var peerIds: [(Int, EnginePeer.Id?)] = [(0, message.author?.id)] var peerIds: [(Int, EnginePeer.Id?)] = [(0, message.author?.id)]
if message.id.peerId.namespace == Namespaces.Peer.CloudUser && message.id.peerId.id._internalGetInt64Value() == 777000 { if message.id.peerId.namespace == Namespaces.Peer.CloudUser && message.id.peerId.id._internalGetInt64Value() == 777000 {
authorName = "Unknown user" authorName = strings.Notification_StarsGift_UnknownUser
peerIds = [] peerIds = []
} }
var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds) var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds)
@ -1010,13 +1009,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
} }
} }
case let .paymentRefunded(peerId, currency, totalAmount, _, _): case let .paymentRefunded(peerId, currency, totalAmount, _, _):
//TODO:localize let patternString = strings.Notification_Refund
let patternString: String
if peerId == message.id.peerId {
patternString = "You received a refund of {amount}"
} else {
patternString = "You received a refund of {amount} from {name}"
}
let mutableString = NSMutableAttributedString() let mutableString = NSMutableAttributedString()
mutableString.append(NSAttributedString(string: patternString, font: titleFont, textColor: primaryTextColor)) mutableString.append(NSAttributedString(string: patternString, font: titleFont, textColor: primaryTextColor))

View File

@ -217,7 +217,7 @@ private class ExtendedMediaOverlayNode: ASDisplayNode {
private let context: AccountContext private let context: AccountContext
private let blurredImageNode: TransformImageNode private let blurredImageNode: TransformImageNode
private let dustNode: MediaDustNode fileprivate let dustNode: MediaDustNode
fileprivate let buttonNode: HighlightTrackingButtonNode fileprivate let buttonNode: HighlightTrackingButtonNode
private let highlightedBackgroundNode: ASDisplayNode private let highlightedBackgroundNode: ASDisplayNode
private let iconNode: ASImageNode private let iconNode: ASImageNode
@ -452,6 +452,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
private var automaticDownload: InteractiveMediaNodeAutodownloadMode? private var automaticDownload: InteractiveMediaNodeAutodownloadMode?
public var automaticPlayback: Bool? public var automaticPlayback: Bool?
private var preferredStoryHighQuality: Bool = false private var preferredStoryHighQuality: Bool = false
private var showSensitiveContent: Bool = false
private let statusDisposable = MetaDisposable() private let statusDisposable = MetaDisposable()
private let fetchControls = Atomic<FetchControls?>(value: nil) private let fetchControls = Atomic<FetchControls?>(value: nil)
@ -1570,6 +1571,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
strongSelf.automaticPlayback = automaticPlayback strongSelf.automaticPlayback = automaticPlayback
strongSelf.automaticDownload = automaticDownload strongSelf.automaticDownload = automaticDownload
strongSelf.preferredStoryHighQuality = associatedData.preferredStoryHighQuality strongSelf.preferredStoryHighQuality = associatedData.preferredStoryHighQuality
strongSelf.showSensitiveContent = associatedData.showSensitiveContent
if let previousArguments = strongSelf.currentImageArguments { if let previousArguments = strongSelf.currentImageArguments {
if previousArguments.imageSize == arguments.imageSize { if previousArguments.imageSize == arguments.imageSize {
@ -2399,20 +2401,25 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
displaySpoiler = true displaySpoiler = true
} else if isSecretMedia { } else if isSecretMedia {
displaySpoiler = true displaySpoiler = true
} else if message.isAgeRestricted() { } else if message.isSensitiveContent(platform: "ios") {
if !self.showSensitiveContent {
displaySpoiler = true displaySpoiler = true
icon = .eye icon = .eye
} }
}
if displaySpoiler { if displaySpoiler, let context = self.context {
if self.extendedMediaOverlayNode == nil, let context = self.context { let extendedMediaOverlayNode: ExtendedMediaOverlayNode
if let current = self.extendedMediaOverlayNode {
extendedMediaOverlayNode = current
} else {
let enableAnimations = context.sharedContext.energyUsageSettings.fullTranslucency && !isPreview let enableAnimations = context.sharedContext.energyUsageSettings.fullTranslucency && !isPreview
let extendedMediaOverlayNode = ExtendedMediaOverlayNode(context: context, hasImageOverlay: !isSecretMedia, icon: icon, enableAnimations: enableAnimations) extendedMediaOverlayNode = ExtendedMediaOverlayNode(context: context, hasImageOverlay: !isSecretMedia, icon: icon, enableAnimations: enableAnimations)
extendedMediaOverlayNode.tapped = { [weak self] in extendedMediaOverlayNode.tapped = { [weak self] in
guard let self else { guard let self else {
return return
} }
if message.isAgeRestricted() { if message.isSensitiveContent(platform: "ios") {
self.activateAgeRestrictedMedia?() self.activateAgeRestrictedMedia?()
} else { } else {
self.internallyVisible = true self.internallyVisible = true
@ -2423,7 +2430,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
self.extendedMediaOverlayNode = extendedMediaOverlayNode self.extendedMediaOverlayNode = extendedMediaOverlayNode
self.pinchContainerNode.contentNode.insertSubnode(extendedMediaOverlayNode, aboveSubnode: self.imageNode) self.pinchContainerNode.contentNode.insertSubnode(extendedMediaOverlayNode, aboveSubnode: self.imageNode)
} }
self.extendedMediaOverlayNode?.frame = self.imageNode.frame extendedMediaOverlayNode.frame = self.imageNode.frame
var tappable = false var tappable = false
if !isSecretMedia { if !isSecretMedia {
@ -2434,13 +2441,12 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
break break
} }
} }
extendedMediaOverlayNode.isUserInteractionEnabled = tappable
self.extendedMediaOverlayNode?.isUserInteractionEnabled = tappable
var viewText: String = "" var viewText: String = ""
if message.isAgeRestricted() { if case .eye = icon {
//TODO:localize viewText = strings.Chat_SensitiveContent
viewText = "18+ Content" extendedMediaOverlayNode.dustNode.revealOnTap = false
} else { } else {
outer: for attribute in message.attributes { outer: for attribute in message.attributes {
if let attribute = attribute as? ReplyMarkupMessageAttribute { if let attribute = attribute as? ReplyMarkupMessageAttribute {
@ -2455,8 +2461,9 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
break break
} }
} }
extendedMediaOverlayNode.dustNode.revealOnTap = true
} }
self.extendedMediaOverlayNode?.update(size: self.imageNode.frame.size, text: viewText, imageSignal: self.currentBlurredImageSignal, imageFrame: self.imageNode.view.convert(self.imageNode.bounds, to: self.extendedMediaOverlayNode?.view), corners: self.currentImageArguments?.corners) extendedMediaOverlayNode.update(size: self.imageNode.frame.size, text: viewText, imageSignal: self.currentBlurredImageSignal, imageFrame: self.imageNode.view.convert(self.imageNode.bounds, to: extendedMediaOverlayNode.view), corners: self.currentImageArguments?.corners)
} else if let extendedMediaOverlayNode = self.extendedMediaOverlayNode { } else if let extendedMediaOverlayNode = self.extendedMediaOverlayNode {
self.extendedMediaOverlayNode = nil self.extendedMediaOverlayNode = nil
extendedMediaOverlayNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak extendedMediaOverlayNode] _ in extendedMediaOverlayNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak extendedMediaOverlayNode] _ in
@ -2664,12 +2671,12 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
} }
public func ignoreTapActionAtPoint(_ point: CGPoint) -> Bool { public func ignoreTapActionAtPoint(_ point: CGPoint) -> Bool {
// if let extendedMediaOverlayNode = self.extendedMediaOverlayNode { if let extendedMediaOverlayNode = self.extendedMediaOverlayNode {
// let convertedPoint = self.view.convert(point, to: extendedMediaOverlayNode.view) let convertedPoint = self.view.convert(point, to: extendedMediaOverlayNode.view)
// if extendedMediaOverlayNode.buttonNode.frame.contains(convertedPoint) { if extendedMediaOverlayNode.buttonNode.frame.contains(convertedPoint) {
// return true return true
// } }
// } }
return false return false
} }
} }

View File

@ -1135,7 +1135,7 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
theme: forceDark ? .dark : .default theme: forceDark ? .dark : .default
) )
self.navigationPresentation = .flatModal self.navigationPresentation = .standaloneModal
self.automaticallyControlPresentationContextLayout = false self.automaticallyControlPresentationContextLayout = false
openPeerImpl = { [weak self] peer in openPeerImpl = { [weak self] peer in

View File

@ -626,7 +626,6 @@ final class StarsTransactionsScreenComponent: Component {
maximumNumberOfLines: 1 maximumNumberOfLines: 1
))) )))
) )
//TODO:localize
let dateText: String let dateText: String
let dateValue = stringForDateWithoutYear(date: Date(timeIntervalSince1970: Double(subscription.untilDate)), strings: environment.strings) let dateValue = stringForDateWithoutYear(date: Date(timeIntervalSince1970: Double(subscription.untilDate)), strings: environment.strings)
if subscription.flags.contains(.isCancelled) { if subscription.flags.contains(.isCancelled) {

View File

@ -77,6 +77,7 @@ private final class SheetContent: CombinedComponent {
private(set) var botPeer: EnginePeer? private(set) var botPeer: EnginePeer?
private(set) var chatPeer: EnginePeer? private(set) var chatPeer: EnginePeer?
private(set) var authorPeer: EnginePeer?
private var peerDisposable: Disposable? private var peerDisposable: Disposable?
private(set) var balance: Int64? private(set) var balance: Int64?
private(set) var form: BotPaymentForm? private(set) var form: BotPaymentForm?
@ -799,7 +800,7 @@ public final class StarsTransferScreen: ViewControllerComponentContainer {
theme: .default theme: .default
) )
self.navigationPresentation = .flatModal self.navigationPresentation = .standaloneModal
starsContext.load(force: false) starsContext.load(force: false)
} }

View File

@ -524,7 +524,7 @@ public final class StarsWithdrawScreen: ViewControllerComponentContainer {
theme: .default theme: .default
) )
self.navigationPresentation = .flatModal self.navigationPresentation = .standaloneModal
} }
required public init(coder aDecoder: NSCoder) { required public init(coder aDecoder: NSCoder) {

View File

@ -294,7 +294,9 @@ func openWebAppImpl(context: AccountContext, parentController: ViewController, u
let controller = webAppLaunchConfirmationController(context: context, updatedPresentationData: updatedPresentationData, peer: botPeer, completion: { _ in let controller = webAppLaunchConfirmationController(context: context, updatedPresentationData: updatedPresentationData, peer: botPeer, completion: { _ in
let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: context.sharedContext.accountManager, peerId: botPeer.id).startStandalone() let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: context.sharedContext.accountManager, peerId: botPeer.id).startStandalone()
openWebView() openWebView()
}, showMore: nil) }, showMore: nil, openTerms: {
})
parentController.present(controller, in: .window(.root)) parentController.present(controller, in: .window(.root))
} }
}) })
@ -312,7 +314,7 @@ public extension ChatControllerImpl {
} }
static func botRequestSwitchInline(context: AccountContext, controller: ChatControllerImpl?, peerId: EnginePeer.Id, botAddress: String, query: String, chatTypes: [ReplyMarkupButtonRequestPeerType]?, completion: @escaping () -> Void) -> Void { static func botRequestSwitchInline(context: AccountContext, controller: ChatControllerImpl?, peerId: EnginePeer.Id, botAddress: String, query: String, chatTypes: [ReplyMarkupButtonRequestPeerType]?, completion: @escaping () -> Void) -> Void {
let activateSwitchInline = { let activateSwitchInline: (EnginePeer?) -> Void = { selectedPeer in
var chatController: ChatControllerImpl? var chatController: ChatControllerImpl?
if let current = controller { if let current = controller {
chatController = current chatController = current
@ -325,7 +327,7 @@ public extension ChatControllerImpl {
} }
} }
if let chatController { if let chatController {
chatController.controllerInteraction?.activateSwitchInline(peerId, "@\(botAddress) \(query)", nil) chatController.controllerInteraction?.activateSwitchInline(selectedPeer?.id ?? peerId, "@\(botAddress) \(query)", nil)
} }
} }
@ -334,7 +336,7 @@ public extension ChatControllerImpl {
peerController.peerSelected = { [weak peerController] peer, _ in peerController.peerSelected = { [weak peerController] peer, _ in
completion() completion()
peerController?.dismiss() peerController?.dismiss()
activateSwitchInline() activateSwitchInline(peer)
} }
if let controller { if let controller {
controller.push(peerController) controller.push(peerController)
@ -342,7 +344,7 @@ public extension ChatControllerImpl {
((context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface)?.viewControllers.last as? ViewController)?.push(peerController) ((context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface)?.viewControllers.last as? ViewController)?.push(peerController)
} }
} else { } else {
activateSwitchInline() activateSwitchInline(nil)
} }
} }
@ -547,6 +549,10 @@ public extension ChatControllerImpl {
if let self { if let self {
self.openResolved(result: .peer(botPeer._asPeer(), .info(nil)), sourceMessageId: nil) self.openResolved(result: .peer(botPeer._asPeer(), .info(nil)), sourceMessageId: nil)
} }
}, openTerms: { [weak self] in
if let self {
self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: self.presentationData.strings.WebApp_LaunchTermsConfirmation_URL, forceExternal: false, presentationData: self.presentationData, navigationController: self.effectiveNavigationController, dismissInput: {})
}
}) })
self.present(controller, in: .window(.root)) self.present(controller, in: .window(.root))
} }

View File

@ -128,7 +128,7 @@ private final class ChatAgeRestrictionAlertContentNode: AlertContentNode {
self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center)
self.textNode.attributedText = NSAttributedString(string: self.text, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center) self.textNode.attributedText = NSAttributedString(string: self.text, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center)
self.alwaysLabelNode.attributedText = formattedText("Always show 18+ media", color: theme.primaryColor) self.alwaysLabelNode.attributedText = formattedText(self.strings.SensitiveContent_ShowAlways, color: theme.primaryColor)
self.actionNodesSeparator.backgroundColor = theme.separatorColor self.actionNodesSeparator.backgroundColor = theme.separatorColor
for actionNode in self.actionNodes { for actionNode in self.actionNodes {
@ -269,10 +269,9 @@ public func chatAgeRestrictionAlertController(context: AccountContext, updatedPr
} }
let strings = presentationData.strings let strings = presentationData.strings
//TODO:localize
var dismissImpl: ((Bool) -> Void)? var dismissImpl: ((Bool) -> Void)?
var getContentNodeImpl: (() -> ChatAgeRestrictionAlertContentNode?)? var getContentNodeImpl: (() -> ChatAgeRestrictionAlertContentNode?)?
let actions: [TextAlertAction] = [TextAlertAction(type: .defaultAction, title: "View Anyway", action: { let actions: [TextAlertAction] = [TextAlertAction(type: .defaultAction, title: strings.SensitiveContent_ViewAnyway, action: {
if let alwaysShow = getContentNodeImpl?()?.alwaysShow { if let alwaysShow = getContentNodeImpl?()?.alwaysShow {
completion(alwaysShow) completion(alwaysShow)
} else { } else {
@ -283,8 +282,8 @@ public func chatAgeRestrictionAlertController(context: AccountContext, updatedPr
dismissImpl?(true) dismissImpl?(true)
})] })]
let title = "18+ Content" let title = strings.SensitiveContent_Title
let text = "This media may contain sensitive content suitable only for adults.\nDo you still want to view it?" let text = strings.SensitiveContent_Text
let contentNode = ChatAgeRestrictionAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: theme, strings: strings, title: title, text: text, actions: actions) let contentNode = ChatAgeRestrictionAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: theme, strings: strings, title: title, text: text, actions: actions)
getContentNodeImpl = { [weak contentNode] in getContentNodeImpl = { [weak contentNode] in

View File

@ -4423,7 +4423,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return return
} }
if alwaysShow { if alwaysShow {
self.present(UndoOverlayController(presentationData: self.presentationData, content: .info(title: nil, text: "You can update the visibility of sensitive media in [Data and Storage > Show 18+ Content]().", timeout: nil, customUndoText: nil), elevatedLayout: false, position: .top, action: { _ in return false }), in: .current) let _ = updateMediaDisplaySettingsInteractively(accountManager: context.sharedContext.accountManager, {
$0.withUpdatedShowSensitiveContent(true)
}).startStandalone()
self.present(UndoOverlayController(presentationData: self.presentationData, content: .info(title: nil, text: self.presentationData.strings.SensitiveContent_SettingsInfo, timeout: nil, customUndoText: nil), elevatedLayout: false, position: .top, action: { [weak self] action in
if case .info = action, let self {
let controller = self.context.sharedContext.makeDataAndStorageController(context: self.context, sensitiveContent: true)
self.push(controller)
}
return false
}), in: .current)
} }
reveal() reveal()
}) })

View File

@ -350,7 +350,8 @@ private func extractAssociatedData(
audioTranscriptionTrial: AudioTranscription.TrialState, audioTranscriptionTrial: AudioTranscription.TrialState,
chatThemes: [TelegramTheme], chatThemes: [TelegramTheme],
deviceContactsNumbers: Set<String>, deviceContactsNumbers: Set<String>,
isInline: Bool isInline: Bool,
showSensitiveContent: Bool
) -> ChatMessageItemAssociatedData { ) -> ChatMessageItemAssociatedData {
var automaticDownloadPeerId: EnginePeer.Id? var automaticDownloadPeerId: EnginePeer.Id?
var automaticMediaDownloadPeerType: MediaAutoDownloadPeerType = .channel var automaticMediaDownloadPeerType: MediaAutoDownloadPeerType = .channel
@ -405,7 +406,7 @@ private func extractAssociatedData(
automaticDownloadPeerId = message.peerId automaticDownloadPeerId = message.peerId
} }
return ChatMessageItemAssociatedData(automaticDownloadPeerType: automaticMediaDownloadPeerType, automaticDownloadPeerId: automaticDownloadPeerId, automaticDownloadNetworkType: automaticDownloadNetworkType, preferredStoryHighQuality: preferredStoryHighQuality, isRecentActions: false, subject: subject, contactsPeerIds: contactsPeerIds, channelDiscussionGroup: channelDiscussionGroup, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, currentlyPlayingMessageId: currentlyPlayingMessageId, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: savedMessageTags, defaultReaction: defaultReaction, isPremium: isPremium, accountPeer: accountPeer, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, topicAuthorId: topicAuthorId, hasBots: hasBots, translateToLanguage: translateToLanguage, maxReadStoryId: maxReadStoryId, recommendedChannels: recommendedChannels, audioTranscriptionTrial: audioTranscriptionTrial, chatThemes: chatThemes, deviceContactsNumbers: deviceContactsNumbers, isInline: isInline) return ChatMessageItemAssociatedData(automaticDownloadPeerType: automaticMediaDownloadPeerType, automaticDownloadPeerId: automaticDownloadPeerId, automaticDownloadNetworkType: automaticDownloadNetworkType, preferredStoryHighQuality: preferredStoryHighQuality, isRecentActions: false, subject: subject, contactsPeerIds: contactsPeerIds, channelDiscussionGroup: channelDiscussionGroup, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, currentlyPlayingMessageId: currentlyPlayingMessageId, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: savedMessageTags, defaultReaction: defaultReaction, isPremium: isPremium, accountPeer: accountPeer, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, topicAuthorId: topicAuthorId, hasBots: hasBots, translateToLanguage: translateToLanguage, maxReadStoryId: maxReadStoryId, recommendedChannels: recommendedChannels, audioTranscriptionTrial: audioTranscriptionTrial, chatThemes: chatThemes, deviceContactsNumbers: deviceContactsNumbers, isInline: isInline, showSensitiveContent: showSensitiveContent)
} }
private extension ChatHistoryLocationInput { private extension ChatHistoryLocationInput {
@ -1557,6 +1558,8 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
self.allAdMessagesPromise.get() self.allAdMessagesPromise.get()
) )
let sharedData = self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.mediaDisplaySettings])
let maxReadStoryId: Signal<Int32?, NoError> let maxReadStoryId: Signal<Int32?, NoError>
if let peerId = self.chatLocation.peerId, peerId.namespace == Namespaces.Peer.CloudUser { if let peerId = self.chatLocation.peerId, peerId.namespace == Namespaces.Peer.CloudUser {
maxReadStoryId = self.context.account.postbox.combinedView(keys: [PostboxViewKey.storiesState(key: .peer(peerId))]) maxReadStoryId = self.context.account.postbox.combinedView(keys: [PostboxViewKey.storiesState(key: .peer(peerId))])
@ -1632,9 +1635,11 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
recommendedChannels, recommendedChannels,
audioTranscriptionTrial, audioTranscriptionTrial,
chatThemes, chatThemes,
deviceContactsNumbers deviceContactsNumbers,
).startStrict(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, preferredStoryHighQuality, animatedEmojiStickers, additionalAnimatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState, availableReactions, availableMessageEffects, savedMessageTags, defaultReaction, accountPeer, suggestAudioTranscription, promises, topicAuthorId, translationState, maxReadStoryId, recommendedChannels, audioTranscriptionTrial, chatThemes, deviceContactsNumbers in sharedData
).startStrict(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, preferredStoryHighQuality, animatedEmojiStickers, additionalAnimatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState, availableReactions, availableMessageEffects, savedMessageTags, defaultReaction, accountPeer, suggestAudioTranscription, promises, topicAuthorId, translationState, maxReadStoryId, recommendedChannels, audioTranscriptionTrial, chatThemes, deviceContactsNumbers, sharedData in
let (historyAppearsCleared, pendingUnpinnedAllMessages, pendingRemovedMessages, currentlyPlayingMessageIdAndType, scrollToMessageId, chatHasBots, allAdMessages) = promises let (historyAppearsCleared, pendingUnpinnedAllMessages, pendingRemovedMessages, currentlyPlayingMessageIdAndType, scrollToMessageId, chatHasBots, allAdMessages) = promises
let mediaSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.mediaDisplaySettings]?.get(MediaDisplaySettings.self) ?? MediaDisplaySettings.defaultSettings
func applyHole() { func applyHole() {
Queue.mainQueue().async { Queue.mainQueue().async {
@ -1852,7 +1857,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
translateToLanguage = languageCode translateToLanguage = languageCode
} }
let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, preferredStoryHighQuality: preferredStoryHighQuality, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, subject: subject, currentlyPlayingMessageId: currentlyPlayingMessageIdAndType?.0, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: savedMessageTags, defaultReaction: defaultReaction, isPremium: isPremium, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, accountPeer: accountPeer, topicAuthorId: topicAuthorId, hasBots: chatHasBots, translateToLanguage: translateToLanguage, maxReadStoryId: maxReadStoryId, recommendedChannels: recommendedChannels, audioTranscriptionTrial: audioTranscriptionTrial, chatThemes: chatThemes, deviceContactsNumbers: deviceContactsNumbers, isInline: !rotated) let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, preferredStoryHighQuality: preferredStoryHighQuality, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, subject: subject, currentlyPlayingMessageId: currentlyPlayingMessageIdAndType?.0, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: savedMessageTags, defaultReaction: defaultReaction, isPremium: isPremium, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, accountPeer: accountPeer, topicAuthorId: topicAuthorId, hasBots: chatHasBots, translateToLanguage: translateToLanguage, maxReadStoryId: maxReadStoryId, recommendedChannels: recommendedChannels, audioTranscriptionTrial: audioTranscriptionTrial, chatThemes: chatThemes, deviceContactsNumbers: deviceContactsNumbers, isInline: !rotated, showSensitiveContent: mediaSettings.showSensitiveContent)
var includeEmbeddedSavedChatInfo = false var includeEmbeddedSavedChatInfo = false
if case let .replyThread(message) = chatLocation, message.peerId == context.account.peerId, !rotated { if case let .replyThread(message) = chatLocation, message.peerId == context.account.peerId, !rotated {

View File

@ -1739,26 +1739,6 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
}))) })))
} }
} }
// if message.id.peerId.isGroupOrChannel {
// //TODO:localize
// if message.isAgeRestricted() {
// actions.append(.action(ContextMenuActionItem(text: "Unmark as 18+", icon: { theme in
// return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AgeUnmark"), color: theme.actionSheet.primaryTextColor)
// }, action: { c, _ in
// c?.dismiss(completion: {
// controllerInteraction.openMessageStats(messages[0].id)
// })
// })))
// } else {
// actions.append(.action(ContextMenuActionItem(text: "Mark as 18+", icon: { theme in
// return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AgeMark"), color: theme.actionSheet.primaryTextColor)
// }, action: { c, _ in
// c?.dismiss(completion: {
// controllerInteraction.openMessageStats(messages[0].id)
// })
// })))
// }
// }
if isReplyThreadHead { if isReplyThreadHead {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ViewInChannel, icon: { theme in actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ViewInChannel, icon: { theme in

View File

@ -2715,6 +2715,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return proxySettingsController(accountManager: sharedContext.accountManager, sharedContext: sharedContext, postbox: account.postbox, network: account.network, mode: .modal, presentationData: sharedContext.currentPresentationData.with { $0 }, updatedPresentationData: sharedContext.presentationData) return proxySettingsController(accountManager: sharedContext.accountManager, sharedContext: sharedContext, postbox: account.postbox, network: account.network, mode: .modal, presentationData: sharedContext.currentPresentationData.with { $0 }, updatedPresentationData: sharedContext.presentationData)
} }
public func makeDataAndStorageController(context: AccountContext, sensitiveContent: Bool) -> ViewController {
return dataAndStorageController(context: context, focusOnItemTag: sensitiveContent ? DataAndStorageEntryTag.sensitiveContent : nil)
}
public func makeInstalledStickerPacksController(context: AccountContext, mode: InstalledStickerPacksControllerMode, forceTheme: PresentationTheme?) -> ViewController { public func makeInstalledStickerPacksController(context: AccountContext, mode: InstalledStickerPacksControllerMode, forceTheme: PresentationTheme?) -> ViewController {
return installedStickerPacksController(context: context, mode: mode, forceTheme: forceTheme) return installedStickerPacksController(context: context, mode: mode, forceTheme: forceTheme)
} }

View File

@ -4,33 +4,41 @@ import SwiftSignalKit
public struct MediaDisplaySettings: Codable, Equatable { public struct MediaDisplaySettings: Codable, Equatable {
public let showNextMediaOnTap: Bool public let showNextMediaOnTap: Bool
public let showSensitiveContent: Bool
public static var defaultSettings: MediaDisplaySettings { public static var defaultSettings: MediaDisplaySettings {
return MediaDisplaySettings(showNextMediaOnTap: true) return MediaDisplaySettings(showNextMediaOnTap: true, showSensitiveContent: false)
} }
public init(showNextMediaOnTap: Bool) { public init(showNextMediaOnTap: Bool, showSensitiveContent: Bool) {
self.showNextMediaOnTap = showNextMediaOnTap self.showNextMediaOnTap = showNextMediaOnTap
self.showSensitiveContent = showSensitiveContent
} }
public init(from decoder: Decoder) throws { public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self) let container = try decoder.container(keyedBy: StringCodingKey.self)
self.showNextMediaOnTap = (try container.decode(Int32.self, forKey: "showNextMediaOnTap")) != 0 self.showNextMediaOnTap = (try container.decode(Int32.self, forKey: "showNextMediaOnTap")) != 0
self.showSensitiveContent = (try container.decode(Int32.self, forKey: "showSensitiveContent")) != 0
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self) var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode((self.showNextMediaOnTap ? 1 : 0) as Int32, forKey: "showNextMediaOnTap") try container.encode((self.showNextMediaOnTap ? 1 : 0) as Int32, forKey: "showNextMediaOnTap")
try container.encode((self.showSensitiveContent ? 1 : 0) as Int32, forKey: "showSensitiveContent")
} }
public static func ==(lhs: MediaDisplaySettings, rhs: MediaDisplaySettings) -> Bool { public static func ==(lhs: MediaDisplaySettings, rhs: MediaDisplaySettings) -> Bool {
return lhs.showNextMediaOnTap == rhs.showNextMediaOnTap return lhs.showNextMediaOnTap == rhs.showNextMediaOnTap && lhs.showSensitiveContent == rhs.showSensitiveContent
} }
public func withUpdatedShowNextMediaOnTap(_ showNextMediaOnTap: Bool) -> MediaDisplaySettings { public func withUpdatedShowNextMediaOnTap(_ showNextMediaOnTap: Bool) -> MediaDisplaySettings {
return MediaDisplaySettings(showNextMediaOnTap: showNextMediaOnTap) return MediaDisplaySettings(showNextMediaOnTap: showNextMediaOnTap, showSensitiveContent: self.showSensitiveContent)
}
public func withUpdatedShowSensitiveContent(_ showSensitiveContent: Bool) -> MediaDisplaySettings {
return MediaDisplaySettings(showNextMediaOnTap: self.showNextMediaOnTap, showSensitiveContent: showSensitiveContent)
} }
} }

View File

@ -231,7 +231,7 @@ public func parseInternalUrl(sharedContext: SharedAccountContext, query: String)
if let phone = phone, let hash = hash { if let phone = phone, let hash = hash {
return .cancelAccountReset(phone: phone, hash: hash) return .cancelAccountReset(phone: phone, hash: hash)
} }
} else if peerName == "msg" { } else if peerName == "msg" || peerName == "share" {
var url: String? var url: String?
var text: String? var text: String?
var to: String? var to: String?
@ -241,7 +241,7 @@ public func parseInternalUrl(sharedContext: SharedAccountContext, query: String)
url = value url = value
} else if queryItem.name == "text" { } else if queryItem.name == "text" {
text = value text = value
} else if queryItem.name == "to" { } else if queryItem.name == "to" && peerName != "share" {
to = value to = value
} }
} }

View File

@ -2072,9 +2072,6 @@ public final class WebAppController: ViewController, AttachmentContainable {
}) })
}))) })))
if let botCommands {
for command in botCommands {
if command.text == "privacy" {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.WebApp_PrivacyPolicy, icon: { theme in items.append(.action(ContextMenuActionItem(text: presentationData.strings.WebApp_PrivacyPolicy, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Privacy"), color: theme.contextMenu.primaryColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Privacy"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] c, _ in }, action: { [weak self] c, _ in
@ -2083,13 +2080,23 @@ public final class WebAppController: ViewController, AttachmentContainable {
guard let self else { guard let self else {
return return
} }
(self.parentController() as? AttachmentController)?.minimizeIfNeeded()
if let botCommands, botCommands.contains(where: { $0.text == "privacy" }) {
let _ = enqueueMessages(account: self.context.account, peerId: self.botId, messages: [.message(text: "/privacy", attributes: [], inlineStickers: [:], mediaReference: nil, threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])]).startStandalone() let _ = enqueueMessages(account: self.context.account, peerId: self.botId, messages: [.message(text: "/privacy", attributes: [], inlineStickers: [:], mediaReference: nil, threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])]).startStandalone()
if let botPeer, let navigationController = self.getNavigationController() { if let botPeer, let navigationController = self.getNavigationController() {
(self.parentController() as? AttachmentController)?.minimizeIfNeeded()
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(botPeer))) self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(botPeer)))
} }
} else {
self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: self.presentationData.strings.WebApp_PrivacyPolicy_URL, forceExternal: false, presentationData: self.presentationData, navigationController: self.getNavigationController(), dismissInput: {})
}
}))) })))
if let botCommands, botCommands.contains(where: { $0.text == "privacy" }) {
for command in botCommands {
if command.text == "privacy" {
} }
} }
} }

View File

@ -16,8 +16,8 @@ import Markdown
private let textFont = Font.regular(13.0) private let textFont = Font.regular(13.0)
private let boldTextFont = Font.semibold(13.0) private let boldTextFont = Font.semibold(13.0)
private func formattedText(_ text: String, color: UIColor, textAlignment: NSTextAlignment = .natural) -> NSAttributedString { private func formattedText(_ text: String, color: UIColor, linkColor: UIColor, textAlignment: NSTextAlignment = .natural) -> NSAttributedString {
return parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: color), bold: MarkdownAttributeSet(font: boldTextFont, textColor: color), link: MarkdownAttributeSet(font: textFont, textColor: color), linkAttribute: { _ in return nil}), textAlignment: textAlignment) return parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: color), bold: MarkdownAttributeSet(font: boldTextFont, textColor: color), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { _ in return nil}), textAlignment: textAlignment)
} }
private final class WebAppLaunchConfirmationAlertContentNode: AlertContentNode { private final class WebAppLaunchConfirmationAlertContentNode: AlertContentNode {
@ -44,6 +44,7 @@ private final class WebAppLaunchConfirmationAlertContentNode: AlertContentNode {
private var validLayout: CGSize? private var validLayout: CGSize?
private let morePressed: () -> Void private let morePressed: () -> Void
private let termsPressed: () -> Void
override var dismissOnOutsideTap: Bool { override var dismissOnOutsideTap: Bool {
return self.isUserInteractionEnabled return self.isUserInteractionEnabled
@ -55,13 +56,14 @@ private final class WebAppLaunchConfirmationAlertContentNode: AlertContentNode {
} }
} }
init(context: AccountContext, theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, peer: EnginePeer, title: String, text: String, showMore: Bool, requestWriteAccess: Bool, actions: [TextAlertAction], morePressed: @escaping () -> Void) { init(context: AccountContext, theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, peer: EnginePeer, title: String, text: String, showMore: Bool, requestWriteAccess: Bool, actions: [TextAlertAction], morePressed: @escaping () -> Void, termsPressed: @escaping () -> Void) {
self.strings = strings self.strings = strings
self.peer = peer self.peer = peer
self.title = title self.title = title
self.text = text self.text = text
self.showMore = showMore self.showMore = showMore
self.morePressed = morePressed self.morePressed = morePressed
self.termsPressed = termsPressed
self.titleNode = ImmediateTextNode() self.titleNode = ImmediateTextNode()
self.titleNode.displaysAsynchronously = false self.titleNode.displaysAsynchronously = false
@ -145,6 +147,8 @@ private final class WebAppLaunchConfirmationAlertContentNode: AlertContentNode {
super.didLoad() super.didLoad()
self.allowWriteLabelNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.allowWriteTap(_:)))) self.allowWriteLabelNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.allowWriteTap(_:))))
self.textNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.termsTap(_:))))
} }
@objc private func allowWriteTap(_ gestureRecognizer: UITapGestureRecognizer) { @objc private func allowWriteTap(_ gestureRecognizer: UITapGestureRecognizer) {
@ -153,18 +157,22 @@ private final class WebAppLaunchConfirmationAlertContentNode: AlertContentNode {
} }
} }
@objc private func termsTap(_ gestureRecognizer: UITapGestureRecognizer) {
self.termsPressed()
}
@objc private func moreButtonPressed() { @objc private func moreButtonPressed() {
self.morePressed() self.morePressed()
} }
override func updateTheme(_ theme: AlertControllerTheme) { override func updateTheme(_ theme: AlertControllerTheme) {
self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center)
self.textNode.attributedText = NSAttributedString(string: self.text, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center) self.textNode.attributedText = formattedText(self.text, color: theme.primaryColor, linkColor: theme.accentColor, textAlignment: .center)
self.moreButton.setAttributedTitle(NSAttributedString(string: self.strings.WebApp_LaunchMoreInfo, font: Font.regular(13.0), textColor: theme.accentColor), for: .normal) self.moreButton.setAttributedTitle(NSAttributedString(string: self.strings.WebApp_LaunchMoreInfo, font: Font.regular(13.0), textColor: theme.accentColor), for: .normal)
self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/AlertArrow"), color: theme.accentColor) self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/AlertArrow"), color: theme.accentColor)
self.allowWriteLabelNode.attributedText = formattedText(strings.WebApp_AddToAttachmentAllowMessages(self.peer.compactDisplayTitle).string, color: theme.primaryColor) self.allowWriteLabelNode.attributedText = formattedText(strings.WebApp_AddToAttachmentAllowMessages(self.peer.compactDisplayTitle).string, color: theme.primaryColor, linkColor: theme.primaryColor)
self.actionNodesSeparator.backgroundColor = theme.separatorColor self.actionNodesSeparator.backgroundColor = theme.separatorColor
for actionNode in self.actionNodes { for actionNode in self.actionNodes {
@ -313,7 +321,15 @@ private final class WebAppLaunchConfirmationAlertContentNode: AlertContentNode {
} }
} }
public func webAppLaunchConfirmationController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peer: EnginePeer, requestWriteAccess: Bool = false, completion: @escaping (Bool) -> Void, showMore: (() -> Void)?) -> AlertController { public func webAppLaunchConfirmationController(
context: AccountContext,
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?,
peer: EnginePeer,
requestWriteAccess: Bool = false,
completion: @escaping (Bool) -> Void,
showMore: (() -> Void)?,
openTerms: @escaping () -> Void
) -> AlertController {
let theme = defaultDarkColorPresentationTheme let theme = defaultDarkColorPresentationTheme
let presentationData: PresentationData let presentationData: PresentationData
if let updatedPresentationData { if let updatedPresentationData {
@ -337,11 +353,14 @@ public func webAppLaunchConfirmationController(context: AccountContext, updatedP
})] })]
let title = peer.compactDisplayTitle let title = peer.compactDisplayTitle
let text = presentationData.strings.WebApp_LaunchConfirmation let text = presentationData.strings.WebApp_LaunchTermsConfirmation
let contentNode = WebAppLaunchConfirmationAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: theme, strings: strings, peer: peer, title: title, text: text, showMore: showMore != nil, requestWriteAccess: requestWriteAccess, actions: actions, morePressed: { let contentNode = WebAppLaunchConfirmationAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: theme, strings: strings, peer: peer, title: title, text: text, showMore: showMore != nil, requestWriteAccess: requestWriteAccess, actions: actions, morePressed: {
dismissImpl?(true) dismissImpl?(true)
showMore?() showMore?()
}, termsPressed: {
dismissImpl?(true)
openTerms()
}) })
getContentNodeImpl = { [weak contentNode] in getContentNodeImpl = { [weak contentNode] in
return contentNode return contentNode