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.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";
"WallpaperPreview.PreviewInNightMode" = "Preview this wallpaper in night mode.";
@ -12517,6 +12519,7 @@ Sorry for the inconvenience.";
"Notification.StarsGift.Title_any" = "%@ Stars";
"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.UnknownUser" = "Unknown user";
"Bot.Settings" = "Bot Settings";
@ -12736,3 +12739,32 @@ Sorry for the inconvenience.";
"Stars.Transfer.Subscribe.Successful.Text" = "%1$@ transferred to %2$@.";
"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 makeDataAndStorageController(context: AccountContext, sensitiveContent: Bool) -> 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

View File

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

View File

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

View File

@ -489,20 +489,19 @@ private func inviteLinkEditControllerEntries(invite: ExportedInvitation?, state:
let isEditingEnabled = invite?.pricing == nil
let isSubscription = state.subscriptionEnabled
if !isGroup {
//TODO:localize
entries.append(.subscriptionFeeToggle(presentationData.theme, "Require Monthly Fee", state.subscriptionEnabled, isEditingEnabled))
entries.append(.subscriptionFeeToggle(presentationData.theme, presentationData.strings.InviteLink_Create_Fee, state.subscriptionEnabled, isEditingEnabled))
if state.subscriptionEnabled {
var label: String = ""
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
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 {
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))
}
@ -511,7 +510,7 @@ private func inviteLinkEditControllerEntries(invite: ExportedInvitation?, state:
entries.append(.requestApproval(presentationData.theme, presentationData.strings.InviteLink_Create_RequestApproval, state.requestApproval, isEditingEnabled && !isSubscription))
var requestApprovalInfoText = presentationData.strings.InviteLink_Create_RequestApprovalOffInfoChannel
if isSubscription {
requestApprovalInfoText = "You can't enable admin approval for links that require a monthly fee."
requestApprovalInfoText = presentationData.strings.InviteLink_Create_RequestApprovalFeeUnavailable
} else {
if state.requestApproval {
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
if let pricing {
//TODO:localize
let text = NSMutableAttributedString()
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: "⭐️") {
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))
@ -841,15 +840,14 @@ public final class InviteLinkViewController: ViewController {
entries.append(.link(presentationData.theme, invite))
if let pricing = invite.pricing {
//TODO:localize
entries.append(.subscriptionHeader(presentationData.theme, "SUBSCRIPTION FEE"))
var title = "⭐️\(pricing.amount) / month"
var subtitle = "No one joined yet"
entries.append(.subscriptionHeader(presentationData.theme, presentationData.strings.InviteLink_SubscriptionFee_Title.uppercased()))
var title = presentationData.strings.InviteLink_SubscriptionFee_PerMonth("\(pricing.amount)").string
var subtitle = presentationData.strings.InviteLink_SubscriptionFee_NoOneJoined
if state.count > 0 {
title += " x \(state.count)"
if let usdRate {
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 {
subtitle = ""
}

View File

@ -404,10 +404,9 @@ public class ItemListInviteLinkItemNode: ListViewItemNode, ItemListItemNode {
}
if let subscriptionPricing {
//TODO:localize
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: "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: "⭐️") {
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))

View File

@ -20,6 +20,9 @@ public extension Message {
public extension RestrictedContentMessageAttribute {
func platformText(platform: String, contentSettings: ContentSettings) -> String? {
for rule in self.rules {
if rule.isSensitive {
continue
}
if rule.platform == "all" || rule.platform == "ios" || contentSettings.addContentRestrictionReasons.contains(rule.platform) {
if !contentSettings.ignoreContentRestrictionReasons.contains(rule.reason) {
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)
}
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> {
if signals.count == 0 {
return single([T](), E.self)

View File

@ -34,9 +34,26 @@ private final class DataAndStorageControllerArguments {
let toggleDownloadInBackground: (Bool) -> Void
let openBrowserSelection: () -> 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.openNetworkUsage = openNetworkUsage
self.openProxy = openProxy
@ -50,7 +67,8 @@ private final class DataAndStorageControllerArguments {
self.toggleDownloadInBackground = toggleDownloadInBackground
self.openBrowserSelection = openBrowserSelection
self.openIntents = openIntents
self.toggleEnableSensitiveContent = toggleEnableSensitiveContent
self.toggleSensitiveContent = toggleSensitiveContent
self.toggleOtherSensitiveContent = toggleOtherSensitiveContent
}
}
@ -62,7 +80,8 @@ private enum DataAndStorageSection: Int32 {
case voiceCalls
case other
case connection
case enableSensitiveContent
case sensitiveContent
case otherSensitiveContent
}
public enum DataAndStorageEntryTag: ItemListItemTag, Equatable {
@ -72,6 +91,7 @@ public enum DataAndStorageEntryTag: ItemListItemTag, Equatable {
case pauseMusicOnRecording
case raiseToListen
case autoSave(AutomaticSaveIncomingPeerType)
case sensitiveContent
public func isEqual(to other: ItemListItemTag) -> Bool {
if let other = other as? DataAndStorageEntryTag, self == other {
@ -107,9 +127,12 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
case raiseToListen(PresentationTheme, String, Bool)
case raiseToListenInfo(PresentationTheme, String)
case sensitiveContent(String, Bool)
case sensitiveContentInfo(String)
case connectionHeader(PresentationTheme, String)
case connectionProxy(PresentationTheme, String, String)
case enableSensitiveContent(String, Bool)
case otherSensitiveContent(String, Bool)
var section: ItemListSectionId {
switch self {
@ -125,10 +148,12 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
return DataAndStorageSection.voiceCalls.rawValue
case .otherHeader, .openLinksIn, .shareSheet, .saveEditedPhotos, .pauseMusicOnRecording, .raiseToListen, .raiseToListenInfo:
return DataAndStorageSection.other.rawValue
case .sensitiveContent, .sensitiveContentInfo:
return DataAndStorageSection.sensitiveContent.rawValue
case .connectionHeader, .connectionProxy:
return DataAndStorageSection.connection.rawValue
case .enableSensitiveContent:
return DataAndStorageSection.enableSensitiveContent.rawValue
case .otherSensitiveContent:
return DataAndStorageSection.otherSensitiveContent.rawValue
}
}
@ -174,12 +199,16 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
return 34
case .raiseToListenInfo:
return 35
case .connectionHeader:
case .sensitiveContent:
return 36
case .connectionProxy:
case .sensitiveContentInfo:
return 37
case .enableSensitiveContent:
case .connectionHeader:
return 38
case .connectionProxy:
return 39
case .otherSensitiveContent:
return 40
}
}
@ -293,6 +322,18 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
} else {
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):
if case let .downloadInBackground(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true
@ -317,8 +358,8 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
} else {
return false
}
case let .enableSensitiveContent(text, value):
if case .enableSensitiveContent(text, value) = rhs {
case let .otherSensitiveContent(text, value):
if case .otherSensitiveContent(text, value) = rhs {
return true
} else {
return false
@ -408,6 +449,12 @@ private enum DataAndStorageEntry: ItemListNodeEntry {
}, tag: DataAndStorageEntryTag.raiseToListen)
case let .raiseToListenInfo(_, text):
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):
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
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: {
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
arguments.toggleEnableSensitiveContent(value)
arguments.toggleOtherSensitiveContent(value)
}, tag: nil)
}
}
@ -588,7 +635,7 @@ private func autosaveLabelAndValue(presentationData: PresentationData, settings:
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] = []
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(.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
if let proxySettings = data.proxySettings, let activeServer = proxySettings.activeServer, proxySettings.enabled {
switch activeServer.connection {
@ -643,7 +693,7 @@ private func dataAndStorageControllerEntries(state: DataAndStorageControllerStat
#if DEBUG
if let contentSettingsConfiguration = contentSettingsConfiguration, contentSettingsConfiguration.canAdjustSensitiveContent {
entries.append(.enableSensitiveContent("Display Sensitive Content", contentSettingsConfiguration.sensitiveContentEnabled))
entries.append(.otherSensitiveContent("Display Sensitive Content", contentSettingsConfiguration.sensitiveContentEnabled))
}
#endif
@ -872,7 +922,11 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da
}, openIntents: {
let controller = intentsSettingsController(context: context)
pushControllerImpl?(controller)
}, toggleEnableSensitiveContent: { value in
}, toggleSensitiveContent: { value in
let _ = updateMediaDisplaySettingsInteractively(accountManager: context.sharedContext.accountManager, {
$0.withUpdatedShowSensitiveContent(value)
}).startStandalone()
}, toggleOtherSensitiveContent: { value in
let _ = (contentSettingsConfiguration.get()
|> take(1)
|> deliverOnMainQueue).start(next: { [weak contentSettingsConfiguration] settings in
@ -905,7 +959,7 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da
context.sharedContext.presentationData,
statePromise.get(),
dataAndStorageDataPromise.get(),
context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.webBrowserSettings]),
context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.webBrowserSettings, ApplicationSpecificSharedDataKeys.mediaDisplaySettings]),
contentSettingsConfiguration.get(),
preferences,
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
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 defaultWebBrowser: String
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 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))
} |> afterDisposed {

View File

@ -275,7 +275,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1038136962] = { return Api.EncryptedFile.parse_encryptedFileEmpty($0) }
dict[-317144808] = { return Api.EncryptedMessage.parse_encryptedMessage($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[206668204] = { return Api.ExportedChatlistInvite.parse_exportedChatlistInvite($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[-701500310] = { return Api.RequestedPeer.parse_requestedPeerUser($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[1730456516] = { return Api.RichText.parse_textBold($0) }
dict[2120376535] = { return Api.RichText.parse_textConcat($0) }

View File

@ -397,6 +397,7 @@ public extension Api {
public extension Api {
enum RestrictionReason: TypeConstructorDescription {
case restrictionReason(platform: String, reason: String, text: String)
case restrictionReasonSensitive(platform: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -408,6 +409,12 @@ public extension Api {
serializeString(reason, buffer: buffer, boxed: false)
serializeString(text, buffer: buffer, boxed: false)
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 {
case .restrictionReason(let platform, let reason, let text):
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
}
}
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 {
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()
buffer.appendInt32(508941107)
buffer.appendInt32(633929278)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
serializeInt32(msgId, buffer: buffer, boxed: false)
serializeInt32(count, 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)
var result: Api.Updates?
if let signature = reader.readInt32() {

View File

@ -1012,14 +1012,14 @@ public extension Api {
}
public extension Api {
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
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
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 {
buffer.appendInt32(-1812799720)
buffer.appendInt32(-1574126186)
}
serializeInt32(flags, 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 << 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 << 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 << 9) != 0 {subscriptionPricing!.serialize(buffer, true)}
break
@ -1044,8 +1045,8 @@ public extension Api {
public func descriptionFields() -> (String, [(String, Any)]) {
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):
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)])
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), ("subscriptionExpired", subscriptionExpired as Any), ("title", title as Any), ("subscriptionPricing", subscriptionPricing as Any)])
case .chatInvitePublicJoinRequests:
return ("chatInvitePublicJoinRequests", [])
}
@ -1070,11 +1071,13 @@ public extension Api {
if Int(_1!) & Int(1 << 3) != 0 {_8 = reader.readInt32() }
var _9: Int32?
if Int(_1!) & Int(1 << 7) != 0 {_9 = reader.readInt32() }
var _10: String?
if Int(_1!) & Int(1 << 8) != 0 {_10 = parseString(reader) }
var _11: Api.StarsSubscriptionPricing?
var _10: Int32?
if Int(_1!) & Int(1 << 10) != 0 {_10 = reader.readInt32() }
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() {
_11 = Api.parse(reader, signature: signature) as? Api.StarsSubscriptionPricing
_12 = Api.parse(reader, signature: signature) as? Api.StarsSubscriptionPricing
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
@ -1085,10 +1088,11 @@ public extension Api {
let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil
let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil
let _c9 = (Int(_1!) & Int(1 << 7) == 0) || _9 != nil
let _c10 = (Int(_1!) & Int(1 << 8) == 0) || _10 != nil
let _c11 = (Int(_1!) & Int(1 << 9) == 0) || _11 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 {
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)
let _c10 = (Int(_1!) & Int(1 << 10) == 0) || _10 != nil
let _c11 = (Int(_1!) & Int(1 << 8) == 0) || _11 != nil
let _c12 = (Int(_1!) & Int(1 << 9) == 0) || _12 != nil
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 {
return nil

View File

@ -6,7 +6,8 @@ import TelegramApi
extension ExportedInvitation {
init(apiExportedInvite: Api.ExportedChatInvite) {
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) })
case .chatInvitePublicJoinRequests:
self = .publicJoinRequest

View File

@ -8,6 +8,8 @@ extension RestrictionRule {
switch apiReason {
case let .restrictionReason(platform, reason, 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 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
return .generic
}

View File

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

View File

@ -30,6 +30,9 @@ public extension Peer {
if let restrictionInfo = restrictionInfo {
for rule in restrictionInfo.rules {
if rule.isSensitive {
continue
}
if rule.platform == "all" || rule.platform == platform || contentSettings.addContentRestrictionReasons.contains(rule.platform) {
if !contentSettings.ignoreContentRestrictionReasons.contains(rule.reason) {
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 {
if let channel = self as? TelegramChannel {
return channel.flags.contains(.isForum)

View File

@ -751,11 +751,10 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
if message.author?.id == accountPeerId {
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_SentYou(price)._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes])
} else {
//TODO:localize
var authorName = compactAuthorName
var peerIds: [(Int, EnginePeer.Id?)] = [(0, message.author?.id)]
if message.id.peerId.namespace == Namespaces.Peer.CloudUser && message.id.peerId.id._internalGetInt64Value() == 777000 {
authorName = "Unknown user"
authorName = strings.Notification_StarsGift_UnknownUser
peerIds = []
}
var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds)
@ -1010,13 +1009,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
}
}
case let .paymentRefunded(peerId, currency, totalAmount, _, _):
//TODO:localize
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 patternString = strings.Notification_Refund
let mutableString = NSMutableAttributedString()
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 blurredImageNode: TransformImageNode
private let dustNode: MediaDustNode
fileprivate let dustNode: MediaDustNode
fileprivate let buttonNode: HighlightTrackingButtonNode
private let highlightedBackgroundNode: ASDisplayNode
private let iconNode: ASImageNode
@ -452,6 +452,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
private var automaticDownload: InteractiveMediaNodeAutodownloadMode?
public var automaticPlayback: Bool?
private var preferredStoryHighQuality: Bool = false
private var showSensitiveContent: Bool = false
private let statusDisposable = MetaDisposable()
private let fetchControls = Atomic<FetchControls?>(value: nil)
@ -1570,6 +1571,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
strongSelf.automaticPlayback = automaticPlayback
strongSelf.automaticDownload = automaticDownload
strongSelf.preferredStoryHighQuality = associatedData.preferredStoryHighQuality
strongSelf.showSensitiveContent = associatedData.showSensitiveContent
if let previousArguments = strongSelf.currentImageArguments {
if previousArguments.imageSize == arguments.imageSize {
@ -2399,20 +2401,25 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
displaySpoiler = true
} else if isSecretMedia {
displaySpoiler = true
} else if message.isAgeRestricted() {
displaySpoiler = true
icon = .eye
} else if message.isSensitiveContent(platform: "ios") {
if !self.showSensitiveContent {
displaySpoiler = true
icon = .eye
}
}
if displaySpoiler {
if self.extendedMediaOverlayNode == nil, let context = self.context {
if displaySpoiler, let context = self.context {
let extendedMediaOverlayNode: ExtendedMediaOverlayNode
if let current = self.extendedMediaOverlayNode {
extendedMediaOverlayNode = current
} else {
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
guard let self else {
return
}
if message.isAgeRestricted() {
if message.isSensitiveContent(platform: "ios") {
self.activateAgeRestrictedMedia?()
} else {
self.internallyVisible = true
@ -2423,7 +2430,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
self.extendedMediaOverlayNode = extendedMediaOverlayNode
self.pinchContainerNode.contentNode.insertSubnode(extendedMediaOverlayNode, aboveSubnode: self.imageNode)
}
self.extendedMediaOverlayNode?.frame = self.imageNode.frame
extendedMediaOverlayNode.frame = self.imageNode.frame
var tappable = false
if !isSecretMedia {
@ -2434,13 +2441,12 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
break
}
}
self.extendedMediaOverlayNode?.isUserInteractionEnabled = tappable
extendedMediaOverlayNode.isUserInteractionEnabled = tappable
var viewText: String = ""
if message.isAgeRestricted() {
//TODO:localize
viewText = "18+ Content"
if case .eye = icon {
viewText = strings.Chat_SensitiveContent
extendedMediaOverlayNode.dustNode.revealOnTap = false
} else {
outer: for attribute in message.attributes {
if let attribute = attribute as? ReplyMarkupMessageAttribute {
@ -2455,8 +2461,9 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
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 {
self.extendedMediaOverlayNode = nil
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 {
// if let extendedMediaOverlayNode = self.extendedMediaOverlayNode {
// let convertedPoint = self.view.convert(point, to: extendedMediaOverlayNode.view)
// if extendedMediaOverlayNode.buttonNode.frame.contains(convertedPoint) {
// return true
// }
// }
if let extendedMediaOverlayNode = self.extendedMediaOverlayNode {
let convertedPoint = self.view.convert(point, to: extendedMediaOverlayNode.view)
if extendedMediaOverlayNode.buttonNode.frame.contains(convertedPoint) {
return true
}
}
return false
}
}

View File

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

View File

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

View File

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

View File

@ -524,7 +524,7 @@ public final class StarsWithdrawScreen: ViewControllerComponentContainer {
theme: .default
)
self.navigationPresentation = .flatModal
self.navigationPresentation = .standaloneModal
}
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 _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: context.sharedContext.accountManager, peerId: botPeer.id).startStandalone()
openWebView()
}, showMore: nil)
}, showMore: nil, openTerms: {
})
parentController.present(controller, in: .window(.root))
}
})
@ -312,38 +314,38 @@ public extension ChatControllerImpl {
}
static func botRequestSwitchInline(context: AccountContext, controller: ChatControllerImpl?, peerId: EnginePeer.Id, botAddress: String, query: String, chatTypes: [ReplyMarkupButtonRequestPeerType]?, completion: @escaping () -> Void) -> Void {
let activateSwitchInline = {
var chatController: ChatControllerImpl?
if let current = controller {
chatController = current
} else if let navigationController = context.sharedContext.mainWindow?.viewController as? NavigationController {
for controller in navigationController.viewControllers.reversed() {
if let controller = controller as? ChatControllerImpl {
chatController = controller
break
}
let activateSwitchInline: (EnginePeer?) -> Void = { selectedPeer in
var chatController: ChatControllerImpl?
if let current = controller {
chatController = current
} else if let navigationController = context.sharedContext.mainWindow?.viewController as? NavigationController {
for controller in navigationController.viewControllers.reversed() {
if let controller = controller as? ChatControllerImpl {
chatController = controller
break
}
}
if let chatController {
chatController.controllerInteraction?.activateSwitchInline(peerId, "@\(botAddress) \(query)", nil)
}
}
if let chatTypes {
let peerController = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.excludeRecent, .doNotSearchMessages], requestPeerType: chatTypes, hasContactSelector: false, hasCreation: false))
peerController.peerSelected = { [weak peerController] peer, _ in
completion()
peerController?.dismiss()
activateSwitchInline()
}
if let controller {
controller.push(peerController)
} else {
((context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface)?.viewControllers.last as? ViewController)?.push(peerController)
}
if let chatController {
chatController.controllerInteraction?.activateSwitchInline(selectedPeer?.id ?? peerId, "@\(botAddress) \(query)", nil)
}
}
if let chatTypes {
let peerController = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.excludeRecent, .doNotSearchMessages], requestPeerType: chatTypes, hasContactSelector: false, hasCreation: false))
peerController.peerSelected = { [weak peerController] peer, _ in
completion()
peerController?.dismiss()
activateSwitchInline(peer)
}
if let controller {
controller.push(peerController)
} else {
activateSwitchInline()
((context.sharedContext.mainWindow?.viewController as? TelegramRootControllerInterface)?.viewControllers.last as? ViewController)?.push(peerController)
}
} else {
activateSwitchInline(nil)
}
}
private static func botOpenPeer(context: AccountContext, peerId: EnginePeer.Id, navigation: ChatControllerInteractionNavigateToPeer, navigationController: NavigationController) {
@ -547,6 +549,10 @@ public extension ChatControllerImpl {
if let self {
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))
}

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.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
for actionNode in self.actionNodes {
@ -269,10 +269,9 @@ public func chatAgeRestrictionAlertController(context: AccountContext, updatedPr
}
let strings = presentationData.strings
//TODO:localize
var dismissImpl: ((Bool) -> Void)?
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 {
completion(alwaysShow)
} else {
@ -283,8 +282,8 @@ public func chatAgeRestrictionAlertController(context: AccountContext, updatedPr
dismissImpl?(true)
})]
let title = "18+ Content"
let text = "This media may contain sensitive content suitable only for adults.\nDo you still want to view it?"
let title = strings.SensitiveContent_Title
let text = strings.SensitiveContent_Text
let contentNode = ChatAgeRestrictionAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: theme, strings: strings, title: title, text: text, actions: actions)
getContentNodeImpl = { [weak contentNode] in

View File

@ -4423,7 +4423,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
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()
})

View File

@ -350,7 +350,8 @@ private func extractAssociatedData(
audioTranscriptionTrial: AudioTranscription.TrialState,
chatThemes: [TelegramTheme],
deviceContactsNumbers: Set<String>,
isInline: Bool
isInline: Bool,
showSensitiveContent: Bool
) -> ChatMessageItemAssociatedData {
var automaticDownloadPeerId: EnginePeer.Id?
var automaticMediaDownloadPeerType: MediaAutoDownloadPeerType = .channel
@ -405,7 +406,7 @@ private func extractAssociatedData(
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 {
@ -1557,6 +1558,8 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
self.allAdMessagesPromise.get()
)
let sharedData = self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.mediaDisplaySettings])
let maxReadStoryId: Signal<Int32?, NoError>
if let peerId = self.chatLocation.peerId, peerId.namespace == Namespaces.Peer.CloudUser {
maxReadStoryId = self.context.account.postbox.combinedView(keys: [PostboxViewKey.storiesState(key: .peer(peerId))])
@ -1632,9 +1635,11 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
recommendedChannels,
audioTranscriptionTrial,
chatThemes,
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
deviceContactsNumbers,
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 mediaSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.mediaDisplaySettings]?.get(MediaDisplaySettings.self) ?? MediaDisplaySettings.defaultSettings
func applyHole() {
Queue.mainQueue().async {
@ -1852,7 +1857,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
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
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 {
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)
}
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 {
return installedStickerPacksController(context: context, mode: mode, forceTheme: forceTheme)
}

View File

@ -4,33 +4,41 @@ import SwiftSignalKit
public struct MediaDisplaySettings: Codable, Equatable {
public let showNextMediaOnTap: Bool
public let showSensitiveContent: Bool
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.showSensitiveContent = showSensitiveContent
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
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 {
var container = encoder.container(keyedBy: StringCodingKey.self)
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 {
return lhs.showNextMediaOnTap == rhs.showNextMediaOnTap
return lhs.showNextMediaOnTap == rhs.showNextMediaOnTap && lhs.showSensitiveContent == rhs.showSensitiveContent
}
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 {
return .cancelAccountReset(phone: phone, hash: hash)
}
} else if peerName == "msg" {
} else if peerName == "msg" || peerName == "share" {
var url: String?
var text: String?
var to: String?
@ -241,7 +241,7 @@ public func parseInternalUrl(sharedContext: SharedAccountContext, query: String)
url = value
} else if queryItem.name == "text" {
text = value
} else if queryItem.name == "to" {
} else if queryItem.name == "to" && peerName != "share" {
to = value
}
}

View File

@ -2072,24 +2072,31 @@ public final class WebAppController: ViewController, AttachmentContainable {
})
})))
if let botCommands {
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)
}, action: { [weak self] c, _ in
c?.dismiss(completion: nil)
guard let self else {
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()
if let botPeer, let navigationController = self.getNavigationController() {
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" {
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)
}, action: { [weak self] c, _ in
c?.dismiss(completion: nil)
guard let self else {
return
}
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() {
(self.parentController() as? AttachmentController)?.minimizeIfNeeded()
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(botPeer)))
}
})))
}
}
}

View File

@ -16,8 +16,8 @@ import Markdown
private let textFont = Font.regular(13.0)
private let boldTextFont = Font.semibold(13.0)
private func formattedText(_ text: String, color: 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)
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: linkColor), linkAttribute: { _ in return nil}), textAlignment: textAlignment)
}
private final class WebAppLaunchConfirmationAlertContentNode: AlertContentNode {
@ -44,6 +44,7 @@ private final class WebAppLaunchConfirmationAlertContentNode: AlertContentNode {
private var validLayout: CGSize?
private let morePressed: () -> Void
private let termsPressed: () -> Void
override var dismissOnOutsideTap: Bool {
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.peer = peer
self.title = title
self.text = text
self.showMore = showMore
self.morePressed = morePressed
self.termsPressed = termsPressed
self.titleNode = ImmediateTextNode()
self.titleNode.displaysAsynchronously = false
@ -145,6 +147,8 @@ private final class WebAppLaunchConfirmationAlertContentNode: AlertContentNode {
super.didLoad()
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) {
@ -153,18 +157,22 @@ private final class WebAppLaunchConfirmationAlertContentNode: AlertContentNode {
}
}
@objc private func termsTap(_ gestureRecognizer: UITapGestureRecognizer) {
self.termsPressed()
}
@objc private func moreButtonPressed() {
self.morePressed()
}
override func updateTheme(_ theme: AlertControllerTheme) {
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.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
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 presentationData: PresentationData
if let updatedPresentationData {
@ -337,11 +353,14 @@ public func webAppLaunchConfirmationController(context: AccountContext, updatedP
})]
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: {
dismissImpl?(true)
showMore?()
}, termsPressed: {
dismissImpl?(true)
openTerms()
})
getContentNodeImpl = { [weak contentNode] in
return contentNode