Merge commit 'f6ff9b86d0e76184cdc047b8d3856ce0aefe545b'

# Conflicts:
#	submodules/TelegramUI/Sources/ChatController.swift
This commit is contained in:
Ali 2023-02-28 22:25:53 +04:00
commit bc5e989351
28 changed files with 253 additions and 114 deletions

View File

@ -8911,8 +8911,9 @@ Sorry for the inconvenience.";
"ChatList.EmptyChatListWithArchive" = "All of your chats are archived.";
"Conversation.AudioRateTooltip15X" = "Audio will play at 1.5X speed.";
"Conversation.AudioRateTooltip15X" = "Audio will play at 1.5x speed.";
"Conversation.AudioRateOptionsTooltip" = "Long tap for more speed values.";
"Conversation.AudioRateTooltipCustom" = "Audio will play at %@x speed.";
"ImportStickerPack.EmojiCount_1" = "%@ Emoji";
"ImportStickerPack.EmojiCount_any" = "%@ Emojis";
@ -8936,3 +8937,5 @@ Sorry for the inconvenience.";
"ChatList.ReadAll" = "Read All";
"ChatList.ClearSavedMessagesConfirmation" = "Are you sure you want to delete all your saved messages?";
"Conversation.Translation.Settings" = "Settings";

View File

@ -751,6 +751,7 @@ public protocol SharedAccountContext: AnyObject {
var immediateExperimentalUISettings: ExperimentalUISettings { get }
var currentInAppNotificationSettings: Atomic<InAppNotificationSettings> { get }
var currentMediaInputSettings: Atomic<MediaInputSettings> { get }
var currentStickerSettings: Atomic<StickerSettings> { get }
var energyUsageSettings: EnergyUsageSettings { get }

View File

@ -2715,7 +2715,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
strongSelf.context.sharedContext.mediaManager.setPlaylist(nil, type: type, control: SharedMediaPlayerControlAction.playback(.pause))
}
}
mediaAccessoryPanel.setRate = { [weak self] rate, fromMenu in
mediaAccessoryPanel.setRate = { [weak self] rate, changeType in
guard let strongSelf = self else {
return
}
@ -2756,10 +2756,25 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
text = presentationData.strings.Conversation_AudioRateTooltipSpeedUp
rate = 2.0
} else {
text = nil
rate = nil
let value = String(format: "%0.1f", baseRate.doubleValue)
text = presentationData.strings.Conversation_AudioRateTooltipCustom(value).string
if case let .sliderCommit(previousValue, newValue) = changeType {
if newValue > previousValue {
rate = .infinity
} else if newValue < previousValue {
rate = -.infinity
} else {
rate = nil
}
} else {
rate = nil
}
}
if let rate, let text, !fromMenu {
var showTooltip = true
if case .sliderChange = changeType {
showTooltip = false
}
if let rate, let text, showTooltip {
controller.present(
UndoOverlayController(
presentationData: presentationData,

View File

@ -615,16 +615,8 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
func setFile(context: AccountContext, userLocation: MediaResourceUserLocation, fileReference: FileMediaReference) {
if self.contextAndMedia == nil || !self.contextAndMedia!.1.media.isEqual(to: fileReference.media) {
if var largestSize = fileReference.media.dimensions {
var displaySize = largestSize.cgSize.dividedByScreenScale()
if let previewDimensions = largestImageRepresentation(fileReference.media.previewRepresentations)?.dimensions {
let previewAspect = CGFloat(previewDimensions.width) / CGFloat(previewDimensions.height)
let aspect = displaySize.width / displaySize.height
if abs(previewAspect - 1.0 / aspect) < 0.1 {
displaySize = CGSize(width: displaySize.height, height: displaySize.width)
largestSize = PixelDimensions(width: largestSize.height, height: largestSize.width)
}
}
if let largestSize = fileReference.media.dimensions {
let displaySize = largestSize.cgSize.dividedByScreenScale()
self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: displaySize, boundingSize: displaySize, intrinsicInsets: UIEdgeInsets()))()
/*if largestSize.width > 2600 || largestSize.height > 2600 {

View File

@ -1904,19 +1904,23 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
}
_ = (ApplicationSpecificNotice.getSetPublicChannelLink(accountManager: context.sharedContext.accountManager) |> deliverOnMainQueue).start(next: { showAlert in
if showAlert {
let text: String
if case .broadcast = peer.info {
text = presentationData.strings.Channel_Edit_PrivatePublicLinkAlert
if !updatedAddressNameValue.isEmpty {
_ = (ApplicationSpecificNotice.getSetPublicChannelLink(accountManager: context.sharedContext.accountManager) |> deliverOnMainQueue).start(next: { showAlert in
if showAlert {
let text: String
if case .broadcast = peer.info {
text = presentationData.strings.Channel_Edit_PrivatePublicLinkAlert
} else {
text = presentationData.strings.Group_Edit_PrivatePublicLinkAlert
}
presentControllerImpl?(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: invokeAction)]), nil)
} else {
text = presentationData.strings.Group_Edit_PrivatePublicLinkAlert
invokeAction()
}
presentControllerImpl?(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: invokeAction)]), nil)
} else {
invokeAction()
}
})
})
} else {
invokeAction()
}
} else {
switch mode {
case .initialSetup:

View File

@ -465,8 +465,15 @@ final class LocalizationListControllerNode: ViewControllerTracingNode {
if let languages = translationSettings.ignoredLanguages {
ignoredLanguages = languages
} else {
if let activeLanguage = activeLanguageCode, supportedTranslationLanguages.contains(activeLanguage) {
ignoredLanguages = [activeLanguage]
if var activeLanguage = activeLanguageCode {
let rawSuffix = "-raw"
if activeLanguage.hasSuffix(rawSuffix) {
activeLanguage = String(activeLanguage.dropLast(rawSuffix.count))
}
if supportedTranslationLanguages.contains(activeLanguage) {
ignoredLanguages = [activeLanguage]
}
}
let systemLanguages = systemLanguageCodes()
for systemLanguage in systemLanguages {

View File

@ -77,13 +77,17 @@ private func translationSettingsControllerEntries(theme: PresentationTheme, stri
if let ignoredLanguages = settings.ignoredLanguages {
selectedLanguages = Set(ignoredLanguages)
} else {
var langCode = strings.baseLanguageCode
if langCode == "nb" {
langCode = "no"
} else if langCode == "pt-br" {
langCode = "pt"
var activeLanguage = strings.baseLanguageCode
let rawSuffix = "-raw"
if activeLanguage.hasSuffix(rawSuffix) {
activeLanguage = String(activeLanguage.dropLast(rawSuffix.count))
}
selectedLanguages = Set([langCode])
if activeLanguage == "nb" {
activeLanguage = "no"
} else if activeLanguage == "pt-br" {
activeLanguage = "pt"
}
selectedLanguages = Set([activeLanguage])
for language in systemLanguageCodes() {
selectedLanguages.insert(language)
}
@ -114,7 +118,11 @@ public func translationSettingsController(context: AccountContext) -> ViewContro
let actionsDisposable = DisposableSet()
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let interfaceLanguageCode = presentationData.strings.baseLanguageCode
var interfaceLanguageCode = presentationData.strings.baseLanguageCode
let rawSuffix = "-raw"
if interfaceLanguageCode.hasSuffix(rawSuffix) {
interfaceLanguageCode = String(interfaceLanguageCode.dropLast(rawSuffix.count))
}
let arguments = TranslationSettingsControllerArguments(context: context, updateLanguageSelected: { code, value in
let _ = updateTranslationSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
@ -185,7 +193,12 @@ public func translationSettingsController(context: AccountContext) -> ViewContro
if let ignoredLanguages = settings.ignoredLanguages {
selectedLanguages = Set(ignoredLanguages)
} else {
selectedLanguages = Set([presentationData.strings.baseLanguageCode])
var activeLanguage = presentationData.strings.baseLanguageCode
let rawSuffix = "-raw"
if activeLanguage.hasSuffix(rawSuffix) {
activeLanguage = String(activeLanguage.dropLast(rawSuffix.count))
}
selectedLanguages = Set([activeLanguage])
for language in systemLanguageCodes() {
selectedLanguages.insert(language)
}

View File

@ -174,7 +174,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
public var tapAction: (() -> Void)?
public var close: (() -> Void)?
public var setRate: ((AudioPlaybackRate, Bool) -> Void)?
public var setRate: ((AudioPlaybackRate, MediaNavigationAccessoryPanel.ChangeType) -> Void)?
public var togglePlayPause: (() -> Void)?
public var playPrevious: (() -> Void)?
public var playNext: (() -> Void)?
@ -500,7 +500,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
} else {
nextRate = .x2
}
self.setRate?(nextRate, false)
self.setRate?(nextRate, .preset)
let frame = self.rateButton.view.convert(self.rateButton.bounds, to: nil)
@ -527,8 +527,10 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
private func contextMenuSpeedItems(dismiss: @escaping () -> Void) -> Signal<[ContextMenuItem], NoError> {
var items: [ContextMenuItem] = []
items.append(.custom(SliderContextItem(minValue: 0.5, maxValue: 2.5, value: self.playbackBaseRate?.doubleValue ?? 1.0, valueChanged: { [weak self] newValue, finished in
self?.setRate?(AudioPlaybackRate(newValue), true)
let previousValue = self.playbackBaseRate?.doubleValue ?? 1.0
items.append(.custom(SliderContextItem(minValue: 0.5, maxValue: 2.5, value: previousValue, valueChanged: { [weak self] newValue, finished in
let type: MediaNavigationAccessoryPanel.ChangeType = finished ? .sliderCommit(previousValue, newValue) : .sliderChange
self?.setRate?(AudioPlaybackRate(newValue), type)
if finished {
dismiss()
}
@ -547,7 +549,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi
}, action: { [weak self] _, f in
f(.default)
self?.setRate?(rate, true)
self?.setRate?(rate, .preset)
})))
}

View File

@ -10,8 +10,14 @@ import TelegramPresentationData
public final class MediaNavigationAccessoryPanel: ASDisplayNode {
public let containerNode: MediaNavigationAccessoryContainerNode
public enum ChangeType {
case preset
case sliderChange
case sliderCommit(CGFloat, CGFloat)
}
public var close: (() -> Void)?
public var setRate: ((AudioPlaybackRate, Bool) -> Void)?
public var setRate: ((AudioPlaybackRate, ChangeType) -> Void)?
public var togglePlayPause: (() -> Void)?
public var tapAction: (() -> Void)?
public var playPrevious: (() -> Void)?
@ -34,8 +40,8 @@ public final class MediaNavigationAccessoryPanel: ASDisplayNode {
close()
}
}
self.containerNode.headerNode.setRate = { [weak self] rate, fromMenu in
self?.setRate?(rate, fromMenu)
self.containerNode.headerNode.setRate = { [weak self] rate, type in
self?.setRate?(rate, type)
}
self.containerNode.headerNode.togglePlayPause = { [weak self] in
if let strongSelf = self, let togglePlayPause = strongSelf.togglePlayPause {

View File

@ -669,7 +669,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
strongSelf.context.sharedContext.mediaManager.setPlaylist(nil, type: type, control: SharedMediaPlayerControlAction.playback(.pause))
}
}
mediaAccessoryPanel.setRate = { [weak self] rate, fromMenu in
mediaAccessoryPanel.setRate = { [weak self] rate, changeType in
guard let strongSelf = self else {
return
}
@ -709,10 +709,25 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
text = presentationData.strings.Conversation_AudioRateTooltipSpeedUp
rate = 2.0
} else {
text = nil
rate = nil
let value = String(format: "%0.1f", baseRate.doubleValue)
text = presentationData.strings.Conversation_AudioRateTooltipCustom(value).string
if case let .sliderCommit(previousValue, newValue) = changeType {
if newValue > previousValue {
rate = .infinity
} else if newValue < previousValue {
rate = -.infinity
} else {
rate = nil
}
} else {
rate = nil
}
}
if let rate, let text, !fromMenu {
var showTooltip = true
if case .sliderChange = changeType {
showTooltip = false
}
if let rate, let text, showTooltip {
strongSelf.present(
UndoOverlayController(
presentationData: presentationData,

View File

@ -218,15 +218,17 @@ public final class InitialPresentationDataAndSettings {
public let callListSettings: CallListSettings
public let inAppNotificationSettings: InAppNotificationSettings
public let mediaInputSettings: MediaInputSettings
public let stickerSettings: StickerSettings
public let experimentalUISettings: ExperimentalUISettings
public init(presentationData: PresentationData, automaticMediaDownloadSettings: MediaAutoDownloadSettings, autodownloadSettings: AutodownloadSettings, callListSettings: CallListSettings, inAppNotificationSettings: InAppNotificationSettings, mediaInputSettings: MediaInputSettings, experimentalUISettings: ExperimentalUISettings) {
public init(presentationData: PresentationData, automaticMediaDownloadSettings: MediaAutoDownloadSettings, autodownloadSettings: AutodownloadSettings, callListSettings: CallListSettings, inAppNotificationSettings: InAppNotificationSettings, mediaInputSettings: MediaInputSettings, stickerSettings: StickerSettings, experimentalUISettings: ExperimentalUISettings) {
self.presentationData = presentationData
self.automaticMediaDownloadSettings = automaticMediaDownloadSettings
self.autodownloadSettings = autodownloadSettings
self.callListSettings = callListSettings
self.inAppNotificationSettings = inAppNotificationSettings
self.mediaInputSettings = mediaInputSettings
self.stickerSettings = stickerSettings
self.experimentalUISettings = experimentalUISettings
}
}
@ -242,6 +244,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
var mediaInputSettings: PreferencesEntry?
var experimentalUISettings: PreferencesEntry?
var contactSynchronizationSettings: PreferencesEntry?
var stickerSettings: PreferencesEntry?
init(
localizationSettings: PreferencesEntry?,
@ -252,7 +255,8 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
inAppNotificationSettings: PreferencesEntry?,
mediaInputSettings: PreferencesEntry?,
experimentalUISettings: PreferencesEntry?,
contactSynchronizationSettings: PreferencesEntry?
contactSynchronizationSettings: PreferencesEntry?,
stickerSettings: PreferencesEntry?
) {
self.localizationSettings = localizationSettings
self.presentationThemeSettings = presentationThemeSettings
@ -263,6 +267,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
self.mediaInputSettings = mediaInputSettings
self.experimentalUISettings = experimentalUISettings
self.contactSynchronizationSettings = contactSynchronizationSettings
self.stickerSettings = stickerSettings
}
}
@ -276,6 +281,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
let mediaInputSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.mediaInputSettings)
let experimentalUISettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings)
let contactSynchronizationSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.contactSynchronizationSettings)
let stickerSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.stickerSettings)
return InternalData(
localizationSettings: localizationSettings,
@ -286,7 +292,8 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
inAppNotificationSettings: inAppNotificationSettings,
mediaInputSettings: mediaInputSettings,
experimentalUISettings: experimentalUISettings,
contactSynchronizationSettings: contactSynchronizationSettings
contactSynchronizationSettings: contactSynchronizationSettings,
stickerSettings: stickerSettings
)
}
|> deliverOn(Queue(name: "PresentationData-Load", qos: .userInteractive))
@ -340,6 +347,13 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
mediaInputSettings = MediaInputSettings.defaultSettings
}
let stickerSettings: StickerSettings
if let value = internalData.stickerSettings?.get(StickerSettings.self) {
stickerSettings = value
} else {
stickerSettings = StickerSettings.defaultSettings
}
let experimentalUISettings: ExperimentalUISettings = internalData.experimentalUISettings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
let contactSettings: ContactSynchronizationSettings = internalData.contactSynchronizationSettings?.get(ContactSynchronizationSettings.self) ?? ContactSynchronizationSettings.defaultSettings
@ -385,7 +399,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
let chatBubbleCorners = PresentationChatBubbleCorners(mainRadius: CGFloat(themeSettings.chatBubbleSettings.mainRadius), auxiliaryRadius: CGFloat(themeSettings.chatBubbleSettings.auxiliaryRadius), mergeBubbleCorners: themeSettings.chatBubbleSettings.mergeBubbleCorners)
return InitialPresentationDataAndSettings(presentationData: PresentationData(strings: stringsValue, theme: theme, autoNightModeTriggered: autoNightModeTriggered, chatWallpaper: effectiveChatWallpaper, chatFontSize: chatFontSize, chatBubbleCorners: chatBubbleCorners, listsFontSize: listsFontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, reduceMotion: themeSettings.reduceMotion, largeEmoji: themeSettings.largeEmoji), automaticMediaDownloadSettings: automaticMediaDownloadSettings, autodownloadSettings: autodownloadSettings, callListSettings: callListSettings, inAppNotificationSettings: inAppNotificationSettings, mediaInputSettings: mediaInputSettings, experimentalUISettings: experimentalUISettings)
return InitialPresentationDataAndSettings(presentationData: PresentationData(strings: stringsValue, theme: theme, autoNightModeTriggered: autoNightModeTriggered, chatWallpaper: effectiveChatWallpaper, chatFontSize: chatFontSize, chatBubbleCorners: chatBubbleCorners, listsFontSize: listsFontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, reduceMotion: themeSettings.reduceMotion, largeEmoji: themeSettings.largeEmoji), automaticMediaDownloadSettings: automaticMediaDownloadSettings, autodownloadSettings: autodownloadSettings, callListSettings: callListSettings, inAppNotificationSettings: inAppNotificationSettings, mediaInputSettings: mediaInputSettings, stickerSettings: stickerSettings, experimentalUISettings: experimentalUISettings)
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -4255,6 +4255,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
if case .user = peerType {
let _ = context.engine.peers.sendBotRequestedPeer(messageId: messageId, buttonId: buttonId, requestedPeerId: peer.id).start()
controller?.dismiss()
} else {
var isChannel = false
if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
@ -10117,7 +10118,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let locale = Locale(identifier: languageCode)
let fromLanguage: String = locale.localizedString(forLanguageCode: langCode) ?? ""
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .image(image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Title Panels/Translate"), color: .white)!, title: nil, text: presentationData.strings.Conversation_Translation_AddedToDoNotTranslateText(fromLanguage).string, round: false, undoText: "Settings"), elevatedLayout: false, animateInAsReplacement: false, action: { [weak self] action in
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .image(image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Title Panels/Translate"), color: .white)!, title: nil, text: presentationData.strings.Conversation_Translation_AddedToDoNotTranslateText(fromLanguage).string, round: false, undoText: presentationData.strings.Conversation_Translation_Settings), elevatedLayout: false, animateInAsReplacement: false, action: { [weak self] action in
if case .undo = action, let strongSelf = self {
let controller = translationSettingsController(context: strongSelf.context)
controller.navigationPresentation = .modal
@ -15633,7 +15634,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.dismiss()
let navigateToLocation: NavigateToChatControllerParams.Location
if let threadId = messages.first?.threadId {
if let message = messages.first, let threadId = message.threadId, threadId != 1 || (message.peers[message.id.peerId] as? TelegramChannel)?.flags.contains(.isForum) == true {
navigateToLocation = .replyThread(ChatReplyThreadMessage(messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false))
} else {
navigateToLocation = .peer(peer)

View File

@ -1114,9 +1114,8 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
}
}
let isCopyProtected = chatPresentationInterfaceState.copyProtectionEnabled || message.isCopyProtected()
if !messageText.isEmpty || (resourceAvailable && isImage) || diceEmoji != nil {
let isCopyProtected = chatPresentationInterfaceState.copyProtectionEnabled || message.isCopyProtected()
if !isExpired {
if !isPoll {
if !isCopyProtected {
@ -1223,35 +1222,35 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
})))
}
}
if resourceAvailable, !message.containsSecretMedia, !chatPresentationInterfaceState.copyProtectionEnabled, !message.isCopyProtected() {
var mediaReference: AnyMediaReference?
var isVideo = false
for media in message.media {
if let image = media as? TelegramMediaImage, let _ = largestImageRepresentation(image.representations) {
mediaReference = ImageMediaReference.standalone(media: image).abstract
break
} else if let file = media as? TelegramMediaFile, file.isVideo {
mediaReference = FileMediaReference.standalone(media: file).abstract
isVideo = true
break
}
}
if let mediaReference = mediaReference {
actions.append(.action(ContextMenuActionItem(text: isVideo ? chatPresentationInterfaceState.strings.Gallery_SaveVideo : chatPresentationInterfaceState.strings.Gallery_SaveImage, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in
let _ = (saveToCameraRoll(context: context, postbox: context.account.postbox, userLocation: .peer(message.id.peerId), mediaReference: mediaReference)
|> deliverOnMainQueue).start(completed: {
Queue.mainQueue().after(0.2) {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
controllerInteraction.presentControllerInCurrent(UndoOverlayController(presentationData: presentationData, content: .mediaSaved(text: isVideo ? presentationData.strings.Gallery_VideoSaved : presentationData.strings.Gallery_ImageSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return true }), nil)
}
})
f(.default)
})))
}
if resourceAvailable, !message.containsSecretMedia && !isCopyProtected {
var mediaReference: AnyMediaReference?
var isVideo = false
for media in message.media {
if let image = media as? TelegramMediaImage, let _ = largestImageRepresentation(image.representations) {
mediaReference = ImageMediaReference.standalone(media: image).abstract
break
} else if let file = media as? TelegramMediaFile, file.isVideo {
mediaReference = FileMediaReference.standalone(media: file).abstract
isVideo = true
break
}
}
if let mediaReference = mediaReference {
actions.append(.action(ContextMenuActionItem(text: isVideo ? chatPresentationInterfaceState.strings.Gallery_SaveVideo : chatPresentationInterfaceState.strings.Gallery_SaveImage, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in
let _ = (saveToCameraRoll(context: context, postbox: context.account.postbox, userLocation: .peer(message.id.peerId), mediaReference: mediaReference)
|> deliverOnMainQueue).start(completed: {
Queue.mainQueue().after(0.2) {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
controllerInteraction.presentControllerInCurrent(UndoOverlayController(presentationData: presentationData, content: .mediaSaved(text: isVideo ? presentationData.strings.Gallery_VideoSaved : presentationData.strings.Gallery_ImageSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return true }), nil)
}
})
f(.default)
})))
}
}
var downloadableMediaResourceInfos: [String] = []

View File

@ -1034,7 +1034,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
}
if item.associatedData.isCopyProtectionEnabled || item.message.isCopyProtected() {
needsShareButton = false
if hasCommentButton(item: item) {
} else {
needsShareButton = false
}
}
}

View File

@ -353,7 +353,10 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
let textFixedFont = Font.regular(fontSize)
let textBlockQuoteFont = Font.regular(fontSize)
let incoming = message.effectivelyIncoming(context.account.peerId)
var incoming = message.effectivelyIncoming(context.account.peerId)
if case .forwardedMessages = associatedData.subject {
incoming = false
}
var horizontalInsets = UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 10.0)
if displayLine {
@ -517,7 +520,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
}
if !skipStandardStatus {
if message.effectivelyIncoming(context.account.peerId) {
if incoming {
if isImage {
imageStatusType = .ImageIncoming
} else {
@ -609,7 +612,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
let automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, authorPeerId: message.author?.id, contactsPeerIds: associatedData.contactsPeerIds, media: file)
let statusType: ChatMessageDateAndStatusType
if message.effectivelyIncoming(context.account.peerId) {
if incoming {
statusType = .BubbleIncoming
} else {
if message.flags.contains(.Failed) {
@ -633,7 +636,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
forcedIsEdited: false,
file: file,
automaticDownload: automaticDownload,
incoming: message.effectivelyIncoming(context.account.peerId),
incoming: incoming,
isRecentActions: false,
forcedResourceStatus: associatedData.forcedResourceStatus,
dateAndStatusType: statusType,

View File

@ -53,7 +53,7 @@ private final class ChatMessageBubbleClippingNode: ASDisplayNode {
}
}
private func hasCommentButton(item: ChatMessageItem) -> Bool {
func hasCommentButton(item: ChatMessageItem) -> Bool {
let firstMessage = item.content.firstMessage
var hasDiscussion = false
@ -4541,6 +4541,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
for contentNode in self.contentNodes {
if let contentNode = contentNode as? ChatMessageFileBubbleContentNode {
return contentNode.interactiveFileNode.hasExpandedAudioTranscription
} else if let contentNode = contentNode as? ChatMessageInstantVideoBubbleContentNode {
return contentNode.hasExpandedAudioTranscription
}
}
return false

View File

@ -88,10 +88,15 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
var selectedContact: TelegramMediaContact?
for media in item.message.media {
if let media = media as? TelegramMediaContact {
selectedContact = media
selectedContact = media;
}
}
var incoming = item.message.effectivelyIncoming(item.context.account.peerId)
if case .forwardedMessages = item.associatedData.subject {
incoming = false
}
var titleString: NSAttributedString?
var textString: NSAttributedString?
var updatedContactInfo: String?
@ -149,8 +154,8 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
updatedContactInfo = info
titleString = NSAttributedString(string: displayName, font: titleFont, textColor: item.message.effectivelyIncoming(item.context.account.peerId) ? item.presentationData.theme.theme.chat.message.incoming.accentTextColor : item.presentationData.theme.theme.chat.message.outgoing.accentTextColor)
textString = NSAttributedString(string: info, font: textFont, textColor: item.message.effectivelyIncoming(item.context.account.peerId) ? item.presentationData.theme.theme.chat.message.incoming.primaryTextColor : item.presentationData.theme.theme.chat.message.outgoing.primaryTextColor)
titleString = NSAttributedString(string: displayName, font: titleFont, textColor: incoming ? item.presentationData.theme.theme.chat.message.incoming.accentTextColor : item.presentationData.theme.theme.chat.message.outgoing.accentTextColor)
textString = NSAttributedString(string: info, font: textFont, textColor: incoming ? item.presentationData.theme.theme.chat.message.incoming.primaryTextColor : item.presentationData.theme.theme.chat.message.outgoing.primaryTextColor)
} else {
updatedContactInfo = nil
}
@ -193,7 +198,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
let statusType: ChatMessageDateAndStatusType?
switch position {
case .linear(_, .None), .linear(_, .Neighbour(true, _, _)):
if item.message.effectivelyIncoming(item.context.account.peerId) {
if incoming {
statusType = .BubbleIncoming
} else {
if item.message.flags.contains(.Failed) {
@ -242,7 +247,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
let titleColor: UIColor
let titleHighlightedColor: UIColor
let avatarPlaceholderColor: UIColor
if item.message.effectivelyIncoming(item.context.account.peerId) {
if incoming {
buttonImage = PresentationResourcesChat.chatMessageAttachedContentButtonIncoming(item.presentationData.theme.theme)!
buttonHighlightedImage = PresentationResourcesChat.chatMessageAttachedContentHighlightedButtonIncoming(item.presentationData.theme.theme)!
titleColor = item.presentationData.theme.theme.chat.message.incoming.accentTextColor

View File

@ -102,7 +102,10 @@ class ChatMessageFileBubbleContentNode: ChatMessageBubbleContentNode {
}
}
let incoming = item.message.effectivelyIncoming(item.context.account.peerId)
var incoming = item.message.effectivelyIncoming(item.context.account.peerId)
if case .forwardedMessages = item.associatedData.subject {
incoming = false
}
let statusType: ChatMessageDateAndStatusType?
switch preparePosition {
case .linear(_, .None), .linear(_, .Neighbour(true, _, _)):
@ -135,7 +138,7 @@ class ChatMessageFileBubbleContentNode: ChatMessageBubbleContentNode {
forcedIsEdited: item.isItemEdited,
file: selectedFile!,
automaticDownload: automaticDownload,
incoming: item.message.effectivelyIncoming(item.context.account.peerId),
incoming: incoming,
isRecentActions: item.associatedData.isRecentActions,
forcedResourceStatus: item.associatedData.forcedResourceStatus,
dateAndStatusType: statusType,

View File

@ -23,6 +23,14 @@ class ChatMessageInstantVideoBubbleContentNode: ChatMessageBubbleContentNode {
private var audioTranscriptionState: AudioTranscriptionButtonComponent.TranscriptionState = .collapsed
var hasExpandedAudioTranscription: Bool {
if case .expanded = self.audioTranscriptionState {
return true
} else {
return false
}
}
override var visibility: ListViewItemNodeVisibility {
didSet {
var wasVisible = false

View File

@ -88,6 +88,11 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
}
}
var incoming = item.message.effectivelyIncoming(item.context.account.peerId)
if case .forwardedMessages = item.associatedData.subject {
incoming = false
}
let bubbleInsets: UIEdgeInsets
if case .color = item.presentationData.theme.wallpaper {
bubbleInsets = UIEdgeInsets()
@ -106,12 +111,12 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
imageSize = CGSize(width: fitWidth, height: floor(fitWidth * 0.5))
if let venue = selectedMedia.venue {
titleString = NSAttributedString(string: venue.title, font: titleFont, textColor: item.message.effectivelyIncoming(item.context.account.peerId) ? item.presentationData.theme.theme.chat.message.incoming.primaryTextColor : item.presentationData.theme.theme.chat.message.outgoing.primaryTextColor)
titleString = NSAttributedString(string: venue.title, font: titleFont, textColor: incoming ? item.presentationData.theme.theme.chat.message.incoming.primaryTextColor : item.presentationData.theme.theme.chat.message.outgoing.primaryTextColor)
if let address = venue.address, !address.isEmpty {
textString = NSAttributedString(string: address, font: textFont, textColor: item.message.effectivelyIncoming(item.context.account.peerId) ? item.presentationData.theme.theme.chat.message.incoming.secondaryTextColor : item.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor)
textString = NSAttributedString(string: address, font: textFont, textColor: incoming ? item.presentationData.theme.theme.chat.message.incoming.secondaryTextColor : item.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor)
}
} else {
textString = NSAttributedString(string: " ", font: textFont, textColor: item.message.effectivelyIncoming(item.context.account.peerId) ? item.presentationData.theme.theme.chat.message.incoming.secondaryTextColor : item.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor)
textString = NSAttributedString(string: " ", font: textFont, textColor: incoming ? item.presentationData.theme.theme.chat.message.incoming.secondaryTextColor : item.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor)
}
} else {
let fitWidth: CGFloat = min(constrainedSize.width, layoutConstants.image.maxDimensions.width)
@ -120,7 +125,7 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
}
if selectedMedia.liveBroadcastingTimeout != nil {
titleString = NSAttributedString(string: item.presentationData.strings.Message_LiveLocation, font: liveTitleFont, textColor: item.message.effectivelyIncoming(item.context.account.peerId) ? item.presentationData.theme.theme.chat.message.incoming.primaryTextColor : item.presentationData.theme.theme.chat.message.outgoing.primaryTextColor)
titleString = NSAttributedString(string: item.presentationData.strings.Message_LiveLocation, font: liveTitleFont, textColor: incoming ? item.presentationData.theme.theme.chat.message.incoming.primaryTextColor : item.presentationData.theme.theme.chat.message.outgoing.primaryTextColor)
}
} else {
imageSize = CGSize(width: 75.0, height: 75.0)
@ -211,7 +216,7 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
switch position {
case .linear(_, .None), .linear(_, .Neighbour(true, _, _)):
if selectedMedia?.venue != nil || activeLiveBroadcastingTimeout != nil {
if item.message.effectivelyIncoming(item.context.account.peerId) {
if incoming {
statusType = .BubbleIncoming
} else {
if item.message.flags.contains(.Failed) {
@ -223,7 +228,7 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
}
}
} else {
if item.message.effectivelyIncoming(item.context.account.peerId) {
if incoming {
statusType = .ImageIncoming
} else {
if item.message.flags.contains(.Failed) {
@ -283,7 +288,7 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
}
return (contentWidth, { boundingWidth in
let arguments = TransformImageArguments(corners: imageCorners, imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: item.message.effectivelyIncoming(item.context.account.peerId) ? item.presentationData.theme.theme.chat.message.incoming.mediaPlaceholderColor : item.presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor)
let arguments = TransformImageArguments(corners: imageCorners, imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: incoming ? item.presentationData.theme.theme.chat.message.incoming.mediaPlaceholderColor : item.presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor)
let imageLayoutSize = CGSize(width: imageSize.width + bubbleInsets.left + bubbleInsets.right, height: imageSize.height + bubbleInsets.top + bubbleInsets.bottom)
@ -379,8 +384,8 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
let timerSize = CGSize(width: 28.0, height: 28.0)
strongSelf.liveTimerNode?.frame = CGRect(origin: CGPoint(x: floor(imageFrame.maxX - 10.0 - timerSize.width), y: floor(imageFrame.maxY + 11.0)), size: timerSize)
let timerForegroundColor: UIColor = item.message.effectivelyIncoming(item.context.account.peerId) ? item.presentationData.theme.theme.chat.message.incoming.accentControlColor : item.presentationData.theme.theme.chat.message.outgoing.accentControlColor
let timerTextColor: UIColor = item.message.effectivelyIncoming(item.context.account.peerId) ? item.presentationData.theme.theme.chat.message.incoming.secondaryTextColor : item.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor
let timerForegroundColor: UIColor = incoming ? item.presentationData.theme.theme.chat.message.incoming.accentControlColor : item.presentationData.theme.theme.chat.message.outgoing.accentControlColor
let timerTextColor: UIColor = incoming ? item.presentationData.theme.theme.chat.message.incoming.secondaryTextColor : item.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor
strongSelf.liveTimerNode?.update(backgroundColor: timerForegroundColor.withAlphaComponent(0.4), foregroundColor: timerForegroundColor, textColor: timerTextColor, beginTimestamp: Double(item.message.timestamp), timeout: Double(activeLiveBroadcastingTimeout), strings: item.presentationData.strings)
if strongSelf.liveTextNode == nil {

View File

@ -505,7 +505,10 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
if item.associatedData.isCopyProtectionEnabled || item.message.isCopyProtected() {
needsShareButton = false
if hasCommentButton(item: item) {
} else {
needsShareButton = false
}
}
}

View File

@ -262,7 +262,7 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
strongSelf.context.sharedContext.mediaManager.setPlaylist(nil, type: type, control: SharedMediaPlayerControlAction.playback(.pause))
}
}
mediaAccessoryPanel.setRate = { [weak self] rate, fromMenu in
mediaAccessoryPanel.setRate = { [weak self] rate, changeType in
guard let strongSelf = self else {
return
}
@ -289,7 +289,7 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
}
return true
})
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
let text: String?
let rate: CGFloat?
@ -303,10 +303,25 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
text = presentationData.strings.Conversation_AudioRateTooltipSpeedUp
rate = 2.0
} else {
text = nil
rate = nil
let value = String(format: "%0.1f", baseRate.doubleValue)
text = presentationData.strings.Conversation_AudioRateTooltipCustom(value).string
if case let .sliderCommit(previousValue, newValue) = changeType {
if newValue > previousValue {
rate = .infinity
} else if newValue < previousValue {
rate = -.infinity
} else {
rate = nil
}
} else {
rate = nil
}
}
if let rate, let text, !fromMenu {
var showTooltip = true
if case .sliderChange = changeType {
showTooltip = false
}
if let rate, let text, showTooltip {
controller.present(
UndoOverlayController(
presentationData: presentationData,

View File

@ -1112,7 +1112,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
}))
}
if user.botInfo != nil, !user.isVerified {
if user.botInfo != nil {
items[.peerInfo]!.append(PeerInfoScreenActionItem(id: 6, text: presentationData.strings.ReportPeer_Report, action: {
interaction.openReport(.default)
}))

View File

@ -161,6 +161,9 @@ public final class SharedAccountContextImpl: SharedAccountContext {
public let currentMediaInputSettings: Atomic<MediaInputSettings>
private var mediaInputSettingsDisposable: Disposable?
public let currentStickerSettings: Atomic<StickerSettings>
private var stickerSettingsDisposable: Disposable?
private let automaticMediaDownloadSettingsDisposable = MetaDisposable()
private var immediateExperimentalUISettingsValue = Atomic<ExperimentalUISettings>(value: ExperimentalUISettings.defaultSettings)
@ -237,6 +240,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
self.currentAutomaticMediaDownloadSettings = initialPresentationDataAndSettings.automaticMediaDownloadSettings
self.currentAutodownloadSettings = Atomic(value: initialPresentationDataAndSettings.autodownloadSettings)
self.currentMediaInputSettings = Atomic(value: initialPresentationDataAndSettings.mediaInputSettings)
self.currentStickerSettings = Atomic(value: initialPresentationDataAndSettings.stickerSettings)
self.currentInAppNotificationSettings = Atomic(value: initialPresentationDataAndSettings.inAppNotificationSettings)
if automaticEnergyUsageShouldBeOnNow(settings: self.currentAutomaticMediaDownloadSettings) {
@ -354,6 +358,15 @@ public final class SharedAccountContextImpl: SharedAccountContext {
}
})
self.stickerSettingsDisposable = (self.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings])
|> deliverOnMainQueue).start(next: { [weak self] sharedData in
if let strongSelf = self {
if let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings]?.get(StickerSettings.self) {
let _ = strongSelf.currentStickerSettings.swap(settings)
}
}
})
let immediateExperimentalUISettingsValue = self.immediateExperimentalUISettingsValue
let _ = immediateExperimentalUISettingsValue.swap(initialPresentationDataAndSettings.experimentalUISettings)
self.experimentalUISettingsDisposable = (self.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.experimentalUISettings])

View File

@ -546,7 +546,11 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.iconCheckNode = nil
let animationName: String
if rate == 1.5 {
if rate == .infinity {
animationName = "anim_voicefast"
} else if rate == -.infinity {
animationName = "anim_voiceslow"
} else if rate == 1.5 {
animationName = "anim_voice1_5x"
} else if rate == 2.0 {
animationName = "anim_voice2x"

View File

@ -962,7 +962,8 @@ public func resolveUrlImpl(context: AccountContext, peerId: PeerId?, url: String
for scheme in schemes {
let basePrefix = scheme + basePath + "/"
var url = url
if (url.lowercased().hasPrefix(scheme) && (url.lowercased().hasSuffix(".\(basePath)") || url.lowercased().contains(".\(basePath)/"))) {
let lowercasedUrl = url.lowercased()
if (lowercasedUrl.hasPrefix(scheme) && (lowercasedUrl.hasSuffix(".\(basePath)") || lowercasedUrl.contains(".\(basePath)/") || lowercasedUrl.contains(".\(basePath)?"))) {
url = basePrefix + String(url[scheme.endIndex...]).replacingOccurrences(of: ".\(basePath)/", with: "").replacingOccurrences(of: ".\(basePath)", with: "")
}
if url.lowercased().hasPrefix(basePrefix) {