diff --git a/Random.txt b/Random.txt index 86eb369131..6eb2db792a 100644 --- a/Random.txt +++ b/Random.txt @@ -1 +1 @@ -4f0d2d13a70664d3029d9b97935089df0426fe53745965d175408752838b80dd \ No newline at end of file +61c0e29ede9b63175583b4609216b9c6083192c87d0e6ee0a42a5ff263b627dc diff --git a/Telegram/SiriIntents/IntentHandler.swift b/Telegram/SiriIntents/IntentHandler.swift index c71c957c1c..f492ef9884 100644 --- a/Telegram/SiriIntents/IntentHandler.swift +++ b/Telegram/SiriIntents/IntentHandler.swift @@ -22,9 +22,7 @@ private func setupSharedLogger(rootPath: String, path: String) { } } -private let accountAuxiliaryMethods = AccountAuxiliaryMethods(updatePeerChatInputState: { interfaceState, inputState -> PeerChatInterfaceState? in - return interfaceState -}, fetchResource: { account, resource, ranges, _ in +private let accountAuxiliaryMethods = AccountAuxiliaryMethods(fetchResource: { account, resource, ranges, _ in return nil }, fetchResourceMediaReferenceHash: { resource in return .single(nil) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 5d7dde7d67..818c215c06 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -100,7 +100,7 @@ "PUSH_MESSAGE_ROUND" = "%1$@|sent you a video message"; "PUSH_MESSAGE" = "%1$@|sent you a message"; "PUSH_MESSAGES_TEXT_1" = "sent you a message"; -"PUSH_MESSAGES_TEXT_any" = "sent you %2$d messages"; +"PUSH_MESSAGES_TEXT_any" = "sent you %d messages"; "PUSH_ALBUM" = "%1$@|sent you an album"; "PUSH_MESSAGE_FILES_TEXT_1" = "sent you a file"; "PUSH_MESSAGE_FILES_TEXT_any" = "sent you %d files"; @@ -168,7 +168,7 @@ "PUSH_CHAT_MESSAGE_PHOTOS_TEXT_any" = "{author} sent %d photos"; "PUSH_CHAT_MESSAGE_VIDEO" = "%2$@|%1$@ sent a video"; "PUSH_CHAT_MESSAGE_VIDEOS_TEXT_1" = "{author} sent a video"; -"PUSH_CHAT_MESSAGE_VIDEOS_TEXT_any" = "{text} sent %d videos"; +"PUSH_CHAT_MESSAGE_VIDEOS_TEXT_any" = "{author} sent %d videos"; "PUSH_CHAT_MESSAGE_ROUND" = "%2$@|%1$@ sent a video message"; "PUSH_CHAT_MESSAGE" = "%2$@|%1$@ sent a message"; "PUSH_CHAT_MESSAGES_TEXT_1" = "{author} sent a message"; @@ -849,7 +849,8 @@ "MessageTimer.ShortWeeks_3_10" = "%@w"; "MessageTimer.ShortWeeks_any" = "%@w"; "MessageTimer.ShortWeeks_many" = "%@w"; -"MessageTimer.ShortWeeks_0" = "%@w"; +"MessageTimer.ShortMonths_1" = "%@mo"; +"MessageTimer.ShortMonths_any" = "%@mo"; "Activity.UploadingPhoto" = "sending photo"; "Activity.UploadingVideo" = "sending video"; @@ -6563,3 +6564,12 @@ Sorry for the inconvenience."; "VoiceChat.VideoPreviewBackCamera" = "Back Camera"; "VoiceChat.VideoPreviewContinue" = "Continue"; "VoiceChat.VideoPreviewShareScreenInfo" = "Everything on your screen\nwill be shared"; + +"Gallery.SaveToGallery" = "Save to Gallery"; +"Gallery.VideoSaved" = "Video Saved"; +"Gallery.WaitForVideoDownoad" = "Please wait for the video to be fully downloaded."; + +"VoiceChat.VideoParticipantsLimitExceededExtended" = "The voice chat is over %@ members.\nNew participants only have access to audio stream. "; + +"PlaybackSpeed.Title" = "Playback Speed"; +"PlaybackSpeed.Normal" = "Normal"; diff --git a/Telegram/WidgetKitWidget/TodayViewController.swift b/Telegram/WidgetKitWidget/TodayViewController.swift index 84bc6339c3..11ce25c07c 100644 --- a/Telegram/WidgetKitWidget/TodayViewController.swift +++ b/Telegram/WidgetKitWidget/TodayViewController.swift @@ -50,9 +50,7 @@ private func setupSharedLogger(rootPath: String, path: String) { } } -private let accountAuxiliaryMethods = AccountAuxiliaryMethods(updatePeerChatInputState: { interfaceState, inputState -> PeerChatInterfaceState? in - return interfaceState -}, fetchResource: { account, resource, ranges, _ in +private let accountAuxiliaryMethods = AccountAuxiliaryMethods(fetchResource: { account, resource, ranges, _ in return nil }, fetchResourceMediaReferenceHash: { resource in return .single(nil) diff --git a/build-system/GenerateStrings/GenerateStrings.py b/build-system/GenerateStrings/GenerateStrings.py index 6730df0ee9..52a54af3b1 100644 --- a/build-system/GenerateStrings/GenerateStrings.py +++ b/build-system/GenerateStrings/GenerateStrings.py @@ -402,9 +402,15 @@ static _FormattedString * _Nonnull formatWithArgumentRanges( [result appendString:[string substringWithRange: NSMakeRange(currentLocation, range.range.location - currentLocation)]]; } + NSString *argument = nil; + if (range.index >= 0 && range.index < arguments.count) { + argument = arguments[range.index]; + } else { + argument = @"?"; + } [resultingRanges addObject:[[_FormattedStringRange alloc] initWithIndex:range.index - range:NSMakeRange(result.length, arguments[range.index].length)]]; - [result appendString:arguments[range.index]]; + range:NSMakeRange(result.length, argument.length)]]; + [result appendString:argument]; currentLocation = range.range.location + range.range.length; } diff --git a/build-system/Make/Make.py b/build-system/Make/Make.py index 1ab6a9176e..2325ac0911 100644 --- a/build-system/Make/Make.py +++ b/build-system/Make/Make.py @@ -249,6 +249,9 @@ class BazelCommandLine: '--disk_cache={path}'.format(path=self.cache_dir) ] + if self.continue_on_error: + combined_arguments += ['--keep_going'] + return combined_arguments def get_additional_build_arguments(self): @@ -365,6 +368,8 @@ def generate_project(arguments): elif arguments.cacheHost is not None: bazel_command_line.add_remote_cache(arguments.cacheHost) + bazel_command_line.set_continue_on_error(arguments.continueOnError) + resolve_configuration(bazel_command_line, arguments) bazel_command_line.set_build_number(arguments.buildNumber) @@ -388,7 +393,7 @@ def generate_project(arguments): disable_provisioning_profiles=disable_provisioning_profiles, generate_dsym=generate_dsym, configuration_path=bazel_command_line.configuration_path, - bazel_app_arguments=bazel_command_line.get_project_generation_arguments() + bazel_app_arguments=bazel_command_line.get_project_generation_arguments(), ) @@ -528,6 +533,13 @@ if __name__ == '__main__': ''' ) + generateProjectParser.add_argument( + '--continueOnError', + action='store_true', + default=False, + help='Continue build process after an error.', + ) + generateProjectParser.add_argument( '--disableProvisioningProfiles', action='store_true', diff --git a/buildbox/build-telegram.sh b/buildbox/build-telegram.sh index f2bc29956c..9a31feed47 100644 --- a/buildbox/build-telegram.sh +++ b/buildbox/build-telegram.sh @@ -4,8 +4,8 @@ set -e BUILD_TELEGRAM_VERSION="1" -MACOS_VERSION="10.15" -XCODE_VERSION="12.4" +MACOS_VERSION="11" +XCODE_VERSION="12.5.1" GUEST_SHELL="bash" VM_BASE_NAME="macos$(echo $MACOS_VERSION | sed -e 's/\.'/_/g)_Xcode$(echo $XCODE_VERSION | sed -e 's/\.'/_/g)" diff --git a/submodules/AccountContext/BUILD b/submodules/AccountContext/BUILD index 280d5f0d5e..b5558e05f4 100644 --- a/submodules/AccountContext/BUILD +++ b/submodules/AccountContext/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramAudio:TelegramAudio", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index d11135430b..be930524f7 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -145,10 +145,12 @@ public struct ChatAvailableMessageActionOptions: OptionSet { public struct ChatAvailableMessageActions { public var options: ChatAvailableMessageActionOptions public var banAuthor: Peer? + public var disableDelete: Bool - public init(options: ChatAvailableMessageActionOptions, banAuthor: Peer?) { + public init(options: ChatAvailableMessageActionOptions, banAuthor: Peer?, disableDelete: Bool) { self.options = options self.banAuthor = banAuthor + self.disableDelete = disableDelete } } @@ -287,9 +289,10 @@ public final class NavigateToChatControllerParams { public let animated: Bool public let options: NavigationAnimationOptions public let parentGroupId: PeerGroupId? + public let chatListFilter: ChatListFilterData? public let completion: (ChatController) -> Void - public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic = Atomic(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: Bool = false, keepStack: NavigateToChatKeepStack = .default, useExisting: Bool = true, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, activateMessageSearch: (ChatSearchDomain, String)? = nil, peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, reportReason: ReportReason? = nil, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, completion: @escaping (ChatController) -> Void = { _ in }) { + public init(navigationController: NavigationController, chatController: ChatController? = nil, context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic = Atomic(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, updateTextInputState: ChatTextInputState? = nil, activateInput: Bool = false, keepStack: NavigateToChatKeepStack = .default, useExisting: Bool = true, purposefulAction: (() -> Void)? = nil, scrollToEndIfExists: Bool = false, activateMessageSearch: (ChatSearchDomain, String)? = nil, peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, reportReason: ReportReason? = nil, animated: Bool = true, options: NavigationAnimationOptions = [], parentGroupId: PeerGroupId? = nil, chatListFilter: ChatListFilterData? = nil, completion: @escaping (ChatController) -> Void = { _ in }) { self.navigationController = navigationController self.chatController = chatController self.chatLocationContextHolder = chatLocationContextHolder @@ -310,6 +313,7 @@ public final class NavigateToChatControllerParams { self.animated = animated self.options = options self.parentGroupId = parentGroupId + self.chatListFilter = chatListFilter self.completion = completion } } @@ -532,7 +536,7 @@ public enum CreateGroupMode { case locatedGroup(latitude: Double, longitude: Double, address: String?) } -public protocol AppLockContext: class { +public protocol AppLockContext: AnyObject { var invalidAttempts: Signal { get } var autolockDeadline: Signal { get } @@ -541,10 +545,10 @@ public protocol AppLockContext: class { func failedUnlockAttempt() } -public protocol RecentSessionsController: class { +public protocol RecentSessionsController: AnyObject { } -public protocol SharedAccountContext: class { +public protocol SharedAccountContext: AnyObject { var sharedContainerPath: String { get } var basePath: String { get } var mainWindow: Window1? { get } @@ -699,16 +703,16 @@ public final class TonContext { public protocol ComposeController: ViewController { } -public protocol ChatLocationContextHolder: class { +public protocol ChatLocationContextHolder: AnyObject { } -public protocol AccountGroupCallContext: class { +public protocol AccountGroupCallContext: AnyObject { } -public protocol AccountGroupCallContextCache: class { +public protocol AccountGroupCallContextCache: AnyObject { } -public protocol AccountContext: class { +public protocol AccountContext: AnyObject { var sharedContext: SharedAccountContext { get } var account: Account { get } var engine: TelegramEngine { get } @@ -736,6 +740,6 @@ public protocol AccountContext: class { func applyMaxReadIndex(for location: ChatLocation, contextHolder: Atomic, messageIndex: MessageIndex) func scheduleGroupCall(peerId: PeerId) - func joinGroupCall(peerId: PeerId, invite: String?, requestJoinAsPeerId: ((@escaping (PeerId?) -> Void) -> Void)?, activeCall: CachedChannelData.ActiveCall) + func joinGroupCall(peerId: PeerId, invite: String?, requestJoinAsPeerId: ((@escaping (PeerId?) -> Void) -> Void)?, activeCall: EngineGroupCallDescription) func requestCall(peerId: PeerId, isVideo: Bool, completion: @escaping () -> Void) } diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index d0919530dd..23a5bbf8b6 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -130,7 +130,7 @@ public enum ChatControllerInteractionNavigateToPeer { case withBotStartPayload(ChatControllerInitialBotStart) } -public struct ChatTextInputState: PostboxCoding, Equatable { +public struct ChatTextInputState: Codable, Equatable { public let inputText: NSAttributedString public let selectionRange: Range @@ -153,29 +153,40 @@ public struct ChatTextInputState: PostboxCoding, Equatable { let length = inputText.length self.selectionRange = length ..< length } - - public init(decoder: PostboxDecoder) { - self.inputText = ((decoder.decodeObjectForKey("at", decoder: { ChatTextInputStateText(decoder: $0) }) as? ChatTextInputStateText) ?? ChatTextInputStateText()).attributedText() - self.selectionRange = Int(decoder.decodeInt32ForKey("as0", orElse: 0)) ..< Int(decoder.decodeInt32ForKey("as1", orElse: 0)) + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + self.inputText = ((try? container.decode(ChatTextInputStateText.self, forKey: "at")) ?? ChatTextInputStateText()).attributedText() + let rangeFrom = (try? container.decode(Int32.self, forKey: "as0")) ?? 0 + let rangeTo = (try? container.decode(Int32.self, forKey: "as1")) ?? 0 + if rangeFrom <= rangeTo { + self.selectionRange = Int(rangeFrom) ..< Int(rangeTo) + } else { + let length = self.inputText.length + self.selectionRange = length ..< length + } } - - public func encode(_ encoder: PostboxEncoder) { - encoder.encodeObject(ChatTextInputStateText(attributedText: self.inputText), forKey: "at") - - encoder.encodeInt32(Int32(self.selectionRange.lowerBound), forKey: "as0") - encoder.encodeInt32(Int32(self.selectionRange.upperBound), forKey: "as1") + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + try container.encode(ChatTextInputStateText(attributedText: self.inputText), forKey: "at") + try container.encode(Int32(self.selectionRange.lowerBound), forKey: "as0") + try container.encode(Int32(self.selectionRange.upperBound), forKey: "as1") } } -public enum ChatTextInputStateTextAttributeType: PostboxCoding, Equatable { +public enum ChatTextInputStateTextAttributeType: Codable, Equatable { case bold case italic case monospace case textMention(EnginePeer.Id) case textUrl(String) - - public init(decoder: PostboxDecoder) { - switch decoder.decodeInt32ForKey("t", orElse: 0) { + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + + switch (try? container.decode(Int32.self, forKey: "t")) ?? 0 { case 0: self = .bold case 1: @@ -183,69 +194,37 @@ public enum ChatTextInputStateTextAttributeType: PostboxCoding, Equatable { case 2: self = .monospace case 3: - self = .textMention(EnginePeer.Id(decoder.decodeInt64ForKey("peerId", orElse: 0))) + let peerId = (try? container.decode(Int64.self, forKey: "peerId")) ?? 0 + self = .textMention(EnginePeer.Id(peerId)) case 4: - self = .textUrl(decoder.decodeStringForKey("url", orElse: "")) + let url = (try? container.decode(String.self, forKey: "url")) ?? "" + self = .textUrl(url) default: assertionFailure() self = .bold } } - - public func encode(_ encoder: PostboxEncoder) { + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) switch self { case .bold: - encoder.encodeInt32(0, forKey: "t") + try container.encode(0 as Int32, forKey: "t") case .italic: - encoder.encodeInt32(1, forKey: "t") + try container.encode(1 as Int32, forKey: "t") case .monospace: - encoder.encodeInt32(2, forKey: "t") + try container.encode(2 as Int32, forKey: "t") case let .textMention(id): - encoder.encodeInt32(3, forKey: "t") - encoder.encodeInt64(id.toInt64(), forKey: "peerId") + try container.encode(3 as Int32, forKey: "t") + try container.encode(id.toInt64(), forKey: "peerId") case let .textUrl(url): - encoder.encodeInt32(4, forKey: "t") - encoder.encodeString(url, forKey: "url") - } - } - - public static func ==(lhs: ChatTextInputStateTextAttributeType, rhs: ChatTextInputStateTextAttributeType) -> Bool { - switch lhs { - case .bold: - if case .bold = rhs { - return true - } else { - return false - } - case .italic: - if case .italic = rhs { - return true - } else { - return false - } - case .monospace: - if case .monospace = rhs { - return true - } else { - return false - } - case let .textMention(id): - if case .textMention(id) = rhs { - return true - } else { - return false - } - case let .textUrl(url): - if case .textUrl(url) = rhs { - return true - } else { - return false - } + try container.encode(4 as Int32, forKey: "t") + try container.encode(url, forKey: "url") } } } -public struct ChatTextInputStateTextAttribute: PostboxCoding, Equatable { +public struct ChatTextInputStateTextAttribute: Codable, Equatable { public let type: ChatTextInputStateTextAttributeType public let range: Range @@ -253,16 +232,23 @@ public struct ChatTextInputStateTextAttribute: PostboxCoding, Equatable { self.type = type self.range = range } - - public init(decoder: PostboxDecoder) { - self.type = decoder.decodeObjectForKey("type", decoder: { ChatTextInputStateTextAttributeType(decoder: $0) }) as! ChatTextInputStateTextAttributeType - self.range = Int(decoder.decodeInt32ForKey("range0", orElse: 0)) ..< Int(decoder.decodeInt32ForKey("range1", orElse: 0)) + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + self.type = try container.decode(ChatTextInputStateTextAttributeType.self, forKey: "type") + let rangeFrom = (try? container.decode(Int32.self, forKey: "range0")) ?? 0 + let rangeTo = (try? container.decode(Int32.self, forKey: "range1")) ?? 0 + + self.range = Int(rangeFrom) ..< Int(rangeTo) } - - public func encode(_ encoder: PostboxEncoder) { - encoder.encodeObject(self.type, forKey: "type") - encoder.encodeInt32(Int32(self.range.lowerBound), forKey: "range0") - encoder.encodeInt32(Int32(self.range.upperBound), forKey: "range1") + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + try container.encode(self.type, forKey: "type") + + try container.encode(Int32(self.range.lowerBound), forKey: "range0") + try container.encode(Int32(self.range.upperBound), forKey: "range1") } public static func ==(lhs: ChatTextInputStateTextAttribute, rhs: ChatTextInputStateTextAttribute) -> Bool { @@ -270,7 +256,7 @@ public struct ChatTextInputStateTextAttribute: PostboxCoding, Equatable { } } -public struct ChatTextInputStateText: PostboxCoding, Equatable { +public struct ChatTextInputStateText: Codable, Equatable { public let text: String public let attributes: [ChatTextInputStateTextAttribute] @@ -304,15 +290,17 @@ public struct ChatTextInputStateText: PostboxCoding, Equatable { }) self.attributes = parsedAttributes } - - public init(decoder: PostboxDecoder) { - self.text = decoder.decodeStringForKey("text", orElse: "") - self.attributes = decoder.decodeObjectArrayWithDecoderForKey("attributes") + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + self.text = (try? container.decode(String.self, forKey: "text")) ?? "" + self.attributes = (try? container.decode([ChatTextInputStateTextAttribute].self, forKey: "attributes")) ?? [] } - - public func encode(_ encoder: PostboxEncoder) { - encoder.encodeString(self.text, forKey: "text") - encoder.encodeObjectArray(self.attributes, forKey: "attributes") + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + try container.encode(self.text, forKey: "text") + try container.encode(self.attributes, forKey: "attributes") } static public func ==(lhs: ChatTextInputStateText, rhs: ChatTextInputStateText) -> Bool { @@ -351,34 +339,6 @@ public enum ChatControllerPresentationMode: Equatable { case inline(NavigationController?) } -public final class ChatEmbeddedInterfaceState: PeerChatListEmbeddedInterfaceState { - public let timestamp: Int32 - public let text: NSAttributedString - - public init(timestamp: Int32, text: NSAttributedString) { - self.timestamp = timestamp - self.text = text - } - - public init(decoder: PostboxDecoder) { - self.timestamp = decoder.decodeInt32ForKey("d", orElse: 0) - self.text = ((decoder.decodeObjectForKey("at", decoder: { ChatTextInputStateText(decoder: $0) }) as? ChatTextInputStateText) ?? ChatTextInputStateText()).attributedText() - } - - public func encode(_ encoder: PostboxEncoder) { - encoder.encodeInt32(self.timestamp, forKey: "d") - encoder.encodeObject(ChatTextInputStateText(attributedText: self.text), forKey: "at") - } - - public func isEqual(to: PeerChatListEmbeddedInterfaceState) -> Bool { - if let to = to as? ChatEmbeddedInterfaceState { - return self.timestamp == to.timestamp && self.text.isEqual(to: to.text) - } else { - return false - } - } -} - public enum ChatPresentationInputQueryResult: Equatable { case stickers([FoundStickerItem]) case hashtags([String]) diff --git a/submodules/AccountContext/Sources/DeviceContactDataManager.swift b/submodules/AccountContext/Sources/DeviceContactDataManager.swift index 10db927b98..ed4d87ee3b 100644 --- a/submodules/AccountContext/Sources/DeviceContactDataManager.swift +++ b/submodules/AccountContext/Sources/DeviceContactDataManager.swift @@ -6,7 +6,7 @@ import SwiftSignalKit public typealias DeviceContactStableId = String -public protocol DeviceContactDataManager: class { +public protocol DeviceContactDataManager: AnyObject { func personNameDisplayOrder() -> Signal func basicData() -> Signal<[DeviceContactStableId: DeviceContactBasicData], NoError> func basicDataForNormalizedPhoneNumber(_ normalizedNumber: DeviceContactNormalizedPhoneNumber) -> Signal<[(DeviceContactStableId, DeviceContactBasicData)], NoError> diff --git a/submodules/AccountContext/Sources/DownloadedMediaStoreManager.swift b/submodules/AccountContext/Sources/DownloadedMediaStoreManager.swift index 05653f8d6d..1b2a9cce55 100644 --- a/submodules/AccountContext/Sources/DownloadedMediaStoreManager.swift +++ b/submodules/AccountContext/Sources/DownloadedMediaStoreManager.swift @@ -4,7 +4,7 @@ import Postbox import TelegramUIPreferences import SwiftSignalKit -public protocol DownloadedMediaStoreManager: class { +public protocol DownloadedMediaStoreManager: AnyObject { func store(_ media: AnyMediaReference, timestamp: Int32, peerType: MediaAutoDownloadPeerType) } diff --git a/submodules/AccountContext/Sources/IsMediaStreamable.swift b/submodules/AccountContext/Sources/IsMediaStreamable.swift index 548fa2f8d6..0c93c9ceb8 100644 --- a/submodules/AccountContext/Sources/IsMediaStreamable.swift +++ b/submodules/AccountContext/Sources/IsMediaStreamable.swift @@ -18,8 +18,8 @@ public func isMediaStreamable(message: Message, media: TelegramMediaFile) -> Boo return false } for attribute in media.attributes { - if case let .Video(video) = attribute { - if video.flags.contains(.supportsStreaming) { + if case let .Video(_, _, flags) = attribute { + if flags.contains(.supportsStreaming) { return true } break @@ -41,8 +41,8 @@ public func isMediaStreamable(media: TelegramMediaFile) -> Bool { return false } for attribute in media.attributes { - if case let .Video(video) = attribute { - if video.flags.contains(.supportsStreaming) { + if case let .Video(_, _, flags) = attribute { + if flags.contains(.supportsStreaming) { return true } break diff --git a/submodules/AccountContext/Sources/MediaManager.swift b/submodules/AccountContext/Sources/MediaManager.swift index e3ddeddff4..35d6a5bcda 100644 --- a/submodules/AccountContext/Sources/MediaManager.swift +++ b/submodules/AccountContext/Sources/MediaManager.swift @@ -132,7 +132,7 @@ public enum MediaManagerPlayerType { case file } -public protocol MediaManager: class { +public protocol MediaManager: AnyObject { var audioSession: ManagedAudioSession { get } var galleryHiddenMediaManager: GalleryHiddenMediaManager { get } var universalVideoManager: UniversalVideoManager { get } @@ -177,11 +177,11 @@ public enum GalleryHiddenMediaId: Hashable { } } -public protocol GalleryHiddenMediaTarget: class { +public protocol GalleryHiddenMediaTarget: AnyObject { func getTransitionInfo(messageId: MessageId, media: Media) -> ((UIView) -> Void, ASDisplayNode, () -> (UIView?, UIView?))? } -public protocol GalleryHiddenMediaManager: class { +public protocol GalleryHiddenMediaManager: AnyObject { func hiddenIds() -> Signal, NoError> func addSource(_ signal: Signal) -> Int func removeSource(_ index: Int) @@ -190,7 +190,7 @@ public protocol GalleryHiddenMediaManager: class { func findTarget(messageId: MessageId, media: Media) -> ((UIView) -> Void, ASDisplayNode, () -> (UIView?, UIView?))? } -public protocol UniversalVideoManager: class { +public protocol UniversalVideoManager: AnyObject { func attachUniversalVideoContent(content: UniversalVideoContent, priority: UniversalVideoPriority, create: () -> UniversalVideoContentNode & ASDisplayNode, update: @escaping (((UniversalVideoContentNode & ASDisplayNode), Bool)?) -> Void) -> (AnyHashable, Int32) func detachUniversalVideoContent(id: AnyHashable, index: Int32) func withUniversalVideoContent(id: AnyHashable, _ f: ((UniversalVideoContentNode & ASDisplayNode)?) -> Void) @@ -218,7 +218,7 @@ public struct RecordedAudioData { } } -public protocol ManagedAudioRecorder: class { +public protocol ManagedAudioRecorder: AnyObject { var beginWithTone: Bool { get } var micLevel: Signal { get } var recordingState: Signal { get } diff --git a/submodules/AccountContext/Sources/OverlayAudioPlayerController.swift b/submodules/AccountContext/Sources/OverlayAudioPlayerController.swift index a3257e0740..b08d747ca4 100644 --- a/submodules/AccountContext/Sources/OverlayAudioPlayerController.swift +++ b/submodules/AccountContext/Sources/OverlayAudioPlayerController.swift @@ -1,5 +1,5 @@ import Foundation -public protocol OverlayAudioPlayerController: class { +public protocol OverlayAudioPlayerController: AnyObject { } diff --git a/submodules/AccountContext/Sources/OverlayMediaManager.swift b/submodules/AccountContext/Sources/OverlayMediaManager.swift index 45d7eec061..e5f860c704 100644 --- a/submodules/AccountContext/Sources/OverlayMediaManager.swift +++ b/submodules/AccountContext/Sources/OverlayMediaManager.swift @@ -15,7 +15,7 @@ public final class OverlayMediaControllerEmbeddingItem { } } -public protocol OverlayMediaController: class { +public protocol OverlayMediaController: AnyObject { var updatePossibleEmbeddingItem: ((OverlayMediaControllerEmbeddingItem?) -> Void)? { get set } var embedPossibleEmbeddingItem: ((OverlayMediaControllerEmbeddingItem) -> Bool)? { get set } diff --git a/submodules/AccountContext/Sources/PeerSelectionController.swift b/submodules/AccountContext/Sources/PeerSelectionController.swift index 21d396b434..e4f30c083b 100644 --- a/submodules/AccountContext/Sources/PeerSelectionController.swift +++ b/submodules/AccountContext/Sources/PeerSelectionController.swift @@ -40,8 +40,9 @@ public final class PeerSelectionControllerParams { public let createNewGroup: (() -> Void)? public let pretendPresentedInModal: Bool public let multipleSelection: Bool + public let forwardedMessagesCount: Int - public init(context: AccountContext, filter: ChatListNodePeersFilter = [.onlyWriteable], hasChatListSelector: Bool = true, hasContactSelector: Bool = true, hasGlobalSearch: Bool = true, title: String? = nil, attemptSelection: ((Peer) -> Void)? = nil, createNewGroup: (() -> Void)? = nil, pretendPresentedInModal: Bool = false, multipleSelection: Bool = false) { + public init(context: AccountContext, filter: ChatListNodePeersFilter = [.onlyWriteable], hasChatListSelector: Bool = true, hasContactSelector: Bool = true, hasGlobalSearch: Bool = true, title: String? = nil, attemptSelection: ((Peer) -> Void)? = nil, createNewGroup: (() -> Void)? = nil, pretendPresentedInModal: Bool = false, multipleSelection: Bool = false, forwardedMessagesCount: Int = 0) { self.context = context self.filter = filter self.hasChatListSelector = hasChatListSelector @@ -52,6 +53,7 @@ public final class PeerSelectionControllerParams { self.createNewGroup = createNewGroup self.pretendPresentedInModal = pretendPresentedInModal self.multipleSelection = multipleSelection + self.forwardedMessagesCount = forwardedMessagesCount } } @@ -63,7 +65,7 @@ public enum PeerSelectionControllerSendMode { public protocol PeerSelectionController: ViewController { var peerSelected: ((Peer) -> Void)? { get set } - var multiplePeersSelected: (([Peer], NSAttributedString, PeerSelectionControllerSendMode) -> Void)? { get set } + var multiplePeersSelected: (([Peer], [PeerId: Peer], NSAttributedString, PeerSelectionControllerSendMode) -> Void)? { get set } var inProgress: Bool { get set } var customDismiss: (() -> Void)? { get set } } diff --git a/submodules/AccountContext/Sources/PresentationCallManager.swift b/submodules/AccountContext/Sources/PresentationCallManager.swift index 557d209f39..6eedded2b2 100644 --- a/submodules/AccountContext/Sources/PresentationCallManager.swift +++ b/submodules/AccountContext/Sources/PresentationCallManager.swift @@ -131,7 +131,7 @@ public final class PresentationCallVideoView { } } -public protocol PresentationCall: class { +public protocol PresentationCall: AnyObject { var context: AccountContext { get } var isIntegratedWithCallKit: Bool { get } var internalId: CallSessionInternalId { get } @@ -168,6 +168,26 @@ public protocol PresentationCall: class { func makeOutgoingVideoView(completion: @escaping (PresentationCallVideoView?) -> Void) } +public struct VoiceChatConfiguration { + public static var defaultValue: VoiceChatConfiguration { + return VoiceChatConfiguration(videoParticipantsMaxCount: 30) + } + + public let videoParticipantsMaxCount: Int32 + + fileprivate init(videoParticipantsMaxCount: Int32) { + self.videoParticipantsMaxCount = videoParticipantsMaxCount + } + + public static func with(appConfiguration: AppConfiguration) -> VoiceChatConfiguration { + if let data = appConfiguration.data, let value = data["groupcall_video_participants_max"] as? Double { + return VoiceChatConfiguration(videoParticipantsMaxCount: Int32(value)) + } else { + return .defaultValue + } + } +} + public struct PresentationGroupCallState: Equatable { public enum NetworkState { case connecting @@ -191,6 +211,7 @@ public struct PresentationGroupCallState: Equatable { public var scheduleTimestamp: Int32? public var subscribedToScheduled: Bool public var isVideoEnabled: Bool + public var isVideoWatchersLimitReached: Bool public init( myPeerId: PeerId, @@ -204,7 +225,8 @@ public struct PresentationGroupCallState: Equatable { raisedHand: Bool, scheduleTimestamp: Int32?, subscribedToScheduled: Bool, - isVideoEnabled: Bool + isVideoEnabled: Bool, + isVideoWatchersLimitReached: Bool ) { self.myPeerId = myPeerId self.networkState = networkState @@ -218,6 +240,7 @@ public struct PresentationGroupCallState: Equatable { self.scheduleTimestamp = scheduleTimestamp self.subscribedToScheduled = subscribedToScheduled self.isVideoEnabled = isVideoEnabled + self.isVideoWatchersLimitReached = isVideoWatchersLimitReached } } @@ -368,7 +391,7 @@ public extension GroupCallParticipantsContext.Participant { } } -public protocol PresentationGroupCall: class { +public protocol PresentationGroupCall: AnyObject { var account: Account { get } var accountContext: AccountContext { get } var internalId: CallSessionInternalId { get } @@ -436,11 +459,11 @@ public protocol PresentationGroupCall: class { func loadMoreMembers(token: String) } -public protocol PresentationCallManager: class { +public protocol PresentationCallManager: AnyObject { var currentCallSignal: Signal { get } var currentGroupCallSignal: Signal { get } func requestCall(context: AccountContext, peerId: PeerId, isVideo: Bool, endCurrentIfAny: Bool) -> RequestCallResult - func joinGroupCall(context: AccountContext, peerId: PeerId, invite: String?, requestJoinAsPeerId: ((@escaping (PeerId?) -> Void) -> Void)?, initialCall: CachedChannelData.ActiveCall, endCurrentIfAny: Bool) -> JoinGroupCallManagerResult + func joinGroupCall(context: AccountContext, peerId: PeerId, invite: String?, requestJoinAsPeerId: ((@escaping (PeerId?) -> Void) -> Void)?, initialCall: EngineGroupCallDescription, endCurrentIfAny: Bool) -> JoinGroupCallManagerResult func scheduleGroupCall(context: AccountContext, peerId: PeerId, endCurrentIfAny: Bool) -> RequestScheduleGroupCallResult } diff --git a/submodules/AccountContext/Sources/SharedMediaPlayer.swift b/submodules/AccountContext/Sources/SharedMediaPlayer.swift index 55285fd463..fee597a187 100644 --- a/submodules/AccountContext/Sources/SharedMediaPlayer.swift +++ b/submodules/AccountContext/Sources/SharedMediaPlayer.swift @@ -156,7 +156,7 @@ public protocol SharedMediaPlaylistLocation { func isEqual(to: SharedMediaPlaylistLocation) -> Bool } -public protocol SharedMediaPlaylist: class { +public protocol SharedMediaPlaylist: AnyObject { var id: SharedMediaPlaylistId { get } var location: SharedMediaPlaylistLocation { get } var state: Signal { get } diff --git a/submodules/AccountContext/Sources/ThemeUpdateManager.swift b/submodules/AccountContext/Sources/ThemeUpdateManager.swift index 1e2f7bef56..093f5b8029 100644 --- a/submodules/AccountContext/Sources/ThemeUpdateManager.swift +++ b/submodules/AccountContext/Sources/ThemeUpdateManager.swift @@ -3,5 +3,5 @@ import SwiftSignalKit import TelegramCore import TelegramPresentationData -public protocol ThemeUpdateManager: class { +public protocol ThemeUpdateManager: AnyObject { } diff --git a/submodules/AccountContext/Sources/UniversalVideoNode.swift b/submodules/AccountContext/Sources/UniversalVideoNode.swift index 8c14428978..0c7e9aa193 100644 --- a/submodules/AccountContext/Sources/UniversalVideoNode.swift +++ b/submodules/AccountContext/Sources/UniversalVideoNode.swift @@ -8,7 +8,7 @@ import Display import TelegramAudio import UniversalMediaPlayer -public protocol UniversalVideoContentNode: class { +public protocol UniversalVideoContentNode: AnyObject { var ready: Signal { get } var status: Signal { get } var bufferingStatus: Signal<(IndexSet, Int)?, NoError> { get } @@ -47,7 +47,7 @@ public extension UniversalVideoContent { } } -public protocol UniversalVideoDecoration: class { +public protocol UniversalVideoDecoration: AnyObject { var backgroundNode: ASDisplayNode? { get } var contentContainerNode: ASDisplayNode { get } var foregroundNode: ASDisplayNode? { get } diff --git a/submodules/AccountContext/Sources/WallpaperUploadManager.swift b/submodules/AccountContext/Sources/WallpaperUploadManager.swift index 5c3a67f351..3787b2fe55 100644 --- a/submodules/AccountContext/Sources/WallpaperUploadManager.swift +++ b/submodules/AccountContext/Sources/WallpaperUploadManager.swift @@ -18,7 +18,7 @@ public enum WallpaperUploadManagerStatus { } } -public protocol WallpaperUploadManager: class { +public protocol WallpaperUploadManager: AnyObject { func stateSignal() -> Signal func presentationDataUpdated(_ presentationData: PresentationData) } diff --git a/submodules/AccountContext/Sources/WatchManager.swift b/submodules/AccountContext/Sources/WatchManager.swift index b7574c28f8..bf9b931d94 100644 --- a/submodules/AccountContext/Sources/WatchManager.swift +++ b/submodules/AccountContext/Sources/WatchManager.swift @@ -16,7 +16,7 @@ public struct WatchRunningTasks: Equatable { } } -public protocol WatchManager: class { +public protocol WatchManager: AnyObject { var watchAppInstalled: Signal { get } var navigateToMessageRequested: Signal { get } var runningTasks: Signal { get } diff --git a/submodules/AccountUtils/BUILD b/submodules/AccountUtils/BUILD index 0d89e02324..56991b2885 100644 --- a/submodules/AccountUtils/BUILD +++ b/submodules/AccountUtils/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/Postbox:Postbox", diff --git a/submodules/ActionSheetPeerItem/BUILD b/submodules/ActionSheetPeerItem/BUILD index a80d9284a8..cacfba4843 100644 --- a/submodules/ActionSheetPeerItem/BUILD +++ b/submodules/ActionSheetPeerItem/BUILD @@ -6,8 +6,10 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ - "//submodules/Postbox:Postbox", "//submodules/TelegramCore:TelegramCore", "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/ActionSheetPeerItem/Sources/ActionSheetPeerItem.swift b/submodules/ActionSheetPeerItem/Sources/ActionSheetPeerItem.swift index 0ebacb71a0..ca81c18456 100644 --- a/submodules/ActionSheetPeerItem/Sources/ActionSheetPeerItem.swift +++ b/submodules/ActionSheetPeerItem/Sources/ActionSheetPeerItem.swift @@ -3,21 +3,20 @@ import UIKit import AsyncDisplayKit import Display import TelegramCore -import Postbox import TelegramPresentationData import AvatarNode import AccountContext public class ActionSheetPeerItem: ActionSheetItem { public let context: AccountContext - public let peer: Peer + public let peer: EnginePeer public let theme: PresentationTheme public let title: String public let isSelected: Bool public let strings: PresentationStrings public let action: () -> Void - public init(context: AccountContext, peer: Peer, title: String, isSelected: Bool, strings: PresentationStrings, theme: PresentationTheme, action: @escaping () -> Void) { + public init(context: AccountContext, peer: EnginePeer, title: String, isSelected: Bool, strings: PresentationStrings, theme: PresentationTheme, action: @escaping () -> Void) { self.context = context self.peer = peer self.title = title diff --git a/submodules/ActivityIndicator/BUILD b/submodules/ActivityIndicator/BUILD index 08c019b637..38c3fb9888 100644 --- a/submodules/ActivityIndicator/BUILD +++ b/submodules/ActivityIndicator/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Display:Display", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/AlertUI/BUILD b/submodules/AlertUI/BUILD index f9b4dacc69..9f7f33fd96 100644 --- a/submodules/AlertUI/BUILD +++ b/submodules/AlertUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Display:Display", ], diff --git a/submodules/AnimatedAvatarSetNode/BUILD b/submodules/AnimatedAvatarSetNode/BUILD index 3e13b3823d..21321e8fc3 100644 --- a/submodules/AnimatedAvatarSetNode/BUILD +++ b/submodules/AnimatedAvatarSetNode/BUILD @@ -6,11 +6,13 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Display:Display", "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", - "//submodules/Postbox:Postbox", "//submodules/TelegramCore:TelegramCore", "//submodules/AccountContext:AccountContext", "//submodules/AvatarNode:AvatarNode", diff --git a/submodules/AnimatedAvatarSetNode/Sources/AnimatedAvatarSetNode.swift b/submodules/AnimatedAvatarSetNode/Sources/AnimatedAvatarSetNode.swift index f4d240578c..fcf565bc4b 100644 --- a/submodules/AnimatedAvatarSetNode/Sources/AnimatedAvatarSetNode.swift +++ b/submodules/AnimatedAvatarSetNode/Sources/AnimatedAvatarSetNode.swift @@ -4,7 +4,6 @@ import Display import AsyncDisplayKit import AvatarNode import SwiftSignalKit -import Postbox import TelegramCore import AccountContext import AudioBlob @@ -13,12 +12,12 @@ public final class AnimatedAvatarSetContext { public final class Content { fileprivate final class Item { fileprivate struct Key: Hashable { - var peerId: PeerId + var peerId: EnginePeer.Id } - fileprivate let peer: Peer + fileprivate let peer: EnginePeer - fileprivate init(peer: Peer) { + fileprivate init(peer: EnginePeer) { self.peer = peer } } @@ -31,20 +30,20 @@ public final class AnimatedAvatarSetContext { } private final class ItemState { - let peer: Peer + let peer: EnginePeer - init(peer: Peer) { + init(peer: EnginePeer) { self.peer = peer } } - private var peers: [Peer] = [] - private var itemStates: [PeerId: ItemState] = [:] + private var peers: [EnginePeer] = [] + private var itemStates: [EnginePeer.Id: ItemState] = [:] public init() { } - public func update(peers: [Peer], animated: Bool) -> Content { + public func update(peers: [EnginePeer], animated: Bool) -> Content { var items: [(Content.Item.Key, Content.Item)] = [] for peer in peers { items.append((Content.Item.Key(peerId: peer.id), Content.Item(peer: peer))) @@ -63,7 +62,7 @@ private final class ContentNode: ASDisplayNode { private var disposable: Disposable? - init(context: AccountContext, peer: Peer, synchronousLoad: Bool) { + init(context: AccountContext, peer: EnginePeer, synchronousLoad: Bool) { self.unclippedNode = ASImageNode() self.clippedNode = ASImageNode() @@ -72,7 +71,7 @@ private final class ContentNode: ASDisplayNode { self.addSubnode(self.unclippedNode) self.addSubnode(self.clippedNode) - if let representation = peer.smallProfileImage, let signal = peerAvatarImage(account: context.account, peerReference: PeerReference(peer), authorOfMessage: nil, representation: representation, displayDimensions: CGSize(width: 30.0, height: 30.0), synchronousLoad: synchronousLoad) { + if let representation = peer.smallProfileImage, let signal = peerAvatarImage(account: context.account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: representation, displayDimensions: CGSize(width: 30.0, height: 30.0), synchronousLoad: synchronousLoad) { let image = generateImage(CGSize(width: 30.0, height: 30.0), rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) context.setFillColor(UIColor.lightGray.cgColor) @@ -252,8 +251,7 @@ public final class AnimatedAvatarSetNode: ASDisplayNode { return CGSize(width: contentWidth, height: contentHeight) } - public func updateAudioLevels(color: UIColor, backgroundColor: UIColor, levels: [PeerId: Float]) { - //print("updateAudioLevels visible: \(self.contentNodes.keys.map(\.peerId.id)) data: \(levels)") + public func updateAudioLevels(color: UIColor, backgroundColor: UIColor, levels: [EnginePeer.Id: Float]) { for (key, itemNode) in self.contentNodes { if let value = levels[key.peerId] { itemNode.updateAudioLevel(color: color, backgroundColor: backgroundColor, value: value) diff --git a/submodules/AnimatedCountLabelNode/BUILD b/submodules/AnimatedCountLabelNode/BUILD index 27cdd1b3f8..55f7c774f7 100644 --- a/submodules/AnimatedCountLabelNode/BUILD +++ b/submodules/AnimatedCountLabelNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Display:Display", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/AnimatedNavigationStripeNode/BUILD b/submodules/AnimatedNavigationStripeNode/BUILD index b0de90d445..74d6a6c970 100644 --- a/submodules/AnimatedNavigationStripeNode/BUILD +++ b/submodules/AnimatedNavigationStripeNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Display:Display", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/AnimatedStickerNode/BUILD b/submodules/AnimatedStickerNode/BUILD index 655f34543c..f983796f85 100644 --- a/submodules/AnimatedStickerNode/BUILD +++ b/submodules/AnimatedStickerNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift index 9606e718e1..2dda65089b 100644 --- a/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift +++ b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift @@ -88,7 +88,7 @@ public final class AnimatedStickerFrame { } } -public protocol AnimatedStickerFrameSource: class { +public protocol AnimatedStickerFrameSource: AnyObject { var frameRate: Int { get } var frameCount: Int { get } var frameIndex: Int { get } @@ -139,7 +139,10 @@ public final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource var frameRate = 0 var frameCount = 0 - if !self.data.withUnsafeBytes({ (bytes: UnsafePointer) -> Bool in + if !self.data.withUnsafeBytes({ buffer -> Bool in + guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return false + } var frameRateValue: Int32 = 0 var frameCountValue: Int32 = 0 var widthValue: Int32 = 0 @@ -180,7 +183,10 @@ public final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource self.decodeBuffer = Data(count: self.bytesPerRow * height) self.frameBuffer = Data(count: self.bytesPerRow * height) let frameBufferLength = self.frameBuffer.count - self.frameBuffer.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + self.frameBuffer.withUnsafeMutableBytes { buffer -> Void in + guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } memset(bytes, 0, frameBufferLength) } } @@ -199,12 +205,19 @@ public final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource let frameIndex = self.frameIndex - self.data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in + self.data.withUnsafeBytes { buffer -> Void in + guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } + if self.offset + 4 > dataLength { if self.dataComplete { self.frameIndex = 0 self.offset = self.initialOffset - self.frameBuffer.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + self.frameBuffer.withUnsafeMutableBytes { buffer -> Void in + guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } memset(bytes, 0, frameBufferLength) } } @@ -221,9 +234,21 @@ public final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource self.offset += 4 if draw { - self.scratchBuffer.withUnsafeMutableBytes { (scratchBytes: UnsafeMutablePointer) -> Void in - self.decodeBuffer.withUnsafeMutableBytes { (decodeBytes: UnsafeMutablePointer) -> Void in - self.frameBuffer.withUnsafeMutableBytes { (frameBytes: UnsafeMutablePointer) -> Void in + self.scratchBuffer.withUnsafeMutableBytes { scratchBuffer -> Void in + guard let scratchBytes = scratchBuffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } + + self.decodeBuffer.withUnsafeMutableBytes { decodeBuffer -> Void in + guard let decodeBytes = decodeBuffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } + + self.frameBuffer.withUnsafeMutableBytes { frameBuffer -> Void in + guard let frameBytes = frameBuffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } + compression_decode_buffer(decodeBytes, decodeBufferLength, bytes.advanced(by: self.offset), Int(frameLength), UnsafeMutableRawPointer(scratchBytes), COMPRESSION_LZFSE) var lhs = UnsafeMutableRawPointer(frameBytes).assumingMemoryBound(to: UInt64.self) @@ -253,7 +278,10 @@ public final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource isLastFrame = true self.frameIndex = 0 self.offset = self.initialOffset - self.frameBuffer.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + self.frameBuffer.withUnsafeMutableBytes { buffer -> Void in + guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } memset(bytes, 0, frameBufferLength) } } @@ -351,7 +379,10 @@ private final class ManagedFileImpl { assert(queue.isCurrent()) } var result = Data(count: count) - result.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + result.withUnsafeMutableBytes { buffer -> Void in + guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } let readCount = self.read(bytes, count) assert(readCount == count) } @@ -399,7 +430,7 @@ private func compressFrame(width: Int, height: Int, rgbData: Data) -> Data? { assert(yuvaPixelsPerAlphaRow % 2 == 0) let yuvaLength = Int(width) * Int(height) * 2 + yuvaPixelsPerAlphaRow * Int(height) / 2 - var yuvaFrameData = malloc(yuvaLength)! + let yuvaFrameData = malloc(yuvaLength)! defer { free(yuvaFrameData) } @@ -422,7 +453,10 @@ private func compressFrame(width: Int, height: Int, rgbData: Data) -> Data? { var maybeResultSize: Int? - compressedFrameData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + compressedFrameData.withUnsafeMutableBytes { buffer -> Void in + guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } let length = compression_encode_buffer(bytes, compressedFrameDataLength, yuvaFrameData.assumingMemoryBound(to: UInt8.self), yuvaLength, scratchData, COMPRESSION_LZFSE) maybeResultSize = length } @@ -579,9 +613,21 @@ private final class AnimatedStickerDirectFrameSourceCache { let decodeBufferLength = self.decodeBuffer.count - compressedData.withUnsafeBytes { (bytes: UnsafePointer) -> Void in - self.scratchBuffer.withUnsafeMutableBytes { (scratchBytes: UnsafeMutablePointer) -> Void in - self.decodeBuffer.withUnsafeMutableBytes { (decodeBytes: UnsafeMutablePointer) -> Void in + compressedData.withUnsafeBytes { buffer -> Void in + guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } + + self.scratchBuffer.withUnsafeMutableBytes { scratchBuffer -> Void in + guard let scratchBytes = scratchBuffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } + + self.decodeBuffer.withUnsafeMutableBytes { decodeBuffer -> Void in + guard let decodeBytes = decodeBuffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } + let resultLength = compression_decode_buffer(decodeBytes, decodeBufferLength, bytes, length, UnsafeMutableRawPointer(scratchBytes), COMPRESSION_LZFSE) frameData = Data(bytes: decodeBytes, count: resultLength) @@ -644,7 +690,11 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource return AnimatedStickerFrame(data: yuvData, type: .yuva, width: self.width, height: self.height, bytesPerRow: 0, index: frameIndex, isLastFrame: frameIndex == self.frameCount - 1, totalFrames: self.frameCount) } else { var frameData = Data(count: self.bytesPerRow * self.height) - frameData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + frameData.withUnsafeMutableBytes { buffer -> Void in + guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } + memset(bytes, 0, self.bytesPerRow * self.height) self.animation.renderFrame(with: Int32(frameIndex), into: bytes, width: Int32(self.width), height: Int32(self.height), bytesPerRow: Int32(self.bytesPerRow)) } @@ -853,7 +903,7 @@ public final class AnimatedStickerNode: ASDisplayNode { strongSelf.isSetUpForPlayback = false strongSelf.isPlaying = true } - var fromIndex = strongSelf.playFromIndex + let fromIndex = strongSelf.playFromIndex strongSelf.playFromIndex = nil strongSelf.play(fromIndex: fromIndex) } else if strongSelf.canDisplayFirstFrame { diff --git a/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift b/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift index 5b23eb8b1d..70658638c4 100644 --- a/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift +++ b/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift @@ -19,15 +19,25 @@ final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer { break } - let image = generateImagePixel(CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, pixelGenerator: { _, pixelData, bytesPerRow in + let image = generateImagePixel(CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, pixelGenerator: { _, pixelData, contextBytesPerRow in switch type { case .yuva: - data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in - decodeYUVAToRGBA(bytes, pixelData, Int32(width), Int32(height), Int32(bytesPerRow)) + data.withUnsafeBytes { bytes -> Void in + guard let baseAddress = bytes.baseAddress else { + return + } + if bytesPerRow * height > bytes.count { + assert(false) + return + } + decodeYUVAToRGBA(baseAddress.assumingMemoryBound(to: UInt8.self), pixelData, Int32(width), Int32(height), Int32(contextBytesPerRow)) } case .argb: - data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in - memcpy(pixelData, bytes, data.count) + data.withUnsafeBytes { bytes -> Void in + guard let baseAddress = bytes.baseAddress else { + return + } + memcpy(pixelData, baseAddress.assumingMemoryBound(to: UInt8.self), bytes.count) } } }) diff --git a/submodules/AnimationUI/BUILD b/submodules/AnimationUI/BUILD index 643f7c7cd3..296930b006 100644 --- a/submodules/AnimationUI/BUILD +++ b/submodules/AnimationUI/BUILD @@ -6,17 +6,11 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ - "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", - "//submodules/Postbox:Postbox", - "//submodules/TelegramCore:TelegramCore", "//submodules/AsyncDisplayKit:AsyncDisplayKit", - "//submodules/Display:Display", - "//submodules/YuvConversion:YuvConversion", - "//submodules/StickerResources:StickerResources", - "//submodules/MediaResources:MediaResources", - "//submodules/Tuples:Tuples", - "//submodules/GZip:GZip", "//submodules/rlottie:RLottieBinding", "//submodules/lottie-ios:Lottie", "//submodules/AppBundle:AppBundle", diff --git a/submodules/AppLock/BUILD b/submodules/AppLock/BUILD index ae89bbf200..ff6e8e8147 100644 --- a/submodules/AppLock/BUILD +++ b/submodules/AppLock/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/AppLock/Sources/AppLock.swift b/submodules/AppLock/Sources/AppLock.swift index 993663bce7..6fe5ddef16 100644 --- a/submodules/AppLock/Sources/AppLock.swift +++ b/submodules/AppLock/Sources/AppLock.swift @@ -150,7 +150,7 @@ public final class AppLockContextImpl: AppLockContext { strongSelf.autolockTimeout.set(nil) strongSelf.autolockReportTimeout.set(nil) } else { - if let autolockTimeout = passcodeSettings.autolockTimeout, !appInForeground { + if let _ = passcodeSettings.autolockTimeout, !appInForeground { shouldDisplayCoveringView = true } @@ -184,7 +184,7 @@ public final class AppLockContextImpl: AppLockContext { } passcodeController.ensureInputFocused() } else { - let passcodeController = PasscodeEntryController(applicationBindings: strongSelf.applicationBindings, accountManager: strongSelf.accountManager, appLockContext: strongSelf, presentationData: presentationData, presentationDataSignal: strongSelf.presentationDataSignal, statusBarHost: window?.statusBarHost, challengeData: accessChallengeData.data, biometrics: biometrics, arguments: PasscodeEntryControllerPresentationArguments(animated: !becameActiveRecently, lockIconInitialFrame: { [weak self] in + let passcodeController = PasscodeEntryController(applicationBindings: strongSelf.applicationBindings, accountManager: strongSelf.accountManager, appLockContext: strongSelf, presentationData: presentationData, presentationDataSignal: strongSelf.presentationDataSignal, statusBarHost: window?.statusBarHost, challengeData: accessChallengeData.data, biometrics: biometrics, arguments: PasscodeEntryControllerPresentationArguments(animated: !becameActiveRecently, lockIconInitialFrame: { if let lockViewFrame = lockIconInitialFrame() { return lockViewFrame } else { @@ -203,7 +203,7 @@ public final class AppLockContextImpl: AppLockContext { passcodeController.isOpaqueWhenInOverlay = true strongSelf.passcodeController = passcodeController if let rootViewController = strongSelf.rootController { - if let presentedViewController = rootViewController.presentedViewController as? UIActivityViewController { + if let _ = rootViewController.presentedViewController as? UIActivityViewController { } else { rootViewController.dismiss(animated: false, completion: nil) } @@ -227,14 +227,14 @@ public final class AppLockContextImpl: AppLockContext { window.coveringView = coveringView if let rootViewController = strongSelf.rootController { - if let presentedViewController = rootViewController.presentedViewController as? UIActivityViewController { + if let _ = rootViewController.presentedViewController as? UIActivityViewController { } else { rootViewController.dismiss(animated: false, completion: nil) } } } } else { - if let coveringView = strongSelf.coveringView { + if let _ = strongSelf.coveringView { strongSelf.coveringView = nil strongSelf.window?.coveringView = nil } diff --git a/submodules/ArchivedStickerPacksNotice/BUILD b/submodules/ArchivedStickerPacksNotice/BUILD index 8d54e521f8..cf24e9bda3 100644 --- a/submodules/ArchivedStickerPacksNotice/BUILD +++ b/submodules/ArchivedStickerPacksNotice/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/ArchivedStickerPacksNotice/Sources/ArchivedStickerPacksNoticeController.swift b/submodules/ArchivedStickerPacksNotice/Sources/ArchivedStickerPacksNoticeController.swift index cb43c40d4a..13a56523d3 100644 --- a/submodules/ArchivedStickerPacksNotice/Sources/ArchivedStickerPacksNoticeController.swift +++ b/submodules/ArchivedStickerPacksNotice/Sources/ArchivedStickerPacksNoticeController.swift @@ -151,14 +151,14 @@ private final class ArchivedStickersNoticeAlertContentNode: AlertContentNode { } private func dequeueTransition() { - guard let layout = self.validLayout, let transition = self.enqueuedTransitions.first else { + guard let _ = self.validLayout, let transition = self.enqueuedTransitions.first else { return } self.enqueuedTransitions.remove(at: 0) - var options = ListViewDeleteAndInsertOptions() + let options = ListViewDeleteAndInsertOptions() - self.listView.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { [weak self] _ in + self.listView.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { _ in }) } @@ -302,14 +302,14 @@ public func archivedStickerPacksNoticeController(context: AccountContext, archiv })]) let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) - let presentationDataDisposable = context.sharedContext.presentationData.start(next: { [weak controller, weak contentNode] presentationData in + let presentationDataDisposable = context.sharedContext.presentationData.start(next: { [weak controller] presentationData in controller?.theme = AlertControllerTheme(presentationData: presentationData) }) controller.dismissed = { presentationDataDisposable.dispose() disposable.dispose() } - dismissImpl = { [weak controller, weak contentNode] in + dismissImpl = { [weak controller] in controller?.dismissAnimated() } return controller diff --git a/submodules/AudioBlob/BUILD b/submodules/AudioBlob/BUILD index 4e0c55ac05..72c69fa1aa 100644 --- a/submodules/AudioBlob/BUILD +++ b/submodules/AudioBlob/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/AuthTransferUI/BUILD b/submodules/AuthTransferUI/BUILD index 82321614ec..dbadbfe3f1 100644 --- a/submodules/AuthTransferUI/BUILD +++ b/submodules/AuthTransferUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramCore:TelegramCore", "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", diff --git a/submodules/AuthTransferUI/Sources/AuthTransferConfirmationScreen.swift b/submodules/AuthTransferUI/Sources/AuthTransferConfirmationScreen.swift index d5b76addce..ccb27eaeeb 100644 --- a/submodules/AuthTransferUI/Sources/AuthTransferConfirmationScreen.swift +++ b/submodules/AuthTransferUI/Sources/AuthTransferConfirmationScreen.swift @@ -253,12 +253,10 @@ private final class AuthDataTransferSplashScreenNode: ViewControllerTracingNode let animationFitSize = CGSize(width: min(500.0, layout.size.width - sideInset + 20.0), height: 500.0) let animationSize = self.animationNode?.preferredSize()?.fitted(animationFitSize) ?? animationFitSize let iconSize: CGSize = animationSize - var iconOffset = CGPoint() + let iconOffset = CGPoint() let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.height)) - let hasRTL = self.badgeTextNodes.first?.cachedLayout?.hasRTL ?? false - var badgeTextSizes: [CGSize] = [] var textSizes: [CGSize] = [] var textContentHeight: CGFloat = 0.0 @@ -290,9 +288,9 @@ private final class AuthDataTransferSplashScreenNode: ViewControllerTracingNode let buttonFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - buttonWidth) / 2.0), y: layout.size.height - bottomInset - buttonHeight), size: CGSize(width: buttonWidth, height: buttonHeight)) transition.updateFrame(node: self.buttonNode, frame: buttonFrame) - self.buttonNode.updateLayout(width: buttonFrame.width, transition: transition) + let _ = self.buttonNode.updateLayout(width: buttonFrame.width, transition: transition) - var maxContentVerticalOrigin = buttonFrame.minY - 12.0 - contentHeight + let maxContentVerticalOrigin = buttonFrame.minY - 12.0 - contentHeight contentVerticalOrigin = min(contentVerticalOrigin, maxContentVerticalOrigin) diff --git a/submodules/AuthTransferUI/Sources/AuthTransferScanScreen.swift b/submodules/AuthTransferUI/Sources/AuthTransferScanScreen.swift index 5fbd877788..4b3d87832f 100644 --- a/submodules/AuthTransferUI/Sources/AuthTransferScanScreen.swift +++ b/submodules/AuthTransferUI/Sources/AuthTransferScanScreen.swift @@ -44,7 +44,7 @@ private func generateFrameImage() -> UIImage? { context.setLineWidth(4.0) context.setLineCap(.round) - var path = CGMutablePath(); + let path = CGMutablePath() path.move(to: CGPoint(x: 2.0, y: 2.0 + 26.0)) path.addArc(tangent1End: CGPoint(x: 2.0, y: 2.0), tangent2End: CGPoint(x: 2.0 + 26.0, y: 2.0), radius: 6.0) path.addLine(to: CGPoint(x: 2.0 + 26.0, y: 2.0)) @@ -412,8 +412,8 @@ private final class AuthTransferScanScreenNode: ViewControllerTracingNode, UIScr let dimAlpha: CGFloat let dimRect: CGRect let controlsAlpha: CGFloat - var centerDimAlpha: CGFloat = 0.0 - var frameAlpha: CGFloat = 1.0 + let centerDimAlpha: CGFloat = 0.0 + let frameAlpha: CGFloat = 1.0 if let focusedRect = self.focusedRect { controlsAlpha = 0.0 dimAlpha = 1.0 diff --git a/submodules/AuthorizationUI/BUILD b/submodules/AuthorizationUI/BUILD index 968bcdf9eb..c3c89a9c0a 100644 --- a/submodules/AuthorizationUI/BUILD +++ b/submodules/AuthorizationUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/TelegramCore:TelegramCore", diff --git a/submodules/AvatarNode/BUILD b/submodules/AvatarNode/BUILD index 7e52852b6b..b2b1a33bdf 100644 --- a/submodules/AvatarNode/BUILD +++ b/submodules/AvatarNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/AvatarNode/Sources/AvatarNode.swift b/submodules/AvatarNode/Sources/AvatarNode.swift index 2986a45a4b..38608a14e7 100644 --- a/submodules/AvatarNode/Sources/AvatarNode.swift +++ b/submodules/AvatarNode/Sources/AvatarNode.swift @@ -1,7 +1,6 @@ import Foundation import UIKit import AsyncDisplayKit -import Postbox import Display import TelegramCore import SwiftSignalKit @@ -28,8 +27,8 @@ public enum AvatarNodeClipStyle { private class AvatarNodeParameters: NSObject { let theme: PresentationTheme? - let accountPeerId: PeerId? - let peerId: PeerId? + let accountPeerId: EnginePeer.Id? + let peerId: EnginePeer.Id? let letters: [String] let font: UIFont let icon: AvatarNodeIcon @@ -37,7 +36,7 @@ private class AvatarNodeParameters: NSObject { let hasImage: Bool let clipStyle: AvatarNodeClipStyle - init(theme: PresentationTheme?, accountPeerId: PeerId?, peerId: PeerId?, letters: [String], font: UIFont, icon: AvatarNodeIcon, explicitColorIndex: Int?, hasImage: Bool, clipStyle: AvatarNodeClipStyle) { + init(theme: PresentationTheme?, accountPeerId: EnginePeer.Id?, peerId: EnginePeer.Id?, letters: [String], font: UIFont, icon: AvatarNodeIcon, explicitColorIndex: Int?, hasImage: Bool, clipStyle: AvatarNodeClipStyle) { self.theme = theme self.accountPeerId = accountPeerId self.peerId = peerId @@ -85,7 +84,7 @@ public enum AvatarNodeExplicitIcon { private enum AvatarNodeState: Equatable { case empty - case peerAvatar(PeerId, [String], TelegramMediaImageRepresentation?) + case peerAvatar(EnginePeer.Id, [String], TelegramMediaImageRepresentation?) case custom(letter: [String], explicitColorIndex: Int?, explicitIcon: AvatarNodeExplicitIcon?) } @@ -303,7 +302,7 @@ public final class AvatarNode: ASDisplayNode { self.imageNode.isHidden = true } - public func setPeer(context: AccountContext, theme: PresentationTheme, peer: Peer?, authorOfMessage: MessageReference? = nil, overrideImage: AvatarNodeImageOverride? = nil, emptyColor: UIColor? = nil, clipStyle: AvatarNodeClipStyle = .round, synchronousLoad: Bool = false, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), storeUnrounded: Bool = false) { + public func setPeer(context: AccountContext, theme: PresentationTheme, peer: EnginePeer?, authorOfMessage: MessageReference? = nil, overrideImage: AvatarNodeImageOverride? = nil, emptyColor: UIColor? = nil, clipStyle: AvatarNodeClipStyle = .round, synchronousLoad: Bool = false, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), storeUnrounded: Bool = false) { var synchronousLoad = synchronousLoad var representation: TelegramMediaImageRepresentation? var icon = AvatarNodeIcon.none @@ -336,7 +335,7 @@ public final class AvatarNode: ASDisplayNode { } else if peer?.restrictionText(platform: "ios", contentSettings: context.currentContentSettings.with { $0 }) == nil { representation = peer?.smallProfileImage } - let updatedState: AvatarNodeState = .peerAvatar(peer?.id ?? PeerId(0), peer?.displayLetters ?? [], representation) + let updatedState: AvatarNodeState = .peerAvatar(peer?.id ?? EnginePeer.Id(0), peer?.displayLetters ?? [], representation) if updatedState != self.state || overrideImage != self.overrideImage || theme !== self.theme { self.state = updatedState self.overrideImage = overrideImage @@ -344,7 +343,7 @@ public final class AvatarNode: ASDisplayNode { let parameters: AvatarNodeParameters - if let peer = peer, let signal = peerAvatarImage(account: context.account, peerReference: PeerReference(peer), authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, emptyColor: emptyColor, synchronousLoad: synchronousLoad, provideUnrounded: storeUnrounded) { + if let peer = peer, let signal = peerAvatarImage(account: context.account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, emptyColor: emptyColor, synchronousLoad: synchronousLoad, provideUnrounded: storeUnrounded) { self.contents = nil self.displaySuspended = true self.imageReady.set(self.imageNode.contentReady) @@ -380,7 +379,7 @@ public final class AvatarNode: ASDisplayNode { } self.editOverlayNode?.isHidden = true - parameters = AvatarNodeParameters(theme: theme, accountPeerId: context.account.peerId, peerId: peer?.id ?? PeerId(0), letters: peer?.displayLetters ?? [], font: self.font, icon: icon, explicitColorIndex: nil, hasImage: false, clipStyle: clipStyle) + parameters = AvatarNodeParameters(theme: theme, accountPeerId: context.account.peerId, peerId: peer?.id ?? EnginePeer.Id(0), letters: peer?.displayLetters ?? [], font: self.font, icon: icon, explicitColorIndex: nil, hasImage: false, clipStyle: clipStyle) } if self.parameters == nil || self.parameters != parameters { self.parameters = parameters @@ -593,7 +592,7 @@ public final class AvatarNode: ASDisplayNode { } } - static func asyncLayout(_ node: AvatarNode?) -> (_ context: AccountContext, _ peer: Peer, _ font: UIFont) -> () -> AvatarNode? { + static func asyncLayout(_ node: AvatarNode?) -> (_ context: AccountContext, _ peer: EnginePeer, _ font: UIFont) -> () -> AvatarNode? { let currentState = node?.state let createNode = node == nil return { [weak node] context, peer, font in @@ -622,7 +621,7 @@ public final class AvatarNode: ASDisplayNode { } } -public func drawPeerAvatarLetters(context: CGContext, size: CGSize, round: Bool = true, font: UIFont, letters: [String], peerId: PeerId) { +public func drawPeerAvatarLetters(context: CGContext, size: CGSize, round: Bool = true, font: UIFont, letters: [String], peerId: EnginePeer.Id) { if round { context.beginPath() context.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: diff --git a/submodules/AvatarNode/Sources/PeerAvatar.swift b/submodules/AvatarNode/Sources/PeerAvatar.swift index 711af8dd55..ca5cb90297 100644 --- a/submodules/AvatarNode/Sources/PeerAvatar.swift +++ b/submodules/AvatarNode/Sources/PeerAvatar.swift @@ -60,8 +60,7 @@ public func peerAvatarImageData(account: Account, peerReference: PeerReference?, subscriber.putNext(nil) } } - }, error: { error in - subscriber.putError(error) + }, error: { _ in }, completed: { subscriber.putCompletion() }) diff --git a/submodules/BotPaymentsUI/BUILD b/submodules/BotPaymentsUI/BUILD index f99f6e631d..30ad6de487 100644 --- a/submodules/BotPaymentsUI/BUILD +++ b/submodules/BotPaymentsUI/BUILD @@ -6,9 +6,11 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", - "//submodules/Postbox:Postbox", "//submodules/TelegramCore:TelegramCore", "//submodules/TelegramPresentationData:TelegramPresentationData", "//submodules/LocalAuth:LocalAuth", diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutActionButton.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutActionButton.swift index e784f933b6..b7aa1ea18d 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutActionButton.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutActionButton.swift @@ -55,7 +55,7 @@ final class BotCheckoutActionButton: HighlightableButtonNode { let previousState = self.state self.state = state - if let (absoluteRect, containerSize) = self.validLayout, let previousState = previousState { + if let (absoluteRect, containerSize) = self.validLayout, let _ = previousState { self.updateLayout(absoluteRect: absoluteRect, containerSize: containerSize, transition: .immediate) } } diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutController.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutController.swift index c9b48ea0bd..0f9fbea0ed 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutController.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutController.swift @@ -4,7 +4,6 @@ import Display import AsyncDisplayKit import TelegramCore import SwiftSignalKit -import Postbox import TelegramPresentationData import AccountContext @@ -25,7 +24,7 @@ public final class BotCheckoutController: ViewController { self.validatedFormInfo = validatedFormInfo } - public static func fetch(context: AccountContext, messageId: MessageId) -> Signal { + public static func fetch(context: AccountContext, messageId: EngineMessage.Id) -> Signal { let presentationData = context.sharedContext.currentPresentationData.with { $0 } let themeParams: [String: Any] = [ "bg_color": Int32(bitPattern: presentationData.theme.list.plainBackgroundColor.argb), @@ -78,8 +77,8 @@ public final class BotCheckoutController: ViewController { private let context: AccountContext private let invoice: TelegramMediaInvoice - private let messageId: MessageId - private let completed: (String, MessageId?) -> Void + private let messageId: EngineMessage.Id + private let completed: (String, EngineMessage.Id?) -> Void private var presentationData: PresentationData @@ -87,7 +86,7 @@ public final class BotCheckoutController: ViewController { private let inputData: Promise - public init(context: AccountContext, invoice: TelegramMediaInvoice, messageId: MessageId, inputData: Promise, completed: @escaping (String, MessageId?) -> Void) { + public init(context: AccountContext, invoice: TelegramMediaInvoice, messageId: EngineMessage.Id, inputData: Promise, completed: @escaping (String, EngineMessage.Id?) -> Void) { self.context = context self.invoice = invoice self.messageId = messageId @@ -120,8 +119,6 @@ public final class BotCheckoutController: ViewController { self?.dismiss() }, completed: self.completed) - //displayNode.enableInteractiveDismiss = true - displayNode.dismiss = { [weak self] in self?.presentingViewController?.dismiss(animated: false, completion: nil) } diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift index 251837ac51..99225ce428 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutControllerNode.swift @@ -2,7 +2,6 @@ import Foundation import UIKit import AsyncDisplayKit import Display -import Postbox import TelegramCore import SwiftSignalKit import PassKit @@ -327,7 +326,7 @@ private func currentTotalPrice(paymentForm: BotPaymentForm?, validatedFormInfo: return totalPrice } -private func botCheckoutControllerEntries(presentationData: PresentationData, state: BotCheckoutControllerState, invoice: TelegramMediaInvoice, paymentForm: BotPaymentForm?, formInfo: BotPaymentRequestedInfo?, validatedFormInfo: BotPaymentValidatedFormInfo?, currentShippingOptionId: String?, currentPaymentMethod: BotCheckoutPaymentMethod?, currentTip: Int64?, botPeer: Peer?) -> [BotCheckoutEntry] { +private func botCheckoutControllerEntries(presentationData: PresentationData, state: BotCheckoutControllerState, invoice: TelegramMediaInvoice, paymentForm: BotPaymentForm?, formInfo: BotPaymentRequestedInfo?, validatedFormInfo: BotPaymentValidatedFormInfo?, currentShippingOptionId: String?, currentPaymentMethod: BotCheckoutPaymentMethod?, currentTip: Int64?, botPeer: EnginePeer?) -> [BotCheckoutEntry] { var entries: [BotCheckoutEntry] = [] var botName = "" @@ -460,7 +459,8 @@ private func formSupportApplePay(_ paymentForm: BotPaymentForm) -> Bool { "yandex", "privatbank", "tranzzo", - "paymaster" + "paymaster", + "smartglocal", ]) if !applePayProviders.contains(nativeProvider.name) { return false @@ -503,10 +503,10 @@ private func availablePaymentMethods(form: BotPaymentForm, current: BotCheckoutP final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthorizationViewControllerDelegate { private weak var controller: BotCheckoutController? private let context: AccountContext - private let messageId: MessageId + private let messageId: EngineMessage.Id private let present: (ViewController, Any?) -> Void private let dismissAnimated: () -> Void - private let completed: (String, MessageId?) -> Void + private let completed: (String, EngineMessage.Id?) -> Void private var stateValue = BotCheckoutControllerState() private let state = ValuePromise(BotCheckoutControllerState(), ignoreRepeated: true) @@ -537,7 +537,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz private var passwordTip: String? private var passwordTipDisposable: Disposable? - init(controller: BotCheckoutController?, navigationBar: NavigationBar, context: AccountContext, invoice: TelegramMediaInvoice, messageId: MessageId, inputData: Promise, present: @escaping (ViewController, Any?) -> Void, dismissAnimated: @escaping () -> Void, completed: @escaping (String, MessageId?) -> Void) { + init(controller: BotCheckoutController?, navigationBar: NavigationBar, context: AccountContext, invoice: TelegramMediaInvoice, messageId: EngineMessage.Id, inputData: Promise, present: @escaping (ViewController, Any?) -> Void, dismissAnimated: @escaping () -> Void, completed: @escaping (String, EngineMessage.Id?) -> Void) { self.controller = controller self.context = context self.messageId = messageId @@ -565,14 +565,22 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz ensureTipInputVisibleImpl?() }) - let paymentBotPeer = paymentFormAndInfo.get() - |> map { paymentFormAndInfo -> PeerId? in - return paymentFormAndInfo?.0.paymentBotId + let paymentBotPeer: Signal = paymentFormAndInfo.get() + |> map { paymentFormAndInfo -> EnginePeer.Id? in + if let paymentBotId = paymentFormAndInfo?.0.paymentBotId { + return paymentBotId + } else { + return nil + } } |> distinctUntilChanged - |> mapToSignal { peerId -> Signal in - return context.account.postbox.transaction { transaction -> Peer? in - return peerId.flatMap(transaction.getPeer) + |> mapToSignal { peerId -> Signal in + if let peerId = peerId { + return context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: peerId) + ) + } else { + return .single(nil) } } @@ -1123,9 +1131,10 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz } let botPeerId = self.messageId.peerId - let _ = (self.context.account.postbox.transaction({ transaction -> Peer? in - return transaction.getPeer(botPeerId) - }) |> deliverOnMainQueue).start(next: { [weak self] botPeer in + let _ = (context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: botPeerId) + ) + |> deliverOnMainQueue).start(next: { [weak self] botPeer in if let strongSelf = self, let botPeer = botPeer { let request = PKPaymentRequest() @@ -1191,10 +1200,17 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz } if !liabilityNoticeAccepted { - let botPeer: Signal = self.context.account.postbox.transaction { transaction -> Peer? in - return transaction.getPeer(paymentForm.paymentBotId) - } - let _ = (combineLatest(ApplicationSpecificNotice.getBotPaymentLiability(accountManager: self.context.sharedContext.accountManager, peerId: paymentForm.paymentBotId), botPeer, self.context.account.postbox.loadedPeerWithId(paymentForm.providerId)) + let botPeer: Signal = context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: paymentForm.paymentBotId) + ) + let providerPeer: Signal = context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: paymentForm.providerId) + ) + let _ = (combineLatest( + ApplicationSpecificNotice.getBotPaymentLiability(accountManager: self.context.sharedContext.accountManager, peerId: paymentForm.paymentBotId), + botPeer, + providerPeer + ) |> deliverOnMainQueue).start(next: { [weak self] value, botPeer, providerPeer in if let strongSelf = self, let botPeer = botPeer { if value { @@ -1202,7 +1218,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz } else { let paymentText = strongSelf.presentationData.strings.Checkout_PaymentLiabilityAlert .replacingOccurrences(of: "{target}", with: botPeer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)) - .replacingOccurrences(of: "{payment_system}", with: providerPeer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)) + .replacingOccurrences(of: "{payment_system}", with: providerPeer?.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder) ?? "") strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Checkout_LiabilityAlertTitle, text: paymentText, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { if let strongSelf = self { @@ -1243,7 +1259,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz applePayController.presentingViewController?.dismiss(animated: true, completion: nil) } - let proceedWithCompletion: (Bool, MessageId?) -> Void = { success, receiptMessageId in + let proceedWithCompletion: (Bool, EngineMessage.Id?) -> Void = { success, receiptMessageId in guard let strongSelf = self else { return } diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutInfoController.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutInfoController.swift index 5365164c59..b3864e50f8 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutInfoController.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutInfoController.swift @@ -3,7 +3,6 @@ import UIKit import SwiftSignalKit import Display import TelegramCore -import Postbox import TelegramPresentationData import ProgressNavigationButtonNode import AccountContext @@ -31,7 +30,7 @@ final class BotCheckoutInfoController: ViewController { private let context: AccountContext private let invoice: BotPaymentInvoice - private let messageId: MessageId + private let messageId: EngineMessage.Id private let initialFormInfo: BotPaymentRequestedInfo private let focus: BotCheckoutInfoControllerFocus @@ -44,7 +43,14 @@ final class BotCheckoutInfoController: ViewController { private var doneItem: UIBarButtonItem? private var activityItem: UIBarButtonItem? - public init(context: AccountContext, invoice: BotPaymentInvoice, messageId: MessageId, initialFormInfo: BotPaymentRequestedInfo, focus: BotCheckoutInfoControllerFocus, formInfoUpdated: @escaping (BotPaymentRequestedInfo, BotPaymentValidatedFormInfo) -> Void) { + public init( + context: AccountContext, + invoice: BotPaymentInvoice, + messageId: EngineMessage.Id, + initialFormInfo: BotPaymentRequestedInfo, + focus: BotCheckoutInfoControllerFocus, + formInfoUpdated: @escaping (BotPaymentRequestedInfo, BotPaymentValidatedFormInfo) -> Void + ) { self.context = context self.invoice = invoice self.messageId = messageId diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutInfoControllerNode.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutInfoControllerNode.swift index 2a92d8c313..45093b3233 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutInfoControllerNode.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutInfoControllerNode.swift @@ -3,7 +3,6 @@ import UIKit import AsyncDisplayKit import Display import TelegramCore -import Postbox import SwiftSignalKit import TelegramPresentationData import AccountContext @@ -96,7 +95,7 @@ enum BotCheckoutInfoControllerStatus { final class BotCheckoutInfoControllerNode: ViewControllerTracingNode, UIScrollViewDelegate { private let context: AccountContext private let invoice: BotPaymentInvoice - private let messageId: MessageId + private let messageId: EngineMessage.Id private var focus: BotCheckoutInfoControllerFocus? private let dismiss: () -> Void @@ -124,7 +123,20 @@ final class BotCheckoutInfoControllerNode: ViewControllerTracingNode, UIScrollVi private let verifyDisposable = MetaDisposable() private var isVerifying = false - init(context: AccountContext, invoice: BotPaymentInvoice, messageId: MessageId, formInfo: BotPaymentRequestedInfo, focus: BotCheckoutInfoControllerFocus, theme: PresentationTheme, strings: PresentationStrings, dismiss: @escaping () -> Void, openCountrySelection: @escaping () -> Void, updateStatus: @escaping (BotCheckoutInfoControllerStatus) -> Void, formInfoUpdated: @escaping (BotPaymentRequestedInfo, BotPaymentValidatedFormInfo) -> Void, present: @escaping (ViewController, Any?) -> Void) { + init( + context: AccountContext, + invoice: BotPaymentInvoice, + messageId: EngineMessage.Id, + formInfo: BotPaymentRequestedInfo, + focus: BotCheckoutInfoControllerFocus, + theme: PresentationTheme, + strings: PresentationStrings, + dismiss: @escaping () -> Void, + openCountrySelection: @escaping () -> Void, + updateStatus: @escaping (BotCheckoutInfoControllerStatus) -> Void, + formInfoUpdated: @escaping (BotPaymentRequestedInfo, BotPaymentValidatedFormInfo) -> Void, + present: @escaping (ViewController, Any?) -> Void + ) { self.context = context self.invoice = invoice self.messageId = messageId diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryController.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryController.swift index a5bb8e6564..e880872994 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryController.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryController.swift @@ -4,7 +4,6 @@ import AsyncDisplayKit import SwiftSignalKit import Display import TelegramCore -import Postbox import TelegramPresentationData import ProgressNavigationButtonNode import AccountContext diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryControllerNode.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryControllerNode.swift index 2cec187417..d28cfddea6 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryControllerNode.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryControllerNode.swift @@ -3,7 +3,6 @@ import UIKit import AsyncDisplayKit import Display import TelegramCore -import Postbox import SwiftSignalKit import TelegramPresentationData import Stripe diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutPaymentMethodSheet.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutPaymentMethodSheet.swift index 2d77d7d1ad..c3f64e00d4 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutPaymentMethodSheet.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutPaymentMethodSheet.swift @@ -38,7 +38,6 @@ final class BotCheckoutPaymentMethodSheetController: ActionSheetController { init(context: AccountContext, currentMethod: BotCheckoutPaymentMethod?, methods: [BotCheckoutPaymentMethod], applyValue: @escaping (BotCheckoutPaymentMethod) -> Void, newCard: @escaping () -> Void) { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let theme = presentationData.theme let strings = presentationData.strings super.init(theme: ActionSheetControllerTheme(presentationData: presentationData)) diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutPaymentShippingOptionSheetController.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutPaymentShippingOptionSheetController.swift index 56587bd3c9..4c00294d07 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutPaymentShippingOptionSheetController.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutPaymentShippingOptionSheetController.swift @@ -12,7 +12,6 @@ final class BotCheckoutPaymentShippingOptionSheetController: ActionSheetControll init(context: AccountContext, currency: String, options: [BotPaymentShippingOption], currentId: String?, applyValue: @escaping (String) -> Void) { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let theme = presentationData.theme let strings = presentationData.strings super.init(theme: ActionSheetControllerTheme(presentationData: presentationData)) diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutWebInteractionController.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutWebInteractionController.swift index faaa68ba27..2bf329b15d 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutWebInteractionController.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutWebInteractionController.swift @@ -4,7 +4,6 @@ import Display import AsyncDisplayKit import TelegramCore import SwiftSignalKit -import Postbox import TelegramPresentationData import AccountContext diff --git a/submodules/BotPaymentsUI/Sources/BotReceiptController.swift b/submodules/BotPaymentsUI/Sources/BotReceiptController.swift index f09ee95dce..9ba4f97970 100644 --- a/submodules/BotPaymentsUI/Sources/BotReceiptController.swift +++ b/submodules/BotPaymentsUI/Sources/BotReceiptController.swift @@ -4,7 +4,6 @@ import Display import AsyncDisplayKit import TelegramCore import SwiftSignalKit -import Postbox import TelegramPresentationData import AccountContext @@ -19,13 +18,13 @@ public final class BotReceiptController: ViewController { } private let context: AccountContext - private let messageId: MessageId + private let messageId: EngineMessage.Id private var presentationData: PresentationData private var didPlayPresentationAnimation = false - public init(context: AccountContext, messageId: MessageId) { + public init(context: AccountContext, messageId: EngineMessage.Id) { self.context = context self.messageId = messageId diff --git a/submodules/BotPaymentsUI/Sources/BotReceiptControllerNode.swift b/submodules/BotPaymentsUI/Sources/BotReceiptControllerNode.swift index a34c40d6e5..fb7524d351 100644 --- a/submodules/BotPaymentsUI/Sources/BotReceiptControllerNode.swift +++ b/submodules/BotPaymentsUI/Sources/BotReceiptControllerNode.swift @@ -2,7 +2,6 @@ import Foundation import UIKit import AsyncDisplayKit import Display -import Postbox import TelegramCore import SwiftSignalKit import TelegramPresentationData @@ -174,7 +173,7 @@ enum BotReceiptEntry: ItemListNodeEntry { } } -private func botReceiptControllerEntries(presentationData: PresentationData, invoice: TelegramMediaInvoice?, formInvoice: BotPaymentInvoice?, formInfo: BotPaymentRequestedInfo?, shippingOption: BotPaymentShippingOption?, paymentMethodTitle: String?, botPeer: Peer?, tipAmount: Int64?) -> [BotReceiptEntry] { +private func botReceiptControllerEntries(presentationData: PresentationData, invoice: TelegramMediaInvoice?, formInvoice: BotPaymentInvoice?, formInfo: BotPaymentRequestedInfo?, shippingOption: BotPaymentShippingOption?, paymentMethodTitle: String?, botPeer: EnginePeer?, tipAmount: Int64?) -> [BotReceiptEntry] { var entries: [BotReceiptEntry] = [] var botName = "" @@ -279,7 +278,7 @@ final class BotReceiptControllerNode: ItemListControllerNode { private let actionButtonPanelSeparator: ASDisplayNode private let actionButton: BotCheckoutActionButton - init(controller: ItemListController?, navigationBar: NavigationBar, context: AccountContext, messageId: MessageId, dismissAnimated: @escaping () -> Void) { + init(controller: ItemListController?, navigationBar: NavigationBar, context: AccountContext, messageId: EngineMessage.Id, dismissAnimated: @escaping () -> Void) { self.context = context self.dismissAnimated = dismissAnimated @@ -287,7 +286,13 @@ final class BotReceiptControllerNode: ItemListControllerNode { let arguments = BotReceiptControllerArguments(account: context.account) - let signal: Signal<(ItemListPresentationData, (ItemListNodeState, Any)), NoError> = combineLatest(context.sharedContext.presentationData, receiptData.get(), context.account.postbox.loadedPeerWithId(messageId.peerId)) + let signal: Signal<(ItemListPresentationData, (ItemListNodeState, Any)), NoError> = combineLatest( + context.sharedContext.presentationData, + receiptData.get(), + context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: messageId.peerId) + ) + ) |> map { presentationData, receiptData, botPeer -> (ItemListPresentationData, (ItemListNodeState, Any)) in let nodeState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: botReceiptControllerEntries(presentationData: presentationData, invoice: receiptData?.4, formInvoice: receiptData?.0, formInfo: receiptData?.1, shippingOption: receiptData?.2, paymentMethodTitle: receiptData?.3, botPeer: botPeer, tipAmount: receiptData?.5), style: .blocks, focusItemTag: nil, emptyStateItem: nil, animateChanges: false) diff --git a/submodules/CallListUI/BUILD b/submodules/CallListUI/BUILD index 15d61132a7..6f02400710 100644 --- a/submodules/CallListUI/BUILD +++ b/submodules/CallListUI/BUILD @@ -6,11 +6,13 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", - "//submodules/Postbox:Postbox", "//submodules/TelegramCore:TelegramCore", "//submodules/TelegramPresentationData:TelegramPresentationData", "//submodules/TelegramUIPreferences:TelegramUIPreferences", diff --git a/submodules/CallListUI/Sources/CallListCallItem.swift b/submodules/CallListUI/Sources/CallListCallItem.swift index 06674a7027..3b08bf4f70 100644 --- a/submodules/CallListUI/Sources/CallListCallItem.swift +++ b/submodules/CallListUI/Sources/CallListCallItem.swift @@ -1,7 +1,6 @@ import Foundation import UIKit import AsyncDisplayKit -import Postbox import Display import SwiftSignalKit import TelegramCore @@ -68,8 +67,8 @@ class CallListCallItem: ListViewItem { let dateTimeFormat: PresentationDateTimeFormat let context: AccountContext let style: ItemListStyle - let topMessage: Message - let messages: [Message] + let topMessage: EngineMessage + let messages: [EngineMessage] let editing: Bool let revealed: Bool let interaction: CallListNodeInteraction @@ -78,7 +77,7 @@ class CallListCallItem: ListViewItem { let headerAccessoryItem: ListViewAccessoryItem? let header: ListViewItemHeader? - init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, context: AccountContext, style: ItemListStyle, topMessage: Message, messages: [Message], editing: Bool, revealed: Bool, displayHeader: Bool, interaction: CallListNodeInteraction) { + init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, context: AccountContext, style: ItemListStyle, topMessage: EngineMessage, messages: [EngineMessage], editing: Bool, revealed: Bool, displayHeader: Bool, interaction: CallListNodeInteraction) { self.presentationData = presentationData self.dateTimeFormat = dateTimeFormat self.context = context @@ -488,7 +487,7 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode { if peer.isDeleted { overrideImage = .deletedIcon } - strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads) + strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: EnginePeer(peer), overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads) } return (strongSelf.avatarNode.ready, { [weak strongSelf] animated in diff --git a/submodules/CallListUI/Sources/CallListController.swift b/submodules/CallListUI/Sources/CallListController.swift index 9fb3a327d0..74c0ec4405 100644 --- a/submodules/CallListUI/Sources/CallListController.swift +++ b/submodules/CallListUI/Sources/CallListController.swift @@ -2,7 +2,6 @@ import Foundation import UIKit import Display import AsyncDisplayKit -import Postbox import TelegramCore import SwiftSignalKit import TelegramPresentationData @@ -205,10 +204,11 @@ public final class CallListController: TelegramBaseController { } }, openInfo: { [weak self] peerId, messages in if let strongSelf = self { - let _ = (strongSelf.context.account.postbox.loadedPeerWithId(peerId) - |> take(1) + let _ = (strongSelf.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: peerId) + ) |> deliverOnMainQueue).start(next: { peer in - if let strongSelf = self, let controller = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer, mode: .calls(messages: messages), avatarInitiallyExpanded: false, fromChat: false) { + if let strongSelf = self, let peer = peer, let controller = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer._asPeer(), mode: .calls(messages: messages.map({ $0._asMessage() })), avatarInitiallyExpanded: false, fromChat: false) { (strongSelf.navigationController as? NavigationController)?.pushViewController(controller) } }) @@ -436,7 +436,7 @@ public final class CallListController: TelegramBaseController { } } - private func call(_ peerId: PeerId, isVideo: Bool, began: (() -> Void)? = nil) { + private func call(_ peerId: EnginePeer.Id, isVideo: Bool, began: (() -> Void)? = nil) { self.peerViewDisposable.set((self.context.account.viewTracker.peerView(peerId) |> take(1) |> deliverOnMainQueue).start(next: { [weak self] view in diff --git a/submodules/CallListUI/Sources/CallListControllerNode.swift b/submodules/CallListUI/Sources/CallListControllerNode.swift index dcd3cbdd65..bd995283c0 100644 --- a/submodules/CallListUI/Sources/CallListControllerNode.swift +++ b/submodules/CallListUI/Sources/CallListControllerNode.swift @@ -2,7 +2,6 @@ import Foundation import UIKit import AsyncDisplayKit import Display -import Postbox import TelegramCore import SwiftSignalKit import TelegramPresentationData @@ -25,8 +24,8 @@ private struct CallListNodeListViewTransition { let stationaryItemRange: (Int, Int)? } -private extension CallListViewEntry { - var lowestIndex: MessageIndex { +private extension EngineCallList.Item { + var lowestIndex: EngineMessage.Index { switch self { case let .hole(index): return index @@ -42,7 +41,7 @@ private extension CallListViewEntry { } } - var highestIndex: MessageIndex { + var highestIndex: EngineMessage.Index { switch self { case let .hole(index): return index @@ -60,14 +59,14 @@ private extension CallListViewEntry { } final class CallListNodeInteraction { - let setMessageIdWithRevealedOptions: (MessageId?, MessageId?) -> Void - let call: (PeerId, Bool) -> Void - let openInfo: (PeerId, [Message]) -> Void - let delete: ([MessageId]) -> Void + let setMessageIdWithRevealedOptions: (EngineMessage.Id?, EngineMessage.Id?) -> Void + let call: (EnginePeer.Id, Bool) -> Void + let openInfo: (EnginePeer.Id, [EngineMessage]) -> Void + let delete: ([EngineMessage.Id]) -> Void let updateShowCallsTab: (Bool) -> Void - let openGroupCall: (PeerId) -> Void + let openGroupCall: (EnginePeer.Id) -> Void - init(setMessageIdWithRevealedOptions: @escaping (MessageId?, MessageId?) -> Void, call: @escaping (PeerId, Bool) -> Void, openInfo: @escaping (PeerId, [Message]) -> Void, delete: @escaping ([MessageId]) -> Void, updateShowCallsTab: @escaping (Bool) -> Void, openGroupCall: @escaping (PeerId) -> Void) { + init(setMessageIdWithRevealedOptions: @escaping (EngineMessage.Id?, EngineMessage.Id?) -> Void, call: @escaping (EnginePeer.Id, Bool) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, delete: @escaping ([EngineMessage.Id]) -> Void, updateShowCallsTab: @escaping (Bool) -> Void, openGroupCall: @escaping (EnginePeer.Id) -> Void) { self.setMessageIdWithRevealedOptions = setMessageIdWithRevealedOptions self.call = call self.openInfo = openInfo @@ -82,7 +81,7 @@ struct CallListNodeState: Equatable { let dateTimeFormat: PresentationDateTimeFormat let disableAnimations: Bool let editing: Bool - let messageIdWithRevealedOptions: MessageId? + let messageIdWithRevealedOptions: EngineMessage.Id? func withUpdatedPresentationData(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, disableAnimations: Bool) -> CallListNodeState { return CallListNodeState(presentationData: presentationData, dateTimeFormat: dateTimeFormat, disableAnimations: disableAnimations, editing: self.editing, messageIdWithRevealedOptions: self.messageIdWithRevealedOptions) @@ -92,7 +91,7 @@ struct CallListNodeState: Equatable { return CallListNodeState(presentationData: self.presentationData, dateTimeFormat: self.dateTimeFormat, disableAnimations: self.disableAnimations, editing: editing, messageIdWithRevealedOptions: self.messageIdWithRevealedOptions) } - func withUpdatedMessageIdWithRevealedOptions(_ messageIdWithRevealedOptions: MessageId?) -> CallListNodeState { + func withUpdatedMessageIdWithRevealedOptions(_ messageIdWithRevealedOptions: EngineMessage.Id?) -> CallListNodeState { return CallListNodeState(presentationData: self.presentationData, dateTimeFormat: self.dateTimeFormat, disableAnimations: self.disableAnimations, editing: self.editing, messageIdWithRevealedOptions: messageIdWithRevealedOptions) } @@ -177,9 +176,9 @@ final class CallListControllerNode: ASDisplayNode { return _ready.get() } - var peerSelected: ((PeerId) -> Void)? + var peerSelected: ((EnginePeer.Id) -> Void)? var activateSearch: (() -> Void)? - var deletePeerChat: ((PeerId) -> Void)? + var deletePeerChat: ((EnginePeer.Id) -> Void)? var startNewCall: (() -> Void)? private let viewProcessingQueue = Queue() @@ -191,7 +190,7 @@ final class CallListControllerNode: ASDisplayNode { private var currentState: CallListNodeState private let statePromise: ValuePromise - private var currentLocationAndType = CallListNodeLocationAndType(location: .initial(count: 50), type: .all) + private var currentLocationAndType = CallListNodeLocationAndType(location: .initial(count: 50), scope: .all) private let callListLocationAndType = ValuePromise() private let callListDisposable = MetaDisposable() @@ -205,9 +204,9 @@ final class CallListControllerNode: ASDisplayNode { private let emptyButtonIconNode: ASImageNode private let emptyButtonTextNode: ImmediateTextNode - private let call: (PeerId, Bool) -> Void - private let joinGroupCall: (PeerId, CachedChannelData.ActiveCall) -> Void - private let openInfo: (PeerId, [Message]) -> Void + private let call: (EnginePeer.Id, Bool) -> Void + private let joinGroupCall: (EnginePeer.Id, EngineGroupCallDescription) -> Void + private let openInfo: (EnginePeer.Id, [EngineMessage]) -> Void private let emptyStateUpdated: (Bool) -> Void private let emptyStatePromise = Promise() @@ -215,7 +214,7 @@ final class CallListControllerNode: ASDisplayNode { private let openGroupCallDisposable = MetaDisposable() - init(controller: CallListController, context: AccountContext, mode: CallListControllerMode, presentationData: PresentationData, call: @escaping (PeerId, Bool) -> Void, joinGroupCall: @escaping (PeerId, CachedChannelData.ActiveCall) -> Void, openInfo: @escaping (PeerId, [Message]) -> Void, emptyStateUpdated: @escaping (Bool) -> Void) { + init(controller: CallListController, context: AccountContext, mode: CallListControllerMode, presentationData: PresentationData, call: @escaping (EnginePeer.Id, Bool) -> Void, joinGroupCall: @escaping (EnginePeer.Id, EngineGroupCallDescription) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, emptyStateUpdated: @escaping (Bool) -> Void) { self.controller = controller self.context = context self.mode = mode @@ -322,13 +321,12 @@ final class CallListControllerNode: ASDisplayNode { }, openInfo: { [weak self] peerId, messages in self?.openInfo(peerId, messages) }, delete: { [weak self] messageIds in - guard let strongSelf = self, let peerId = messageIds.first?.peerId else { + guard let peerId = messageIds.first?.peerId else { return } - - let _ = (strongSelf.context.account.postbox.transaction { transaction -> Peer? in - return transaction.getPeer(peerId) - } + let _ = (context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: peerId) + ) |> deliverOnMainQueue).start(next: { peer in guard let strongSelf = self, let peer = peer else { return @@ -381,19 +379,12 @@ final class CallListControllerNode: ASDisplayNode { } let disposable = strongSelf.openGroupCallDisposable - - let account = strongSelf.context.account + let engine = strongSelf.context.engine - var signal: Signal = strongSelf.context.account.postbox.transaction { transaction -> CachedChannelData.ActiveCall? in - let cachedData = transaction.getPeerCachedData(peerId: peerId) - if let cachedData = cachedData as? CachedChannelData { - return cachedData.activeCall - } else if let cachedData = cachedData as? CachedGroupData { - return cachedData.activeCall - } - return nil - } - |> mapToSignal { activeCall -> Signal in + var signal: Signal = context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.GroupCallDescription(id: peerId) + ) + |> mapToSignal { activeCall -> Signal in if let activeCall = activeCall { return .single(activeCall) } else { @@ -446,7 +437,7 @@ final class CallListControllerNode: ASDisplayNode { let callListViewUpdate = self.callListLocationAndType.get() |> distinctUntilChanged |> mapToSignal { locationAndType in - return callListViewForLocationAndType(locationAndType: locationAndType, account: context.account) + return callListViewForLocationAndType(locationAndType: locationAndType, engine: context.engine) } let previousView = Atomic(value: nil) @@ -468,10 +459,10 @@ final class CallListControllerNode: ASDisplayNode { return value } - let currentGroupCallPeerId: Signal + let currentGroupCallPeerId: Signal if let callManager = context.sharedContext.callManager { currentGroupCallPeerId = callManager.currentGroupCallSignal - |> map { call -> PeerId? in + |> map { call -> EnginePeer.Id? in call?.peerId } |> distinctUntilChanged @@ -479,19 +470,14 @@ final class CallListControllerNode: ASDisplayNode { currentGroupCallPeerId = .single(nil) } - let groupCalls = context.account.postbox.tailChatListView(groupId: .root, count: 100, summaryComponents: ChatListEntrySummaryComponents()) - |> map { view -> [Peer] in - var result: [Peer] = [] - for entry in view.0.entries { - switch entry { - case let .MessageEntry(_, _, _, _, _, renderedPeer, _, _, _, _): - if let channel = renderedPeer.peer as? TelegramChannel, channel.flags.contains(.hasActiveVoiceChat) { - result.append(channel) - } else if let group = renderedPeer.peer as? TelegramGroup, group.flags.contains(.hasActiveVoiceChat) { - result.append(group) - } - default: - break + let groupCalls: Signal<[EnginePeer], NoError> = context.engine.messages.chatList(group: .root, count: 100) + |> map { chatList -> [EnginePeer] in + var result: [EnginePeer] = [] + for item in chatList.items { + if case let .channel(channel) = item.renderedPeer.peer, channel.flags.contains(.hasActiveVoiceChat) { + result.append(.channel(channel)) + } else if case let .legacyGroup(group) = item.renderedPeer.peer, group.flags.contains(.hasActiveVoiceChat) { + result.append(.legacyGroup(group)) } } return result.sorted(by: { lhs, rhs in @@ -503,19 +489,15 @@ final class CallListControllerNode: ASDisplayNode { return lhs.id < rhs.id }) } - |> distinctUntilChanged(isEqual: { lhs, rhs in - if lhs.count != rhs.count { - return false - } - for i in 0 ..< lhs.count { - if !lhs[i].isEqual(rhs[i]) { - return false - } - } - return true - }) + |> distinctUntilChanged - let callListNodeViewTransition = combineLatest(callListViewUpdate, self.statePromise.get(), groupCalls, showCallsTab, currentGroupCallPeerId) + let callListNodeViewTransition = combineLatest( + callListViewUpdate, + self.statePromise.get(), + groupCalls, + showCallsTab, + currentGroupCallPeerId + ) |> mapToQueue { (updateAndType, state, groupCalls, showCallsTab, currentGroupCallPeerId) -> Signal in let (update, type) = updateAndType @@ -545,7 +527,7 @@ final class CallListControllerNode: ASDisplayNode { } } else { if previous?.originalView === update.view { - let previousCalls = previous?.filteredEntries.compactMap { item -> PeerId? in + let previousCalls = previous?.filteredEntries.compactMap { item -> EnginePeer.Id? in switch item { case let .groupCall(peer, _, _): return peer.id @@ -553,7 +535,7 @@ final class CallListControllerNode: ASDisplayNode { return nil } } - let updatedCalls = processedView.filteredEntries.compactMap { item -> PeerId? in + let updatedCalls = processedView.filteredEntries.compactMap { item -> EnginePeer.Id? in switch item { case let .groupCall(peer, _, _): return peer.id @@ -582,12 +564,14 @@ final class CallListControllerNode: ASDisplayNode { } } - return preparedCallListNodeViewTransition(from: previous, to: processedView, reason: reason, disableAnimations: disableAnimations, account: context.account, scrollPosition: update.scrollPosition) + return preparedCallListNodeViewTransition(from: previous, to: processedView, reason: reason, disableAnimations: disableAnimations, context: context, scrollPosition: update.scrollPosition) |> map({ mappedCallListNodeViewListTransition(context: context, presentationData: state.presentationData, showSettings: showSettings, nodeInteraction: nodeInteraction, transition: $0) }) |> runOn(prepareOnMainQueue ? Queue.mainQueue() : viewProcessingQueue) } - let appliedTransition = callListNodeViewTransition |> deliverOnMainQueue |> mapToQueue { [weak self] transition -> Signal in + let appliedTransition = callListNodeViewTransition + |> deliverOnMainQueue + |> mapToQueue { [weak self] transition -> Signal in if let strongSelf = self { return strongSelf.enqueueTransition(transition) } @@ -597,14 +581,14 @@ final class CallListControllerNode: ASDisplayNode { self.listNode.displayedItemRangeChanged = { [weak self] range, transactionOpaqueState in if let strongSelf = self, let range = range.loadedRange, let view = (transactionOpaqueState as? CallListOpaqueTransactionState)?.callListView.originalView { var location: CallListNodeLocation? - if range.firstIndex < 5 && view.later != nil { - location = .navigation(index: view.entries[view.entries.count - 1].highestIndex) - } else if range.firstIndex >= 5 && range.lastIndex >= view.entries.count - 5 && view.earlier != nil { - location = .navigation(index: view.entries[0].lowestIndex) + if range.firstIndex < 5 && view.hasLater { + location = .navigation(index: view.items[view.items.count - 1].highestIndex) + } else if range.firstIndex >= 5 && range.lastIndex >= view.items.count - 5 && view.hasEarlier { + location = .navigation(index: view.items[0].lowestIndex) } if let location = location, location != strongSelf.currentLocationAndType.location { - strongSelf.currentLocationAndType = CallListNodeLocationAndType(location: location, type: strongSelf.currentLocationAndType.type) + strongSelf.currentLocationAndType = CallListNodeLocationAndType(location: location, scope: strongSelf.currentLocationAndType.scope) strongSelf.callListLocationAndType.set(strongSelf.currentLocationAndType) } } @@ -615,9 +599,10 @@ final class CallListControllerNode: ASDisplayNode { self.callListLocationAndType.set(self.currentLocationAndType) let emptySignal = self.emptyStatePromise.get() |> distinctUntilChanged - let typeSignal = self.callListLocationAndType.get() |> map { locationAndType -> CallListViewType in - return locationAndType.type - } |> distinctUntilChanged + let typeSignal = self.callListLocationAndType.get() |> map { locationAndType -> EngineCallList.Scope in + return locationAndType.scope + } + |> distinctUntilChanged self.emptyStateDisposable.set((combineLatest(emptySignal, typeSignal, self.statePromise.get()) |> deliverOnMainQueue).start(next: { [weak self] isEmpty, type, state in if let strongSelf = self { @@ -649,7 +634,7 @@ final class CallListControllerNode: ASDisplayNode { self.emptyButtonIconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Call List/CallIcon"), color: presentationData.theme.list.itemAccentColor) - self.updateEmptyPlaceholder(theme: presentationData.theme, strings: presentationData.strings, type: self.currentLocationAndType.type, isHidden: self.emptyTextNode.alpha.isZero) + self.updateEmptyPlaceholder(theme: presentationData.theme, strings: presentationData.strings, type: self.currentLocationAndType.scope, isHidden: self.emptyTextNode.alpha.isZero) self.updateState { return $0.withUpdatedPresentationData(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, disableAnimations: true) @@ -666,7 +651,7 @@ final class CallListControllerNode: ASDisplayNode { private let textFont = Font.regular(16.0) private let buttonFont = Font.regular(17.0) - func updateEmptyPlaceholder(theme: PresentationTheme, strings: PresentationStrings, type: CallListViewType, isHidden: Bool) { + func updateEmptyPlaceholder(theme: PresentationTheme, strings: PresentationStrings, type: EngineCallList.Scope, isHidden: Bool) { let alpha: CGFloat = isHidden ? 0.0 : 1.0 let previousAlpha = self.emptyTextNode.alpha self.emptyTextNode.alpha = alpha @@ -691,7 +676,7 @@ final class CallListControllerNode: ASDisplayNode { self.emptyButtonNode.isUserInteractionEnabled = !isHidden if !isHidden { - let type = self.currentLocationAndType.type + let type = self.currentLocationAndType.scope let emptyText: String let buttonText = strings.Calls_StartNewCall if type == .missed { @@ -730,16 +715,16 @@ final class CallListControllerNode: ASDisplayNode { } } - func updateType(_ type: CallListViewType) { - if type != self.currentLocationAndType.type { + func updateType(_ type: EngineCallList.Scope) { + if type != self.currentLocationAndType.scope { if let view = self.callListView?.originalView { - var index: MessageIndex - if !view.entries.isEmpty { - index = view.entries[view.entries.count - 1].highestIndex + var index: EngineMessage.Index + if !view.items.isEmpty { + index = view.items[view.items.count - 1].highestIndex } else { - index = MessageIndex.absoluteUpperBound() + index = EngineMessage.Index.absoluteUpperBound() } - self.currentLocationAndType = CallListNodeLocationAndType(location: .changeType(index: index), type: type) + self.currentLocationAndType = CallListNodeLocationAndType(location: .changeType(index: index), scope: type) self.emptyStatePromise.set(.single(false)) self.callListLocationAndType.set(self.currentLocationAndType) } @@ -799,11 +784,11 @@ final class CallListControllerNode: ASDisplayNode { } func scrollToLatest() { - if let view = self.callListView?.originalView, view.later == nil { + if let view = self.callListView?.originalView, !view.hasLater { self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) } else { - let location: CallListNodeLocation = .scroll(index: MessageIndex.absoluteUpperBound(), sourceIndex: MessageIndex.absoluteLowerBound(), scrollPosition: .top(0.0), animated: true) - self.currentLocationAndType = CallListNodeLocationAndType(location: location, type: self.currentLocationAndType.type) + let location: CallListNodeLocation = .scroll(index: EngineMessage.Index.absoluteUpperBound(), sourceIndex: EngineMessage.Index.absoluteLowerBound(), scrollPosition: .top(0.0), animated: true) + self.currentLocationAndType = CallListNodeLocationAndType(location: location, scope: self.currentLocationAndType.scope) self.callListLocationAndType.set(self.currentLocationAndType) } } diff --git a/submodules/CallListUI/Sources/CallListGroupCallItem.swift b/submodules/CallListUI/Sources/CallListGroupCallItem.swift index 8cbdd2028f..d75d37dbc2 100644 --- a/submodules/CallListUI/Sources/CallListGroupCallItem.swift +++ b/submodules/CallListUI/Sources/CallListGroupCallItem.swift @@ -1,7 +1,6 @@ import Foundation import UIKit import AsyncDisplayKit -import Postbox import Display import SwiftSignalKit import TelegramCore @@ -60,7 +59,7 @@ class CallListGroupCallItem: ListViewItem { let presentationData: ItemListPresentationData let context: AccountContext let style: ItemListStyle - let peer: Peer + let peer: EnginePeer let isActive: Bool let editing: Bool let interaction: CallListNodeInteraction @@ -69,7 +68,7 @@ class CallListGroupCallItem: ListViewItem { let headerAccessoryItem: ListViewAccessoryItem? let header: ListViewItemHeader? - init(presentationData: ItemListPresentationData, context: AccountContext, style: ItemListStyle, peer: Peer, isActive: Bool, editing: Bool, interaction: CallListNodeInteraction) { + init(presentationData: ItemListPresentationData, context: AccountContext, style: ItemListStyle, peer: EnginePeer, isActive: Bool, editing: Bool, interaction: CallListNodeInteraction) { self.presentationData = presentationData self.context = context self.style = style diff --git a/submodules/CallListUI/Sources/CallListHoleItem.swift b/submodules/CallListUI/Sources/CallListHoleItem.swift index a75785810e..1dd52fda6b 100644 --- a/submodules/CallListUI/Sources/CallListHoleItem.swift +++ b/submodules/CallListUI/Sources/CallListHoleItem.swift @@ -1,7 +1,6 @@ import Foundation import UIKit import AsyncDisplayKit -import Postbox import Display import SwiftSignalKit import TelegramPresentationData diff --git a/submodules/CallListUI/Sources/CallListNodeEntries.swift b/submodules/CallListUI/Sources/CallListNodeEntries.swift index 42e20926b1..47f83e21e0 100644 --- a/submodules/CallListUI/Sources/CallListNodeEntries.swift +++ b/submodules/CallListUI/Sources/CallListNodeEntries.swift @@ -1,18 +1,17 @@ import Foundation import UIKit -import Postbox import TelegramCore import TelegramPresentationData import MergeLists enum CallListNodeEntryId: Hashable { case setting(Int32) - case groupCall(PeerId) - case hole(MessageIndex) - case message(MessageIndex) + case groupCall(EnginePeer.Id) + case hole(EngineMessage.Index) + case message(EngineMessage.Index) } -private func areMessagesEqual(_ lhsMessage: Message, _ rhsMessage: Message) -> Bool { +private func areMessagesEqual(_ lhsMessage: EngineMessage, _ rhsMessage: EngineMessage) -> Bool { if lhsMessage.stableVersion != rhsMessage.stableVersion { return false } @@ -26,9 +25,9 @@ enum CallListNodeEntry: Comparable, Identifiable { enum SortIndex: Comparable { case displayTab case displayTabInfo - case groupCall(PeerId, String) - case message(MessageIndex) - case hole(MessageIndex) + case groupCall(EnginePeer.Id, String) + case message(EngineMessage.Index) + case hole(EngineMessage.Index) static func <(lhs: SortIndex, rhs: SortIndex) -> Bool { switch lhs { @@ -79,9 +78,9 @@ enum CallListNodeEntry: Comparable, Identifiable { case displayTab(PresentationTheme, String, Bool) case displayTabInfo(PresentationTheme, String) - case groupCall(peer: Peer, editing: Bool, isActive: Bool) - case messageEntry(topMessage: Message, messages: [Message], theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, editing: Bool, hasActiveRevealControls: Bool, displayHeader: Bool, missed: Bool) - case holeEntry(index: MessageIndex, theme: PresentationTheme) + case groupCall(peer: EnginePeer, editing: Bool, isActive: Bool) + case messageEntry(topMessage: EngineMessage, messages: [EngineMessage], theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, editing: Bool, hasActiveRevealControls: Bool, displayHeader: Bool, missed: Bool) + case holeEntry(index: EngineMessage.Index, theme: PresentationTheme) var sortIndex: SortIndex { switch self { @@ -133,7 +132,7 @@ enum CallListNodeEntry: Comparable, Identifiable { } case let .groupCall(lhsPeer, lhsEditing, lhsIsActive): if case let .groupCall(rhsPeer, rhsEditing, rhsIsActive) = rhs { - if !lhsPeer.isEqual(rhsPeer) { + if lhsPeer != rhsPeer { return false } if lhsEditing != rhsEditing { @@ -194,9 +193,9 @@ enum CallListNodeEntry: Comparable, Identifiable { } } -func callListNodeEntriesForView(view: CallListView, groupCalls: [Peer], state: CallListNodeState, showSettings: Bool, showCallsTab: Bool, isRecentCalls: Bool, currentGroupCallPeerId: PeerId?) -> [CallListNodeEntry] { +func callListNodeEntriesForView(view: EngineCallList, groupCalls: [EnginePeer], state: CallListNodeState, showSettings: Bool, showCallsTab: Bool, isRecentCalls: Bool, currentGroupCallPeerId: EnginePeer.Id?) -> [CallListNodeEntry] { var result: [CallListNodeEntry] = [] - for entry in view.entries { + for entry in view.items { switch entry { case let .message(topMessage, messages): result.append(.messageEntry(topMessage: topMessage, messages: messages, theme: state.presentationData.theme, strings: state.presentationData.strings, dateTimeFormat: state.dateTimeFormat, editing: state.editing, hasActiveRevealControls: state.messageIdWithRevealedOptions == topMessage.id, displayHeader: !showSettings && isRecentCalls, missed: !isRecentCalls)) @@ -205,7 +204,7 @@ func callListNodeEntriesForView(view: CallListView, groupCalls: [Peer], state: C } } - if view.later == nil { + if !view.hasLater { if !showSettings && isRecentCalls { for peer in groupCalls.sorted(by: { lhs, rhs in let lhsTitle = lhs.compactDisplayTitle diff --git a/submodules/CallListUI/Sources/CallListNodeLocation.swift b/submodules/CallListUI/Sources/CallListNodeLocation.swift index b7905b6f3c..5a9c07ddcb 100644 --- a/submodules/CallListUI/Sources/CallListNodeLocation.swift +++ b/submodules/CallListUI/Sources/CallListNodeLocation.swift @@ -1,15 +1,14 @@ import Foundation import UIKit -import Postbox import TelegramCore import SwiftSignalKit import Display enum CallListNodeLocation: Equatable { case initial(count: Int) - case changeType(index: MessageIndex) - case navigation(index: MessageIndex) - case scroll(index: MessageIndex, sourceIndex: MessageIndex, scrollPosition: ListViewScrollPosition, animated: Bool) + case changeType(index: EngineMessage.Index) + case navigation(index: EngineMessage.Index) + case scroll(index: EngineMessage.Index, sourceIndex: EngineMessage.Index, scrollPosition: ListViewScrollPosition, animated: Bool) static func ==(lhs: CallListNodeLocation, rhs: CallListNodeLocation) -> Bool { switch lhs { @@ -28,11 +27,7 @@ enum CallListNodeLocation: Equatable { struct CallListNodeLocationAndType: Equatable { let location: CallListNodeLocation - let type: CallListViewType - - static func ==(lhs: CallListNodeLocationAndType, rhs: CallListNodeLocationAndType) -> Bool { - return lhs.location == rhs.location && lhs.type == rhs.type - } + let scope: EngineCallList.Scope } enum CallListNodeViewUpdateType { @@ -44,47 +39,67 @@ enum CallListNodeViewUpdateType { } struct CallListNodeViewUpdate { - let view: CallListView + let view: EngineCallList let type: CallListNodeViewUpdateType let scrollPosition: CallListNodeViewScrollPosition? } -func callListViewForLocationAndType(locationAndType: CallListNodeLocationAndType, account: Account) -> Signal<(CallListNodeViewUpdate, CallListViewType), NoError> { +func callListViewForLocationAndType(locationAndType: CallListNodeLocationAndType, engine: TelegramEngine) -> Signal<(CallListNodeViewUpdate, EngineCallList.Scope), NoError> { switch locationAndType.location { - case let .initial(count): - return account.viewTracker.callListView(type: locationAndType.type, index: MessageIndex.absoluteUpperBound(), count: count) |> map { view -> (CallListNodeViewUpdate, CallListViewType) in - return (CallListNodeViewUpdate(view: view, type: .Generic, scrollPosition: nil), locationAndType.type) + case let .initial(count): + return engine.messages.callList( + scope: locationAndType.scope, + index: EngineMessage.Index.absoluteUpperBound(), + itemCount: count + ) + |> map { view -> (CallListNodeViewUpdate, EngineCallList.Scope) in + return (CallListNodeViewUpdate(view: view, type: .Generic, scrollPosition: nil), locationAndType.scope) + } + case let .changeType(index): + return engine.messages.callList( + scope: locationAndType.scope, + index: index, + itemCount: 120 + ) + |> map { view -> (CallListNodeViewUpdate, EngineCallList.Scope) in + return (CallListNodeViewUpdate(view: view, type: .ReloadAnimated, scrollPosition: nil), locationAndType.scope) + } + case let .navigation(index): + var first = true + return engine.messages.callList( + scope: locationAndType.scope, + index: index, + itemCount: 120 + ) + |> map { view -> (CallListNodeViewUpdate, EngineCallList.Scope) in + let genericType: CallListNodeViewUpdateType + if first { + first = false + genericType = .UpdateVisible + } else { + genericType = .Generic } - case let .changeType(index): - return account.viewTracker.callListView(type: locationAndType.type, index: index, count: 120) |> map { view -> (CallListNodeViewUpdate, CallListViewType) in - return (CallListNodeViewUpdate(view: view, type: .ReloadAnimated, scrollPosition: nil), locationAndType.type) - } - case let .navigation(index): - var first = true - return account.viewTracker.callListView(type: locationAndType.type, index: index, count: 120) |> map { view -> (CallListNodeViewUpdate, CallListViewType) in - let genericType: CallListNodeViewUpdateType - if first { - first = false - genericType = .UpdateVisible - } else { - genericType = .Generic - } - return (CallListNodeViewUpdate(view: view, type: genericType, scrollPosition: nil), locationAndType.type) - } - case let .scroll(index, sourceIndex, scrollPosition, animated): - let directionHint: ListViewScrollToItemDirectionHint = sourceIndex > index ? .Down : .Up - let callScrollPosition: CallListNodeViewScrollPosition = .index(index: index, position: scrollPosition, directionHint: directionHint, animated: animated) - var first = true - return account.viewTracker.callListView(type: locationAndType.type, index: index, count: 120) |> map { view -> (CallListNodeViewUpdate, CallListViewType) in - let genericType: CallListNodeViewUpdateType - let scrollPosition: CallListNodeViewScrollPosition? = first ? callScrollPosition : nil - if first { - first = false - genericType = .UpdateVisible - } else { - genericType = .Generic - } - return (CallListNodeViewUpdate(view: view, type: genericType, scrollPosition: scrollPosition), locationAndType.type) + return (CallListNodeViewUpdate(view: view, type: genericType, scrollPosition: nil), locationAndType.scope) + } + case let .scroll(index, sourceIndex, scrollPosition, animated): + let directionHint: ListViewScrollToItemDirectionHint = sourceIndex > index ? .Down : .Up + let callScrollPosition: CallListNodeViewScrollPosition = .index(index: index, position: scrollPosition, directionHint: directionHint, animated: animated) + var first = true + return engine.messages.callList( + scope: locationAndType.scope, + index: index, + itemCount: 120 + ) + |> map { view -> (CallListNodeViewUpdate, EngineCallList.Scope) in + let genericType: CallListNodeViewUpdateType + let scrollPosition: CallListNodeViewScrollPosition? = first ? callScrollPosition : nil + if first { + first = false + genericType = .UpdateVisible + } else { + genericType = .Generic } + return (CallListNodeViewUpdate(view: view, type: genericType, scrollPosition: scrollPosition), locationAndType.scope) + } } } diff --git a/submodules/CallListUI/Sources/CallListViewTransition.swift b/submodules/CallListUI/Sources/CallListViewTransition.swift index aa70ded605..27c8725318 100644 --- a/submodules/CallListUI/Sources/CallListViewTransition.swift +++ b/submodules/CallListUI/Sources/CallListViewTransition.swift @@ -1,14 +1,14 @@ import Foundation import UIKit -import Postbox import TelegramCore import SwiftSignalKit import Display import MergeLists import ItemListUI +import AccountContext struct CallListNodeView { - let originalView: CallListView + let originalView: EngineCallList let filteredEntries: [CallListNodeEntry] let presentationData: ItemListPresentationData } @@ -45,10 +45,10 @@ struct CallListNodeViewTransition { } enum CallListNodeViewScrollPosition { - case index(index: MessageIndex, position: ListViewScrollPosition, directionHint: ListViewScrollToItemDirectionHint, animated: Bool) + case index(index: EngineMessage.Index, position: ListViewScrollPosition, directionHint: ListViewScrollToItemDirectionHint, animated: Bool) } -func preparedCallListNodeViewTransition(from fromView: CallListNodeView?, to toView: CallListNodeView, reason: CallListNodeViewTransitionReason, disableAnimations: Bool, account: Account, scrollPosition: CallListNodeViewScrollPosition?) -> Signal { +func preparedCallListNodeViewTransition(from fromView: CallListNodeView?, to toView: CallListNodeView, reason: CallListNodeViewTransitionReason, disableAnimations: Bool, context: AccountContext, scrollPosition: CallListNodeViewScrollPosition?) -> Signal { return Signal { subscriber in let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromView?.filteredEntries ?? [], rightList: toView.filteredEntries, allUpdated: fromView?.presentationData != toView.presentationData) @@ -68,11 +68,11 @@ func preparedCallListNodeViewTransition(from fromView: CallListNodeView?, to toV var options: ListViewDeleteAndInsertOptions = [] var maxAnimatedInsertionIndex = -1 - var stationaryItemRange: (Int, Int)? - var scrollToItem: ListViewScrollToItem? + let stationaryItemRange: (Int, Int)? = nil + var scrollToItem: ListViewScrollToItem? = nil var wasEmpty = false - if let fromView = fromView, fromView.originalView.entries.isEmpty { + if let fromView = fromView, fromView.originalView.items.isEmpty { wasEmpty = true } diff --git a/submodules/Camera/BUILD b/submodules/Camera/BUILD index 6cced36441..4cb5ed826e 100644 --- a/submodules/Camera/BUILD +++ b/submodules/Camera/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/ChatHistoryImportTasks/BUILD b/submodules/ChatHistoryImportTasks/BUILD index 6e0b39f4c3..b7be33ae9a 100644 --- a/submodules/ChatHistoryImportTasks/BUILD +++ b/submodules/ChatHistoryImportTasks/BUILD @@ -6,10 +6,10 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ - "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", - "//submodules/Postbox:Postbox", - "//submodules/TelegramCore:TelegramCore", ], visibility = [ "//visibility:public", diff --git a/submodules/ChatHistoryImportTasks/Sources/ChatHistoryImportTask.swift b/submodules/ChatHistoryImportTasks/Sources/ChatHistoryImportTask.swift index 2a50b6f8cb..66621571a3 100644 --- a/submodules/ChatHistoryImportTasks/Sources/ChatHistoryImportTask.swift +++ b/submodules/ChatHistoryImportTasks/Sources/ChatHistoryImportTask.swift @@ -1,14 +1,4 @@ import Foundation -import Postbox -import TelegramCore -import SwiftSignalKit public enum ChatHistoryImportTasks { - public final class Context { - - } - - public static func importState(peerId: PeerId) -> Signal { - return .single(nil) - } } diff --git a/submodules/ChatImportUI/BUILD b/submodules/ChatImportUI/BUILD index 63889b0ae2..4a4235c56a 100644 --- a/submodules/ChatImportUI/BUILD +++ b/submodules/ChatImportUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/ChatInterfaceState/BUILD b/submodules/ChatInterfaceState/BUILD index 3ed561edb1..c1850fcdaa 100644 --- a/submodules/ChatInterfaceState/BUILD +++ b/submodules/ChatInterfaceState/BUILD @@ -6,11 +6,13 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", - "//submodules/Postbox:Postbox", "//submodules/TelegramCore:TelegramCore", "//submodules/TextFormat:TextFormat", "//submodules/AccountContext:AccountContext", diff --git a/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift b/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift index 2daae7aa39..ff8496c42d 100644 --- a/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift +++ b/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift @@ -1,80 +1,89 @@ import Foundation import UIKit -import Postbox import TelegramCore import TextFormat import AccountContext +import SwiftSignalKit public enum ChatTextInputMediaRecordingButtonMode: Int32 { case audio = 0 case video = 1 } -public struct ChatInterfaceSelectionState: PostboxCoding, Equatable { - public let selectedIds: Set +public struct ChatInterfaceSelectionState: Codable, Equatable { + public let selectedIds: Set public static func ==(lhs: ChatInterfaceSelectionState, rhs: ChatInterfaceSelectionState) -> Bool { return lhs.selectedIds == rhs.selectedIds } - public init(selectedIds: Set) { + public init(selectedIds: Set) { self.selectedIds = selectedIds } - - public init(decoder: PostboxDecoder) { - if let data = decoder.decodeBytesForKeyNoCopy("i") { - self.selectedIds = Set(MessageId.decodeArrayFromBuffer(data)) + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + + if let data = try? container.decodeIfPresent(Data.self, forKey: "i") { + self.selectedIds = Set(EngineMessage.Id.decodeArrayFromData(data)) } else { self.selectedIds = Set() } } - - public func encode(_ encoder: PostboxEncoder) { - let buffer = WriteBuffer() - MessageId.encodeArrayToBuffer(Array(selectedIds), buffer: buffer) - encoder.encodeBytes(buffer, forKey: "i") + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + let data = EngineMessage.Id.encodeArrayToData(Array(selectedIds)) + + try container.encode(data, forKey: "i") } } -public struct ChatEditMessageState: PostboxCoding, Equatable { - public let messageId: MessageId +public struct ChatEditMessageState: Codable, Equatable { + public let messageId: EngineMessage.Id public let inputState: ChatTextInputState public let disableUrlPreview: String? public let inputTextMaxLength: Int32? - public init(messageId: MessageId, inputState: ChatTextInputState, disableUrlPreview: String?, inputTextMaxLength: Int32?) { + public init(messageId: EngineMessage.Id, inputState: ChatTextInputState, disableUrlPreview: String?, inputTextMaxLength: Int32?) { self.messageId = messageId self.inputState = inputState self.disableUrlPreview = disableUrlPreview self.inputTextMaxLength = inputTextMaxLength } - - public init(decoder: PostboxDecoder) { - self.messageId = MessageId(peerId: PeerId(decoder.decodeInt64ForKey("mp", orElse: 0)), namespace: decoder.decodeInt32ForKey("mn", orElse: 0), id: decoder.decodeInt32ForKey("mi", orElse: 0)) - if let inputState = decoder.decodeObjectForKey("is", decoder: { return ChatTextInputState(decoder: $0) }) as? ChatTextInputState { + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + + self.messageId = EngineMessage.Id( + peerId: EnginePeer.Id((try? container.decode(Int64.self, forKey: "mp")) ?? 0), + namespace: (try? container.decode(Int32.self, forKey: "mn")) ?? 0, + id: (try? container.decode(Int32.self, forKey: "mi")) ?? 0 + ) + + if let inputState = try? container.decode(ChatTextInputState.self, forKey: "is") { self.inputState = inputState } else { self.inputState = ChatTextInputState() } - self.disableUrlPreview = decoder.decodeOptionalStringForKey("dup") - self.inputTextMaxLength = decoder.decodeOptionalInt32ForKey("tl") + + self.disableUrlPreview = try? container.decodeIfPresent(String.self, forKey: "dup") + self.inputTextMaxLength = try? container.decodeIfPresent(Int32.self, forKey: "tl") } - - public func encode(_ encoder: PostboxEncoder) { - encoder.encodeInt64(self.messageId.peerId.toInt64(), forKey: "mp") - encoder.encodeInt32(self.messageId.namespace, forKey: "mn") - encoder.encodeInt32(self.messageId.id, forKey: "mi") - encoder.encodeObject(self.inputState, forKey: "is") - if let disableUrlPreview = self.disableUrlPreview { - encoder.encodeString(disableUrlPreview, forKey: "dup") - } else { - encoder.encodeNil(forKey: "dup") - } - if let inputTextMaxLength = self.inputTextMaxLength { - encoder.encodeInt32(inputTextMaxLength, forKey: "ml") - } else { - encoder.encodeNil(forKey: "ml") - } + + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + try container.encode(self.messageId.peerId.toInt64(), forKey: "mp") + try container.encode(self.messageId.namespace, forKey: "mn") + try container.encode(self.messageId.id, forKey: "mi") + + try container.encode(self.inputState, forKey: "is") + + try container.encodeIfPresent(self.disableUrlPreview, forKey: "dup") + try container.encodeIfPresent(self.inputTextMaxLength, forKey: "tl") } public static func ==(lhs: ChatEditMessageState, rhs: ChatEditMessageState) -> Bool { @@ -90,11 +99,11 @@ public struct ChatEditMessageState: PostboxCoding, Equatable { } } -public struct ChatInterfaceMessageActionsState: PostboxCoding, Equatable { - public var closedButtonKeyboardMessageId: MessageId? - public var dismissedButtonKeyboardMessageId: MessageId? - public var processedSetupReplyMessageId: MessageId? - public var closedPinnedMessageId: MessageId? +public struct ChatInterfaceMessageActionsState: Codable, Equatable { + public var closedButtonKeyboardMessageId: EngineMessage.Id? + public var dismissedButtonKeyboardMessageId: EngineMessage.Id? + public var processedSetupReplyMessageId: EngineMessage.Id? + public var closedPinnedMessageId: EngineMessage.Id? public var closedPeerSpecificPackSetup: Bool = false public var dismissedAddContactPhoneNumber: String? @@ -111,7 +120,7 @@ public struct ChatInterfaceMessageActionsState: PostboxCoding, Equatable { self.dismissedAddContactPhoneNumber = nil } - public init(closedButtonKeyboardMessageId: MessageId?, dismissedButtonKeyboardMessageId: MessageId?, processedSetupReplyMessageId: MessageId?, closedPinnedMessageId: MessageId?, closedPeerSpecificPackSetup: Bool, dismissedAddContactPhoneNumber: String?) { + public init(closedButtonKeyboardMessageId: EngineMessage.Id?, dismissedButtonKeyboardMessageId: EngineMessage.Id?, processedSetupReplyMessageId: EngineMessage.Id?, closedPinnedMessageId: EngineMessage.Id?, closedPeerSpecificPackSetup: Bool, dismissedAddContactPhoneNumber: String?) { self.closedButtonKeyboardMessageId = closedButtonKeyboardMessageId self.dismissedButtonKeyboardMessageId = dismissedButtonKeyboardMessageId self.processedSetupReplyMessageId = processedSetupReplyMessageId @@ -120,105 +129,122 @@ public struct ChatInterfaceMessageActionsState: PostboxCoding, Equatable { self.dismissedAddContactPhoneNumber = dismissedAddContactPhoneNumber } - public init(decoder: PostboxDecoder) { - if let closedMessageIdPeerId = decoder.decodeOptionalInt64ForKey("cb.p"), let closedMessageIdNamespace = decoder.decodeOptionalInt32ForKey("cb.n"), let closedMessageIdId = decoder.decodeOptionalInt32ForKey("cb.i") { - self.closedButtonKeyboardMessageId = MessageId(peerId: PeerId(closedMessageIdPeerId), namespace: closedMessageIdNamespace, id: closedMessageIdId) + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + + if let closedMessageIdPeerId = try? container.decodeIfPresent(Int64.self, forKey: "cb.p"), let closedMessageIdNamespace = try? container.decodeIfPresent(Int32.self, forKey: "cb.n"), let closedMessageIdId = try? container.decodeIfPresent(Int32.self, forKey: "cb.i") { + self.closedButtonKeyboardMessageId = EngineMessage.Id(peerId: EnginePeer.Id(closedMessageIdPeerId), namespace: closedMessageIdNamespace, id: closedMessageIdId) } else { self.closedButtonKeyboardMessageId = nil } - if let messageIdPeerId = decoder.decodeOptionalInt64ForKey("dismissedbuttons.p"), let messageIdNamespace = decoder.decodeOptionalInt32ForKey("dismissedbuttons.n"), let messageIdId = decoder.decodeOptionalInt32ForKey("dismissedbuttons.i") { - self.dismissedButtonKeyboardMessageId = MessageId(peerId: PeerId(messageIdPeerId), namespace: messageIdNamespace, id: messageIdId) + if let messageIdPeerId = try? container.decodeIfPresent(Int64.self, forKey: "dismissedbuttons.p"), let messageIdNamespace = try? container.decodeIfPresent(Int32.self, forKey: "dismissedbuttons.n"), let messageIdId = try? container.decodeIfPresent(Int32.self, forKey: "dismissedbuttons.i") { + self.dismissedButtonKeyboardMessageId = EngineMessage.Id(peerId: EnginePeer.Id(messageIdPeerId), namespace: messageIdNamespace, id: messageIdId) } else { self.dismissedButtonKeyboardMessageId = nil } - if let processedMessageIdPeerId = decoder.decodeOptionalInt64ForKey("pb.p"), let processedMessageIdNamespace = decoder.decodeOptionalInt32ForKey("pb.n"), let processedMessageIdId = decoder.decodeOptionalInt32ForKey("pb.i") { - self.processedSetupReplyMessageId = MessageId(peerId: PeerId(processedMessageIdPeerId), namespace: processedMessageIdNamespace, id: processedMessageIdId) + if let processedMessageIdPeerId = try? container.decodeIfPresent(Int64.self, forKey: "pb.p"), let processedMessageIdNamespace = try? container.decodeIfPresent(Int32.self, forKey: "pb.n"), let processedMessageIdId = try? container.decodeIfPresent(Int32.self, forKey: "pb.i") { + self.processedSetupReplyMessageId = EngineMessage.Id(peerId: EnginePeer.Id(processedMessageIdPeerId), namespace: processedMessageIdNamespace, id: processedMessageIdId) } else { self.processedSetupReplyMessageId = nil } - if let closedPinnedMessageIdPeerId = decoder.decodeOptionalInt64ForKey("cp.p"), let closedPinnedMessageIdNamespace = decoder.decodeOptionalInt32ForKey("cp.n"), let closedPinnedMessageIdId = decoder.decodeOptionalInt32ForKey("cp.i") { - self.closedPinnedMessageId = MessageId(peerId: PeerId(closedPinnedMessageIdPeerId), namespace: closedPinnedMessageIdNamespace, id: closedPinnedMessageIdId) + if let closedPinnedMessageIdPeerId = try? container.decodeIfPresent(Int64.self, forKey: "cp.p"), let closedPinnedMessageIdNamespace = try? container.decodeIfPresent(Int32.self, forKey: "cp.n"), let closedPinnedMessageIdId = try? container.decodeIfPresent(Int32.self, forKey: "cp.i") { + self.closedPinnedMessageId = EngineMessage.Id(peerId: EnginePeer.Id(closedPinnedMessageIdPeerId), namespace: closedPinnedMessageIdNamespace, id: closedPinnedMessageIdId) } else { self.closedPinnedMessageId = nil } - self.closedPeerSpecificPackSetup = decoder.decodeInt32ForKey("cpss", orElse: 0) != 0 + self.closedPeerSpecificPackSetup = ((try? container.decode(Int32.self, forKey: "cpss")) ?? 0) != 0 + + self.dismissedAddContactPhoneNumber = try? container.decodeIfPresent(String.self, forKey: "dismissedAddContactPhoneNumber") } - public func encode(_ encoder: PostboxEncoder) { + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + if let closedButtonKeyboardMessageId = self.closedButtonKeyboardMessageId { - encoder.encodeInt64(closedButtonKeyboardMessageId.peerId.toInt64(), forKey: "cb.p") - encoder.encodeInt32(closedButtonKeyboardMessageId.namespace, forKey: "cb.n") - encoder.encodeInt32(closedButtonKeyboardMessageId.id, forKey: "cb.i") + try container.encode(closedButtonKeyboardMessageId.peerId.toInt64(), forKey: "cb.p") + try container.encode(closedButtonKeyboardMessageId.namespace, forKey: "cb.n") + try container.encode(closedButtonKeyboardMessageId.id, forKey: "cb.i") } else { - encoder.encodeNil(forKey: "cb.p") - encoder.encodeNil(forKey: "cb.n") - encoder.encodeNil(forKey: "cb.i") + try container.encodeNil(forKey: "cb.p") + try container.encodeNil(forKey: "cb.n") + try container.encodeNil(forKey: "cb.i") } if let dismissedButtonKeyboardMessageId = self.dismissedButtonKeyboardMessageId { - encoder.encodeInt64(dismissedButtonKeyboardMessageId.peerId.toInt64(), forKey: "dismissedbuttons.p") - encoder.encodeInt32(dismissedButtonKeyboardMessageId.namespace, forKey: "dismissedbuttons.n") - encoder.encodeInt32(dismissedButtonKeyboardMessageId.id, forKey: "dismissedbuttons.i") + try container.encode(dismissedButtonKeyboardMessageId.peerId.toInt64(), forKey: "dismissedbuttons.p") + try container.encode(dismissedButtonKeyboardMessageId.namespace, forKey: "dismissedbuttons.n") + try container.encode(dismissedButtonKeyboardMessageId.id, forKey: "dismissedbuttons.i") } else { - encoder.encodeNil(forKey: "dismissedbuttons.p") - encoder.encodeNil(forKey: "dismissedbuttons.n") - encoder.encodeNil(forKey: "dismissedbuttons.i") + try container.encodeNil(forKey: "dismissedbuttons.p") + try container.encodeNil(forKey: "dismissedbuttons.n") + try container.encodeNil(forKey: "dismissedbuttons.i") } if let processedSetupReplyMessageId = self.processedSetupReplyMessageId { - encoder.encodeInt64(processedSetupReplyMessageId.peerId.toInt64(), forKey: "pb.p") - encoder.encodeInt32(processedSetupReplyMessageId.namespace, forKey: "pb.n") - encoder.encodeInt32(processedSetupReplyMessageId.id, forKey: "pb.i") + try container.encode(processedSetupReplyMessageId.peerId.toInt64(), forKey: "pb.p") + try container.encode(processedSetupReplyMessageId.namespace, forKey: "pb.n") + try container.encode(processedSetupReplyMessageId.id, forKey: "pb.i") } else { - encoder.encodeNil(forKey: "pb.p") - encoder.encodeNil(forKey: "pb.n") - encoder.encodeNil(forKey: "pb.i") + try container.encodeNil(forKey: "pb.p") + try container.encodeNil(forKey: "pb.n") + try container.encodeNil(forKey: "pb.i") } if let closedPinnedMessageId = self.closedPinnedMessageId { - encoder.encodeInt64(closedPinnedMessageId.peerId.toInt64(), forKey: "cp.p") - encoder.encodeInt32(closedPinnedMessageId.namespace, forKey: "cp.n") - encoder.encodeInt32(closedPinnedMessageId.id, forKey: "cp.i") + try container.encode(closedPinnedMessageId.peerId.toInt64(), forKey: "cp.p") + try container.encode(closedPinnedMessageId.namespace, forKey: "cp.n") + try container.encode(closedPinnedMessageId.id, forKey: "cp.i") } else { - encoder.encodeNil(forKey: "cp.p") - encoder.encodeNil(forKey: "cp.n") - encoder.encodeNil(forKey: "cp.i") + try container.encodeNil(forKey: "cp.p") + try container.encodeNil(forKey: "cp.n") + try container.encodeNil(forKey: "cp.i") } - encoder.encodeInt32(self.closedPeerSpecificPackSetup ? 1 : 0, forKey: "cpss") + try container.encode((self.closedPeerSpecificPackSetup ? 1 : 0) as Int32, forKey: "cpss") if let dismissedAddContactPhoneNumber = self.dismissedAddContactPhoneNumber { - encoder.encodeString(dismissedAddContactPhoneNumber, forKey: "dismissedAddContactPhoneNumber") + try container.encode(dismissedAddContactPhoneNumber, forKey: "dismissedAddContactPhoneNumber") } else { - encoder.encodeNil(forKey: "dismissedAddContactPhoneNumber") + try container.encodeNil(forKey: "dismissedAddContactPhoneNumber") } } } -public struct ChatInterfaceHistoryScrollState: PostboxCoding, Equatable { - public let messageIndex: MessageIndex +public struct ChatInterfaceHistoryScrollState: Codable, Equatable { + public let messageIndex: EngineMessage.Index public let relativeOffset: Double - public init(messageIndex: MessageIndex, relativeOffset: Double) { + public init(messageIndex: EngineMessage.Index, relativeOffset: Double) { self.messageIndex = messageIndex self.relativeOffset = relativeOffset } - public init(decoder: PostboxDecoder) { - self.messageIndex = MessageIndex(id: MessageId(peerId: PeerId(decoder.decodeInt64ForKey("m.p", orElse: 0)), namespace: decoder.decodeInt32ForKey("m.n", orElse: 0), id: decoder.decodeInt32ForKey("m.i", orElse: 0)), timestamp: decoder.decodeInt32ForKey("m.t", orElse: 0)) - self.relativeOffset = decoder.decodeDoubleForKey("ro", orElse: 0.0) + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + + self.messageIndex = EngineMessage.Index( + id: EngineMessage.Id( + peerId: EnginePeer.Id((try? container.decode(Int64.self, forKey: "m.p")) ?? 0), + namespace: (try? container.decode(Int32.self, forKey: "m.n")) ?? 0, + id: (try? container.decode(Int32.self, forKey: "m.i")) ?? 0 + ), + timestamp: (try? container.decode(Int32.self, forKey: "m.t")) ?? 0 + ) + self.relativeOffset = (try? container.decode(Double.self, forKey: "ro")) ?? 0.0 } - public func encode(_ encoder: PostboxEncoder) { - encoder.encodeInt32(self.messageIndex.timestamp, forKey: "m.t") - encoder.encodeInt64(self.messageIndex.id.peerId.toInt64(), forKey: "m.p") - encoder.encodeInt32(self.messageIndex.id.namespace, forKey: "m.n") - encoder.encodeInt32(self.messageIndex.id.id, forKey: "m.i") - encoder.encodeDouble(self.relativeOffset, forKey: "ro") + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + try container.encode(self.messageIndex.timestamp, forKey: "m.t") + try container.encode(self.messageIndex.id.peerId.toInt64(), forKey: "m.p") + try container.encode(self.messageIndex.id.namespace, forKey: "m.n") + try container.encode(self.messageIndex.id.id, forKey: "m.i") + try container.encode(self.relativeOffset, forKey: "ro") } public static func ==(lhs: ChatInterfaceHistoryScrollState, rhs: ChatInterfaceHistoryScrollState) -> Bool { @@ -232,12 +258,12 @@ public struct ChatInterfaceHistoryScrollState: PostboxCoding, Equatable { } } -public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equatable { +public final class ChatInterfaceState: Codable, Equatable { public let timestamp: Int32 public let composeInputState: ChatTextInputState public let composeDisableUrlPreview: String? - public let replyMessageId: MessageId? - public let forwardMessageIds: [MessageId]? + public let replyMessageId: EngineMessage.Id? + public let forwardMessageIds: [EngineMessage.Id]? public let editMessage: ChatEditMessageState? public let selectionState: ChatInterfaceSelectionState? public let messageActionsState: ChatInterfaceMessageActionsState @@ -246,35 +272,15 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata public let silentPosting: Bool public let inputLanguage: String? - public var associatedMessageIds: [MessageId] { - var ids: [MessageId] = [] - if let editMessage = self.editMessage { - ids.append(editMessage.messageId) - } - return ids - } - - public var chatListEmbeddedState: PeerChatListEmbeddedInterfaceState? { - if self.composeInputState.inputText.length != 0 && self.timestamp != 0 { - return ChatEmbeddedInterfaceState(timestamp: self.timestamp, text: self.composeInputState.inputText) - } else { - return nil - } - } - public var synchronizeableInputState: SynchronizeableChatInputState? { if self.composeInputState.inputText.length == 0 { return nil } else { - return SynchronizeableChatInputState(replyToMessageId: self.replyMessageId, text: self.composeInputState.inputText.string, entities: generateChatInputTextEntities(self.composeInputState.inputText), timestamp: self.timestamp) + return SynchronizeableChatInputState(replyToMessageId: self.replyMessageId, text: self.composeInputState.inputText.string, entities: generateChatInputTextEntities(self.composeInputState.inputText), timestamp: self.timestamp, textSelection: self.composeInputState.selectionRange) } } - - public var historyScrollMessageIndex: MessageIndex? { - return self.historyScrollState?.messageIndex - } - - public func withUpdatedSynchronizeableInputState(_ state: SynchronizeableChatInputState?) -> SynchronizeableChatInterfaceState { + + public func withUpdatedSynchronizeableInputState(_ state: SynchronizeableChatInputState?) -> ChatInterfaceState { var result = self.withUpdatedComposeInputState(ChatTextInputState(inputText: chatInputStateStringWithAppliedEntities(state?.text ?? "", entities: state?.entities ?? []))).withUpdatedReplyMessageId(state?.replyToMessageId) if let timestamp = state?.timestamp { result = result.withUpdatedTimestamp(timestamp) @@ -282,6 +288,10 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata return result } + public var historyScrollMessageIndex: EngineMessage.Index? { + return self.historyScrollState?.messageIndex + } + public var effectiveInputState: ChatTextInputState { if let editMessage = self.editMessage { return editMessage.inputState @@ -305,7 +315,7 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata self.inputLanguage = nil } - public init(timestamp: Int32, composeInputState: ChatTextInputState, composeDisableUrlPreview: String?, replyMessageId: MessageId?, forwardMessageIds: [MessageId]?, editMessage: ChatEditMessageState?, selectionState: ChatInterfaceSelectionState?, messageActionsState: ChatInterfaceMessageActionsState, historyScrollState: ChatInterfaceHistoryScrollState?, mediaRecordingMode: ChatTextInputMediaRecordingButtonMode, silentPosting: Bool, inputLanguage: String?) { + public init(timestamp: Int32, composeInputState: ChatTextInputState, composeDisableUrlPreview: String?, replyMessageId: EngineMessage.Id?, forwardMessageIds: [EngineMessage.Id]?, editMessage: ChatEditMessageState?, selectionState: ChatInterfaceSelectionState?, messageActionsState: ChatInterfaceMessageActionsState, historyScrollState: ChatInterfaceHistoryScrollState?, mediaRecordingMode: ChatTextInputMediaRecordingButtonMode, silentPosting: Bool, inputLanguage: String?) { self.timestamp = timestamp self.composeInputState = composeInputState self.composeDisableUrlPreview = composeDisableUrlPreview @@ -320,114 +330,108 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata self.inputLanguage = inputLanguage } - public init(decoder: PostboxDecoder) { - self.timestamp = decoder.decodeInt32ForKey("ts", orElse: 0) - if let inputState = decoder.decodeObjectForKey("is", decoder: { return ChatTextInputState(decoder: $0) }) as? ChatTextInputState { + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + + self.timestamp = (try? container.decode(Int32.self, forKey: "ts")) ?? 0 + if let inputState = try? container.decode(ChatTextInputState.self, forKey: "is") { self.composeInputState = inputState } else { self.composeInputState = ChatTextInputState() } - if let composeDisableUrlPreview = decoder.decodeOptionalStringForKey("dup") { + if let composeDisableUrlPreview = try? container.decodeIfPresent(String.self, forKey: "dup") { self.composeDisableUrlPreview = composeDisableUrlPreview } else { self.composeDisableUrlPreview = nil } - let replyMessageIdPeerId: Int64? = decoder.decodeOptionalInt64ForKey("r.p") - let replyMessageIdNamespace: Int32? = decoder.decodeOptionalInt32ForKey("r.n") - let replyMessageIdId: Int32? = decoder.decodeOptionalInt32ForKey("r.i") + let replyMessageIdPeerId: Int64? = try? container.decodeIfPresent(Int64.self, forKey: "r.p") + let replyMessageIdNamespace: Int32? = try? container.decodeIfPresent(Int32.self, forKey: "r.n") + let replyMessageIdId: Int32? = try? container.decodeIfPresent(Int32.self, forKey: "r.i") if let replyMessageIdPeerId = replyMessageIdPeerId, let replyMessageIdNamespace = replyMessageIdNamespace, let replyMessageIdId = replyMessageIdId { - self.replyMessageId = MessageId(peerId: PeerId(replyMessageIdPeerId), namespace: replyMessageIdNamespace, id: replyMessageIdId) + self.replyMessageId = EngineMessage.Id(peerId: EnginePeer.Id(replyMessageIdPeerId), namespace: replyMessageIdNamespace, id: replyMessageIdId) } else { self.replyMessageId = nil } - if let forwardMessageIdsData = decoder.decodeBytesForKeyNoCopy("fm") { - self.forwardMessageIds = MessageId.decodeArrayFromBuffer(forwardMessageIdsData) + if let forwardMessageIdsData = try? container.decodeIfPresent(Data.self, forKey: "fm") { + self.forwardMessageIds = EngineMessage.Id.decodeArrayFromData(forwardMessageIdsData) } else { self.forwardMessageIds = nil } - if let editMessage = decoder.decodeObjectForKey("em", decoder: { ChatEditMessageState(decoder: $0) }) as? ChatEditMessageState { + if let editMessage = try? container.decodeIfPresent(ChatEditMessageState.self, forKey: "em") { self.editMessage = editMessage } else { self.editMessage = nil } - if let selectionState = decoder.decodeObjectForKey("ss", decoder: { return ChatInterfaceSelectionState(decoder: $0) }) as? ChatInterfaceSelectionState { + if let selectionState = try? container.decodeIfPresent(ChatInterfaceSelectionState.self, forKey: "ss") { self.selectionState = selectionState } else { self.selectionState = nil } - - if let messageActionsState = decoder.decodeObjectForKey("as", decoder: { ChatInterfaceMessageActionsState(decoder: $0) }) as? ChatInterfaceMessageActionsState { + + if let messageActionsState = try? container.decodeIfPresent(ChatInterfaceMessageActionsState.self, forKey: "as") { self.messageActionsState = messageActionsState } else { self.messageActionsState = ChatInterfaceMessageActionsState() } + + self.historyScrollState = try? container.decodeIfPresent(ChatInterfaceHistoryScrollState.self, forKey: "hss") - self.historyScrollState = decoder.decodeObjectForKey("hss", decoder: { ChatInterfaceHistoryScrollState(decoder: $0) }) as? ChatInterfaceHistoryScrollState + self.mediaRecordingMode = ChatTextInputMediaRecordingButtonMode(rawValue: (try? container.decodeIfPresent(Int32.self, forKey: "mrm")) ?? 0) ?? .audio - self.mediaRecordingMode = ChatTextInputMediaRecordingButtonMode(rawValue: decoder.decodeInt32ForKey("mrm", orElse: 0))! - - self.silentPosting = decoder.decodeInt32ForKey("sip", orElse: 0) != 0 - self.inputLanguage = decoder.decodeOptionalStringForKey("inputLanguage") + self.silentPosting = ((try? container.decode(Int32.self, forKey: "sip")) ?? 0) != 0 + self.inputLanguage = try? container.decodeIfPresent(String.self, forKey: "inputLanguage") } - public func encode(_ encoder: PostboxEncoder) { - encoder.encodeInt32(self.timestamp, forKey: "ts") - encoder.encodeObject(self.composeInputState, forKey: "is") + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + try container.encode(self.timestamp, forKey: "ts") + try container.encode(self.composeInputState, forKey: "is") if let composeDisableUrlPreview = self.composeDisableUrlPreview { - encoder.encodeString(composeDisableUrlPreview, forKey: "dup") + try container.encode(composeDisableUrlPreview, forKey: "dup") } else { - encoder.encodeNil(forKey: "dup") + try container.encodeNil(forKey: "dup") } if let replyMessageId = self.replyMessageId { - encoder.encodeInt64(replyMessageId.peerId.toInt64(), forKey: "r.p") - encoder.encodeInt32(replyMessageId.namespace, forKey: "r.n") - encoder.encodeInt32(replyMessageId.id, forKey: "r.i") + try container.encode(replyMessageId.peerId.toInt64(), forKey: "r.p") + try container.encode(replyMessageId.namespace, forKey: "r.n") + try container.encode(replyMessageId.id, forKey: "r.i") } else { - encoder.encodeNil(forKey: "r.p") - encoder.encodeNil(forKey: "r.n") - encoder.encodeNil(forKey: "r.i") + try container.encodeNil(forKey: "r.p") + try container.encodeNil(forKey: "r.n") + try container.encodeNil(forKey: "r.i") } if let forwardMessageIds = self.forwardMessageIds { - let buffer = WriteBuffer() - MessageId.encodeArrayToBuffer(forwardMessageIds, buffer: buffer) - encoder.encodeBytes(buffer, forKey: "fm") + try container.encode(EngineMessage.Id.encodeArrayToData(forwardMessageIds), forKey: "fm") } else { - encoder.encodeNil(forKey: "fm") + try container.encodeNil(forKey: "fm") } if let editMessage = self.editMessage { - encoder.encodeObject(editMessage, forKey: "em") + try container.encode(editMessage, forKey: "em") } else { - encoder.encodeNil(forKey: "em") + try container.encodeNil(forKey: "em") } if let selectionState = self.selectionState { - encoder.encodeObject(selectionState, forKey: "ss") + try container.encode(selectionState, forKey: "ss") } else { - encoder.encodeNil(forKey: "ss") + try container.encodeNil(forKey: "ss") } if self.messageActionsState.isEmpty { - encoder.encodeNil(forKey: "as") + try container.encodeNil(forKey: "as") } else { - encoder.encodeObject(self.messageActionsState, forKey: "as") + try container.encode(self.messageActionsState, forKey: "as") } if let historyScrollState = self.historyScrollState { - encoder.encodeObject(historyScrollState, forKey: "hss") + try container.encode(historyScrollState, forKey: "hss") } else { - encoder.encodeNil(forKey: "hss") + try container.encodeNil(forKey: "hss") } - encoder.encodeInt32(self.mediaRecordingMode.rawValue, forKey: "mrm") - encoder.encodeInt32(self.silentPosting ? 1 : 0, forKey: "sip") + try container.encode(self.mediaRecordingMode.rawValue, forKey: "mrm") + try container.encode((self.silentPosting ? 1 : 0) as Int32, forKey: "sip") if let inputLanguage = self.inputLanguage { - encoder.encodeString(inputLanguage, forKey: "inputLanguage") + try container.encode(inputLanguage, forKey: "inputLanguage") } else { - encoder.encodeNil(forKey: "inputLanguage") - } - } - - public func isEqual(to: PeerChatInterfaceState) -> Bool { - if let to = to as? ChatInterfaceState, self == to { - return true - } else { - return false + try container.encodeNil(forKey: "inputLanguage") } } @@ -482,16 +486,16 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata return ChatInterfaceState(timestamp: self.timestamp, composeInputState: updatedComposeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: updatedEditMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage) } - public func withUpdatedReplyMessageId(_ replyMessageId: MessageId?) -> ChatInterfaceState { + public func withUpdatedReplyMessageId(_ replyMessageId: EngineMessage.Id?) -> ChatInterfaceState { return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage) } - public func withUpdatedForwardMessageIds(_ forwardMessageIds: [MessageId]?) -> ChatInterfaceState { + public func withUpdatedForwardMessageIds(_ forwardMessageIds: [EngineMessage.Id]?) -> ChatInterfaceState { return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage) } - public func withUpdatedSelectedMessages(_ messageIds: [MessageId]) -> ChatInterfaceState { - var selectedIds = Set() + public func withUpdatedSelectedMessages(_ messageIds: [EngineMessage.Id]) -> ChatInterfaceState { + var selectedIds = Set() if let selectionState = self.selectionState { selectedIds.formUnion(selectionState.selectedIds) } @@ -501,8 +505,8 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: ChatInterfaceSelectionState(selectedIds: selectedIds), messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: self.inputLanguage) } - public func withToggledSelectedMessages(_ messageIds: [MessageId], value: Bool) -> ChatInterfaceState { - var selectedIds = Set() + public func withToggledSelectedMessages(_ messageIds: [EngineMessage.Id], value: Bool) -> ChatInterfaceState { + var selectedIds = Set() if let selectionState = self.selectionState { selectedIds.formUnion(selectionState.selectedIds) } @@ -547,4 +551,34 @@ public final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equata public func withUpdatedInputLanguage(_ inputLanguage: String?) -> ChatInterfaceState { return ChatInterfaceState(timestamp: self.timestamp, composeInputState: self.composeInputState, composeDisableUrlPreview: self.composeDisableUrlPreview, replyMessageId: self.replyMessageId, forwardMessageIds: self.forwardMessageIds, editMessage: self.editMessage, selectionState: self.selectionState, messageActionsState: self.messageActionsState, historyScrollState: self.historyScrollState, mediaRecordingMode: self.mediaRecordingMode, silentPosting: self.silentPosting, inputLanguage: inputLanguage) } + + public static func parse(_ state: OpaqueChatInterfaceState) -> ChatInterfaceState { + guard let opaqueData = state.opaqueData else { + return ChatInterfaceState().withUpdatedSynchronizeableInputState(state.synchronizeableInputState) + } + guard var decodedState = try? EngineDecoder.decode(ChatInterfaceState.self, from: opaqueData) else { + return ChatInterfaceState().withUpdatedSynchronizeableInputState(state.synchronizeableInputState) + } + decodedState = decodedState.withUpdatedSynchronizeableInputState(state.synchronizeableInputState) + return decodedState + } + + public static func update(engine: TelegramEngine, peerId: EnginePeer.Id, threadId: Int64?, _ f: @escaping (ChatInterfaceState) -> ChatInterfaceState) -> Signal { + return engine.peers.getOpaqueChatInterfaceState(peerId: peerId, threadId: threadId) + |> mapToSignal { previousOpaqueState -> Signal in + let previousState = previousOpaqueState.flatMap(ChatInterfaceState.parse) + let updatedState = f(previousState ?? ChatInterfaceState()) + + let updatedOpaqueData = try? EngineEncoder.encode(updatedState) + + return engine.peers.setOpaqueChatInterfaceState( + peerId: peerId, + threadId: threadId, + state: OpaqueChatInterfaceState( + opaqueData: updatedOpaqueData, + historyScrollMessageIndex: updatedState.historyScrollMessageIndex, + synchronizeableInputState: updatedState.synchronizeableInputState + )) + } + } } diff --git a/submodules/ChatListFilterSettingsHeaderItem/BUILD b/submodules/ChatListFilterSettingsHeaderItem/BUILD index d3d38bff2d..9fc2439438 100644 --- a/submodules/ChatListFilterSettingsHeaderItem/BUILD +++ b/submodules/ChatListFilterSettingsHeaderItem/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/ChatListSearchItemHeader/BUILD b/submodules/ChatListSearchItemHeader/BUILD index edfacc2082..8154e629b4 100644 --- a/submodules/ChatListSearchItemHeader/BUILD +++ b/submodules/ChatListSearchItemHeader/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Display:Display", "//submodules/TelegramPresentationData:TelegramPresentationData", diff --git a/submodules/ChatListSearchItemNode/BUILD b/submodules/ChatListSearchItemNode/BUILD index c57793411e..173234c78e 100644 --- a/submodules/ChatListSearchItemNode/BUILD +++ b/submodules/ChatListSearchItemNode/BUILD @@ -6,11 +6,13 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", - "//submodules/Postbox:Postbox", "//submodules/TelegramPresentationData:TelegramPresentationData", "//submodules/SearchBarNode:SearchBarNode", ], diff --git a/submodules/ChatListSearchItemNode/Sources/ChatListSearchItem.swift b/submodules/ChatListSearchItemNode/Sources/ChatListSearchItem.swift index b3a6df7f8b..bb4472f5a6 100644 --- a/submodules/ChatListSearchItemNode/Sources/ChatListSearchItem.swift +++ b/submodules/ChatListSearchItemNode/Sources/ChatListSearchItem.swift @@ -1,7 +1,6 @@ import Foundation import UIKit import AsyncDisplayKit -import Postbox import Display import SwiftSignalKit import TelegramPresentationData diff --git a/submodules/ChatListSearchRecentPeersNode/BUILD b/submodules/ChatListSearchRecentPeersNode/BUILD index 1010ab4162..6b9a159477 100644 --- a/submodules/ChatListSearchRecentPeersNode/BUILD +++ b/submodules/ChatListSearchRecentPeersNode/BUILD @@ -6,11 +6,13 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", - "//submodules/Postbox:Postbox", "//submodules/TelegramCore:TelegramCore", "//submodules/ListSectionHeaderNode:ListSectionHeaderNode", "//submodules/HorizontalPeerItem:HorizontalPeerItem", diff --git a/submodules/ChatListSearchRecentPeersNode/Sources/ChatListSearchRecentPeersNode.swift b/submodules/ChatListSearchRecentPeersNode/Sources/ChatListSearchRecentPeersNode.swift index b1bf7c83b6..e6e160558c 100644 --- a/submodules/ChatListSearchRecentPeersNode/Sources/ChatListSearchRecentPeersNode.swift +++ b/submodules/ChatListSearchRecentPeersNode/Sources/ChatListSearchRecentPeersNode.swift @@ -3,7 +3,6 @@ import UIKit import AsyncDisplayKit import Display import SwiftSignalKit -import Postbox import TelegramCore import TelegramPresentationData import MergeLists @@ -32,13 +31,13 @@ private func calculateItemCustomWidth(width: CGFloat) -> CGFloat { private struct ChatListSearchRecentPeersEntry: Comparable, Identifiable { let index: Int - let peer: Peer - let presence: PeerPresence? + let peer: EnginePeer + let presence: EnginePeer.Presence? let unreadBadge: (Int32, Bool)? let theme: PresentationTheme let strings: PresentationStrings let itemCustomWidth: CGFloat? - var stableId: PeerId { + var stableId: EnginePeer.Id { return self.peer.id } @@ -49,14 +48,10 @@ private struct ChatListSearchRecentPeersEntry: Comparable, Identifiable { if lhs.itemCustomWidth != rhs.itemCustomWidth { return false } - if !lhs.peer.isEqual(rhs.peer) { + if lhs.peer != rhs.peer { return false } - if let lhsPresence = lhs.presence, let rhsPresence = rhs.presence { - if !lhsPresence.isEqual(to: rhsPresence) { - return false - } - } else if (lhs.presence != nil) != (rhs.presence != nil) { + if lhs.presence != rhs.presence { return false } if lhs.unreadBadge?.0 != rhs.unreadBadge?.0 { @@ -78,7 +73,7 @@ private struct ChatListSearchRecentPeersEntry: Comparable, Identifiable { return lhs.index < rhs.index } - func item(context: AccountContext, mode: HorizontalPeerItemMode, peerSelected: @escaping (Peer) -> Void, peerContextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void, isPeerSelected: @escaping (PeerId) -> Bool) -> ListViewItem { + func item(context: AccountContext, mode: HorizontalPeerItemMode, peerSelected: @escaping (EnginePeer) -> Void, peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?) -> Void, isPeerSelected: @escaping (EnginePeer.Id) -> Bool) -> ListViewItem { return HorizontalPeerItem(theme: self.theme, strings: self.strings, mode: mode, context: context, peer: self.peer, presence: self.presence, unreadBadge: self.unreadBadge, action: peerSelected, contextAction: { peer, node, gesture in peerContextAction(peer, node, gesture) }, isPeerSelected: isPeerSelected, customWidth: self.itemCustomWidth) @@ -93,7 +88,7 @@ private struct ChatListSearchRecentNodeTransition { let animated: Bool } -private func preparedRecentPeersTransition(context: AccountContext, mode: HorizontalPeerItemMode, peerSelected: @escaping (Peer) -> Void, peerContextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void, isPeerSelected: @escaping (PeerId) -> Bool, share: Bool = false, from fromEntries: [ChatListSearchRecentPeersEntry], to toEntries: [ChatListSearchRecentPeersEntry], firstTime: Bool, animated: Bool) -> ChatListSearchRecentNodeTransition { +private func preparedRecentPeersTransition(context: AccountContext, mode: HorizontalPeerItemMode, peerSelected: @escaping (EnginePeer) -> Void, peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?) -> Void, isPeerSelected: @escaping (EnginePeer.Id) -> Bool, share: Bool = false, from fromEntries: [ChatListSearchRecentPeersEntry], to toEntries: [ChatListSearchRecentPeersEntry], firstTime: Bool, animated: Bool) -> ChatListSearchRecentNodeTransition { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } @@ -111,9 +106,9 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode { private let listView: ListView private let share: Bool - private let peerSelected: (Peer) -> Void - private let peerContextAction: (Peer, ASDisplayNode, ContextGesture?) -> Void - private let isPeerSelected: (PeerId) -> Bool + private let peerSelected: (EnginePeer) -> Void + private let peerContextAction: (EnginePeer, ASDisplayNode, ContextGesture?) -> Void + private let isPeerSelected: (EnginePeer.Id) -> Bool private let disposable = MetaDisposable() private let itemCustomWidthValuePromise: ValuePromise = ValuePromise(nil, ignoreRepeated: true) @@ -127,7 +122,7 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode { return self.ready.get() } - public init(context: AccountContext, theme: PresentationTheme, mode: HorizontalPeerItemMode, strings: PresentationStrings, peerSelected: @escaping (Peer) -> Void, peerContextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void, isPeerSelected: @escaping (PeerId) -> Bool, share: Bool = false) { + public init(context: AccountContext, theme: PresentationTheme, mode: HorizontalPeerItemMode, strings: PresentationStrings, peerSelected: @escaping (EnginePeer) -> Void, peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?) -> Void, isPeerSelected: @escaping (EnginePeer.Id) -> Bool, share: Bool = false) { self.theme = theme self.strings = strings self.themeAndStringsPromise = Promise((self.theme, self.strings)) @@ -149,7 +144,7 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode { let peersDisposable = DisposableSet() - let recent: Signal<([Peer], [PeerId: (Int32, Bool)], [PeerId : PeerPresence]), NoError> = context.engine.peers.recentPeers() + let recent: Signal<([EnginePeer], [EnginePeer.Id: (Int32, Bool)], [EnginePeer.Id : EnginePeer.Presence]), NoError> = context.engine.peers.recentPeers() |> filter { value -> Bool in switch value { case .disabled: @@ -163,14 +158,21 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode { case .disabled: return .single(([], [:], [:])) case let .peers(peers): - return combineLatest(queue: .mainQueue(), peers.filter { !$0.isDeleted }.map {context.account.postbox.peerView(id: $0.id)}) |> mapToSignal { peerViews -> Signal<([Peer], [PeerId: (Int32, Bool)], [PeerId: PeerPresence]), NoError> in + return combineLatest(queue: .mainQueue(), + peers.filter { + !$0.isDeleted + }.map { + context.account.postbox.peerView(id: $0.id) + } + ) + |> mapToSignal { peerViews -> Signal<([EnginePeer], [EnginePeer.Id: (Int32, Bool)], [EnginePeer.Id: EnginePeer.Presence]), NoError> in return context.account.postbox.unreadMessageCountsView(items: peerViews.map { .peer($0.peerId) }) |> map { values in - var peers: [Peer] = [] - var unread: [PeerId: (Int32, Bool)] = [:] - var presences: [PeerId: PeerPresence] = [:] + var peers: [EnginePeer] = [] + var unread: [EnginePeer.Id: (Int32, Bool)] = [:] + var presences: [EnginePeer.Id: EnginePeer.Presence] = [:] for peerView in peerViews { if let peer = peerViewMainPeer(peerView) { var isMuted: Bool = false @@ -189,10 +191,10 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode { } if let presence = peerView.peerPresences[peer.id] { - presences[peer.id] = presence + presences[peer.id] = EnginePeer.Presence(presence) } - peers.append(peer) + peers.append(EnginePeer(peer)) } } return (peers, unread, presences) @@ -286,7 +288,7 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode { self.itemCustomWidthValuePromise.set(itemCustomWidth) } - public func viewAndPeerAtPoint(_ point: CGPoint) -> (UIView, PeerId)? { + public func viewAndPeerAtPoint(_ point: CGPoint) -> (UIView, EnginePeer.Id)? { let adjustedPoint = self.view.convert(point, to: self.listView.view) var selectedItemNode: ASDisplayNode? self.listView.forEachItemNode { itemNode in diff --git a/submodules/ChatListUI/BUILD b/submodules/ChatListUI/BUILD index f404887b30..5123ae2fd3 100644 --- a/submodules/ChatListUI/BUILD +++ b/submodules/ChatListUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/ChatListUI/Sources/ChatContextMenus.swift b/submodules/ChatListUI/Sources/ChatContextMenus.swift index 94f0a9847e..b13e549773 100644 --- a/submodules/ChatListUI/Sources/ChatContextMenus.swift +++ b/submodules/ChatListUI/Sources/ChatContextMenus.swift @@ -271,7 +271,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch }))) } - let archiveEnabled = !isSavedMessages && peerId != PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(777000)) && peerId == context.account.peerId + let archiveEnabled = !isSavedMessages && peerId != PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id. _internalFromInt64Value(777000)) && peerId == context.account.peerId if let (group, _) = groupAndIndex { if archiveEnabled { let isArchived = group == Namespaces.PeerGroup.archive @@ -361,14 +361,15 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch joinChannelDisposable.set((createSignal |> deliverOnMainQueue).start(next: { _ in - if let navigationController = (chatListController?.navigationController as? NavigationController) { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) - } }, error: { _ in if let chatListController = chatListController { let presentationData = context.sharedContext.currentPresentationData.with { $0 } chatListController.present(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } + }, completed: { + if let navigationController = (chatListController?.navigationController as? NavigationController) { + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) + } })) f(.default) }))) diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 5c035532d7..1d9cd0209d 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -611,8 +611,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if let layout = strongSelf.validLayout, case .regular = layout.metrics.widthClass { scrollToEndIfExists = true } - - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), activateInput: activateInput && !peer.isDeleted, scrollToEndIfExists: scrollToEndIfExists, animated: !scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : [], parentGroupId: strongSelf.groupId, completion: { [weak self] controller in + + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), activateInput: activateInput && !peer.isDeleted, scrollToEndIfExists: scrollToEndIfExists, animated: !scrollToEndIfExists, options: strongSelf.groupId == PeerGroupId.root ? [.removeOnMasterDetails] : [], parentGroupId: strongSelf.groupId, chatListFilter: strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter?.data, completion: { [weak self] controller in self?.chatListDisplayNode.containerNode.currentItemNode.clearHighlightAnimated(true) if let promoInfo = promoInfo { switch promoInfo { @@ -836,10 +836,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } switch item.content { - case let .groupReference(groupReference): - let chatListController = ChatListControllerImpl(context: strongSelf.context, groupId: groupReference.groupId, controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false) + case let .groupReference(groupId, _, _, _, _): + let chatListController = ChatListControllerImpl(context: strongSelf.context, groupId: groupId, controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false) chatListController.navigationPresentation = .master - let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: archiveContextMenuItems(context: strongSelf.context, groupId: groupReference.groupId, chatListController: strongSelf), reactionItems: [], gesture: gesture) + let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: archiveContextMenuItems(context: strongSelf.context, groupId: groupId, chatListController: strongSelf), reactionItems: [], gesture: gesture) strongSelf.presentInGlobalOverlay(contextController) case let .peer(_, peer, _, _, _, _, _, _, promoInfo, _, _, _): let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.peerId), subject: nil, botStart: nil, mode: .standard(previewing: true)) @@ -1126,6 +1126,26 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.chatListDisplayNode.containerNode.updateEnableAdjacentFilterLoading(true) + self.chatListDisplayNode.containerNode.didBeginSelectingChats = { [weak self] in + guard let strongSelf = self else { + return + } + if !strongSelf.chatListDisplayNode.didBeginSelectingChatsWhileEditing { + var isEditing = false + strongSelf.chatListDisplayNode.containerNode.updateState { state in + isEditing = state.editing + return state + } + if !isEditing { + strongSelf.editPressed() + } + strongSelf.chatListDisplayNode.didBeginSelectingChatsWhileEditing = true + if let layout = strongSelf.validLayout { + strongSelf.updateLayout(layout: layout, transition: .animated(duration: 0.2, curve: .easeInOut)) + } + } + } + guard case .root = self.groupId else { return } @@ -1265,27 +1285,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return true }) } - - self.chatListDisplayNode.containerNode.didBeginSelectingChats = { [weak self] in - guard let strongSelf = self else { - return - } - if !strongSelf.chatListDisplayNode.didBeginSelectingChatsWhileEditing { - var isEditing = false - strongSelf.chatListDisplayNode.containerNode.updateState { state in - isEditing = state.editing - return state - } - if !isEditing { - strongSelf.editPressed() - } - strongSelf.chatListDisplayNode.didBeginSelectingChatsWhileEditing = true - if let layout = strongSelf.validLayout { - strongSelf.updateLayout(layout: layout, transition: .animated(duration: 0.2, curve: .easeInOut)) - } - } - } - + if !self.processedFeaturedFilters { let initializedFeatured = self.context.account.postbox.preferencesView(keys: [ PreferencesKeys.chatListFiltersFeaturedState @@ -1481,8 +1481,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController switch entry { case .all: return nil - case let .filter(filter): - return filter.id + case let .filter(id, _, _): + return id } } @@ -1996,7 +1996,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return false } if value == .commit { - let context = strongSelf.context let presentationData = strongSelf.presentationData let progressSignal = Signal { subscriber in let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil)) @@ -2228,7 +2227,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } if canRemoveGlobally, (mainPeer is TelegramGroup || mainPeer is TelegramChannel) { - items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .deleteAndLeave, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) + items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: EnginePeer(mainPeer), chatPeer: EnginePeer(chatPeer), action: .deleteAndLeave, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in actionSheet?.dismissAnimated() @@ -2266,7 +2265,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController ], parseMarkdown: true), in: .window(.root)) })) } else { - items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .delete, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) + items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: EnginePeer(mainPeer), chatPeer: EnginePeer(chatPeer), action: .delete, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) if canClear { let beginClear: (InteractiveHistoryClearingType) -> Void = { type in @@ -2327,7 +2326,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData) var items: [ActionSheetItem] = [] - items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .clearHistory(canClearCache: false), strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) + items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: EnginePeer(mainPeer), chatPeer: EnginePeer(chatPeer), action: .clearHistory(canClearCache: false), strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) if joined || mainPeer.isDeleted { items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Delete, color: .destructive, action: { [weak actionSheet] in @@ -2387,7 +2386,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData) var items: [ActionSheetItem] = [] - items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .deleteAndLeave, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) + items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: EnginePeer(mainPeer), chatPeer: EnginePeer(chatPeer), action: .deleteAndLeave, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in actionSheet?.dismissAnimated() @@ -2492,7 +2491,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let actionSheet = ActionSheetController(presentationData: self.presentationData) var items: [ActionSheetItem] = [] - items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: mainPeer, chatPeer: chatPeer, action: .delete, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder)) + items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: EnginePeer(mainPeer), chatPeer: EnginePeer(chatPeer), action: .delete, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder)) if joined || mainPeer.isDeleted { items.append(ActionSheetButtonItem(title: self.presentationData.strings.Common_Delete, color: .destructive, action: { [weak self, weak actionSheet] in diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index b7da7cc8d4..6ff6c48ad8 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -181,7 +181,7 @@ private final class ChatListShimmerNode: ASDisplayNode { let peer1 = TelegramUser(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "FirstName", lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) let timestamp1: Int32 = 100000 let peers = SimpleDictionary() - let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in + let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in gesture?.cancel() }, present: { _ in }) @@ -1165,12 +1165,9 @@ final class ChatListControllerNode: ASDisplayNode { return nil } - var filter: ChatListNodePeersFilter = [] - if false, case .group = self.groupId { - filter.insert(.excludeRecent) - } + let filter: ChatListNodePeersFilter = [] - let contentNode = ChatListSearchContainerNode(context: self.context, filter: filter, groupId: self.groupId, displaySearchFilters: displaySearchFilters, initialFilter: initialFilter, openPeer: { [weak self] peer, dismissSearch in + let contentNode = ChatListSearchContainerNode(context: self.context, filter: filter, groupId: self.groupId, displaySearchFilters: displaySearchFilters, initialFilter: initialFilter, openPeer: { [weak self] peer, _, dismissSearch in self?.requestOpenPeerFromSearch?(peer, dismissSearch) }, openDisabledPeer: { _ in }, openRecentPeerOptions: { [weak self] peer in diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetCategoryItem.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetCategoryItem.swift index 95eafaf35f..3115272611 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetCategoryItem.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetCategoryItem.swift @@ -157,7 +157,6 @@ class ChatListFilterPresetCategoryItemNode: ItemListRevealOptionsItemNode, ItemL func asyncLayout() -> (_ item: ChatListFilterPresetCategoryItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors, _ headerAtTop: Bool) -> (ListViewItemNodeLayout, (Bool, Bool) -> Void) { let makeTitleLayout = TextNode.asyncLayout(self.titleNode) - let editableControlLayout = ItemListEditableControlNode.asyncLayout(self.editableControlNode) let currentItem = self.item @@ -193,16 +192,10 @@ class ChatListFilterPresetCategoryItemNode: ItemListRevealOptionsItemNode, ItemL avatarSize = 40.0 leftInset = 65.0 + params.leftInset - var editableControlSizeAndApply: (CGFloat, (CGFloat) -> ItemListEditableControlNode)? + let editableControlSizeAndApply: (CGFloat, (CGFloat) -> ItemListEditableControlNode)? = nil let editingOffset: CGFloat - if false { - let sizeAndApply = editableControlLayout(item.presentationData.theme, false) - editableControlSizeAndApply = sizeAndApply - editingOffset = sizeAndApply.0 - } else { - editingOffset = 0.0 - } + editingOffset = 0.0 let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 12.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift index 64ca31b31f..a4d2703731 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift @@ -260,8 +260,8 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry { return .index(3) case .addIncludePeer: return .index(4) - case let .includeCategory(includeCategory): - return .includeCategory(includeCategory.category) + case let .includeCategory(_, category, _, _): + return .includeCategory(category) case .includeExpand: return .index(5) case .includePeerInfo: @@ -270,16 +270,16 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry { return .index(7) case .addExcludePeer: return .index(8) - case let .excludeCategory(excludeCategory): - return .excludeCategory(excludeCategory.category) + case let .excludeCategory(_, category, _, _): + return .excludeCategory(category) case .excludeExpand: return .index(9) case .excludePeerInfo: return .index(10) - case let .includePeer(peer): - return .peer(peer.peer.peerId) - case let .excludePeer(peer): - return .peer(peer.peer.peerId) + case let .includePeer(_, peer, _): + return .peer(peer.peerId) + case let .excludePeer(_, peer, _): + return .peer(peer.peerId) } } @@ -295,10 +295,10 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry { return .includeIndex(0) case .addIncludePeer: return .includeIndex(1) - case let .includeCategory(includeCategory): - return .includeIndex(2 + includeCategory.index) - case let .includePeer(includePeer): - return .includeIndex(200 + includePeer.index) + case let .includeCategory(index, _, _, _): + return .includeIndex(2 + index) + case let .includePeer(index, _, _): + return .includeIndex(200 + index) case .includeExpand: return .includeIndex(999) case .includePeerInfo: @@ -307,10 +307,10 @@ private enum ChatListFilterPresetEntry: ItemListNodeEntry { return .excludeIndex(0) case .addExcludePeer: return .excludeIndex(1) - case let .excludeCategory(excludeCategory): - return .excludeIndex(2 + excludeCategory.index) - case let .excludePeer(excludePeer): - return .excludeIndex(200 + excludePeer.index) + case let .excludeCategory(index, _, _, _): + return .excludeIndex(2 + index) + case let .excludePeer(index, _, _): + return .excludeIndex(200 + index) case .excludeExpand: return .excludeIndex(999) case .excludePeerInfo: diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift index 1a170067c4..e9a86e85ff 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift @@ -94,16 +94,16 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry { return 0 case .listHeader: return 100 - case let .preset(preset): - return 101 + preset.index.value + case let .preset(index, _, _, _, _, _, _): + return 101 + index.value case .addItem: return 1000 case .listFooter: return 1001 case .suggestedListHeader: return 1002 - case let .suggestedPreset(suggestedPreset): - return 1003 + suggestedPreset.index.value + case let .suggestedPreset(index, _, _, _): + return 1003 + index.value case .suggestedAddCustom: return 2000 } @@ -115,14 +115,14 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry { return .screenHeader case .suggestedListHeader: return .suggestedListHeader - case let .suggestedPreset(suggestedPreset): - return .suggestedPreset(suggestedPreset.preset) + case let .suggestedPreset(_, _, _, preset): + return .suggestedPreset(preset) case .suggestedAddCustom: return .suggestedAddCustom case .listHeader: return .listHeader - case let .preset(preset): - return .preset(preset.preset.id) + case let .preset(_, _, _, preset, _, _, _): + return .preset(preset.id) case .addItem: return .addItem case .listFooter: @@ -297,8 +297,6 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch presentControllerImpl?(actionSheet) }) - let chatCountCache = Atomic<[ChatListFilterData: Int]>(value: [:]) - let filtersWithCountsSignal = context.engine.peers.updatedChatListFilters() |> distinctUntilChanged |> mapToSignal { filters -> Signal<[(ChatListFilter, Int)], NoError> in @@ -430,7 +428,7 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch } controller.setReorderEntry({ (fromIndex: Int, toIndex: Int, entries: [ChatListFilterPresetListEntry]) -> Signal in let fromEntry = entries[fromIndex] - guard case let .preset(fromFilter) = fromEntry else { + guard case let .preset(_, _, _, fromPreset, _, _, _) = fromEntry else { return .single(false) } var referenceFilter: ChatListFilter? @@ -438,8 +436,8 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch var afterAll = false if toIndex < entries.count { switch entries[toIndex] { - case let .preset(toFilter): - referenceFilter = toFilter.preset + case let .preset(_, _, _, preset, _, _, _): + referenceFilter = preset default: if entries[toIndex] < fromEntry { beforeAll = true @@ -459,7 +457,7 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch var filters = filtersWithAppliedOrder(filters: filtersWithCountsValue, order: updatedFilterOrderValue).map { $0.0 } let initialOrder = filters.map { $0.id } - if let index = filters.firstIndex(where: { $0.id == fromFilter.preset.id }) { + if let index = filters.firstIndex(where: { $0.id == fromPreset.id }) { filters.remove(at: index) } if let referenceFilter = referenceFilter { @@ -467,21 +465,21 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch for i in 0 ..< filters.count { if filters[i].id == referenceFilter.id { if fromIndex < toIndex { - filters.insert(fromFilter.preset, at: i + 1) + filters.insert(fromPreset, at: i + 1) } else { - filters.insert(fromFilter.preset, at: i) + filters.insert(fromPreset, at: i) } inserted = true break } } if !inserted { - filters.append(fromFilter.preset) + filters.append(fromPreset) } } else if beforeAll { - filters.insert(fromFilter.preset, at: 0) + filters.insert(fromPreset, at: 0) } else if afterAll { - filters.append(fromFilter.preset) + filters.append(fromPreset) } let updatedOrder = filters.map { $0.id } diff --git a/submodules/ChatListUI/Sources/ChatListFilterTabInlineContainerNode.swift b/submodules/ChatListUI/Sources/ChatListFilterTabInlineContainerNode.swift index 954642fba7..e6674f6282 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterTabInlineContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterTabInlineContainerNode.swift @@ -146,7 +146,7 @@ private final class ItemNode: ASDisplayNode { return } - if isExtracted, let theme = strongSelf.theme { + if isExtracted { strongSelf.extractedBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 32.0, color: strongSelf.isSelected ? UIColor(rgb: 0xbbbbbb) : UIColor(rgb: 0xf1f1f1)) } transition.updateAlpha(node: strongSelf.extractedBackgroundNode, alpha: isExtracted ? 1.0 : 0.0, completion: { _ in @@ -652,8 +652,8 @@ final class ChatListFilterTabInlineContainerNode: ASDisplayNode { strongSelf.scrollNode.view.panGestureRecognizer.isEnabled = true strongSelf.scrollNode.view.setContentOffset(strongSelf.scrollNode.view.contentOffset, animated: false) switch filter { - case let .filter(filter): - strongSelf.contextGesture?(filter.id, sourceNode, gesture) + case let .filter(id, _, _): + strongSelf.contextGesture?(id, sourceNode, gesture) default: strongSelf.contextGesture?(nil, sourceNode, gesture) } @@ -666,11 +666,11 @@ final class ChatListFilterTabInlineContainerNode: ASDisplayNode { return } switch filter { - case let .filter(filter): + case let .filter(id, _, _): strongSelf.scrollNode.view.panGestureRecognizer.isEnabled = false strongSelf.scrollNode.view.panGestureRecognizer.isEnabled = true strongSelf.scrollNode.view.setContentOffset(strongSelf.scrollNode.view.contentOffset, animated: false) - strongSelf.contextGesture?(filter.id, sourceNode, gesture) + strongSelf.contextGesture?(id, sourceNode, gesture) default: strongSelf.contextGesture?(nil, sourceNode, gesture) } @@ -685,9 +685,9 @@ final class ChatListFilterTabInlineContainerNode: ASDisplayNode { unreadCount = count unreadHasUnmuted = true isNoFilter = true - case let .filter(filter): - unreadCount = filter.unread.value - unreadHasUnmuted = filter.unread.hasUnmuted + case let .filter(_, _, unread): + unreadCount = unread.value + unreadHasUnmuted = unread.hasUnmuted } if !wasAdded && (itemNodePair.regular.unreadCount != 0) != (unreadCount != 0) { badgeAnimations[filter.id] = (unreadCount != 0) ? .in : .out @@ -830,9 +830,7 @@ final class ChatListFilterTabInlineContainerNode: ASDisplayNode { transition.updateFrame(node: self.itemsBackgroundTintNode, frame: backgroundFrame) self.scrollNode.view.contentSize = CGSize(width: itemsBackgroundRightX + 8.0, height: size.height) - - var previousFrame: CGRect? - var nextFrame: CGRect? + var selectedFrame: CGRect? if let selectedFilter = selectedFilter, let currentIndex = reorderedFilters.firstIndex(where: { $0.id == selectedFilter }) { func interpolateFrame(from fromValue: CGRect, to toValue: CGRect, t: CGFloat) -> CGRect { diff --git a/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift b/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift index f119038ac7..dc71fbd908 100644 --- a/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift +++ b/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift @@ -122,9 +122,9 @@ class ChatListRecentPeersListItemNode: ListViewItemNode { peersNode.updateThemeAndStrings(theme: item.theme, strings: item.strings) } else { peersNode = ChatListSearchRecentPeersNode(context: item.context, theme: item.theme, mode: .list, strings: item.strings, peerSelected: { peer in - self?.item?.peerSelected(peer) + self?.item?.peerSelected(peer._asPeer()) }, peerContextAction: { peer, node, gesture in - self?.item?.peerContextAction(peer, node, gesture) + self?.item?.peerContextAction(peer._asPeer(), node, gesture) }, isPeerSelected: { _ in return false }) diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index c7dd83d666..5224869b22 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -38,7 +38,7 @@ private enum ChatListTokenId: Int32 { } final class ChatListSearchInteraction { - let openPeer: (Peer, Bool) -> Void + let openPeer: (Peer, Peer?, Bool) -> Void let openDisabledPeer: (Peer) -> Void let openMessage: (Peer, MessageId, Bool) -> Void let openUrl: (String) -> Void @@ -52,7 +52,7 @@ final class ChatListSearchInteraction { let dismissInput: () -> Void let getSelectedMessageIds: () -> Set? - init(openPeer: @escaping (Peer, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openMessage: @escaping (Peer, MessageId, Bool) -> Void, openUrl: @escaping (String) -> Void, clearRecentSearch: @escaping () -> Void, addContact: @escaping (String) -> Void, toggleMessageSelection: @escaping (MessageId, Bool) -> Void, messageContextAction: @escaping ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), mediaMessageContextAction: @escaping ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, getSelectedMessageIds: @escaping () -> Set?) { + init(openPeer: @escaping (Peer, Peer?, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openMessage: @escaping (Peer, MessageId, Bool) -> Void, openUrl: @escaping (String) -> Void, clearRecentSearch: @escaping () -> Void, addContact: @escaping (String) -> Void, toggleMessageSelection: @escaping (MessageId, Bool) -> Void, messageContextAction: @escaping ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), mediaMessageContextAction: @escaping ((Message, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, getSelectedMessageIds: @escaping () -> Set?) { self.openPeer = openPeer self.openDisabledPeer = openDisabledPeer self.openMessage = openMessage @@ -124,7 +124,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo private var validLayout: (ContainerViewLayout, CGFloat)? - public init(context: AccountContext, filter: ChatListNodePeersFilter, groupId: PeerGroupId, displaySearchFilters: Bool, initialFilter: ChatListSearchFilter = .chats, openPeer originalOpenPeer: @escaping (Peer, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openRecentPeerOptions: @escaping (Peer) -> Void, openMessage originalOpenMessage: @escaping (Peer, MessageId, Bool) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?) { + public init(context: AccountContext, filter: ChatListNodePeersFilter, groupId: PeerGroupId, displaySearchFilters: Bool, initialFilter: ChatListSearchFilter = .chats, openPeer originalOpenPeer: @escaping (Peer, Peer?, Bool) -> Void, openDisabledPeer: @escaping (Peer) -> Void, openRecentPeerOptions: @escaping (Peer) -> Void, openMessage originalOpenMessage: @escaping (Peer, MessageId, Bool) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?) { self.context = context self.peersFilter = filter self.groupId = groupId @@ -149,8 +149,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo self.addSubnode(self.paneContainerNode) - let interaction = ChatListSearchInteraction(openPeer: { peer, value in - originalOpenPeer(peer, value) + let interaction = ChatListSearchInteraction(openPeer: { peer, chatPeer, value in + originalOpenPeer(peer, chatPeer, value) if peer.id.namespace != Namespaces.Peer.SecretChat { addAppLogEvent(postbox: context.account.postbox, type: "search_global_open_peer", peerId: peer.id) } @@ -827,14 +827,11 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo if let strongSelf = self, !actions.options.isEmpty { let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData) var items: [ActionSheetItem] = [] - var personalPeerName: String? - var isChannel = false + let personalPeerName: String? = nil if actions.options.contains(.deleteGlobally) { let globalTitle: String - if isChannel { - globalTitle = strongSelf.presentationData.strings.Conversation_DeleteMessagesForMe - } else if let personalPeerName = personalPeerName { + if let personalPeerName = personalPeerName { globalTitle = strongSelf.presentationData.strings.Conversation_DeleteMessagesFor(personalPeerName).string } else { globalTitle = strongSelf.presentationData.strings.Conversation_DeleteMessagesForEveryone @@ -854,14 +851,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo })) } if actions.options.contains(.deleteLocally) { - var localOptionText = strongSelf.presentationData.strings.Conversation_DeleteMessagesForMe -// if strongSelf.context.account.peerId == strongSelf.peerId { -// if messageIds.count == 1 { -// localOptionText = strongSelf.presentationData.strings.Conversation_Moderate_Delete -// } else { -// localOptionText = strongSelf.presentationData.strings.Conversation_DeleteManyMessages -// } -// } + let localOptionText = strongSelf.presentationData.strings.Conversation_DeleteMessagesForMe items.append(ActionSheetButtonItem(title: localOptionText, color: .destructive, action: { [weak actionSheet] in actionSheet?.dismissAnimated() if let strongSelf = self { @@ -901,7 +891,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo }).start() let peerSelectionController = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: [.onlyWriteable, .excludeDisabled], multipleSelection: true)) - peerSelectionController.multiplePeersSelected = { [weak self, weak peerSelectionController] peers, messageText, mode in + peerSelectionController.multiplePeersSelected = { [weak self, weak peerSelectionController] peers, peerMap, messageText, mode in guard let strongSelf = self, let strongController = peerSelectionController else { return } @@ -926,6 +916,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo return .forward(source: messageId, grouping: .auto, attributes: [], correlationId: nil) }) + var displayPeers: [Peer] = [] for peer in peers { let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: result) |> deliverOnMainQueue).start(next: { messageIds in @@ -951,31 +942,38 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo |> deliverOnMainQueue).start()) } }) - - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let text: String - var savedMessages = false - if peers.count == 1, let peerId = peers.first?.id, peerId == strongSelf.context.account.peerId { - text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many - savedMessages = true - } else { - if peers.count == 1, let peer = peers.first { - let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string - } else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last { - let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string - } else if let peer = peers.first { - let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(peers.count - 1)").string - } else { - text = "" + if let secretPeer = peer as? TelegramSecretChat { + if let peer = peerMap[secretPeer.regularPeerId] { + displayPeers.append(peer) } + } else { + displayPeers.append(peer) } - - (strongSelf.navigationController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) } + + let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + let text: String + var savedMessages = false + if displayPeers.count == 1, let peerId = displayPeers.first?.id, peerId == strongSelf.context.account.peerId { + text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many + savedMessages = true + } else { + if displayPeers.count == 1, let peer = displayPeers.first { + let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string + } else if displayPeers.count == 2, let firstPeer = displayPeers.first, let secondPeer = displayPeers.last { + let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string + } else if let peer = displayPeers.first { + let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(displayPeers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(displayPeers.count - 1)").string + } else { + text = "" + } + } + + (strongSelf.navigationController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) } peerSelectionController.peerSelected = { [weak self, weak peerSelectionController] peer in let peerId = peer.id @@ -1023,15 +1021,10 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) } } else { - let _ = (strongSelf.context.account.postbox.transaction({ transaction -> Void in - transaction.updatePeerChatInterfaceState(peerId, update: { currentState in - if let currentState = currentState as? ChatInterfaceState { - return currentState.withUpdatedForwardMessageIds(Array(messageIds)) - } else { - return ChatInterfaceState().withUpdatedForwardMessageIds(Array(messageIds)) - } - }) - }) |> deliverOnMainQueue).start(completed: { + let _ = (ChatInterfaceState.update(engine: strongSelf.context.engine, peerId: peerId, threadId: nil, { currentState in + return currentState.withUpdatedForwardMessageIds(Array(messageIds)) + }) + |> deliverOnMainQueue).start(completed: { if let strongSelf = self { let controller = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peerId), subject: nil, botStart: nil, mode: .standard(previewing: false)) controller.purposefulAction = { [weak self] in diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 5c97f5915c..00027258e5 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -440,8 +440,12 @@ public enum ChatListSearchEntry: Comparable, Identifiable { }) } - return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .generalSearch, peer: .peer(peer: primaryPeer, chatPeer: chatPeer), status: .none, badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in - interaction.peerSelected(peer, nil) + return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .generalSearch, peer: .peer(peer: primaryPeer, chatPeer: chatPeer), status: .none, badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { contactPeer in + if case let .peer(maybePeer, maybeChatPeer) = contactPeer, let peer = maybePeer, let chatPeer = maybeChatPeer { + interaction.peerSelected(chatPeer, peer, nil) + } else { + interaction.peerSelected(peer, nil, nil) + } }, contextAction: peerContextAction.flatMap { peerContextAction in return { node, gesture in if let chatPeer = chatPeer, chatPeer.id.namespace != Namespaces.Peer.SecretChat { @@ -504,7 +508,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable { } return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .generalSearch, peer: .peer(peer: peer.peer, chatPeer: peer.peer), status: .addressName(suffixString), badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in - interaction.peerSelected(peer.peer, nil) + interaction.peerSelected(peer.peer, nil, nil) }, contextAction: peerContextAction.flatMap { peerContextAction in return { node, gesture in peerContextAction(peer.peer, .search(nil), node, gesture) @@ -1209,9 +1213,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } let chatListInteraction = ChatListNodeInteraction(activateSearch: { - }, peerSelected: { [weak self] peer, _ in + }, peerSelected: { [weak self] peer, chatPeer, _ in interaction.dismissInput() - interaction.openPeer(peer, false) + interaction.openPeer(peer, chatPeer, false) let _ = context.engine.peers.addRecentlySearchedPeer(peerId: peer.id).start() self?.listNode.clearHighlightAnimated(true) }, disabledPeerSelected: { _ in @@ -1478,7 +1482,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { let firstTime = previousEntries == nil let transition = chatListSearchContainerPreparedRecentTransition(from: previousEntries ?? [], to: entries, context: context, presentationData: presentationData, filter: peersFilter, peerSelected: { peer in - interaction.openPeer(peer, true) + interaction.openPeer(peer, nil, true) let _ = context.engine.peers.addRecentlySearchedPeer(peerId: peer.id).start() self?.recentListNode.clearHighlightAnimated(true) }, disabledPeerSelected: { peer in @@ -1723,32 +1727,28 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { let mediaAccessoryPanel = MediaNavigationAccessoryPanel(context: self.context, displayBackground: true) mediaAccessoryPanel.containerNode.headerNode.displayScrubber = item.playbackData?.type != .instantVideo + mediaAccessoryPanel.getController = { [weak self] in + return self?.navigationController?.topViewController as? ViewController + } + mediaAccessoryPanel.presentInGlobalOverlay = { [weak self] c in + (self?.navigationController?.topViewController as? ViewController)?.presentInGlobalOverlay(c) + } mediaAccessoryPanel.close = { [weak self] in if let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType { strongSelf.context.sharedContext.mediaManager.setPlaylist(nil, type: type, control: SharedMediaPlayerControlAction.playback(.pause)) } } - mediaAccessoryPanel.toggleRate = { - [weak self] in + mediaAccessoryPanel.setRate = { [weak self] rate in guard let strongSelf = self else { return } let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> AudioPlaybackRate in let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings) as? MusicPlaybackSettings ?? MusicPlaybackSettings.defaultSettings - - let nextRate: AudioPlaybackRate - switch settings.voicePlaybackRate { - case .x1: - nextRate = .x2 - case .x2: - nextRate = .x1 - default: - nextRate = .x1 - } + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings, { _ in - return settings.withUpdatedVoicePlaybackRate(nextRate) + return settings.withUpdatedVoicePlaybackRate(rate) }) - return nextRate + return rate } |> deliverOnMainQueue).start(next: { baseRate in guard let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType else { @@ -1767,22 +1767,31 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { }) let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let slowdown = baseRate == .x1 - controller.present( - UndoOverlayController( - presentationData: presentationData, - content: .audioRate( - slowdown: slowdown, - text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp + let slowdown: Bool? + if baseRate == .x1 { + slowdown = true + } else if baseRate == .x2 { + slowdown = false + } else { + slowdown = nil + } + if let slowdown = slowdown { + controller.present( + UndoOverlayController( + presentationData: presentationData, + content: .audioRate( + slowdown: slowdown, + text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp + ), + elevatedLayout: false, + animateInAsReplacement: hasTooltip, + action: { action in + return true + } ), - elevatedLayout: false, - animateInAsReplacement: hasTooltip, - action: { action in - return true - } - ), - in: .current - ) + in: .current + ) + } } }) } @@ -2324,7 +2333,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode { let timestamp1: Int32 = 100000 var peers = SimpleDictionary() peers[peer1.id] = peer1 - let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in + let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in gesture?.cancel() }, present: { _ in }) diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 5962f36651..3cce18b827 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -17,9 +17,10 @@ import PeerPresenceStatusManager import PhotoResources import ChatListSearchItemNode import ContextUI +import ChatInterfaceState public enum ChatListItemContent { - case peer(messages: [Message], peer: RenderedPeer, combinedReadState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, embeddedState: PeerChatListEmbeddedInterfaceState?, inputActivities: [(Peer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, ignoreUnreadBadge: Bool, displayAsMessage: Bool, hasFailedMessages: Bool) + case peer(messages: [Message], peer: RenderedPeer, combinedReadState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, embeddedState: StoredPeerChatInterfaceState?, inputActivities: [(Peer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, ignoreUnreadBadge: Bool, displayAsMessage: Bool, hasFailedMessages: Bool) case groupReference(groupId: PeerGroupId, peers: [ChatListGroupReferencePeer], message: Message?, unreadState: PeerGroupUnreadCountersCombinedSummary, hiddenByDefault: Bool) public var chatLocation: ChatLocation? { @@ -127,9 +128,9 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour { if let message = messages.last, let peer = peer.peer { self.interaction.messageSelected(peer, message, promoInfo) } else if let peer = peer.peer { - self.interaction.peerSelected(peer, promoInfo) + self.interaction.peerSelected(peer, nil, promoInfo) } else if let peer = peer.peers[peer.peerId] { - self.interaction.peerSelected(peer, promoInfo) + self.interaction.peerSelected(peer, nil, promoInfo) } case let .groupReference(groupId, _, _, _, _): self.interaction.groupSelected(groupId) @@ -675,12 +676,12 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { if peerValue.peerId.namespace == Namespaces.Peer.SecretChat { enablePreview = false } - case let .groupReference(groupReference): - if let previousItem = previousItem, case let .groupReference(previousGroupReference) = previousItem.content, groupReference.hiddenByDefault != previousGroupReference.hiddenByDefault { + case let .groupReference(_, _, _, _, hiddenByDefault): + if let previousItem = previousItem, case let .groupReference(_, _, _, _, previousHiddenByDefault) = previousItem.content, hiddenByDefault != previousHiddenByDefault { UIView.transition(with: self.avatarNode.view, duration: 0.3, options: [.transitionCrossDissolve], animations: { }, completion: nil) } - self.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: .archivedChatsIcon(hiddenByDefault: groupReference.hiddenByDefault), emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads) + self.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer.flatMap(EnginePeer.init), overrideImage: .archivedChatsIcon(hiddenByDefault: hiddenByDefault), emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads) } if let peer = peer { @@ -692,7 +693,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { } else if peer.isDeleted { overrideImage = .deletedIcon } - self.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads, displayDimensions: CGSize(width: 60.0, height: 60.0)) + self.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: EnginePeer(peer), overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads, displayDimensions: CGSize(width: 60.0, height: 60.0)) } self.contextContainer.isGestureEnabled = enablePreview && !item.editing @@ -813,7 +814,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { let unreadCount: (count: Int32, unread: Bool, muted: Bool, mutedCount: Int32?) let isRemovedFromTotalUnreadCount: Bool let peerPresence: PeerPresence? - let embeddedState: PeerChatListEmbeddedInterfaceState? + let embeddedState: StoredPeerChatInterfaceState? let summaryInfo: ChatListMessageTagSummaryInfo let inputActivities: [(Peer, PeerInputActivity)]? let isPeerGroup: Bool @@ -824,7 +825,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { var groupHiddenByDefault = false switch item.content { - case let .peer(messagesValue, peerValue, combinedReadStateValue, isRemovedFromTotalUnreadCountValue, peerPresenceValue, summaryInfoValue, embeddedStateValue, inputActivitiesValue, promoInfoValue, ignoreUnreadBadge, displayAsMessageValue, hasFailedMessagesValue): + case let .peer(messagesValue, peerValue, combinedReadStateValue, isRemovedFromTotalUnreadCountValue, peerPresenceValue, summaryInfoValue, embeddedStateValue, inputActivitiesValue, promoInfoValue, ignoreUnreadBadge, displayAsMessageValue, _): messages = messagesValue contentPeer = .chat(peerValue) combinedReadState = combinedReadStateValue @@ -1021,11 +1022,15 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { chatListText = (text, messageText) } - if inlineAuthorPrefix == nil, let embeddedState = embeddedState as? ChatEmbeddedInterfaceState { + if inlineAuthorPrefix == nil, let embeddedState = embeddedState, embeddedState.overrideChatTimestamp != nil, let opaqueState = _internal_decodeStoredChatInterfaceState(state: embeddedState) { + let interfaceState = ChatInterfaceState.parse(opaqueState) + hasDraft = true authorAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Draft, font: textFont, textColor: theme.messageDraftTextColor) + + let draftText: String = interfaceState.composeInputState.inputText.string - attributedText = NSAttributedString(string: foldLineBreaks(embeddedState.text.string.replacingOccurrences(of: "\n\n", with: " ")), font: textFont, textColor: theme.messageTextColor) + attributedText = NSAttributedString(string: foldLineBreaks(draftText.replacingOccurrences(of: "\n\n", with: " ")), font: textFont, textColor: theme.messageTextColor) } else if let message = messages.last { var composedString: NSMutableAttributedString if let inlineAuthorPrefix = inlineAuthorPrefix { @@ -1275,7 +1280,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { } } - var isMuted = isRemovedFromTotalUnreadCount + let isMuted = isRemovedFromTotalUnreadCount if isMuted { currentMutedIconImage = PresentationResourcesChatList.mutedIcon(item.presentationData.theme) } @@ -1346,7 +1351,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { let layoutOffset: CGFloat = 0.0 - let rawContentOriginX = 2.0 let rawContentWidth = params.width - leftInset - params.rightInset - 10.0 - editingOffset let (dateLayout, dateApply) = dateLayout(TextNodeLayoutArguments(attributedString: dateAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: rawContentWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) @@ -1412,7 +1416,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { if !displayAsMessage { if let peer = renderedPeer.chatMainPeer as? TelegramUser, let presence = presence as? TelegramUserPresence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != item.context.account.peerId { let updatedPresence = TelegramUserPresence(status: presence.status, lastActivity: 0) - let relativeStatus = relativeUserPresenceStatus(updatedPresence, relativeTo: timestamp) + let relativeStatus = relativeUserPresenceStatus(EnginePeer.Presence(updatedPresence), relativeTo: timestamp) if case .online = relativeStatus { online = true } @@ -1820,7 +1824,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { } let separatorInset: CGFloat - if case let .groupReference(groupReference) = item.content, groupReference.hiddenByDefault { + if case let .groupReference(_, _, _, _, hiddenByDefault) = item.content, hiddenByDefault { separatorInset = 0.0 } else if (!nextIsPinned && item.index.pinningIndex != nil) || last { separatorInset = 0.0 @@ -1835,7 +1839,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { if item.selected { backgroundColor = theme.itemSelectedBackgroundColor } else if item.index.pinningIndex != nil { - if case let .groupReference(groupReference) = item.content, groupReference.hiddenByDefault { + if case let .groupReference(_, _, _, _, hiddenByDefault) = item.content, hiddenByDefault { backgroundColor = theme.itemBackgroundColor } else { backgroundColor = theme.pinnedItemBackgroundColor diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 2d96d5dbd0..e9c213c773 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -53,7 +53,7 @@ public final class ChatListNodeInteraction { } let activateSearch: () -> Void - let peerSelected: (Peer, ChatListNodeEntryPromoInfo?) -> Void + let peerSelected: (Peer, Peer?, ChatListNodeEntryPromoInfo?) -> Void let disabledPeerSelected: (Peer) -> Void let togglePeerSelected: (Peer) -> Void let togglePeersSelection: ([PeerEntry], Bool) -> Void @@ -75,7 +75,7 @@ public final class ChatListNodeInteraction { public var searchTextHighightState: String? var highlightedChatLocation: ChatListHighlightedLocation? - public init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer, ChatListNodeEntryPromoInfo?) -> Void, disabledPeerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (Peer) -> Void, togglePeersSelection: @escaping ([PeerEntry], Bool) -> Void, additionalCategorySelected: @escaping (Int) -> Void, messageSelected: @escaping (Peer, Message, ChatListNodeEntryPromoInfo?) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId, Bool) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, hidePsa: @escaping (PeerId) -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void, present: @escaping (ViewController) -> Void) { + public init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer, Peer?, ChatListNodeEntryPromoInfo?) -> Void, disabledPeerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (Peer) -> Void, togglePeersSelection: @escaping ([PeerEntry], Bool) -> Void, additionalCategorySelected: @escaping (Int) -> Void, messageSelected: @escaping (Peer, Message, ChatListNodeEntryPromoInfo?) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId, Bool) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, hidePsa: @escaping (PeerId) -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void, present: @escaping (ViewController) -> Void) { self.activateSearch = activateSearch self.peerSelected = peerSelected self.disabledPeerSelected = disabledPeerSelected @@ -106,6 +106,18 @@ public final class ChatListNodePeerInputActivities { } } +private func areFoundPeerArraysEqual(_ lhs: [(Peer, Peer?)], _ rhs: [(Peer, Peer?)]) -> Bool { + if lhs.count != rhs.count { + return false + } + for i in 0 ..< lhs.count { + if !arePeersEqual(lhs[i].0, rhs[i].0) || !arePeersEqual(lhs[i].1, rhs[i].1) { + return false + } + } + return true +} + public struct ChatListNodeState: Equatable { public var presentationData: ChatListPresentationData public var editing: Bool @@ -117,10 +129,10 @@ public struct ChatListNodeState: Equatable { public var archiveShouldBeTemporaryRevealed: Bool public var selectedAdditionalCategoryIds: Set public var hiddenPsaPeerId: PeerId? - public var foundPeers: [Peer] + public var foundPeers: [(Peer, Peer?)] public var selectedPeerMap: [PeerId: Peer] - public init(presentationData: ChatListPresentationData, editing: Bool, peerIdWithRevealedOptions: PeerId?, selectedPeerIds: Set, foundPeers: [Peer], selectedPeerMap: [PeerId: Peer], selectedAdditionalCategoryIds: Set, peerInputActivities: ChatListNodePeerInputActivities?, pendingRemovalPeerIds: Set, pendingClearHistoryPeerIds: Set, archiveShouldBeTemporaryRevealed: Bool, hiddenPsaPeerId: PeerId?) { + public init(presentationData: ChatListPresentationData, editing: Bool, peerIdWithRevealedOptions: PeerId?, selectedPeerIds: Set, foundPeers: [(Peer, Peer?)], selectedPeerMap: [PeerId: Peer], selectedAdditionalCategoryIds: Set, peerInputActivities: ChatListNodePeerInputActivities?, pendingRemovalPeerIds: Set, pendingClearHistoryPeerIds: Set, archiveShouldBeTemporaryRevealed: Bool, hiddenPsaPeerId: PeerId?) { self.presentationData = presentationData self.editing = editing self.peerIdWithRevealedOptions = peerIdWithRevealedOptions @@ -148,7 +160,7 @@ public struct ChatListNodeState: Equatable { if lhs.selectedPeerIds != rhs.selectedPeerIds { return false } - if arePeerArraysEqual(lhs.foundPeers, rhs.foundPeers) { + if areFoundPeerArraysEqual(lhs.foundPeers, rhs.foundPeers) { return false } if arePeerDictionariesEqual(lhs.selectedPeerMap, rhs.selectedPeerMap) { @@ -302,7 +314,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL if editing { nodeInteraction.togglePeerSelected(chatPeer) } else { - nodeInteraction.peerSelected(chatPeer, nil) + nodeInteraction.peerSelected(chatPeer, nil, nil) } } }, disabledAction: { _ in @@ -383,7 +395,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL if editing { nodeInteraction.togglePeerSelected(chatPeer) } else { - nodeInteraction.peerSelected(chatPeer, nil) + nodeInteraction.peerSelected(chatPeer, nil, nil) } } }, disabledAction: { _ in @@ -602,7 +614,7 @@ public final class ChatListNode: ListView { if let strongSelf = self, let activateSearch = strongSelf.activateSearch { activateSearch() } - }, peerSelected: { [weak self] peer, promoInfo in + }, peerSelected: { [weak self] peer, _, promoInfo in if let strongSelf = self, let peerSelected = strongSelf.peerSelected { peerSelected(peer, true, true, promoInfo) } @@ -952,8 +964,8 @@ public final class ChatListNode: ListView { if index.messageIndex.id.peerId == removingPeerId { didIncludeRemovingPeerId = true } - } else if case let .GroupReferenceEntry(entry) = entry { - didIncludeHiddenByDefaultArchive = entry.hiddenByDefault + } else if case let .GroupReferenceEntry(_, _, _, _, _, _, _, _, hiddenByDefault) = entry { + didIncludeHiddenByDefaultArchive = hiddenByDefault } } } @@ -961,16 +973,16 @@ public final class ChatListNode: ListView { var doesIncludeArchive = false var doesIncludeHiddenByDefaultArchive = false for entry in processedView.filteredEntries { - if case let .PeerEntry(peerEntry) = entry { - if peerEntry.index.pinningIndex != nil { - updatedPinnedChats.append(peerEntry.index.messageIndex.id.peerId) + if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = entry { + if index.pinningIndex != nil { + updatedPinnedChats.append(index.messageIndex.id.peerId) } - if peerEntry.index.messageIndex.id.peerId == removingPeerId { + if index.messageIndex.id.peerId == removingPeerId { doesIncludeRemovingPeerId = true } - } else if case let .GroupReferenceEntry(entry) = entry { + } else if case let .GroupReferenceEntry(_, _, _, _, _, _, _, _, hiddenByDefault) = entry { doesIncludeArchive = true - doesIncludeHiddenByDefaultArchive = entry.hiddenByDefault + doesIncludeHiddenByDefaultArchive = hiddenByDefault } } if previousPinnedChats != updatedPinnedChats { @@ -1331,8 +1343,8 @@ public final class ChatListNode: ListView { var isHiddenArchiveVisible = false strongSelf.forEachItemNode({ itemNode in if let itemNode = itemNode as? ChatListItemNode, let item = itemNode.item { - if case let .groupReference(groupReference) = item.content { - if groupReference.hiddenByDefault { + if case let .groupReference(_, _, _, _, hiddenByDefault) = item.content { + if hiddenByDefault { isHiddenArchiveVisible = true } } @@ -1587,8 +1599,8 @@ public final class ChatListNode: ListView { for item in transition.insertItems { if let item = item.item as? ChatListItem { switch item.content { - case let .peer(peer): - insertedPeerIds.append(peer.peer.peerId) + case let .peer(_, peer, _, _, _, _, _, _, _, _, _, _): + insertedPeerIds.append(peer.peerId) case .groupReference: break } @@ -1997,6 +2009,13 @@ public final class ChatListNode: ListView { } private func handlePanSelection(location: CGPoint) { + var location = location + if location.y < self.insets.top { + location.y = self.insets.top + 5.0 + } else if location.y > self.frame.height - self.insets.bottom { + location.y = self.frame.height - self.insets.bottom - 5.0 + } + if let state = self.selectionPanState { if let peer = self.peerAtPoint(location) { if peer.id == state.initialPeerId { diff --git a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift index ac87a13490..4882cb291f 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift @@ -46,7 +46,7 @@ public enum ChatListNodeEntryPromoInfo: Equatable { enum ChatListNodeEntry: Comparable, Identifiable { case HeaderEntry - case PeerEntry(index: ChatListIndex, presentationData: ChatListPresentationData, messages: [Message], readState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, peer: RenderedPeer, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, inputActivities: [(Peer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, hasFailedMessages: Bool, isContact: Bool) + case PeerEntry(index: ChatListIndex, presentationData: ChatListPresentationData, messages: [Message], readState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: StoredPeerChatInterfaceState?, peer: RenderedPeer, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, inputActivities: [(Peer, PeerInputActivity)]?, promoInfo: ChatListNodeEntryPromoInfo?, hasFailedMessages: Bool, isContact: Bool) case HoleEntry(ChatListHole, theme: PresentationTheme) case GroupReferenceEntry(index: ChatListIndex, presentationData: ChatListPresentationData, groupId: PeerGroupId, peers: [ChatListGroupReferencePeer], message: Message?, editing: Bool, unreadState: PeerGroupUnreadCountersCombinedSummary, revealed: Bool, hiddenByDefault: Bool) case ArchiveIntro(presentationData: ChatListPresentationData) @@ -64,8 +64,8 @@ enum ChatListNodeEntry: Comparable, Identifiable { return .index(index) case .ArchiveIntro: return .index(ChatListIndex.absoluteUpperBound.successor) - case let .AdditionalCategory(additionalCategory): - return .additionalCategory(additionalCategory.index) + case let .AdditionalCategory(index, _, _, _, _, _, _): + return .additionalCategory(index) } } @@ -81,8 +81,8 @@ enum ChatListNodeEntry: Comparable, Identifiable { return .GroupId(groupId) case .ArchiveIntro: return .ArchiveIntro - case let .AdditionalCategory(additionalCategory): - return .additionalCategory(additionalCategory.id) + case let .AdditionalCategory(_, id, _, _, _, _, _): + return .additionalCategory(id) } } @@ -144,7 +144,7 @@ enum ChatListNodeEntry: Comparable, Identifiable { return false } if let lhsEmbeddedState = lhsEmbeddedState, let rhsEmbeddedState = rhsEmbeddedState { - if !lhsEmbeddedState.isEqual(to: rhsEmbeddedState) { + if lhsEmbeddedState != rhsEmbeddedState { return false } } else if (lhsEmbeddedState != nil) != (rhsEmbeddedState != nil) { @@ -281,7 +281,7 @@ private func offsetPinnedIndex(_ index: ChatListIndex, offset: UInt16) -> ChatLi } } -func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState, savedMessagesPeer: Peer?, foundPeers: [Peer], hideArchivedFolderByDefault: Bool, displayArchiveIntro: Bool, mode: ChatListNodeMode) -> (entries: [ChatListNodeEntry], loading: Bool) { +func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState, savedMessagesPeer: Peer?, foundPeers: [(Peer, Peer?)], hideArchivedFolderByDefault: Bool, displayArchiveIntro: Bool, mode: ChatListNodeMode) -> (entries: [ChatListNodeEntry], loading: Bool) { var result: [ChatListNodeEntry] = [] var pinnedIndexOffset: UInt16 = 0 @@ -300,7 +300,7 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState, var foundPeerIds = Set() for peer in foundPeers { - foundPeerIds.insert(peer.id) + foundPeerIds.insert(peer.0.id) } if view.laterIndex == nil && savedMessagesPeer == nil { @@ -338,8 +338,13 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState, if !foundPeers.isEmpty { var foundPinningIndex: UInt16 = UInt16(foundPeers.count) for peer in foundPeers.reversed() { - let messageIndex = MessageIndex(id: MessageId(peerId: peer.id, namespace: 0, id: 0), timestamp: 1) - result.append(.PeerEntry(index: ChatListIndex(pinningIndex: foundPinningIndex, messageIndex: messageIndex), presentationData: state.presentationData, messages: [], readState: nil, isRemovedFromTotalUnreadCount: false, embeddedInterfaceState: nil, peer: RenderedPeer(peerId: peer.id, peers: SimpleDictionary([peer.id: peer])), presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), editing: state.editing, hasActiveRevealControls: false, selected: state.selectedPeerIds.contains(peer.id), inputActivities: nil, promoInfo: nil, hasFailedMessages: false, isContact: false)) + var peers: [PeerId: Peer] = [peer.0.id: peer.0] + if let chatPeer = peer.1 { + peers[chatPeer.id] = chatPeer + } + + let messageIndex = MessageIndex(id: MessageId(peerId: peer.0.id, namespace: 0, id: 0), timestamp: 1) + result.append(.PeerEntry(index: ChatListIndex(pinningIndex: foundPinningIndex, messageIndex: messageIndex), presentationData: state.presentationData, messages: [], readState: nil, isRemovedFromTotalUnreadCount: false, embeddedInterfaceState: nil, peer: RenderedPeer(peerId: peer.0.id, peers: SimpleDictionary(peers)), presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), editing: state.editing, hasActiveRevealControls: false, selected: state.selectedPeerIds.contains(peer.0.id), inputActivities: nil, promoInfo: nil, hasFailedMessages: false, isContact: false)) if foundPinningIndex != 0 { foundPinningIndex -= 1 } diff --git a/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift b/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift index 4e96f658ad..122970ea2e 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNodeLocation.swift @@ -12,12 +12,12 @@ enum ChatListNodeLocation: Equatable { var filter: ChatListFilter? { switch self { - case let .initial(initial): - return initial.filter - case let .navigation(navigation): - return navigation.filter - case let .scroll(scroll): - return scroll.filter + case let .initial(_, filter): + return filter + case let .navigation(_, filter): + return filter + case let .scroll(_, _, _, _, filter): + return filter } } } @@ -28,7 +28,7 @@ struct ChatListNodeViewUpdate { let scrollPosition: ChatListNodeViewScrollPosition? } -func chatListFilterPredicate(filter: ChatListFilterData) -> ChatListFilterPredicate { +public func chatListFilterPredicate(filter: ChatListFilterData) -> ChatListFilterPredicate { var includePeers = Set(filter.includePeers.peers) var excludePeers = Set(filter.excludePeers) diff --git a/submodules/ChatMessageInteractiveMediaBadge/BUILD b/submodules/ChatMessageInteractiveMediaBadge/BUILD index bb8f11a6d3..4fe306285a 100644 --- a/submodules/ChatMessageInteractiveMediaBadge/BUILD +++ b/submodules/ChatMessageInteractiveMediaBadge/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/ChatTitleActivityNode/BUILD b/submodules/ChatTitleActivityNode/BUILD index c6d3c8966e..e258d48149 100644 --- a/submodules/ChatTitleActivityNode/BUILD +++ b/submodules/ChatTitleActivityNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/CheckNode/BUILD b/submodules/CheckNode/BUILD index d64f036003..5fd684a8d2 100644 --- a/submodules/CheckNode/BUILD +++ b/submodules/CheckNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/CloudData/BUILD b/submodules/CloudData/BUILD index 662ebc2ee3..83da0d43aa 100644 --- a/submodules/CloudData/BUILD +++ b/submodules/CloudData/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/Postbox:Postbox", diff --git a/submodules/CloudData/Sources/CloudData.swift b/submodules/CloudData/Sources/CloudData.swift index 04f1df80be..12bb67f42b 100644 --- a/submodules/CloudData/Sources/CloudData.swift +++ b/submodules/CloudData/Sources/CloudData.swift @@ -14,15 +14,15 @@ private func fetchRawData(prefix: String) -> Signal { return Signal { subscriber in #if targetEnvironment(simulator) return EmptyDisposable - #endif - + #else let container = CKContainer.default() let publicDatabase = container.database(with: .public) let recordId = CKRecord.ID(recordName: "emergency-datacenter-\(prefix)") publicDatabase.fetch(withRecordID: recordId, completionHandler: { record, error in if let error = error { print("publicDatabase.fetch error: \(error)") - if let error = error as? NSError, error.domain == CKError.errorDomain, error.code == 1 { + let nsError = error as NSError + if nsError.domain == CKError.errorDomain, nsError.code == 1 { subscriber.putError(.networkUnavailable) } else { subscriber.putError(.generic) @@ -46,8 +46,8 @@ private func fetchRawData(prefix: String) -> Signal { }) return ActionDisposable { - } + #endif } } diff --git a/submodules/ComponentFlow/BUILD b/submodules/ComponentFlow/BUILD new file mode 100644 index 0000000000..6c7d864ad5 --- /dev/null +++ b/submodules/ComponentFlow/BUILD @@ -0,0 +1,17 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ComponentFlow", + module_name = "ComponentFlow", + srcs = glob([ + "Source/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/ComponentFlow/Source/Base/ChildComponentTransitions.swift b/submodules/ComponentFlow/Source/Base/ChildComponentTransitions.swift new file mode 100644 index 0000000000..881c14d844 --- /dev/null +++ b/submodules/ComponentFlow/Source/Base/ChildComponentTransitions.swift @@ -0,0 +1,78 @@ +import Foundation +import UIKit + +public extension Transition.Appear { + static func `default`(scale: Bool = false, alpha: Bool = false) -> Transition.Appear { + return Transition.Appear { component, view, transition in + if scale { + transition.animateScale(view: view, from: 0.01, to: 1.0) + } + if alpha { + transition.animateAlpha(view: view, from: 0.0, to: 1.0) + } + } + } + + static func scaleIn() -> Transition.Appear { + return Transition.Appear { component, view, transition in + transition.animateScale(view: view, from: 0.01, to: 1.0) + } + } +} + +public extension Transition.AppearWithGuide { + static func `default`(scale: Bool = false, alpha: Bool = false) -> Transition.AppearWithGuide { + return Transition.AppearWithGuide { component, view, guide, transition in + if scale { + transition.animateScale(view: view, from: 0.01, to: 1.0) + } + if alpha { + transition.animateAlpha(view: view, from: 0.0, to: 1.0) + } + transition.animatePosition(view: view, from: CGPoint(x: guide.x - view.center.x, y: guide.y - view.center.y), to: CGPoint(), additive: true) + } + } +} + +public extension Transition.Disappear { + static let `default` = Transition.Disappear { view, transition, completion in + transition.setAlpha(view: view, alpha: 0.0, completion: { _ in + completion() + }) + } +} + +public extension Transition.DisappearWithGuide { + static func `default`(alpha: Bool = true) -> Transition.DisappearWithGuide { + return Transition.DisappearWithGuide { stage, view, guide, transition, completion in + switch stage { + case .begin: + if alpha { + transition.setAlpha(view: view, alpha: 0.0, completion: { _ in + completion() + }) + } + transition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: guide.x - view.bounds.width / 2.0, y: guide.y - view.bounds.height / 2.0), size: view.bounds.size), completion: { _ in + if !alpha { + completion() + } + }) + case .update: + transition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: guide.x - view.bounds.width / 2.0, y: guide.y - view.bounds.height / 2.0), size: view.bounds.size)) + } + } + } +} + +public extension Transition.Update { + static let `default` = Transition.Update { component, view, transition in + let frame = component.size.centered(around: component._position ?? CGPoint()) + if view.frame != frame { + transition.setFrame(view: view, frame: frame) + } + let opacity = component._opacity ?? 1.0 + if view.alpha != opacity { + transition.setAlpha(view: view, alpha: opacity) + } + } +} diff --git a/submodules/ComponentFlow/Source/Base/CombinedComponent.swift b/submodules/ComponentFlow/Source/Base/CombinedComponent.swift new file mode 100644 index 0000000000..ead71c9b1f --- /dev/null +++ b/submodules/ComponentFlow/Source/Base/CombinedComponent.swift @@ -0,0 +1,798 @@ +import Foundation +import UIKit + +private func updateChildAnyComponent( + id: _AnyChildComponent.Id, + component: AnyComponent, + view: UIView, + availableSize: CGSize, + transition: Transition +) -> _UpdatedChildComponent { + let parentContext = _AnyCombinedComponentContext.current + + if !parentContext.updateContext.updatedViews.insert(id).inserted { + preconditionFailure("Child component can only be processed once") + } + + let context = view.context(component: component) + var isEnvironmentUpdated = false + var isStateUpdated = false + var isComponentUpdated = false + var availableSizeUpdated = false + + if context.environment.calculateIsUpdated() { + context.environment._isUpdated = false + isEnvironmentUpdated = true + } + + if context.erasedState.isUpdated { + context.erasedState.isUpdated = false + isStateUpdated = true + } + + if context.erasedComponent != component { + isComponentUpdated = true + } + context.erasedComponent = component + + if context.layoutResult.availableSize != availableSize { + context.layoutResult.availableSize = availableSize + availableSizeUpdated = true + } + + let isUpdated = isEnvironmentUpdated || isStateUpdated || isComponentUpdated || availableSizeUpdated + + if !isUpdated, let size = context.layoutResult.size { + return _UpdatedChildComponent( + id: id, + component: component, + view: view, + context: context, + size: size + ) + } else { + let size = component._update( + view: view, + availableSize: availableSize, + transition: transition + ) + context.layoutResult.size = size + + return _UpdatedChildComponent( + id: id, + component: component, + view: view, + context: context, + size: size + ) + } +} + +public class _AnyChildComponent { + fileprivate enum Id: Hashable { + case direct(Int) + case mapped(Int, AnyHashable) + } + + fileprivate var directId: Int { + return Int(bitPattern: Unmanaged.passUnretained(self).toOpaque()) + } +} + +public final class _ConcreteChildComponent: _AnyChildComponent { + fileprivate var id: Id { + return .direct(self.directId) + } + + public func update(component: ComponentType, @EnvironmentBuilder environment: () -> Environment, availableSize: CGSize, transition: Transition) -> _UpdatedChildComponent { + let parentContext = _AnyCombinedComponentContext.current + if !parentContext.updateContext.configuredViews.insert(self.id).inserted { + preconditionFailure("Child component can only be configured once") + } + + var transition = transition + + let view: ComponentType.View + if let current = parentContext.childViews[self.id] { + // TODO: Check if the type is the same + view = current.view as! ComponentType.View + } else { + view = component.makeView() + transition = .immediate + } + + let context = view.context(component: component) + EnvironmentBuilder._environment = context.erasedEnvironment + let _ = environment() + EnvironmentBuilder._environment = nil + + return updateChildAnyComponent( + id: self.id, + component: AnyComponent(component), + view: view, + availableSize: availableSize, + transition: transition + ) + } +} + +public extension _ConcreteChildComponent where ComponentType.EnvironmentType == Empty { + func update(component: ComponentType, availableSize: CGSize, transition: Transition) -> _UpdatedChildComponent { + return self.update(component: component, environment: {}, availableSize: availableSize, transition: transition) + } +} + +public final class _UpdatedChildComponentGuide { + fileprivate let instance: _ChildComponentGuide + + fileprivate init(instance: _ChildComponentGuide) { + self.instance = instance + } +} + +public final class _ChildComponentGuide { + fileprivate var directId: Int { + return Int(bitPattern: Unmanaged.passUnretained(self).toOpaque()) + } + + fileprivate var id: _AnyChildComponent.Id { + return .direct(self.directId) + } + + public func update(position: CGPoint, transition: Transition) -> _UpdatedChildComponentGuide { + let parentContext = _AnyCombinedComponentContext.current + + let previousPosition = parentContext.guides[self.id] + + if parentContext.updateContext.configuredGuides.updateValue(_AnyCombinedComponentContext.UpdateContext.ConfiguredGuide(previousPosition: previousPosition ?? position, position: position), forKey: self.id) != nil { + preconditionFailure("Child guide can only be configured once") + } + + for disappearingView in parentContext.disappearingChildViews { + if disappearingView.guideId == self.id { + disappearingView.transitionWithGuide?( + stage: .update, + view: disappearingView.view, + guide: position, + transition: transition, + completion: disappearingView.completion + ) + } + } + + return _UpdatedChildComponentGuide(instance: self) + } +} + +public final class _UpdatedChildComponent { + fileprivate let id: _AnyChildComponent.Id + fileprivate let component: _TypeErasedComponent + fileprivate let view: UIView + fileprivate let context: _TypeErasedComponentContext + + public let size: CGSize + + var _removed: Bool = false + var _position: CGPoint? + var _opacity: CGFloat? + + fileprivate var transitionAppear: Transition.Appear? + fileprivate var transitionAppearWithGuide: (Transition.AppearWithGuide, _AnyChildComponent.Id)? + fileprivate var transitionDisappear: Transition.Disappear? + fileprivate var transitionDisappearWithGuide: (Transition.DisappearWithGuide, _AnyChildComponent.Id)? + fileprivate var transitionUpdate: Transition.Update? + fileprivate var gestures: [Gesture] = [] + + fileprivate init( + id: _AnyChildComponent.Id, + component: _TypeErasedComponent, + view: UIView, + context: _TypeErasedComponentContext, + size: CGSize + ) { + self.id = id + self.component = component + self.view = view + self.context = context + self.size = size + } + + @discardableResult public func appear(_ transition: Transition.Appear) -> _UpdatedChildComponent { + self.transitionAppear = transition + self.transitionAppearWithGuide = nil + return self + } + + @discardableResult public func appear(_ transition: Transition.AppearWithGuide, guide: _UpdatedChildComponentGuide) -> _UpdatedChildComponent { + self.transitionAppear = nil + self.transitionAppearWithGuide = (transition, guide.instance.id) + return self + } + + @discardableResult public func disappear(_ transition: Transition.Disappear) -> _UpdatedChildComponent { + self.transitionDisappear = transition + self.transitionDisappearWithGuide = nil + return self + } + + @discardableResult public func disappear(_ transition: Transition.DisappearWithGuide, guide: _UpdatedChildComponentGuide) -> _UpdatedChildComponent { + self.transitionDisappear = nil + self.transitionDisappearWithGuide = (transition, guide.instance.id) + return self + } + + @discardableResult public func update(_ transition: Transition.Update) -> _UpdatedChildComponent { + self.transitionUpdate = transition + return self + } + + @discardableResult public func removed(_ removed: Bool) -> _UpdatedChildComponent { + self._removed = removed + return self + } + + @discardableResult public func position(_ position: CGPoint) -> _UpdatedChildComponent { + self._position = position + return self + } + + @discardableResult public func opacity(_ opacity: CGFloat) -> _UpdatedChildComponent { + self._opacity = opacity + return self + } + + @discardableResult public func gesture(_ gesture: Gesture) -> _UpdatedChildComponent { + self.gestures.append(gesture) + return self + } +} + +public final class _EnvironmentChildComponent: _AnyChildComponent { + fileprivate var id: Id { + return .direct(self.directId) + } + + func update(component: AnyComponent, @EnvironmentBuilder environment: () -> Environment, availableSize: CGSize, transition: Transition) -> _UpdatedChildComponent { + let parentContext = _AnyCombinedComponentContext.current + if !parentContext.updateContext.configuredViews.insert(self.id).inserted { + preconditionFailure("Child component can only be configured once") + } + + var transition = transition + + let view: UIView + if let current = parentContext.childViews[self.id] { + // Check if the type is the same + view = current.view + } else { + view = component._makeView() + transition = .immediate + } + + EnvironmentBuilder._environment = view.context(component: component).erasedEnvironment + let _ = environment() + EnvironmentBuilder._environment = nil + + return updateChildAnyComponent( + id: self.id, + component: component, + view: view, + availableSize: availableSize, + transition: transition + ) + } +} + +public extension _EnvironmentChildComponent where EnvironmentType == Empty { + func update(component: AnyComponent, availableSize: CGSize, transition: Transition) -> _UpdatedChildComponent { + return self.update(component: component, environment: {}, availableSize: availableSize, transition: transition) + } +} + +public extension _EnvironmentChildComponent { + func update(_ component: ComponentType, @EnvironmentBuilder environment: () -> Environment, availableSize: CGSize, transition: Transition) -> _UpdatedChildComponent where ComponentType.EnvironmentType == EnvironmentType { + return self.update(component: AnyComponent(component), environment: environment, availableSize: availableSize, transition: transition) + } + + func update(_ component: ComponentType, @EnvironmentBuilder environment: () -> Environment, availableSize: CGSize, transition: Transition) -> _UpdatedChildComponent where ComponentType.EnvironmentType == EnvironmentType, EnvironmentType == Empty { + return self.update(component: AnyComponent(component), environment: {}, availableSize: availableSize, transition: transition) + } +} + +public final class _EnvironmentChildComponentFromMap: _AnyChildComponent { + private let id: Id + + fileprivate init(id: Id) { + self.id = id + } + + public func update(component: AnyComponent, @EnvironmentBuilder environment: () -> Environment, availableSize: CGSize, transition: Transition) -> _UpdatedChildComponent { + let parentContext = _AnyCombinedComponentContext.current + if !parentContext.updateContext.configuredViews.insert(self.id).inserted { + preconditionFailure("Child component can only be configured once") + } + + var transition = transition + + let view: UIView + if let current = parentContext.childViews[self.id] { + // Check if the type is the same + view = current.view + } else { + view = component._makeView() + transition = .immediate + } + + EnvironmentBuilder._environment = view.context(component: component).erasedEnvironment + let _ = environment() + EnvironmentBuilder._environment = nil + + return updateChildAnyComponent( + id: self.id, + component: component, + view: view, + availableSize: availableSize, + transition: transition + ) + } +} + +public extension _EnvironmentChildComponentFromMap where EnvironmentType == Empty { + func update(component: AnyComponent, availableSize: CGSize, transition: Transition) -> _UpdatedChildComponent { + return self.update(component: component, environment: {}, availableSize: availableSize, transition: transition) + } +} + +public final class _EnvironmentChildComponentMap { + private var directId: Int { + return Int(bitPattern: Unmanaged.passUnretained(self).toOpaque()) + } + + public subscript(_ key: Key) -> _EnvironmentChildComponentFromMap { + get { + return _EnvironmentChildComponentFromMap(id: .mapped(self.directId, key)) + } + } +} + +public final class CombinedComponentContext { + fileprivate let escapeGuard = EscapeGuard() + + private let context: ComponentContext + public let view: UIView + + public let component: ComponentType + public let availableSize: CGSize + public let transition: Transition + private let addImpl: (_ updatedComponent: _UpdatedChildComponent) -> Void + + public var environment: Environment { + return self.context.environment + } + public var state: ComponentType.State { + return self.context.state + } + + fileprivate init( + context: ComponentContext, + view: UIView, + component: ComponentType, + availableSize: CGSize, + transition: Transition, + add: @escaping (_ updatedComponent: _UpdatedChildComponent) -> Void + ) { + self.context = context + self.view = view + self.component = component + self.availableSize = availableSize + self.transition = transition + self.addImpl = add + } + + public func add(_ updatedComponent: _UpdatedChildComponent) { + self.addImpl(updatedComponent) + } +} + +public protocol CombinedComponent: Component { + typealias Body = (CombinedComponentContext) -> CGSize + + static var body: Body { get } +} + +private class _AnyCombinedComponentContext { + class UpdateContext { + struct ConfiguredGuide { + var previousPosition: CGPoint + var position: CGPoint + } + + var configuredViews: Set<_AnyChildComponent.Id> = Set() + var updatedViews: Set<_AnyChildComponent.Id> = Set() + var configuredGuides: [_AnyChildComponent.Id: ConfiguredGuide] = [:] + } + + private static var _current: _AnyCombinedComponentContext? + static var current: _AnyCombinedComponentContext { + return self._current! + } + + static func push(_ context: _AnyCombinedComponentContext) -> _AnyCombinedComponentContext? { + let previous = self._current + + precondition(context._updateContext == nil) + context._updateContext = UpdateContext() + self._current = context + + return previous + } + + static func pop(_ context: _AnyCombinedComponentContext, stack: _AnyCombinedComponentContext?) { + precondition(context._updateContext != nil) + context._updateContext = nil + + self._current = stack + } + + class ChildView { + let view: UIView + var index: Int + var transition: Transition.Disappear? + var transitionWithGuide: (Transition.DisappearWithGuide, _AnyChildComponent.Id)? + + var gestures: [UInt: UIGestureRecognizer] = [:] + + init(view: UIView, index: Int) { + self.view = view + self.index = index + } + + func updateGestures(_ gestures: [Gesture]) { + var validIds: [UInt] = [] + for gesture in gestures { + validIds.append(gesture.id.id) + if let current = self.gestures[gesture.id.id] { + gesture.update(gesture: current) + } else { + let gestureInstance = gesture.create() + self.gestures[gesture.id.id] = gestureInstance + self.view.addGestureRecognizer(gestureInstance) + } + } + var removeIds: [UInt] = [] + for id in self.gestures.keys { + if !validIds.contains(id) { + removeIds.append(id) + } + } + for id in removeIds { + if let gestureInstance = self.gestures.removeValue(forKey: id) { + self.view.removeGestureRecognizer(gestureInstance) + } + } + } + } + + class DisappearingChildView { + let view: UIView + let guideId: _AnyChildComponent.Id? + let transition: Transition.Disappear? + let transitionWithGuide: Transition.DisappearWithGuide? + let completion: () -> Void + + init( + view: UIView, + guideId: _AnyChildComponent.Id?, + transition: Transition.Disappear?, + transitionWithGuide: Transition.DisappearWithGuide?, + completion: @escaping () -> Void + ) { + self.view = view + self.guideId = guideId + self.transition = transition + self.transitionWithGuide = transitionWithGuide + self.completion = completion + } + } + + var childViews: [_AnyChildComponent.Id: ChildView] = [:] + var childViewIndices: [_AnyChildComponent.Id] = [] + var guides: [_AnyChildComponent.Id: CGPoint] = [:] + var disappearingChildViews: [DisappearingChildView] = [] + + private var _updateContext: UpdateContext? + var updateContext: UpdateContext { + return self._updateContext! + } +} + +private final class _CombinedComponentContext: _AnyCombinedComponentContext { + var body: ComponentType.Body? +} + +private var UIView_CombinedComponentContextKey: Int? + +private extension UIView { + func getCombinedComponentContext(_ type: ComponentType.Type) -> _CombinedComponentContext { + if let context = objc_getAssociatedObject(self, &UIView_CombinedComponentContextKey) as? _CombinedComponentContext { + return context + } else { + let context = _CombinedComponentContext() + objc_setAssociatedObject(self, &UIView_CombinedComponentContextKey, context, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + return context + } + } +} + +public extension Transition { + final class Appear { + private let f: (_UpdatedChildComponent, UIView, Transition) -> Void + + public init(_ f: @escaping (_UpdatedChildComponent, UIView, Transition) -> Void) { + self.f = f + } + + public func callAsFunction(component: _UpdatedChildComponent, view: UIView, transition: Transition) { + self.f(component, view, transition) + } + } + + final class AppearWithGuide { + private let f: (_UpdatedChildComponent, UIView, CGPoint, Transition) -> Void + + public init(_ f: @escaping (_UpdatedChildComponent, UIView, CGPoint, Transition) -> Void) { + self.f = f + } + + public func callAsFunction(component: _UpdatedChildComponent, view: UIView, guide: CGPoint, transition: Transition) { + self.f(component, view, guide, transition) + } + } + + final class Disappear { + private let f: (UIView, Transition, @escaping () -> Void) -> Void + + public init(_ f: @escaping (UIView, Transition, @escaping () -> Void) -> Void) { + self.f = f + } + + public func callAsFunction(view: UIView, transition: Transition, completion: @escaping () -> Void) { + self.f(view, transition, completion) + } + } + + final class DisappearWithGuide { + public enum Stage { + case begin + case update + } + + private let f: (Stage, UIView, CGPoint, Transition, @escaping () -> Void) -> Void + + public init(_ f: @escaping (Stage, UIView, CGPoint, Transition, @escaping () -> Void) -> Void + ) { + self.f = f + } + + public func callAsFunction(stage: Stage, view: UIView, guide: CGPoint, transition: Transition, completion: @escaping () -> Void) { + self.f(stage, view, guide, transition, completion) + } + } + + final class Update { + private let f: (_UpdatedChildComponent, UIView, Transition) -> Void + + public init(_ f: @escaping (_UpdatedChildComponent, UIView, Transition) -> Void) { + self.f = f + } + + public func callAsFunction(component: _UpdatedChildComponent, view: UIView, transition: Transition) { + self.f(component, view, transition) + } + } +} + +public extension CombinedComponent { + func makeView() -> UIView { + return UIView() + } + + func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize { + let context = view.getCombinedComponentContext(Self.self) + + let storedBody: Body + if let current = context.body { + storedBody = current + } else { + storedBody = Self.body + context.body = storedBody + } + + let viewContext = view.context(component: self) + + var nextChildIndex = 0 + var addedChildIds = Set<_AnyChildComponent.Id>() + + let contextStack = _AnyCombinedComponentContext.push(context) + + let escapeStatus: EscapeGuard.Status + let size: CGSize + do { + let bodyContext = CombinedComponentContext( + context: viewContext, + view: view, + component: self, + availableSize: availableSize, + transition: transition, + add: { updatedChild in + if !addedChildIds.insert(updatedChild.id).inserted { + preconditionFailure("Child component can only be added once") + } + + let index = nextChildIndex + nextChildIndex += 1 + + if let previousView = context.childViews[updatedChild.id] { + precondition(updatedChild.view === previousView.view) + + if index != previousView.index { + assert(index < previousView.index) + for i in index ..< previousView.index { + if let moveView = context.childViews[context.childViewIndices[i]] { + moveView.index += 1 + } + } + context.childViewIndices.remove(at: previousView.index) + context.childViewIndices.insert(updatedChild.id, at: index) + previousView.index = index + view.insertSubview(previousView.view, at: index) + } + + previousView.updateGestures(updatedChild.gestures) + previousView.transition = updatedChild.transitionDisappear + previousView.transitionWithGuide = updatedChild.transitionDisappearWithGuide + + (updatedChild.transitionUpdate ?? Transition.Update.default)(component: updatedChild, view: updatedChild.view, transition: transition) + } else { + for i in index ..< context.childViewIndices.count { + if let moveView = context.childViews[context.childViewIndices[i]] { + moveView.index += 1 + } + } + + context.childViewIndices.insert(updatedChild.id, at: index) + let childView = _AnyCombinedComponentContext.ChildView(view: updatedChild.view, index: index) + context.childViews[updatedChild.id] = childView + + childView.updateGestures(updatedChild.gestures) + childView.transition = updatedChild.transitionDisappear + childView.transitionWithGuide = updatedChild.transitionDisappearWithGuide + + view.insertSubview(updatedChild.view, at: index) + + updatedChild.view.frame = updatedChild.size.centered(around: updatedChild._position ?? CGPoint()) + updatedChild.view.alpha = updatedChild._opacity ?? 1.0 + updatedChild.view.context(typeErasedComponent: updatedChild.component).erasedState._updated = { [weak viewContext] transition in + guard let viewContext = viewContext else { + return + } + viewContext.state.updated(transition: transition) + } + + if let transitionAppearWithGuide = updatedChild.transitionAppearWithGuide { + guard let guide = context.updateContext.configuredGuides[transitionAppearWithGuide.1] else { + preconditionFailure("Guide should be configured before using") + } + transitionAppearWithGuide.0( + component: updatedChild, + view: updatedChild.view, + guide: guide.previousPosition, + transition: transition + ) + } else if let transitionAppear = updatedChild.transitionAppear { + transitionAppear( + component: updatedChild, + view: updatedChild.view, + transition: transition + ) + } + } + } + ) + + escapeStatus = bodyContext.escapeGuard.status + size = storedBody(bodyContext) + } + + assert(escapeStatus.isDeallocated, "Body context should not be stored for later use") + + if nextChildIndex < context.childViewIndices.count { + for i in nextChildIndex ..< context.childViewIndices.count { + let id = context.childViewIndices[i] + if let childView = context.childViews.removeValue(forKey: id) { + let view = childView.view + let completion: () -> Void = { [weak context, weak view] in + view?.removeFromSuperview() + + if let context = context { + for i in 0 ..< context.disappearingChildViews.count { + if context.disappearingChildViews[i].view === view { + context.disappearingChildViews.remove(at: i) + break + } + } + } + } + if let transitionWithGuide = childView.transitionWithGuide { + guard let guide = context.updateContext.configuredGuides[transitionWithGuide.1] else { + preconditionFailure("Guide should be configured before using") + } + context.disappearingChildViews.append(_AnyCombinedComponentContext.DisappearingChildView( + view: view, + guideId: transitionWithGuide.1, + transition: nil, + transitionWithGuide: transitionWithGuide.0, + completion: completion + )) + view.isUserInteractionEnabled = false + transitionWithGuide.0( + stage: .begin, + view: view, + guide: guide.position, + transition: transition, + completion: completion + ) + } else if let simpleTransition = childView.transition { + context.disappearingChildViews.append(_AnyCombinedComponentContext.DisappearingChildView( + view: view, + guideId: nil, + transition: simpleTransition, + transitionWithGuide: nil, + completion: completion + )) + view.isUserInteractionEnabled = false + simpleTransition( + view: view, + transition: transition, + completion: completion + ) + } else { + childView.view.removeFromSuperview() + } + } + } + context.childViewIndices.removeSubrange(nextChildIndex...) + } + + if addedChildIds != context.updateContext.updatedViews { + preconditionFailure("Updated and added child lists do not match") + } + + context.guides.removeAll() + for (id, guide) in context.updateContext.configuredGuides { + context.guides[id] = guide.position + } + + _AnyCombinedComponentContext.pop(context, stack: contextStack) + + return size + } +} + +public extension CombinedComponent { + static func Child(environment: Environment.Type) -> _EnvironmentChildComponent { + return _EnvironmentChildComponent() + } + + static func ChildMap(environment: Environment.Type, keyedBy keyType: Key.Type) -> _EnvironmentChildComponentMap { + return _EnvironmentChildComponentMap() + } + + static func Child(_ type: ComponentType.Type) -> _ConcreteChildComponent { + return _ConcreteChildComponent() + } + + static func Guide() -> _ChildComponentGuide { + return _ChildComponentGuide() + } +} diff --git a/submodules/ComponentFlow/Source/Base/Component.swift b/submodules/ComponentFlow/Source/Base/Component.swift new file mode 100644 index 0000000000..7e150609c7 --- /dev/null +++ b/submodules/ComponentFlow/Source/Base/Component.swift @@ -0,0 +1,203 @@ +import Foundation +import UIKit +import ObjectiveC + +public class ComponentLayoutResult { + var availableSize: CGSize? + var size: CGSize? +} + +public protocol _TypeErasedComponentContext: AnyObject { + var erasedEnvironment: _Environment { get } + var erasedState: ComponentState { get } + + var layoutResult: ComponentLayoutResult { get } +} + +class AnyComponentContext: _TypeErasedComponentContext { + var erasedComponent: AnyComponent { + get { + preconditionFailure() + } set(value) { + preconditionFailure() + } + } + var erasedState: ComponentState { + preconditionFailure() + } + var erasedEnvironment: _Environment { + return self.environment + } + + let layoutResult: ComponentLayoutResult + var environment: Environment + + init(environment: Environment) { + self.layoutResult = ComponentLayoutResult() + self.environment = environment + } +} + +class ComponentContext: AnyComponentContext { + override var erasedComponent: AnyComponent { + get { + return AnyComponent(self.component) + } set(value) { + self.component = value.wrapped as! ComponentType + } + } + + var component: ComponentType + let state: ComponentType.State + + override var erasedState: ComponentState { + return self.state + } + + init(component: ComponentType, environment: Environment, state: ComponentType.State) { + self.component = component + self.state = state + + super.init(environment: environment) + } +} + +private var UIView_TypeErasedComponentContextKey: Int? + +extension UIView { + func context(component: AnyComponent) -> AnyComponentContext { + return self.context(typeErasedComponent: component) as! AnyComponentContext + } + + func context(component: ComponentType) -> ComponentContext { + return self.context(typeErasedComponent: component) as! ComponentContext + } + + func context(typeErasedComponent component: _TypeErasedComponent) -> _TypeErasedComponentContext{ + if let context = objc_getAssociatedObject(self, &UIView_TypeErasedComponentContextKey) as? _TypeErasedComponentContext { + return context + } else { + let context = component._makeContext() + objc_setAssociatedObject(self, &UIView_TypeErasedComponentContextKey, context, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + return context + } + } +} + +public class ComponentState { + var _updated: ((Transition) -> Void)? + var isUpdated: Bool = false + + public final func updated(transition: Transition = .immediate) { + self.isUpdated = true + self._updated?(transition) + } +} + +public final class EmptyComponentState: ComponentState { +} + +public protocol _TypeErasedComponent { + func _makeView() -> UIView + func _makeContext() -> _TypeErasedComponentContext + func _update(view: UIView, availableSize: CGSize, transition: Transition) -> CGSize + func _isEqual(to other: _TypeErasedComponent) -> Bool +} + +public protocol Component: _TypeErasedComponent, Equatable { + associatedtype EnvironmentType = Empty + associatedtype View: UIView = UIView + associatedtype State: ComponentState = EmptyComponentState + + func makeView() -> View + func makeState() -> State + func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize +} + +public extension Component { + func _makeView() -> UIView { + return self.makeView() + } + + func _makeContext() -> _TypeErasedComponentContext { + return ComponentContext(component: self, environment: Environment(), state: self.makeState()) + } + + func _update(view: UIView, availableSize: CGSize, transition: Transition) -> CGSize { + return self.update(view: view as! Self.View, availableSize: availableSize, transition: transition) + } + + func _isEqual(to other: _TypeErasedComponent) -> Bool { + if let other = other as? Self { + return self == other + } else { + return false + } + } +} + +public extension Component where Self.View == UIView { + func makeView() -> UIView { + return UIView() + } +} + +public extension Component where Self.State == EmptyComponentState { + func makeState() -> State { + return EmptyComponentState() + } +} + +public class ComponentGesture { + public static func tap(action: @escaping() -> Void) -> ComponentGesture { + preconditionFailure() + } +} + +public class AnyComponent: _TypeErasedComponent, Equatable { + fileprivate let wrapped: _TypeErasedComponent + + public init(_ component: ComponentType) where ComponentType.EnvironmentType == EnvironmentType { + self.wrapped = component + } + + public static func ==(lhs: AnyComponent, rhs: AnyComponent) -> Bool { + return lhs.wrapped._isEqual(to: rhs.wrapped) + } + + public func _makeView() -> UIView { + return self.wrapped._makeView() + } + + public func _makeContext() -> _TypeErasedComponentContext { + return self.wrapped._makeContext() + } + + public func _update(view: UIView, availableSize: CGSize, transition: Transition) -> CGSize { + return self.wrapped._update(view: view, availableSize: availableSize, transition: transition) + } + + public func _isEqual(to other: _TypeErasedComponent) -> Bool { + return self.wrapped._isEqual(to: other) + } +} + +public final class AnyComponentWithIdentity: Equatable { + public let id: AnyHashable + public let component: AnyComponent + + public init(id: IdType, component: AnyComponent) { + self.id = AnyHashable(id) + self.component = component + } + + public static func == (lhs: AnyComponentWithIdentity, rhs: AnyComponentWithIdentity) -> Bool { + if lhs.id != rhs.id { + return false + } + if lhs.component != rhs.component { + return false + } + return true + } +} diff --git a/submodules/ComponentFlow/Source/Base/Environment.swift b/submodules/ComponentFlow/Source/Base/Environment.swift new file mode 100644 index 0000000000..d3582772ae --- /dev/null +++ b/submodules/ComponentFlow/Source/Base/Environment.swift @@ -0,0 +1,219 @@ +import Foundation +import UIKit + +public final class Empty: Equatable { + static let shared: Empty = Empty() + + public static func ==(lhs: Empty, rhs: Empty) -> Bool { + return true + } +} + +public class _Environment { + fileprivate var data: [Int: _EnvironmentValue] = [:] + var _isUpdated: Bool = false + + func calculateIsUpdated() -> Bool { + if self._isUpdated { + return true + } + for (_, item) in self.data { + if let parentEnvironment = item.parentEnvironment, parentEnvironment.calculateIsUpdated() { + return true + } + } + return false + } + + fileprivate func set(index: Int, value: EnvironmentValue) { + if let current = self.data[index] { + self.data[index] = value + if current as! EnvironmentValue != value { + self._isUpdated = true + } + } else { + self.data[index] = value + self._isUpdated = true + } + } +} + +private enum EnvironmentValueStorage { + case direct(T) + case reference(_Environment, Int) +} + +public class _EnvironmentValue { + fileprivate let parentEnvironment: _Environment? + + fileprivate init(parentEnvironment: _Environment?) { + self.parentEnvironment = parentEnvironment + } +} + +@dynamicMemberLookup +public final class EnvironmentValue: _EnvironmentValue, Equatable { + private var storage: EnvironmentValueStorage + + fileprivate var value: T { + switch self.storage { + case let .direct(value): + return value + case let .reference(environment, index): + return (environment.data[index] as! EnvironmentValue).value + } + } + + fileprivate init(_ value: T) { + self.storage = .direct(value) + + super.init(parentEnvironment: nil) + } + + fileprivate init(environment: _Environment, index: Int) { + self.storage = .reference(environment, index) + + super.init(parentEnvironment: environment) + } + + public static func ==(lhs: EnvironmentValue, rhs: EnvironmentValue) -> Bool { + if lhs === rhs { + return true + } + // TODO: follow the reference chain for faster equality checking + return lhs.value == rhs.value + } + + public subscript(dynamicMember keyPath: KeyPath) -> V { + return self.value[keyPath: keyPath] + } +} + +public class Environment: _Environment { + private let file: StaticString + private let line: Int + + public init(_ file: StaticString = #file, _ line: Int = #line) { + self.file = file + self.line = line + } +} + +public extension Environment where T == Empty { + static let value: Environment = { + let result = Environment() + result.set(index: 0, value: EnvironmentValue(Empty())) + return result + }() +} + +public extension Environment { + subscript(_ t1: T.Type) -> EnvironmentValue where T: Equatable { + return EnvironmentValue(environment: self, index: 0) + } + + subscript(_ t1: T1.Type) -> EnvironmentValue where T == (T1, T2), T1: Equatable, T2: Equatable { + return EnvironmentValue(environment: self, index: 0) + } + + subscript(_ t2: T2.Type) -> EnvironmentValue where T == (T1, T2), T1: Equatable, T2: Equatable { + return EnvironmentValue(environment: self, index: 1) + } + + subscript(_ t1: T1.Type) -> EnvironmentValue where T == (T1, T2, T3), T1: Equatable, T2: Equatable, T3: Equatable { + return EnvironmentValue(environment: self, index: 0) + } + + subscript(_ t2: T2.Type) -> EnvironmentValue where T == (T1, T2, T3), T1: Equatable, T2: Equatable, T3: Equatable { + return EnvironmentValue(environment: self, index: 1) + } + + subscript(_ t3: T3.Type) -> EnvironmentValue where T == (T1, T2, T3), T1: Equatable, T2: Equatable, T3: Equatable { + return EnvironmentValue(environment: self, index: 2) + } + + subscript(_ t1: T1.Type) -> EnvironmentValue where T == (T1, T2, T3, T4), T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable { + return EnvironmentValue(environment: self, index: 0) + } + + subscript(_ t2: T2.Type) -> EnvironmentValue where T == (T1, T2, T3, T4), T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable { + return EnvironmentValue(environment: self, index: 1) + } + + subscript(_ t3: T3.Type) -> EnvironmentValue where T == (T1, T2, T3, T4), T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable { + return EnvironmentValue(environment: self, index: 2) + } + + subscript(_ t4: T4.Type) -> EnvironmentValue where T == (T1, T2, T3, T4), T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable { + return EnvironmentValue(environment: self, index: 3) + } +} + +@resultBuilder +public struct EnvironmentBuilder { + static var _environment: _Environment? + private static func current(_ type: T.Type) -> Environment { + return self._environment as! Environment + } + + public struct Partial { + fileprivate var value: EnvironmentValue + } + + public static func buildBlock() -> Environment { + let result = self.current(Empty.self) + result.set(index: 0, value: EnvironmentValue(Empty.shared)) + return result + } + + public static func buildExpression(_ expression: T) -> Partial { + return Partial(value: EnvironmentValue(expression)) + } + + public static func buildExpression(_ expression: EnvironmentValue) -> Partial { + return Partial(value: expression) + } + + public static func buildBlock(_ t1: Partial) -> Environment { + let result = self.current(T1.self) + result.set(index: 0, value: t1.value) + return result + } + + public static func buildBlock(_ t1: Partial, _ t2: Partial) -> Environment<(T1, T2)> { + let result = self.current((T1, T2).self) + result.set(index: 0, value: t1.value) + result.set(index: 1, value: t2.value) + return result + } + + public static func buildBlock(_ t1: Partial, _ t2: Partial, _ t3: Partial) -> Environment<(T1, T2, T3)> { + let result = self.current((T1, T2, T3).self) + result.set(index: 0, value: t1.value) + result.set(index: 1, value: t2.value) + result.set(index: 2, value: t3.value) + return result + } + + public static func buildBlock(_ t1: Partial, _ t2: Partial, _ t3: Partial, _ t4: Partial) -> Environment<(T1, T2, T3, T4)> { + let result = self.current((T1, T2, T3, T4).self) + result.set(index: 0, value: t1.value) + result.set(index: 1, value: t2.value) + result.set(index: 2, value: t3.value) + result.set(index: 3, value: t4.value) + return result + } +} + +@propertyWrapper +public struct ZeroEquatable: Equatable { + public var wrappedValue: T + + public init(_ wrappedValue: T) { + self.wrappedValue = wrappedValue + } + + public static func ==(lhs: ZeroEquatable, rhs: ZeroEquatable) -> Bool { + return true + } +} diff --git a/submodules/ComponentFlow/Source/Base/Transition.swift b/submodules/ComponentFlow/Source/Base/Transition.swift new file mode 100644 index 0000000000..f0187f4d08 --- /dev/null +++ b/submodules/ComponentFlow/Source/Base/Transition.swift @@ -0,0 +1,270 @@ +import Foundation +import UIKit + +private extension UIView { + static var animationDurationFactor: Double { + return 1.0 + } +} + +@objc private class CALayerAnimationDelegate: NSObject, CAAnimationDelegate { + private let keyPath: String? + var completion: ((Bool) -> Void)? + + init(animation: CAAnimation, completion: ((Bool) -> Void)?) { + if let animation = animation as? CABasicAnimation { + self.keyPath = animation.keyPath + } else { + self.keyPath = nil + } + self.completion = completion + + super.init() + } + + @objc func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { + if let anim = anim as? CABasicAnimation { + if anim.keyPath != self.keyPath { + return + } + } + if let completion = self.completion { + completion(flag) + } + } +} + +private func makeSpringAnimation(keyPath: String) -> CASpringAnimation { + let springAnimation = CASpringAnimation(keyPath: keyPath) + springAnimation.mass = 3.0; + springAnimation.stiffness = 1000.0 + springAnimation.damping = 500.0 + springAnimation.duration = 0.5 + springAnimation.timingFunction = CAMediaTimingFunction(name: .linear) + return springAnimation +} + +private extension CALayer { + func makeAnimation(from: AnyObject, to: AnyObject, keyPath: String, duration: Double, delay: Double, curve: Transition.Animation.Curve, removeOnCompletion: Bool, additive: Bool, completion: ((Bool) -> Void)? = nil) -> CAAnimation { + switch curve { + case .spring: + let animation = makeSpringAnimation(keyPath: keyPath) + animation.fromValue = from + animation.toValue = to + animation.isRemovedOnCompletion = removeOnCompletion + animation.fillMode = .forwards + if let completion = completion { + animation.delegate = CALayerAnimationDelegate(animation: animation, completion: completion) + } + + let k = Float(UIView.animationDurationFactor) + var speed: Float = 1.0 + if k != 0 && k != 1 { + speed = Float(1.0) / k + } + + animation.speed = speed * Float(animation.duration / duration) + animation.isAdditive = additive + + if !delay.isZero { + animation.beginTime = self.convertTime(CACurrentMediaTime(), from: nil) + delay * UIView.animationDurationFactor + animation.fillMode = .both + } + + return animation + default: + let k = Float(UIView.animationDurationFactor) + var speed: Float = 1.0 + if k != 0 && k != 1 { + speed = Float(1.0) / k + } + + let animation = CABasicAnimation(keyPath: keyPath) + animation.fromValue = from + animation.toValue = to + animation.duration = duration + animation.timingFunction = curve.asTimingFunction() + animation.isRemovedOnCompletion = removeOnCompletion + animation.fillMode = .forwards + animation.speed = speed + animation.isAdditive = additive + if let completion = completion { + animation.delegate = CALayerAnimationDelegate(animation: animation, completion: completion) + } + + if !delay.isZero { + animation.beginTime = self.convertTime(CACurrentMediaTime(), from: nil) + delay * UIView.animationDurationFactor + animation.fillMode = .both + } + + return animation + } + } + + func animate(from: AnyObject, to: AnyObject, keyPath: String, duration: Double, delay: Double, curve: Transition.Animation.Curve, removeOnCompletion: Bool, additive: Bool, completion: ((Bool) -> Void)? = nil) { + let animation = self.makeAnimation(from: from, to: to, keyPath: keyPath, duration: duration, delay: delay, curve: curve, removeOnCompletion: removeOnCompletion, additive: additive, completion: completion) + self.add(animation, forKey: additive ? nil : keyPath) + } +} + +private extension Transition.Animation.Curve { + func asTimingFunction() -> CAMediaTimingFunction { + switch self { + case .easeInOut: + return CAMediaTimingFunction(name: .easeInEaseOut) + case .spring: + preconditionFailure() + } + } +} + +public struct Transition { + public enum Animation { + public enum Curve { + case easeInOut + case spring + } + + case none + case curve(duration: Double, curve: Curve) + } + + public var animation: Animation + private var _userData: [Any] = [] + + public func userData(_ type: T.Type) -> T? { + for item in self._userData { + if let item = item as? T { + return item + } + } + return nil + } + + public func withUserData(_ userData: Any) -> Transition { + var result = self + result._userData.append(userData) + return result + } + + public static var immediate: Transition = Transition(animation: .none) + + public static func easeInOut(duration: Double) -> Transition { + return Transition(animation: .curve(duration: duration, curve: .easeInOut)) + } + + public init(animation: Animation) { + self.animation = animation + } + + public func setFrame(view: UIView, frame: CGRect, completion: ((Bool) -> Void)? = nil) { + if view.frame == frame { + completion?(true) + return + } + switch self.animation { + case .none: + view.frame = frame + completion?(true) + case .curve: + let previousPosition = view.center + let previousBounds = view.bounds + view.frame = frame + + self.animatePosition(view: view, from: previousPosition, to: view.center, completion: completion) + self.animateBounds(view: view, from: previousBounds, to: view.bounds) + } + } + + public func setAlpha(view: UIView, alpha: CGFloat, completion: ((Bool) -> Void)? = nil) { + if view.alpha == alpha { + completion?(true) + return + } + switch self.animation { + case .none: + view.alpha = alpha + completion?(true) + case .curve: + let previousAlpha = view.alpha + view.alpha = alpha + self.animateAlpha(view: view, from: previousAlpha, to: alpha, completion: completion) + } + } + + public func animateScale(view: UIView, from fromValue: CGFloat, to toValue: CGFloat, additive: Bool = false, completion: ((Bool) -> Void)? = nil) { + switch self.animation { + case .none: + completion?(true) + case let .curve(duration, curve): + view.layer.animate( + from: fromValue as NSNumber, + to: toValue as NSNumber, + keyPath: "transform.scale", + duration: duration, + delay: 0.0, + curve: curve, + removeOnCompletion: true, + additive: additive, + completion: completion + ) + } + } + + public func animateAlpha(view: UIView, from fromValue: CGFloat, to toValue: CGFloat, additive: Bool = false, completion: ((Bool) -> Void)? = nil) { + switch self.animation { + case .none: + completion?(true) + case let .curve(duration, curve): + view.layer.animate( + from: fromValue as NSNumber, + to: toValue as NSNumber, + keyPath: "opacity", + duration: duration, + delay: 0.0, + curve: curve, + removeOnCompletion: true, + additive: additive, + completion: completion + ) + } + } + + public func animatePosition(view: UIView, from fromValue: CGPoint, to toValue: CGPoint, additive: Bool = false, completion: ((Bool) -> Void)? = nil) { + switch self.animation { + case .none: + completion?(true) + case let .curve(duration, curve): + view.layer.animate( + from: NSValue(cgPoint: fromValue), + to: NSValue(cgPoint: toValue), + keyPath: "position", + duration: duration, + delay: 0.0, + curve: curve, + removeOnCompletion: true, + additive: additive, + completion: completion + ) + } + } + + public func animateBounds(view: UIView, from fromValue: CGRect, to toValue: CGRect, additive: Bool = false, completion: ((Bool) -> Void)? = nil) { + switch self.animation { + case .none: + break + case let .curve(duration, curve): + view.layer.animate( + from: NSValue(cgRect: fromValue), + to: NSValue(cgRect: toValue), + keyPath: "bounds", + duration: duration, + delay: 0.0, + curve: curve, + removeOnCompletion: true, + additive: additive, + completion: completion + ) + } + } +} diff --git a/submodules/ComponentFlow/Source/Components/Button.swift b/submodules/ComponentFlow/Source/Components/Button.swift new file mode 100644 index 0000000000..33f7a3634d --- /dev/null +++ b/submodules/ComponentFlow/Source/Components/Button.swift @@ -0,0 +1,68 @@ +import Foundation +import UIKit + +final class Button: CombinedComponent, Equatable { + let content: AnyComponent + let insets: UIEdgeInsets + let action: () -> Void + + init( + content: AnyComponent, + insets: UIEdgeInsets, + action: @escaping () -> Void + ) { + self.content = content + self.insets = insets + self.action = action + } + + static func ==(lhs: Button, rhs: Button) -> Bool { + if lhs.content != rhs.content { + return false + } + if lhs.insets != rhs.insets { + return false + } + return true + } + + final class State: ComponentState { + var isHighlighted = false + + override init() { + super.init() + } + } + + func makeState() -> State { + return State() + } + + static var body: Body { + let content = Child(environment: Empty.self) + + return { context in + let content = content.update( + component: context.component.content, + availableSize: CGSize(width: context.availableSize.width, height: 44.0), transition: context.transition + ) + + let size = CGSize(width: content.size.width + context.component.insets.left + context.component.insets.right, height: content.size.height + context.component.insets.top + context.component.insets.bottom) + + let component = context.component + + context.add(content + .position(CGPoint(x: size.width / 2.0, y: size.height / 2.0)) + .opacity(context.state.isHighlighted ? 0.2 : 1.0) + .update(Transition.Update { component, view, transition in + view.frame = component.size.centered(around: component._position ?? CGPoint()) + }) + .gesture(.tap { + component.action() + }) + ) + + return size + } + } +} diff --git a/submodules/ComponentFlow/Source/Components/List.swift b/submodules/ComponentFlow/Source/Components/List.swift new file mode 100644 index 0000000000..b73b6d7efd --- /dev/null +++ b/submodules/ComponentFlow/Source/Components/List.swift @@ -0,0 +1,48 @@ +import Foundation +import UIKit + +public final class List: CombinedComponent { + public typealias EnvironmentType = ChildEnvironment + + private let items: [AnyComponentWithIdentity] + private let appear: Transition.Appear + + public init(_ items: [AnyComponentWithIdentity], appear: Transition.Appear = .default()) { + self.items = items + self.appear = appear + } + + public static func ==(lhs: List, rhs: List) -> Bool { + if lhs.items != rhs.items { + return false + } + return true + } + + public static var body: Body { + let children = ChildMap(environment: ChildEnvironment.self, keyedBy: AnyHashable.self) + + return { context in + let updatedChildren = context.component.items.map { item in + return children[item.id].update( + component: item.component, environment: { + context.environment[ChildEnvironment.self] + }, + availableSize: context.availableSize, + transition: context.transition + ) + } + + var nextOrigin: CGFloat = 0.0 + for child in updatedChildren { + context.add(child + .position(CGPoint(x: child.size.width / 2.0, y: nextOrigin + child.size.height / 2.0)) + .appear(context.component.appear) + ) + nextOrigin += child.size.height + } + + return context.availableSize + } + } +} diff --git a/submodules/ComponentFlow/Source/Components/Rectangle.swift b/submodules/ComponentFlow/Source/Components/Rectangle.swift new file mode 100644 index 0000000000..885bb0ef29 --- /dev/null +++ b/submodules/ComponentFlow/Source/Components/Rectangle.swift @@ -0,0 +1,41 @@ +import Foundation +import UIKit + +public final class Rectangle: Component { + private let color: UIColor + private let width: CGFloat? + private let height: CGFloat? + + public init(color: UIColor, width: CGFloat? = nil, height: CGFloat? = nil) { + self.color = color + self.width = width + self.height = height + } + + public static func ==(lhs: Rectangle, rhs: Rectangle) -> Bool { + if !lhs.color.isEqual(rhs.color) { + return false + } + if lhs.width != rhs.width { + return false + } + if lhs.height != rhs.height { + return false + } + return true + } + + public func update(view: UIView, availableSize: CGSize, transition: Transition) -> CGSize { + var size = availableSize + if let width = self.width { + size.width = min(size.width, width) + } + if let height = self.height { + size.height = min(size.height, height) + } + + view.backgroundColor = self.color + + return size + } +} diff --git a/submodules/ComponentFlow/Source/Components/Text.swift b/submodules/ComponentFlow/Source/Components/Text.swift new file mode 100644 index 0000000000..f3b66ce6f6 --- /dev/null +++ b/submodules/ComponentFlow/Source/Components/Text.swift @@ -0,0 +1,101 @@ +import Foundation +import UIKit + +public final class Text: Component { + private final class MeasureState: Equatable { + let attributedText: NSAttributedString + let availableSize: CGSize + let size: CGSize + + init(attributedText: NSAttributedString, availableSize: CGSize, size: CGSize) { + self.attributedText = attributedText + self.availableSize = availableSize + self.size = size + } + + static func ==(lhs: MeasureState, rhs: MeasureState) -> Bool { + if !lhs.attributedText.isEqual(rhs.attributedText) { + return false + } + if lhs.availableSize != rhs.availableSize { + return false + } + if lhs.size != rhs.size { + return false + } + return true + } + } + + public final class View: UIView { + private var measureState: MeasureState? + + func update(component: Text, availableSize: CGSize) -> CGSize { + let attributedText = NSAttributedString(string: component.text, attributes: [ + NSAttributedString.Key.font: component.font, + NSAttributedString.Key.foregroundColor: component.color + ]) + + if let measureState = self.measureState { + if measureState.attributedText.isEqual(to: attributedText) && measureState.availableSize == availableSize { + return measureState.size + } + } + + var boundingRect = attributedText.boundingRect(with: availableSize, options: .usesLineFragmentOrigin, context: nil) + boundingRect.size.width = ceil(boundingRect.size.width) + boundingRect.size.height = ceil(boundingRect.size.height) + + let measureState = MeasureState(attributedText: attributedText, availableSize: availableSize, size: boundingRect.size) + if #available(iOS 10.0, *) { + let renderer = UIGraphicsImageRenderer(bounds: CGRect(origin: CGPoint(), size: measureState.size)) + let image = renderer.image { context in + UIGraphicsPushContext(context.cgContext) + measureState.attributedText.draw(at: CGPoint()) + UIGraphicsPopContext() + } + self.layer.contents = image.cgImage + } else { + UIGraphicsBeginImageContextWithOptions(measureState.size, false, 0.0) + measureState.attributedText.draw(at: CGPoint()) + self.layer.contents = UIGraphicsGetImageFromCurrentImageContext()?.cgImage + UIGraphicsEndImageContext() + } + + self.measureState = measureState + + return boundingRect.size + } + } + + public let text: String + public let font: UIFont + public let color: UIColor + + public init(text: String, font: UIFont, color: UIColor) { + self.text = text + self.font = font + self.color = color + } + + public static func ==(lhs: Text, rhs: Text) -> Bool { + if lhs.text != rhs.text { + return false + } + if !lhs.font.isEqual(rhs.font) { + return false + } + if !lhs.color.isEqual(rhs.color) { + return false + } + return true + } + + public func makeView() -> View { + return View() + } + + public func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize) + } +} diff --git a/submodules/ComponentFlow/Source/Gestures/Gesture.swift b/submodules/ComponentFlow/Source/Gestures/Gesture.swift new file mode 100644 index 0000000000..a67e5895b0 --- /dev/null +++ b/submodules/ComponentFlow/Source/Gestures/Gesture.swift @@ -0,0 +1,29 @@ +import Foundation +import UIKit + +public class Gesture { + class Id { + private var _id: UInt = 0 + public var id: UInt { + return self._id + } + + init() { + self._id = UInt(bitPattern: Unmanaged.passUnretained(self).toOpaque()) + } + } + + let id: Id + + init(id: Id) { + self.id = id + } + + func create() -> UIGestureRecognizer { + preconditionFailure() + } + + func update(gesture: UIGestureRecognizer) { + preconditionFailure() + } +} diff --git a/submodules/ComponentFlow/Source/Gestures/PanGesture.swift b/submodules/ComponentFlow/Source/Gestures/PanGesture.swift new file mode 100644 index 0000000000..38a5e9b538 --- /dev/null +++ b/submodules/ComponentFlow/Source/Gestures/PanGesture.swift @@ -0,0 +1,59 @@ +import Foundation +import UIKit + +public extension Gesture { + enum PanGestureState { + case began + case updated(offset: CGPoint) + case ended + } + + private final class PanGesture: Gesture { + private class Impl: UIPanGestureRecognizer { + var action: (PanGestureState) -> Void + + init(action: @escaping (PanGestureState) -> Void) { + self.action = action + + super.init(target: nil, action: nil) + self.addTarget(self, action: #selector(self.onAction)) + } + + @objc private func onAction() { + switch self.state { + case .began: + self.action(.began) + case .ended, .cancelled: + self.action(.ended) + case .changed: + let offset = self.translation(in: self.view) + self.action(.updated(offset: offset)) + default: + break + } + } + } + + static let id = Id() + + private let action: (PanGestureState) -> Void + + init(action: @escaping (PanGestureState) -> Void) { + self.action = action + + super.init(id: Self.id) + } + + override func create() -> UIGestureRecognizer { + return Impl(action: self.action) + } + + override func update(gesture: UIGestureRecognizer) { + (gesture as! Impl).action = action + } + } + + static func pan(_ action: @escaping (PanGestureState) -> Void) -> Gesture { + return PanGesture(action: action) + } +} diff --git a/submodules/ComponentFlow/Source/Gestures/TapGesture.swift b/submodules/ComponentFlow/Source/Gestures/TapGesture.swift new file mode 100644 index 0000000000..da93e71d5a --- /dev/null +++ b/submodules/ComponentFlow/Source/Gestures/TapGesture.swift @@ -0,0 +1,43 @@ +import Foundation +import UIKit + +public extension Gesture { + private final class TapGesture: Gesture { + private class Impl: UITapGestureRecognizer { + var action: () -> Void + + init(action: @escaping () -> Void) { + self.action = action + + super.init(target: nil, action: nil) + self.addTarget(self, action: #selector(self.onAction)) + } + + @objc private func onAction() { + self.action() + } + } + + static let id = Id() + + private let action: () -> Void + + init(action: @escaping () -> Void) { + self.action = action + + super.init(id: Self.id) + } + + override func create() -> UIGestureRecognizer { + return Impl(action: self.action) + } + + override func update(gesture: UIGestureRecognizer) { + (gesture as! Impl).action = action + } + } + + static func tap(_ action: @escaping () -> Void) -> Gesture { + return TapGesture(action: action) + } +} diff --git a/submodules/ComponentFlow/Source/Host/ComponentHostView.swift b/submodules/ComponentFlow/Source/Host/ComponentHostView.swift new file mode 100644 index 0000000000..9cd8780b38 --- /dev/null +++ b/submodules/ComponentFlow/Source/Host/ComponentHostView.swift @@ -0,0 +1,69 @@ +import Foundation +import UIKit + +public final class ComponentHostView: UIView { + private var componentView: UIView? + private(set) var isUpdating: Bool = false + + public init() { + super.init(frame: CGRect()) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public func update(transition: Transition, component: AnyComponent, @EnvironmentBuilder environment: () -> Environment, containerSize: CGSize) -> CGSize { + self._update(transition: transition, component: component, maybeEnvironment: environment, updateEnvironment: true, containerSize: containerSize) + } + + private func _update(transition: Transition, component: AnyComponent, maybeEnvironment: () -> Environment, updateEnvironment: Bool, containerSize: CGSize) -> CGSize { + precondition(!self.isUpdating) + self.isUpdating = true + + precondition(containerSize.width.isFinite) + precondition(containerSize.width < .greatestFiniteMagnitude) + precondition(containerSize.height.isFinite) + precondition(containerSize.height < .greatestFiniteMagnitude) + + let componentView: UIView + if let current = self.componentView { + componentView = current + } else { + componentView = component._makeView() + self.componentView = componentView + self.addSubview(componentView) + } + + let context = componentView.context(component: component) + + let componentState: ComponentState = context.erasedState + + if updateEnvironment { + EnvironmentBuilder._environment = context.erasedEnvironment + let _ = maybeEnvironment() + EnvironmentBuilder._environment = nil + } + + componentState._updated = { [weak self] transition in + guard let strongSelf = self else { + return + } + let _ = strongSelf._update(transition: transition, component: component, maybeEnvironment: { + preconditionFailure() + } as () -> Environment, updateEnvironment: false, containerSize: containerSize) + } + + let updatedSize = component._update(view: componentView, availableSize: containerSize, transition: transition) + transition.setFrame(view: componentView, frame: CGRect(origin: CGPoint(), size: updatedSize)) + + self.isUpdating = false + + return updatedSize + } + + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + let result = super.hitTest(point, with: event) + return result + } +} diff --git a/submodules/ComponentFlow/Source/Host/NavigationLayoutEnvironment.swift b/submodules/ComponentFlow/Source/Host/NavigationLayoutEnvironment.swift new file mode 100644 index 0000000000..d6eb55eb31 --- /dev/null +++ b/submodules/ComponentFlow/Source/Host/NavigationLayoutEnvironment.swift @@ -0,0 +1,8 @@ +import Foundation +import UIKit + +public struct NavigationLayout: Equatable { + public var statusBarHeight: CGFloat + public var inputHeight: CGFloat + public var bottomNavigationHeight: CGFloat +} diff --git a/submodules/ComponentFlow/Source/Host/RootHostView.swift b/submodules/ComponentFlow/Source/Host/RootHostView.swift new file mode 100644 index 0000000000..5a5d9fec91 --- /dev/null +++ b/submodules/ComponentFlow/Source/Host/RootHostView.swift @@ -0,0 +1,134 @@ +import Foundation +import UIKit + +public final class RootHostView: UIViewController { + private let content: AnyComponent<(NavigationLayout, EnvironmentType)> + + private var keyboardWillChangeFrameObserver: NSObjectProtocol? + private var inputHeight: CGFloat = 0.0 + + private let environment: Environment + private var componentView: ComponentHostView<(NavigationLayout, EnvironmentType)> + + private var scheduledTransition: Transition? + + public init( + content: AnyComponent<(NavigationLayout, EnvironmentType)>, + @EnvironmentBuilder environment: () -> Environment + ) { + self.content = content + + self.environment = Environment() + self.componentView = ComponentHostView<(NavigationLayout, EnvironmentType)>() + + EnvironmentBuilder._environment = self.environment + let _ = environment() + EnvironmentBuilder._environment = nil + + super.init(nibName: nil, bundle: nil) + + NotificationCenter.default.addObserver(forName: UIApplication.keyboardWillChangeFrameNotification, object: nil, queue: nil, using: { [weak self] notification in + guard let strongSelf = self else { + return + } + guard let keyboardFrame = notification.userInfo?[UIApplication.keyboardFrameEndUserInfoKey] as? CGRect else { + return + } + + var duration: Double = (notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0.0 + if duration > Double.ulpOfOne { + duration = 0.5 + } + let curve: UInt = (notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber)?.uintValue ?? 7 + + let transition: Transition + if curve == 7 { + transition = Transition(animation: .curve(duration: duration, curve: .spring)) + } else { + transition = Transition(animation: .curve(duration: duration, curve: .easeInOut)) + } + + strongSelf.updateKeyboardLayout(keyboardFrame: keyboardFrame, transition: transition) + }) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.keyboardWillChangeFrameObserver.flatMap(NotificationCenter.default.removeObserver) + } + + private func updateKeyboardLayout(keyboardFrame: CGRect, transition: Transition) { + self.inputHeight = max(0.0, self.view.bounds.height - keyboardFrame.minY) + if self.componentView.isUpdating || true { + if let _ = self.scheduledTransition { + if case .curve = transition.animation { + self.scheduledTransition = transition + } + } else { + self.scheduledTransition = transition + } + self.view.setNeedsLayout() + } else { + self.updateComponent(size: self.view.bounds.size, transition: transition) + } + } + + private func updateComponent(size: CGSize, transition: Transition) { + self.environment._isUpdated = false + + transition.setFrame(view: self.componentView, frame: CGRect(origin: CGPoint(), size: size)) + let _ = self.componentView.update( + transition: transition, + component: self.content, + environment: { + NavigationLayout( + statusBarHeight: size.width > size.height ? 0.0 : 40.0, + inputHeight: self.inputHeight, + bottomNavigationHeight: 22.0 + ) + self.environment[EnvironmentType.self] + }, + containerSize: size + ) + } + + public func updateEnvironment(@EnvironmentBuilder environment: () -> Environment) { + EnvironmentBuilder._environment = self.environment + let _ = environment() + EnvironmentBuilder._environment = nil + + if self.environment.calculateIsUpdated() { + if !self.view.bounds.size.width.isZero { + self.updateComponent(size: self.view.bounds.size, transition: .immediate) + } + } + } + + override public func viewDidLoad() { + super.viewDidLoad() + + self.view.addSubview(self.componentView) + + if !self.view.bounds.size.width.isZero { + self.updateComponent(size: self.view.bounds.size, transition: .immediate) + } + } + + override public func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + if let scheduledTransition = self.scheduledTransition { + self.scheduledTransition = nil + self.updateComponent(size: self.view.bounds.size, transition: scheduledTransition) + } + } + + override public func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + + self.updateComponent(size: size, transition: coordinator.isAnimated ? .easeInOut(duration: 0.3) : .immediate) + } +} diff --git a/submodules/ComponentFlow/Source/Utils/Color.swift b/submodules/ComponentFlow/Source/Utils/Color.swift new file mode 100644 index 0000000000..1eb3f11a39 --- /dev/null +++ b/submodules/ComponentFlow/Source/Utils/Color.swift @@ -0,0 +1,8 @@ +import Foundation +import UIKit + +extension UIColor { + convenience init(rgb: UInt32) { + self.init(red: CGFloat((rgb >> 16) & 0xff) / 255.0, green: CGFloat((rgb >> 8) & 0xff) / 255.0, blue: CGFloat(rgb & 0xff) / 255.0, alpha: 1.0) + } +} diff --git a/submodules/ComponentFlow/Source/Utils/Condition.swift b/submodules/ComponentFlow/Source/Utils/Condition.swift new file mode 100644 index 0000000000..f74c72e8cf --- /dev/null +++ b/submodules/ComponentFlow/Source/Utils/Condition.swift @@ -0,0 +1,9 @@ +import Foundation + +public func Condition(_ f: @autoclosure () -> Bool, _ pass: () -> R) -> R? { + if f() { + return pass() + } else { + return nil + } +} diff --git a/submodules/ComponentFlow/Source/Utils/EscapeGuard.swift b/submodules/ComponentFlow/Source/Utils/EscapeGuard.swift new file mode 100644 index 0000000000..94d6b9cd25 --- /dev/null +++ b/submodules/ComponentFlow/Source/Utils/EscapeGuard.swift @@ -0,0 +1,13 @@ +import Foundation + +final class EscapeGuard { + final class Status { + fileprivate(set) var isDeallocated: Bool = false + } + + let status = Status() + + deinit { + self.status.isDeallocated = true + } +} diff --git a/submodules/ComponentFlow/Source/Utils/Insets.swift b/submodules/ComponentFlow/Source/Utils/Insets.swift new file mode 100644 index 0000000000..f456851c1a --- /dev/null +++ b/submodules/ComponentFlow/Source/Utils/Insets.swift @@ -0,0 +1,8 @@ +import Foundation +import UIKit + +public extension UIEdgeInsets { + init(_ value: CGFloat) { + self.init(top: value, left: value, bottom: value, right: value) + } +} diff --git a/submodules/ComponentFlow/Source/Utils/Rect.swift b/submodules/ComponentFlow/Source/Utils/Rect.swift new file mode 100644 index 0000000000..7b5b76d064 --- /dev/null +++ b/submodules/ComponentFlow/Source/Utils/Rect.swift @@ -0,0 +1,8 @@ +import Foundation +import UIKit + +public extension CGRect { + var center: CGPoint { + return CGPoint(x: self.midX, y: self.midY) + } +} diff --git a/submodules/ComponentFlow/Source/Utils/Size.swift b/submodules/ComponentFlow/Source/Utils/Size.swift new file mode 100644 index 0000000000..310f8ce322 --- /dev/null +++ b/submodules/ComponentFlow/Source/Utils/Size.swift @@ -0,0 +1,28 @@ +import Foundation +import UIKit + +public extension CGSize { + func centered(in rect: CGRect) -> CGRect { + return CGRect(origin: CGPoint(x: rect.minX + floor((rect.width - self.width) / 2.0), y: rect.minY + floor((rect.height - self.height) / 2.0)), size: self) + } + + func centered(around position: CGPoint) -> CGRect { + return CGRect(origin: CGPoint(x: position.x - self.width / 2.0, y: position.y - self.height / 2.0), size: self) + } + + func leftCentered(in rect: CGRect) -> CGRect { + return CGRect(origin: CGPoint(x: rect.minX, y: rect.minY + floor((rect.height - self.height) / 2.0)), size: self) + } + + func rightCentered(in rect: CGRect) -> CGRect { + return CGRect(origin: CGPoint(x: rect.maxX - self.width, y: rect.minY + floor((rect.height - self.height) / 2.0)), size: self) + } + + func topCentered(in rect: CGRect) -> CGRect { + return CGRect(origin: CGPoint(x: rect.minX + floor((rect.width - self.width) / 2.0), y: 0.0), size: self) + } + + func bottomCentered(in rect: CGRect) -> CGRect { + return CGRect(origin: CGPoint(x: rect.minX + floor((rect.width - self.width) / 2.0), y: rect.maxY - self.height), size: self) + } +} diff --git a/submodules/ComposePollUI/BUILD b/submodules/ComposePollUI/BUILD index 78a59ef096..f076121bd0 100644 --- a/submodules/ComposePollUI/BUILD +++ b/submodules/ComposePollUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", @@ -17,6 +20,7 @@ swift_library( "//submodules/AlertUI:AlertUI", "//submodules/PresentationDataUtils:PresentationDataUtils", "//submodules/TextFormat:TextFormat", + "//submodules/ObjCRuntimeUtils:ObjCRuntimeUtils", ], visibility = [ "//visibility:public", diff --git a/submodules/ComposePollUI/Sources/CreatePollController.swift b/submodules/ComposePollUI/Sources/CreatePollController.swift index 304a5ebcd2..3a8e3eb8a4 100644 --- a/submodules/ComposePollUI/Sources/CreatePollController.swift +++ b/submodules/ComposePollUI/Sources/CreatePollController.swift @@ -260,8 +260,8 @@ private enum CreatePollEntry: ItemListNodeEntry { switch self { case .text: return CreatePollEntryTag.text - case let .option(option): - return CreatePollEntryTag.option(option.id) + case let .option(id, _, _, _, _, _, _, _, _): + return CreatePollEntryTag.option(id) default: break } @@ -276,8 +276,8 @@ private enum CreatePollEntry: ItemListNodeEntry { return .text case .optionsHeader: return .optionsHeader - case let .option(option): - return .option(option.id) + case let .option(id, _, _, _, _, _, _, _, _): + return .option(id) case .optionsInfo: return .optionsInfo case .anonymousVotes: @@ -305,7 +305,7 @@ private enum CreatePollEntry: ItemListNodeEntry { return 1 case .optionsHeader: return 2 - case let .option(option): + case .option: return 3 case .optionsInfo: return 1001 @@ -328,10 +328,10 @@ private enum CreatePollEntry: ItemListNodeEntry { static func <(lhs: CreatePollEntry, rhs: CreatePollEntry) -> Bool { switch lhs { - case let .option(lhsOption): + case let .option(_, lhsOrdering, _, _, _, _, _, _, _): switch rhs { - case let .option(rhsOption): - return lhsOption.ordering < rhsOption.ordering + case let .option(_, rhsOrdering, _, _, _, _, _, _, _): + return lhsOrdering < rhsOrdering default: break } @@ -992,17 +992,16 @@ public func createPollController(context: AccountContext, peer: Peer, isQuiz: Bo } controller.setReorderEntry({ (fromIndex: Int, toIndex: Int, entries: [CreatePollEntry]) -> Signal in let fromEntry = entries[fromIndex] - guard case let .option(option) = fromEntry else { + guard case let .option(id, _, _, _, _, _, _, _, _) = fromEntry else { return .single(false) } - let id = option.id var referenceId: Int? var beforeAll = false var afterAll = false if toIndex < entries.count { switch entries[toIndex] { - case let .option(toOption): - referenceId = toOption.id + case let .option(toId, _, _, _, _, _, _, _, _): + referenceId = toId default: if entries[toIndex] < fromEntry { beforeAll = true diff --git a/submodules/ComposePollUI/Sources/CreatePollTextInputItem.swift b/submodules/ComposePollUI/Sources/CreatePollTextInputItem.swift index 3b007588be..d7e607bf25 100644 --- a/submodules/ComposePollUI/Sources/CreatePollTextInputItem.swift +++ b/submodules/ComposePollUI/Sources/CreatePollTextInputItem.swift @@ -6,6 +6,7 @@ import SwiftSignalKit import TelegramPresentationData import ItemListUI import TextFormat +import ObjCRuntimeUtils public enum CreatePollTextInputItemTextLimitMode { case characters @@ -292,7 +293,7 @@ public class CreatePollTextInputItemNode: ListViewItemNode, ASEditableTextNodeDe let textLength: Int switch maxLength.mode { case .characters: - textLength = item.text.string.count ?? 0 + textLength = item.text.string.count case .bytes: textLength = item.text.string.data(using: .utf8, allowLossyConversion: true)?.count ?? 0 } @@ -511,7 +512,7 @@ public class CreatePollTextInputItemNode: ListViewItemNode, ASEditableTextNodeDe } public func editableTextNodeTarget(forAction action: Selector) -> ASEditableTextNodeTargetForAction? { - if action == Selector(("_showTextStyleOptions:")) { + if action == makeSelectorFromString("_showTextStyleOptions:") { if case .general = self.inputMenu.state { if self.textNode.attributedText == nil || self.textNode.attributedText!.length == 0 || self.textNode.selectedRange.length == 0 { return ASEditableTextNodeTargetForAction(target: nil) diff --git a/submodules/ConfettiEffect/BUILD b/submodules/ConfettiEffect/BUILD index 6103e15e37..9acf41b4d6 100644 --- a/submodules/ConfettiEffect/BUILD +++ b/submodules/ConfettiEffect/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/ConfettiEffect/Sources/ConfettiView.swift b/submodules/ConfettiEffect/Sources/ConfettiView.swift index 9eda1bd814..441cc4cc8f 100644 --- a/submodules/ConfettiEffect/Sources/ConfettiView.swift +++ b/submodules/ConfettiEffect/Sources/ConfettiView.swift @@ -87,7 +87,6 @@ public final class ConfettiView: UIView { let velocityYRange = Float(3.0) ..< Float(5.0) let angularVelocityRange = Float(1.0) ..< Float(6.0) let sizeVariation = Float(0.8) ..< Float(1.6) - let topDelayRange = Float(0.0) ..< Float(0.0) for i in 0 ..< 70 { let (image, size) = images[i % imageCount] @@ -99,9 +98,6 @@ public final class ConfettiView: UIView { let sideMassRange: Range = 110.0 ..< 120.0 let sideOriginYBase: Float = Float(frame.size.height * 9.0 / 10.0) - let sideOriginYVariation: Float = Float(frame.size.height / 12.0) - let sideOriginYRange = Float(sideOriginYBase - sideOriginYVariation) ..< Float(sideOriginYBase + sideOriginYVariation) - let sideOriginXRange = Float(0.0) ..< Float(100.0) let sideOriginVelocityValueRange = Float(1.1) ..< Float(1.3) let sideOriginVelocityValueScaling: Float = 2400.0 * Float(frame.height) / 896.0 let sideOriginVelocityBase: Float = Float.pi / 2.0 + atanf(Float(CGFloat(sideOriginYBase) / (frame.size.width * 0.8))) @@ -152,11 +148,7 @@ public final class ConfettiView: UIView { self.slowdownStartTimestamps[0] = 0.33 var haveParticlesAboveGround = false - let minPositionY: CGFloat = 0.0 let maxPositionY = self.bounds.height + 30.0 - let minDampingX: CGFloat = 40.0 - let maxDampingX: CGFloat = self.bounds.width - 40.0 - let centerX: CGFloat = self.bounds.width / 2.0 let dt: Float = 1.0 * 1.0 / 60.0 let typeDelays: [Float] = [0.0, 0.01, 0.08] @@ -220,7 +212,7 @@ public final class ConfettiView: UIView { var typesWithPositiveVelocity: [Bool] = [false, false, false] for particle in self.particles { - let (localDt, damping_) = dtAndDamping[particle.type] + let (localDt, _) = dtAndDamping[particle.type] if localDt.isZero { continue } diff --git a/submodules/ContactListUI/BUILD b/submodules/ContactListUI/BUILD index 28a4a99837..aa31a500f9 100644 --- a/submodules/ContactListUI/BUILD +++ b/submodules/ContactListUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/ContactListUI/Sources/ContactListNode.swift b/submodules/ContactListUI/Sources/ContactListNode.swift index 286c371b31..d8a2854c12 100644 --- a/submodules/ContactListUI/Sources/ContactListNode.swift +++ b/submodules/ContactListUI/Sources/ContactListNode.swift @@ -1258,8 +1258,8 @@ public final class ContactListNode: ASDisplayNode { var peers: [(Peer, Int32)] = [] for entry in view.entries { switch entry { - case let .MessageEntry(messageEntry): - if let peer = messageEntry.5.peer { + case let .MessageEntry(_, _, _, _, _, renderedPeer, _, _, _, _): + if let peer = renderedPeer.peer { if peer is TelegramGroup { peers.append((peer, 0)) } else if let channel = peer as? TelegramChannel, case .group = channel.info { diff --git a/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift b/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift index fceb01fc3b..13c1fc2757 100644 --- a/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift @@ -48,7 +48,7 @@ private enum InviteContactsEntry: Comparable, Identifiable { func item(context: AccountContext, presentationData: PresentationData, interaction: InviteContactsInteraction) -> ListViewItem { switch self { - case let .option(_, option, theme, _): + case let .option(_, option, _, _): return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: option.title, icon: option.icon, header: nil, action: option.action) case let .peer(_, id, contact, count, selection, theme, strings, nameSortOrder, nameDisplayOrder): let status: ContactsPeerItemStatus diff --git a/submodules/ContactsPeerItem/BUILD b/submodules/ContactsPeerItem/BUILD index 47a5925430..fd0f757c66 100644 --- a/submodules/ContactsPeerItem/BUILD +++ b/submodules/ContactsPeerItem/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift index fab112e2e2..6b619c9599 100644 --- a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift +++ b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift @@ -825,7 +825,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { } else if peer.isDeleted { overrideImage = .deletedIcon } - strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads) + strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: EnginePeer(peer), overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads) } case let .deviceContact(_, contact): let letters: [String] diff --git a/submodules/ContextUI/BUILD b/submodules/ContextUI/BUILD index f8157be861..8f1126f0c8 100644 --- a/submodules/ContextUI/BUILD +++ b/submodules/ContextUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index 8f033a294f..883a5902a4 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -1010,10 +1010,8 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi var completedEffect = false var completedContentNode = false var completedActionsNode = false - var targetNode: ASDisplayNode? if let transitionInfo = transitionInfo, let (sourceNode, sourceNodeRect) = transitionInfo.sourceNode() { - targetNode = sourceNode let projectedFrame = convertFrame(sourceNodeRect, from: sourceNode.view, to: self.view) self.originalProjectedContentViewFrame = (projectedFrame, projectedFrame) @@ -1090,18 +1088,10 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi animateOutToItem = false } - if animateOutToItem, let targetNode = targetNode, let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame { - let actionsSideInset: CGFloat = (validLayout?.safeInsets.left ?? 0.0) + 11.0 + if animateOutToItem, let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame { let localSourceFrame = self.view.convert(CGRect(origin: CGPoint(x: originalProjectedContentViewFrame.1.minX, y: originalProjectedContentViewFrame.1.minY), size: CGSize(width: originalProjectedContentViewFrame.1.width, height: originalProjectedContentViewFrame.1.height)), to: self.scrollNode.view) - if let snapshotView = targetNode.view.snapshotContentTree(unhide: true, keepTransform: true), false { - self.view.addSubview(snapshotView) - snapshotView.layer.animatePosition(from: CGPoint(x: self.contentContainerNode.frame.midX, y: self.contentContainerNode.frame.minY + localSourceFrame.height / 2.0), to: localSourceFrame.center, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false) - snapshotView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2 * animationDurationFactor, removeOnCompletion: false, completion: { _ in - }) - } - self.actionsContainerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y), duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false, additive: true) let contentContainerOffset = CGPoint(x: localSourceFrame.center.x - self.contentContainerNode.frame.center.x, y: localSourceFrame.center.y - self.contentContainerNode.frame.center.y) self.contentContainerNode.layer.animatePosition(from: CGPoint(), to: contentContainerOffset, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false, additive: true, completion: { [weak self] _ in @@ -1724,7 +1714,7 @@ public final class ContextControllerReferenceViewInfo { } } -public protocol ContextReferenceContentSource: class { +public protocol ContextReferenceContentSource: AnyObject { func transitionInfo() -> ContextControllerReferenceViewInfo? } @@ -1750,7 +1740,7 @@ public final class ContextControllerPutBackViewInfo { } } -public protocol ContextExtractedContentSource: class { +public protocol ContextExtractedContentSource: AnyObject { var centerVertically: Bool { get } var keepInPlace: Bool { get } var ignoreContentTouches: Bool { get } @@ -1781,7 +1771,7 @@ public final class ContextControllerTakeControllerInfo { } } -public protocol ContextControllerContentSource: class { +public protocol ContextControllerContentSource: AnyObject { var controller: ViewController { get } var navigationController: NavigationController? { get } var passthroughTouches: Bool { get } diff --git a/submodules/CounterContollerTitleView/BUILD b/submodules/CounterContollerTitleView/BUILD index 0462915bd5..7ad134d009 100644 --- a/submodules/CounterContollerTitleView/BUILD +++ b/submodules/CounterContollerTitleView/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/CountrySelectionUI/BUILD b/submodules/CountrySelectionUI/BUILD index b5284ea80d..e681f57857 100644 --- a/submodules/CountrySelectionUI/BUILD +++ b/submodules/CountrySelectionUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/DatePickerNode/BUILD b/submodules/DatePickerNode/BUILD index cc26958683..02d1f00f53 100644 --- a/submodules/DatePickerNode/BUILD +++ b/submodules/DatePickerNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/DatePickerNode/Sources/TapeNode.swift b/submodules/DatePickerNode/Sources/TapeNode.swift index a1ad195d49..feccc0dda5 100644 --- a/submodules/DatePickerNode/Sources/TapeNode.swift +++ b/submodules/DatePickerNode/Sources/TapeNode.swift @@ -4,7 +4,7 @@ import Display import AsyncDisplayKit import AudioToolbox -@objc public protocol PickerViewDelegate: class { +@objc public protocol PickerViewDelegate: AnyObject { func pickerViewHeightForRows(_ pickerView: TapeNode) -> CGFloat @objc optional func pickerView(_ pickerView: TapeNode, didSelectRow row: Int) @objc optional func pickerView(_ pickerView: TapeNode, didTapRow row: Int) diff --git a/submodules/DateSelectionUI/BUILD b/submodules/DateSelectionUI/BUILD index 2d9080285d..be8c15b508 100644 --- a/submodules/DateSelectionUI/BUILD +++ b/submodules/DateSelectionUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/DebugSettingsUI/BUILD b/submodules/DebugSettingsUI/BUILD index 9b154444b7..f536359fa2 100644 --- a/submodules/DebugSettingsUI/BUILD +++ b/submodules/DebugSettingsUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/Display:Display", diff --git a/submodules/DebugSettingsUI/Sources/DebugAccountsController.swift b/submodules/DebugSettingsUI/Sources/DebugAccountsController.swift index 32a67c8272..0c88bf011b 100644 --- a/submodules/DebugSettingsUI/Sources/DebugAccountsController.swift +++ b/submodules/DebugSettingsUI/Sources/DebugAccountsController.swift @@ -75,11 +75,11 @@ private enum DebugAccountsControllerEntry: ItemListNodeEntry { func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem { let arguments = arguments as! DebugAccountsControllerArguments switch self { - case let .record(theme, record, current): + case let .record(_, record, current): return ItemListCheckboxItem(presentationData: presentationData, title: "\(UInt64(bitPattern: record.id.int64))", style: .left, checked: current, zeroSeparatorInsets: false, sectionId: self.section, action: { arguments.switchAccount(record.id) }) - case let .loginNewAccount(theme): + case .loginNewAccount: return ItemListActionItem(presentationData: presentationData, title: "Login to another account", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { arguments.loginNewAccount() }) diff --git a/submodules/DebugSettingsUI/Sources/DebugController.swift b/submodules/DebugSettingsUI/Sources/DebugController.swift index c40825879d..10f86849f8 100644 --- a/submodules/DebugSettingsUI/Sources/DebugController.swift +++ b/submodules/DebugSettingsUI/Sources/DebugController.swift @@ -78,7 +78,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { case knockoutWallpaper(PresentationTheme, Bool) case demoVideoChats(Bool) case experimentalCompatibility(Bool) - case enableNoiseSuppression(Bool) + case enableDebugDataDisplay(Bool) case playerEmbedding(Bool) case playlistPlayback(Bool) case voiceConference @@ -100,7 +100,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { return DebugControllerSection.logging.rawValue case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries: return DebugControllerSection.experiments.rawValue - case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .demoVideoChats, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableNoiseSuppression: + case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .demoVideoChats, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay: return DebugControllerSection.experiments.rawValue case .preferredVideoCodec: return DebugControllerSection.videoExperiments.rawValue @@ -167,7 +167,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { return 25 case .experimentalCompatibility: return 26 - case .enableNoiseSuppression: + case .enableDebugDataDisplay: return 27 case .playerEmbedding: return 28 @@ -617,7 +617,6 @@ private enum DebugControllerEntry: ItemListNodeEntry { let databasePath = context.account.basePath + "/postbox/db" let _ = try? FileManager.default.removeItem(atPath: databasePath) exit(0) - preconditionFailure() }), ]), ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in @@ -640,7 +639,6 @@ private enum DebugControllerEntry: ItemListNodeEntry { let databasePath = context.account.basePath + "/postbox" let _ = try? FileManager.default.removeItem(atPath: databasePath) exit(0) - preconditionFailure() }), ]), ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in @@ -741,12 +739,12 @@ private enum DebugControllerEntry: ItemListNodeEntry { }) }).start() }) - case let .enableNoiseSuppression(value): - return ItemListSwitchItem(presentationData: presentationData, title: "Noise Suppression", value: value, sectionId: self.section, style: .blocks, updated: { value in + case let .enableDebugDataDisplay(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Debug Data Display", value: value, sectionId: self.section, style: .blocks, updated: { value in let _ = arguments.sharedContext.accountManager.transaction ({ transaction in transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings - settings.enableNoiseSuppression = value + settings.enableDebugDataDisplay = value return settings }) }).start() @@ -862,7 +860,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present entries.append(.knockoutWallpaper(presentationData.theme, experimentalSettings.knockoutWallpaper)) entries.append(.demoVideoChats(experimentalSettings.demoVideoChats)) entries.append(.experimentalCompatibility(experimentalSettings.experimentalCompatibility)) - entries.append(.enableNoiseSuppression(experimentalSettings.enableNoiseSuppression)) + entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay)) entries.append(.playerEmbedding(experimentalSettings.playerEmbedding)) entries.append(.playlistPlayback(experimentalSettings.playlistPlayback)) } diff --git a/submodules/DeleteChatPeerActionSheetItem/BUILD b/submodules/DeleteChatPeerActionSheetItem/BUILD index d991fcc4b4..da28adac66 100644 --- a/submodules/DeleteChatPeerActionSheetItem/BUILD +++ b/submodules/DeleteChatPeerActionSheetItem/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/DeleteChatPeerActionSheetItem/Sources/DeleteChatPeerActionSheetItem.swift b/submodules/DeleteChatPeerActionSheetItem/Sources/DeleteChatPeerActionSheetItem.swift index 43e975e098..32e6f49319 100644 --- a/submodules/DeleteChatPeerActionSheetItem/Sources/DeleteChatPeerActionSheetItem.swift +++ b/submodules/DeleteChatPeerActionSheetItem/Sources/DeleteChatPeerActionSheetItem.swift @@ -1,7 +1,6 @@ import Foundation import UIKit import Display -import Postbox import TelegramCore import TelegramPresentationData import TelegramUIPreferences @@ -22,13 +21,13 @@ private let avatarFont = avatarPlaceholderFont(size: 26.0) public final class DeleteChatPeerActionSheetItem: ActionSheetItem { let context: AccountContext - let peer: Peer - let chatPeer: Peer + let peer: EnginePeer + let chatPeer: EnginePeer let action: DeleteChatPeerAction let strings: PresentationStrings let nameDisplayOrder: PresentationPersonNameOrder - public init(context: AccountContext, peer: Peer, chatPeer: Peer, action: DeleteChatPeerAction, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder) { + public init(context: AccountContext, peer: EnginePeer, chatPeer: EnginePeer, action: DeleteChatPeerAction, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder) { self.context = context self.peer = peer self.chatPeer = chatPeer @@ -54,7 +53,7 @@ private final class DeleteChatPeerActionSheetItemNode: ActionSheetItemNode { private let accessibilityArea: AccessibilityAreaNode - init(theme: ActionSheetControllerTheme, strings: PresentationStrings, nameOrder: PresentationPersonNameOrder, context: AccountContext, peer: Peer, chatPeer: Peer, action: DeleteChatPeerAction) { + init(theme: ActionSheetControllerTheme, strings: PresentationStrings, nameOrder: PresentationPersonNameOrder, context: AccountContext, peer: EnginePeer, chatPeer: EnginePeer, action: DeleteChatPeerAction) { self.theme = theme self.strings = strings @@ -107,11 +106,11 @@ private final class DeleteChatPeerActionSheetItemNode: ActionSheetItemNode { case .delete: if chatPeer.id == context.account.peerId { text = PresentationStrings.FormattedString(string: strings.ChatList_DeleteSavedMessagesConfirmation, ranges: []) - } else if let chatPeer = chatPeer as? TelegramGroup { + } else if case let .legacyGroup(chatPeer) = chatPeer { text = strings.ChatList_LeaveGroupConfirmation(chatPeer.title) - } else if let chatPeer = chatPeer as? TelegramChannel { + } else if case let .channel(chatPeer) = chatPeer { text = strings.ChatList_LeaveGroupConfirmation(chatPeer.title) - } else if chatPeer is TelegramSecretChat { + } else if case .secretChat = chatPeer { text = strings.ChatList_DeleteSecretChatConfirmation(peer.displayTitle(strings: strings, displayOrder: nameOrder)) } else { text = strings.ChatList_DeleteChatConfirmation(peer.displayTitle(strings: strings, displayOrder: nameOrder)) @@ -119,11 +118,11 @@ private final class DeleteChatPeerActionSheetItemNode: ActionSheetItemNode { case .deleteAndLeave: if chatPeer.id == context.account.peerId { text = PresentationStrings.FormattedString(string: strings.ChatList_DeleteSavedMessagesConfirmation, ranges: []) - } else if let chatPeer = chatPeer as? TelegramGroup { + } else if case let .legacyGroup(chatPeer) = chatPeer { text = strings.ChatList_DeleteAndLeaveGroupConfirmation(chatPeer.title) - } else if let chatPeer = chatPeer as? TelegramChannel { + } else if case let .channel(chatPeer) = chatPeer { text = strings.ChatList_DeleteAndLeaveGroupConfirmation(chatPeer.title) - } else if chatPeer is TelegramSecretChat { + } else if case .secretChat = chatPeer { text = strings.ChatList_DeleteSecretChatConfirmation(peer.displayTitle(strings: strings, displayOrder: nameOrder)) } else { text = strings.ChatList_DeleteChatConfirmation(peer.displayTitle(strings: strings, displayOrder: nameOrder)) @@ -131,7 +130,7 @@ private final class DeleteChatPeerActionSheetItemNode: ActionSheetItemNode { case let .clearHistory(canClearCache): if peer.id == context.account.peerId { text = PresentationStrings.FormattedString(string: strings.ChatList_DeleteSavedMessagesConfirmation, ranges: []) - } else if peer is TelegramUser { + } else if case .user = peer { text = strings.ChatList_ClearChatConfirmation(peer.displayTitle(strings: strings, displayOrder: nameOrder)) } else { text = strings.Conversation_DeleteAllMessagesInChat(peer.displayTitle(strings: strings, displayOrder: nameOrder)) diff --git a/submodules/DeviceAccess/BUILD b/submodules/DeviceAccess/BUILD index 77cdb116dd..83e31b8d67 100644 --- a/submodules/DeviceAccess/BUILD +++ b/submodules/DeviceAccess/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramCore:TelegramCore", "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", diff --git a/submodules/DeviceLocationManager/BUILD b/submodules/DeviceLocationManager/BUILD index 583f811cfb..8635180aed 100644 --- a/submodules/DeviceLocationManager/BUILD +++ b/submodules/DeviceLocationManager/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", ], diff --git a/submodules/DirectionalPanGesture/BUILD b/submodules/DirectionalPanGesture/BUILD index 9e62d95bb1..e0b8dd6c1f 100644 --- a/submodules/DirectionalPanGesture/BUILD +++ b/submodules/DirectionalPanGesture/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], visibility = [ "//visibility:public", ], diff --git a/submodules/Display/BUILD b/submodules/Display/BUILD index 277d09ae20..8e56c1d1ec 100644 --- a/submodules/Display/BUILD +++ b/submodules/Display/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Source/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/ObjCRuntimeUtils:ObjCRuntimeUtils", "//submodules/UIKitRuntimeUtils:UIKitRuntimeUtils", diff --git a/submodules/Display/Source/ContainableController.swift b/submodules/Display/Source/ContainableController.swift index ad9eb30558..22da56873a 100644 --- a/submodules/Display/Source/ContainableController.swift +++ b/submodules/Display/Source/ContainableController.swift @@ -2,11 +2,11 @@ import UIKit import AsyncDisplayKit import SwiftSignalKit -public protocol PresentableController: class { +public protocol PresentableController: AnyObject { func viewDidAppear(completion: @escaping () -> Void) } -public protocol ContainableController: class { +public protocol ContainableController: AnyObject { var view: UIView! { get } var displayNode: ASDisplayNode { get } var isViewLoaded: Bool { get } diff --git a/submodules/Display/Source/ContainedViewLayoutTransition.swift b/submodules/Display/Source/ContainedViewLayoutTransition.swift index 019a41615b..e1eebfc499 100644 --- a/submodules/Display/Source/ContainedViewLayoutTransition.swift +++ b/submodules/Display/Source/ContainedViewLayoutTransition.swift @@ -799,8 +799,6 @@ public extension ContainedViewLayoutTransition { } func animateTransformScale(node: ASDisplayNode, from fromScale: CGPoint, completion: ((Bool) -> Void)? = nil) { - let t = node.layer.transform - switch self { case .immediate: if let completion = completion { diff --git a/submodules/Display/Source/GenerateImage.swift b/submodules/Display/Source/GenerateImage.swift index 0606e987ef..8d3e7a6ae6 100644 --- a/submodules/Display/Source/GenerateImage.swift +++ b/submodules/Display/Source/GenerateImage.swift @@ -553,7 +553,6 @@ public class DrawingContext { } if self.hasGeneratedImage { preconditionFailure() - return nil } self.hasGeneratedImage = true diff --git a/submodules/Display/Source/ListView.swift b/submodules/Display/Source/ListView.swift index 489fc15251..f8615f2ebc 100644 --- a/submodules/Display/Source/ListView.swift +++ b/submodules/Display/Source/ListView.swift @@ -2661,24 +2661,6 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } } - if !offset.isZero { - let scrollToItemTransition: ContainedViewLayoutTransition - if !scrollToItem.animated { - scrollToItemTransition = .immediate - } else { - switch scrollToItem.curve { - case let .Spring(duration): - scrollToItemTransition = .animated(duration: duration, curve: .spring) - case let .Default(duration): - scrollToItemTransition = .animated(duration: duration ?? 0.3, curve: .easeInOut) - case let .Custom(duration, cp1x, cp1y, cp2x, cp2y): - scrollToItemTransition = .animated(duration: duration, curve: .custom(cp1x, cp1y, cp2x, cp2y)) - } - } - - //self.didScrollWithOffset?(-offset, scrollToItemTransition, nil) - } - for itemNode in self.itemNodes { var frame = itemNode.frame frame.origin.y += offset @@ -2750,24 +2732,6 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture self.ensureTopInsetForOverlayHighlightedItems = updateSizeAndInsets.ensureTopInsetForOverlayHighlightedItems self.visibleSize = updateSizeAndInsets.size - let updateSizeAndInsetsTransition: ContainedViewLayoutTransition - if updateSizeAndInsets.duration.isZero { - updateSizeAndInsetsTransition = .immediate - } else { - switch updateSizeAndInsets.curve { - case let .Spring(duration): - updateSizeAndInsetsTransition = .animated(duration: duration, curve: .spring) - case let .Default(duration): - updateSizeAndInsetsTransition = .animated(duration: duration ?? 0.3, curve: .easeInOut) - case let .Custom(duration, cp1x, cp1y, cp2x, cp2y): - updateSizeAndInsetsTransition = .animated(duration: duration, curve: .custom(cp1x, cp1y, cp2x, cp2y)) - } - } - - if !offsetFix.isZero { - //self.didScrollWithOffset?(-offsetFix, updateSizeAndInsetsTransition, nil) - } - for itemNode in self.itemNodes { itemNode.updateFrame(itemNode.frame.offsetBy(dx: 0.0, dy: offsetFix), within: self.visibleSize) } @@ -4437,7 +4401,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture for (_, headerNode) in self.itemHeaderNodes { let headerNodeFrame = headerNode.frame if headerNodeFrame.contains(point) { - return headerNode.hitTest(point.offsetBy(dx: -headerNodeFrame.minX, dy: -headerNodeFrame.minY), with: event) + return headerNode.hitTest(self.view.convert(point, to: headerNode.view), with: event) } } return nil diff --git a/submodules/Display/Source/Navigation/NavigationContainer.swift b/submodules/Display/Source/Navigation/NavigationContainer.swift index 51f9c7ddb9..6aa0a28880 100644 --- a/submodules/Display/Source/Navigation/NavigationContainer.swift +++ b/submodules/Display/Source/Navigation/NavigationContainer.swift @@ -480,6 +480,7 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate { toValue.value.setIgnoreAppearanceMethodInvocations(true) self.addSubnode(toValue.value.displayNode) toValue.value.setIgnoreAppearanceMethodInvocations(false) + toValue.value.displayNode.recursivelyEnsureDisplaySynchronously(true) toValue.value.viewDidAppear(false) } } diff --git a/submodules/Display/Source/Navigation/NavigationController.swift b/submodules/Display/Source/Navigation/NavigationController.swift index eddc06841b..d855be2af2 100644 --- a/submodules/Display/Source/Navigation/NavigationController.swift +++ b/submodules/Display/Source/Navigation/NavigationController.swift @@ -107,7 +107,7 @@ private final class NavigationControllerNode: ASDisplayNode { } } -public protocol NavigationControllerDropContentItem: class { +public protocol NavigationControllerDropContentItem: AnyObject { } public final class NavigationControllerDropContent { @@ -406,7 +406,7 @@ open class NavigationController: UINavigationController, ContainableController, globalScrollToTopNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -1.0), size: CGSize(width: layout.size.width, height: 1.0)) } - var overlayContainerLayout = layout + let overlayContainerLayout = layout if let inCallStatusBar = self.inCallStatusBar { var inCallStatusBarFrame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: max(layout.statusBarHeight ?? 0.0, max(40.0, layout.safeInsets.top)))) diff --git a/submodules/Display/Source/NavigationButtonNode.swift b/submodules/Display/Source/NavigationButtonNode.swift index 8cb826e190..656774cc07 100644 --- a/submodules/Display/Source/NavigationButtonNode.swift +++ b/submodules/Display/Source/NavigationButtonNode.swift @@ -53,7 +53,7 @@ private final class NavigationButtonItemNode: ImmediateTextNode { } } - private var imageNode: ASImageNode? + private(set) var imageNode: ASImageNode? private let imageRippleNode: ASImageNode private var _image: UIImage? @@ -226,7 +226,7 @@ private final class NavigationButtonItemNode: ImmediateTextNode { } else if let imageNode = self.imageNode { let nodeSize = imageNode.image?.size ?? CGSize() let size = CGSize(width: max(nodeSize.width, superSize.width), height: max(44.0, max(nodeSize.height, superSize.height))) - let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - nodeSize.width) / 2.0) + 5.0, y: floorToScreenPixels((size.height - nodeSize.height) / 2.0)), size: nodeSize) + let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - nodeSize.width) / 2.0), y: floorToScreenPixels((size.height - nodeSize.height) / 2.0)), size: nodeSize) imageNode.frame = imageFrame self.imageRippleNode.frame = imageFrame return size @@ -462,24 +462,30 @@ public final class NavigationButtonNode: ASDisplayNode { public func updateLayout(constrainedSize: CGSize, isLandscape: Bool) -> CGSize { var nodeOrigin = CGPoint() - var totalSize = CGSize() - for node in self.nodes { - if !totalSize.width.isZero { - totalSize.width += 16.0 - nodeOrigin.x += 16.0 + var totalHeight: CGFloat = 0.0 + for i in 0 ..< self.nodes.count { + if i != 0 { + nodeOrigin.x += 10.0 } + + let node = self.nodes[i] + var nodeSize = node.updateLayout(constrainedSize) + nodeSize.width = ceil(nodeSize.width) nodeSize.height = ceil(nodeSize.height) - totalSize.width += nodeSize.width - totalSize.height = max(totalSize.height, nodeSize.height) - node.frame = CGRect(origin: CGPoint(x: nodeOrigin.x, y: floor((totalSize.height - nodeSize.height) / 2.0)), size: nodeSize) + totalHeight = max(totalHeight, nodeSize.height) + node.frame = CGRect(origin: CGPoint(x: nodeOrigin.x, y: floor((totalHeight - nodeSize.height) / 2.0)), size: nodeSize) nodeOrigin.x += node.bounds.width if isLandscape { nodeOrigin.x += 16.0 } + + if node.node == nil && node.imageNode != nil && i == self.nodes.count - 1 { + nodeOrigin.x -= 5.0 + } } - return totalSize + return CGSize(width: nodeOrigin.x, height: totalHeight) } func internalHitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { diff --git a/submodules/Display/Source/StatusBarProxyNode.swift b/submodules/Display/Source/StatusBarProxyNode.swift index c429a8c176..cb29181ec1 100644 --- a/submodules/Display/Source/StatusBarProxyNode.swift +++ b/submodules/Display/Source/StatusBarProxyNode.swift @@ -118,7 +118,7 @@ private class StatusBarItemNode: ASDisplayNode { } } } else { - if self.targetView.checkIsKind(of: timeViewClass) { + if let timeViewClass = timeViewClass, self.targetView.checkIsKind(of: timeViewClass) { context.withContext { c in c.translateBy(x: containingBounds.minX, y: -containingBounds.minY) UIGraphicsPushContext(c) diff --git a/submodules/Display/Source/ViewController.swift b/submodules/Display/Source/ViewController.swift index b5b9e7cac5..3eb5c4492d 100644 --- a/submodules/Display/Source/ViewController.swift +++ b/submodules/Display/Source/ViewController.swift @@ -396,8 +396,8 @@ public enum TabBarItemContextActionType { } navigationBar.updateLayout(size: navigationBarFrame.size, defaultHeight: navigationLayout.defaultContentHeight, additionalTopHeight: statusBarHeight, additionalContentHeight: self.additionalNavigationBarHeight, additionalBackgroundHeight: additionalBackgroundHeight, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, appearsHidden: !self.displayNavigationBar, isLandscape: isLandscape, transition: transition) if !transition.isAnimated { - navigationBar.layer.cancelAnimationsRecursive(key: "bounds") - navigationBar.layer.cancelAnimationsRecursive(key: "position") + navigationBar.layer.removeAnimation(forKey: "bounds") + navigationBar.layer.removeAnimation(forKey: "position") } transition.updateFrame(node: navigationBar, frame: navigationBarFrame) navigationBar.setHidden(!self.displayNavigationBar, animated: transition.isAnimated) diff --git a/submodules/Display/Source/WindowInputAccessoryHeightProvider.swift b/submodules/Display/Source/WindowInputAccessoryHeightProvider.swift index ef51090cbc..4b70932191 100644 --- a/submodules/Display/Source/WindowInputAccessoryHeightProvider.swift +++ b/submodules/Display/Source/WindowInputAccessoryHeightProvider.swift @@ -1,6 +1,6 @@ import Foundation import UIKit -public protocol WindowInputAccessoryHeightProvider: class { +public protocol WindowInputAccessoryHeightProvider: AnyObject { func getWindowInputAccessoryHeight() -> CGFloat } diff --git a/submodules/Emoji/BUILD b/submodules/Emoji/BUILD index 9b5e934bcf..cba5496dea 100644 --- a/submodules/Emoji/BUILD +++ b/submodules/Emoji/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], visibility = [ "//visibility:public", ], diff --git a/submodules/EncryptionKeyVisualization/BUILD b/submodules/EncryptionKeyVisualization/BUILD index f19adbb6ab..63804e3386 100644 --- a/submodules/EncryptionKeyVisualization/BUILD +++ b/submodules/EncryptionKeyVisualization/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramCore:TelegramCore", "//submodules/EncryptionKeyVisualization/Impl:EncryptionKeyVisualizationImpl", diff --git a/submodules/FileMediaResourceStatus/BUILD b/submodules/FileMediaResourceStatus/BUILD index 84090d2b33..44d70774c6 100644 --- a/submodules/FileMediaResourceStatus/BUILD +++ b/submodules/FileMediaResourceStatus/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/GalleryData/BUILD b/submodules/GalleryData/BUILD index d433b87091..5e3be4d4e2 100644 --- a/submodules/GalleryData/BUILD +++ b/submodules/GalleryData/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/GalleryData/Sources/GalleryData.swift b/submodules/GalleryData/Sources/GalleryData.swift index 4cc8b1b532..43632cd6ac 100644 --- a/submodules/GalleryData/Sources/GalleryData.swift +++ b/submodules/GalleryData/Sources/GalleryData.swift @@ -266,7 +266,7 @@ public enum ChatMessagePreviewControllerData { public func chatMessagePreviewControllerData(context: AccountContext, chatLocation: ChatLocation?, chatLocationContextHolder: Atomic?, message: Message, standalone: Bool, reverseMessageGalleryOrder: Bool, navigationController: NavigationController?) -> ChatMessagePreviewControllerData? { if let mediaData = chatMessageGalleryControllerData(context: context, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, message: message, navigationController: navigationController, standalone: standalone, reverseMessageGalleryOrder: reverseMessageGalleryOrder, mode: .default, source: nil, synchronousLoad: true, actionInteraction: nil) { switch mediaData { - case let .gallery(gallery): + case .gallery: break case let .instantPage(gallery, centralIndex, galleryMedia): return .instantPage(gallery, centralIndex, galleryMedia) diff --git a/submodules/GalleryUI/BUILD b/submodules/GalleryUI/BUILD index 115b6fc4b4..d7df405df9 100644 --- a/submodules/GalleryUI/BUILD +++ b/submodules/GalleryUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift index 5ebe3a5a8f..7a260ecc23 100644 --- a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift +++ b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift @@ -601,6 +601,9 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll default: break } + if let file = content.file, !file.isAnimated, file.isVideo { + canFullscreen = true + } } } diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index 5dc82d014c..df117ca2e1 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -144,7 +144,7 @@ private func galleryMessageCaptionText(_ message: Message) -> String { return message.text } -public func galleryItemForEntry(context: AccountContext, presentationData: PresentationData, entry: MessageHistoryEntry, isCentral: Bool = false, streamVideos: Bool, loopVideos: Bool = false, hideControls: Bool = false, fromPlayingVideo: Bool = false, isSecret: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackRate: Double? = nil, displayInfoOnTop: Bool = false, configuration: GalleryConfiguration? = nil, tempFilePath: String? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }, openActionOptions: @escaping (GalleryControllerInteractionTapAction, Message) -> Void = { _, _ in }, storeMediaPlaybackState: @escaping (MessageId, Double?, Double) -> Void = { _, _, _ in }, present: @escaping (ViewController, Any?) -> Void) -> GalleryItem? { +public func galleryItemForEntry(context: AccountContext, presentationData: PresentationData, entry: MessageHistoryEntry, isCentral: Bool = false, streamVideos: Bool, loopVideos: Bool = false, hideControls: Bool = false, fromPlayingVideo: Bool = false, isSecret: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackRate: @escaping () -> Double?, displayInfoOnTop: Bool = false, configuration: GalleryConfiguration? = nil, tempFilePath: String? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void = { _ in }, openActionOptions: @escaping (GalleryControllerInteractionTapAction, Message) -> Void = { _, _ in }, storeMediaPlaybackState: @escaping (MessageId, Double?, Double) -> Void = { _, _, _ in }, present: @escaping (ViewController, Any?) -> Void) -> GalleryItem? { let message = entry.message let location = entry.location if let (media, mediaImage) = mediaForMessage(message: message) { @@ -348,7 +348,7 @@ public class GalleryController: ViewController, StandalonePresentableController private let fromPlayingVideo: Bool private let landscape: Bool private let timecode: Double? - private let playbackRate: Double? + private var playbackRate: Double? private let accountInUseDisposable = MetaDisposable() private let disposable = MetaDisposable() @@ -538,7 +538,7 @@ public class GalleryController: ViewController, StandalonePresentableController if entry.message.stableId == strongSelf.centralEntryStableId { isCentral = true } - if let item = galleryItemForEntry(context: context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: streamSingleVideo, fromPlayingVideo: isCentral && fromPlayingVideo, landscape: isCentral && landscape, timecode: isCentral ? timecode : nil, playbackRate: isCentral ? playbackRate : nil, displayInfoOnTop: displayInfoOnTop, configuration: configuration, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions, storeMediaPlaybackState: strongSelf.actionInteraction?.storeMediaPlaybackState ?? { _, _, _ in }, present: { [weak self] c, a in + if let item = galleryItemForEntry(context: context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: streamSingleVideo, fromPlayingVideo: isCentral && fromPlayingVideo, landscape: isCentral && landscape, timecode: isCentral ? timecode : nil, playbackRate: { return self?.playbackRate }, displayInfoOnTop: displayInfoOnTop, configuration: configuration, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions, storeMediaPlaybackState: strongSelf.actionInteraction?.storeMediaPlaybackState ?? { _, _, _ in }, present: { [weak self] c, a in if let strongSelf = self { strongSelf.presentInGlobalOverlay(c, with: a) } @@ -868,11 +868,7 @@ public class GalleryController: ViewController, StandalonePresentableController ActionSheetButtonItem(title: isCopyLink ? strongSelf.presentationData.strings.Conversation_ContextMenuCopyLink : strongSelf.presentationData.strings.Conversation_LinkDialogCopy, color: .accent, action: { [weak actionSheet, weak self] in actionSheet?.dismissAnimated() if isCopyLink, let channel = message.peers[message.id.peerId] as? TelegramChannel { - var threadMessageId: MessageId? -// if case let .replyThread(replyThreadMessage) = chatPresentationInterfaceState.chatLocation { -// threadMessageId = replyThreadMessage.messageId -// } - let _ = (context.engine.messages.exportMessageLink(peerId: message.id.peerId, messageId: message.id, isThread: threadMessageId != nil) + let _ = (context.engine.messages.exportMessageLink(peerId: message.id.peerId, messageId: message.id, isThread: false) |> map { result -> String? in return result } @@ -1045,18 +1041,9 @@ public class GalleryController: ViewController, StandalonePresentableController if let strongSelf = self { strongSelf._hiddenMedia.set(.single(nil)) - var animatedOutNode = true - var animatedOutInterface = false - - let completion = { - if animatedOutNode && animatedOutInterface { - //self?.presentingViewController?.dismiss(animated: false, completion: nil) - } - } + let animatedOutNode = true strongSelf.galleryNode.animateOut(animateContent: animatedOutNode, completion: { - animatedOutInterface = true - //completion() }) } } @@ -1102,7 +1089,7 @@ public class GalleryController: ViewController, StandalonePresentableController if entry.message.stableId == self.centralEntryStableId { isCentral = true } - if let item = galleryItemForEntry(context: self.context, presentationData: self.presentationData, entry: entry, streamVideos: self.streamVideos, fromPlayingVideo: isCentral && self.fromPlayingVideo, landscape: isCentral && self.landscape, timecode: isCentral ? self.timecode : nil, playbackRate: isCentral ? self.playbackRate : 1.0, displayInfoOnTop: displayInfoOnTop, configuration: self.configuration, performAction: self.performAction, openActionOptions: self.openActionOptions, storeMediaPlaybackState: self.actionInteraction?.storeMediaPlaybackState ?? { _, _, _ in }, present: { [weak self] c, a in + if let item = galleryItemForEntry(context: self.context, presentationData: self.presentationData, entry: entry, streamVideos: self.streamVideos, fromPlayingVideo: isCentral && self.fromPlayingVideo, landscape: isCentral && self.landscape, timecode: isCentral ? self.timecode : nil, playbackRate: { [weak self] in return self?.playbackRate }, displayInfoOnTop: displayInfoOnTop, configuration: self.configuration, performAction: self.performAction, openActionOptions: self.openActionOptions, storeMediaPlaybackState: self.actionInteraction?.storeMediaPlaybackState ?? { _, _, _ in }, present: { [weak self] c, a in if let strongSelf = self { strongSelf.presentInGlobalOverlay(c, with: a) } @@ -1182,7 +1169,7 @@ public class GalleryController: ViewController, StandalonePresentableController if entry.message.stableId == strongSelf.centralEntryStableId { isCentral = true } - if let item = galleryItemForEntry(context: strongSelf.context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: false, fromPlayingVideo: isCentral && strongSelf.fromPlayingVideo, landscape: isCentral && strongSelf.landscape, timecode: isCentral ? strongSelf.timecode : nil, displayInfoOnTop: displayInfoOnTop, configuration: strongSelf.configuration, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions, storeMediaPlaybackState: strongSelf.actionInteraction?.storeMediaPlaybackState ?? { _, _, _ in }, present: { [weak self] c, a in + if let item = galleryItemForEntry(context: strongSelf.context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: false, fromPlayingVideo: isCentral && strongSelf.fromPlayingVideo, landscape: isCentral && strongSelf.landscape, timecode: isCentral ? strongSelf.timecode : nil, playbackRate: { return self?.playbackRate }, displayInfoOnTop: displayInfoOnTop, configuration: strongSelf.configuration, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions, storeMediaPlaybackState: strongSelf.actionInteraction?.storeMediaPlaybackState ?? { _, _, _ in }, present: { [weak self] c, a in if let strongSelf = self { strongSelf.presentInGlobalOverlay(c, with: a) } @@ -1234,7 +1221,7 @@ public class GalleryController: ViewController, StandalonePresentableController if entry.message.stableId == strongSelf.centralEntryStableId { isCentral = true } - if let item = galleryItemForEntry(context: strongSelf.context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: false, fromPlayingVideo: isCentral && strongSelf.fromPlayingVideo, landscape: isCentral && strongSelf.landscape, timecode: isCentral ? strongSelf.timecode : nil, displayInfoOnTop: displayInfoOnTop, configuration: strongSelf.configuration, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions, storeMediaPlaybackState: strongSelf.actionInteraction?.storeMediaPlaybackState ?? { _, _, _ in }, present: { [weak self] c, a in + if let item = galleryItemForEntry(context: strongSelf.context, presentationData: strongSelf.presentationData, entry: entry, isCentral: isCentral, streamVideos: false, fromPlayingVideo: isCentral && strongSelf.fromPlayingVideo, landscape: isCentral && strongSelf.landscape, timecode: isCentral ? strongSelf.timecode : nil, playbackRate: { return self?.playbackRate }, displayInfoOnTop: displayInfoOnTop, configuration: strongSelf.configuration, performAction: strongSelf.performAction, openActionOptions: strongSelf.openActionOptions, storeMediaPlaybackState: strongSelf.actionInteraction?.storeMediaPlaybackState ?? { _, _, _ in }, present: { [weak self] c, a in if let strongSelf = self { strongSelf.presentInGlobalOverlay(c, with: a) } @@ -1369,4 +1356,14 @@ public class GalleryController: ViewController, StandalonePresentableController } } } + + func updateSharedPlaybackRate(_ playbackRate: Double?) { + self.playbackRate = playbackRate + + self.galleryNode.pager.forEachItemNode { itemNode in + if let itemNode = itemNode as? UniversalVideoGalleryItemNode { + itemNode.updatePlaybackRate(playbackRate) + } + } + } } diff --git a/submodules/GalleryUI/Sources/GalleryFooterNode.swift b/submodules/GalleryUI/Sources/GalleryFooterNode.swift index 95acee8794..4f14a43e08 100644 --- a/submodules/GalleryUI/Sources/GalleryFooterNode.swift +++ b/submodules/GalleryUI/Sources/GalleryFooterNode.swift @@ -37,15 +37,11 @@ public final class GalleryFooterNode: ASDisplayNode { let cleanInsets = layout.insets(options: []) var dismissedCurrentFooterContentNode: GalleryFooterContentNode? - var dismissedThumbnailPanelHeight: CGFloat? if self.currentFooterContentNode !== footerContentNode { if let currentFooterContentNode = self.currentFooterContentNode { currentFooterContentNode.requestLayout = nil dismissedCurrentFooterContentNode = currentFooterContentNode } - if let currentThumbnailPanelHeight = self.currentThumbnailPanelHeight { - dismissedThumbnailPanelHeight = currentThumbnailPanelHeight - } self.currentThumbnailPanelHeight = thumbnailPanelHeight self.currentFooterContentNode = footerContentNode if let footerContentNode = footerContentNode { diff --git a/submodules/GalleryUI/Sources/GalleryItemTransitionNode.swift b/submodules/GalleryUI/Sources/GalleryItemTransitionNode.swift index f3ca3f979e..807c3d8148 100644 --- a/submodules/GalleryUI/Sources/GalleryItemTransitionNode.swift +++ b/submodules/GalleryUI/Sources/GalleryItemTransitionNode.swift @@ -1,7 +1,7 @@ import Foundation import AccountContext -public protocol GalleryItemTransitionNode: class { +public protocol GalleryItemTransitionNode: AnyObject { func isAvailableForGalleryTransition() -> Bool func isAvailableForInstantPageTransition() -> Bool var decoration: UniversalVideoDecoration? { get } diff --git a/submodules/GalleryUI/Sources/GalleryPagerNode.swift b/submodules/GalleryUI/Sources/GalleryPagerNode.swift index 80217fe39e..bc6f4c86a5 100644 --- a/submodules/GalleryUI/Sources/GalleryPagerNode.swift +++ b/submodules/GalleryUI/Sources/GalleryPagerNode.swift @@ -168,7 +168,11 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest } } else if point.x > size.width - edgeWidth(width: size.width) && strongSelf.canGoToNextItem() { if strongSelf.items.count > 1 { - highlightedSide = true + if point.y < 80.0 { + highlightedSide = nil + } else { + highlightedSide = true + } } } @@ -176,7 +180,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest return .fail } - if let result = strongSelf.hitTest(point, with: nil), let node = result.asyncdisplaykit_node as? ASButtonNode { + if let result = strongSelf.hitTest(point, with: nil), let _ = result.asyncdisplaykit_node as? ASButtonNode { return .fail } return .keepWithSingleTap @@ -702,4 +706,10 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest self.centralItemIndexOffsetUpdated(nil) } } + + public func forEachItemNode(_ f: (GalleryItemNode) -> Void) { + for itemNode in self.itemNodes { + f(itemNode) + } + } } diff --git a/submodules/GalleryUI/Sources/GalleryThumbnailContainerNode.swift b/submodules/GalleryUI/Sources/GalleryThumbnailContainerNode.swift index 0af6fc4002..94a0c87568 100644 --- a/submodules/GalleryUI/Sources/GalleryThumbnailContainerNode.swift +++ b/submodules/GalleryUI/Sources/GalleryThumbnailContainerNode.swift @@ -280,9 +280,7 @@ public final class GalleryThumbnailContainerNode: ASDisplayNode, UIScrollViewDel return } - if let (centralIndex, progress) = self.centralIndexAndProgress { - //let contentOffset = contentOffsetToCenterItem(index: centralIndex, progress: progress, contentInset: self.scrollNode.view.contentInset) - + if let _ = self.centralIndexAndProgress { let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring) if !decelerate { self.isPanning = false diff --git a/submodules/GalleryUI/Sources/Items/ChatAnimationGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatAnimationGalleryItem.swift index 87078d9697..386a27af17 100644 --- a/submodules/GalleryUI/Sources/Items/ChatAnimationGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatAnimationGalleryItem.swift @@ -208,7 +208,7 @@ final class ChatAnimationGalleryItemNode: ZoomableContentGalleryItemNode { strongSelf.statusNode.alpha = 1.0 strongSelf.statusNodeContainer.isUserInteractionEnabled = true strongSelf.statusNode.transitionToState(.download(.white), completion: {}) - case let .Fetching(isActive, progress): + case let .Fetching(_, progress): strongSelf.statusNode.isHidden = false strongSelf.statusNode.alpha = 1.0 strongSelf.statusNodeContainer.isUserInteractionEnabled = true @@ -312,7 +312,7 @@ final class ChatAnimationGalleryItemNode: ZoomableContentGalleryItemNode { override func visibilityUpdated(isVisible: Bool) { super.visibilityUpdated(isVisible: isVisible) - if let (context, mediaReference) = self.contextAndMedia, let fileReference = mediaReference.concrete(TelegramMediaFile.self) { + if let (_, mediaReference) = self.contextAndMedia, let _ = mediaReference.concrete(TelegramMediaFile.self) { if isVisible { } else { self.fetchDisposable.set(nil) diff --git a/submodules/GalleryUI/Sources/Items/ChatDocumentGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatDocumentGalleryItem.swift index 12829f7c15..4379cf5aca 100644 --- a/submodules/GalleryUI/Sources/Items/ChatDocumentGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatDocumentGalleryItem.swift @@ -197,7 +197,7 @@ class ChatDocumentGalleryItemNode: ZoomableContentGalleryItemNode, WKNavigationD strongSelf.statusNode.alpha = 1.0 strongSelf.statusNodeContainer.isUserInteractionEnabled = true strongSelf.statusNode.transitionToState(.download(.white), completion: {}) - case let .Fetching(isActive, progress): + case let .Fetching(_, progress): strongSelf.statusNode.isHidden = false strongSelf.statusNode.alpha = 1.0 strongSelf.statusNodeContainer.isUserInteractionEnabled = true diff --git a/submodules/GalleryUI/Sources/Items/ChatExternalFileGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatExternalFileGalleryItem.swift index 1dadd77a30..bd7f697d09 100644 --- a/submodules/GalleryUI/Sources/Items/ChatExternalFileGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatExternalFileGalleryItem.swift @@ -195,7 +195,7 @@ class ChatExternalFileGalleryItemNode: GalleryItemNode { strongSelf.statusNode.alpha = 1.0 strongSelf.statusNodeContainer.isUserInteractionEnabled = true strongSelf.statusNode.transitionToState(.download(.white), completion: {}) - case let .Fetching(isActive, progress): + case let .Fetching(_, progress): strongSelf.statusNode.isHidden = false strongSelf.statusNode.alpha = 1.0 strongSelf.statusNodeContainer.isUserInteractionEnabled = true diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift index c02208680d..3b14a1891e 100644 --- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift @@ -420,7 +420,7 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { strongSelf.statusNode.alpha = 1.0 strongSelf.statusNodeContainer.isUserInteractionEnabled = true strongSelf.statusNode.transitionToState(.download(.white), completion: {}) - case let .Fetching(isActive, progress): + case let .Fetching(_, progress): strongSelf.statusNode.isHidden = false strongSelf.statusNode.alpha = 1.0 strongSelf.statusNodeContainer.isUserInteractionEnabled = true @@ -596,7 +596,7 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { override func visibilityUpdated(isVisible: Bool) { super.visibilityUpdated(isVisible: isVisible) - if let (context, mediaReference) = self.contextAndMedia, let fileReference = mediaReference.concrete(TelegramMediaFile.self) { + if let (_, mediaReference) = self.contextAndMedia, let _ = mediaReference.concrete(TelegramMediaFile.self) { if isVisible { } else { self.fetchDisposable.set(nil) diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index 5423b55c56..56578535cd 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -44,7 +44,7 @@ public class UniversalVideoGalleryItem: GalleryItem { let isSecret: Bool let landscape: Bool let timecode: Double? - let playbackRate: Double? + let playbackRate: () -> Double? let configuration: GalleryConfiguration? let playbackCompleted: () -> Void let performAction: (GalleryControllerInteractionTapAction) -> Void @@ -52,7 +52,7 @@ public class UniversalVideoGalleryItem: GalleryItem { let storeMediaPlaybackState: (MessageId, Double?, Double) -> Void let present: (ViewController, Any?) -> Void - public init(context: AccountContext, presentationData: PresentationData, content: UniversalVideoContent, originData: GalleryItemOriginData?, indexData: GalleryItemIndexData?, contentInfo: UniversalVideoGalleryItemContentInfo?, caption: NSAttributedString, credit: NSAttributedString? = nil, displayInfoOnTop: Bool = false, hideControls: Bool = false, fromPlayingVideo: Bool = false, isSecret: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackRate: Double? = nil, configuration: GalleryConfiguration? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction, Message) -> Void, storeMediaPlaybackState: @escaping (MessageId, Double?, Double) -> Void, present: @escaping (ViewController, Any?) -> Void) { + public init(context: AccountContext, presentationData: PresentationData, content: UniversalVideoContent, originData: GalleryItemOriginData?, indexData: GalleryItemIndexData?, contentInfo: UniversalVideoGalleryItemContentInfo?, caption: NSAttributedString, credit: NSAttributedString? = nil, displayInfoOnTop: Bool = false, hideControls: Bool = false, fromPlayingVideo: Bool = false, isSecret: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackRate: @escaping () -> Double?, configuration: GalleryConfiguration? = nil, playbackCompleted: @escaping () -> Void = {}, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction, Message) -> Void, storeMediaPlaybackState: @escaping (MessageId, Double?, Double) -> Void, present: @escaping (ViewController, Any?) -> Void) { self.context = context self.presentationData = presentationData self.content = content @@ -364,13 +364,15 @@ private final class MoreHeaderButton: HighlightableButtonNode { strongSelf.contextAction?(strongSelf.containerNode, gesture) } - self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: wide ? 32.0 : 22.0, height: 22.0)) + self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 26.0, height: 44.0)) self.referenceNode.frame = self.containerNode.bounds self.iconNode.image = optionsCircleImage(dark: false) if let image = self.iconNode.image { self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) } + + self.hitTestSlop = UIEdgeInsets(top: 0.0, left: -4.0, bottom: 0.0, right: -4.0) } private var content: Content? @@ -380,7 +382,8 @@ private final class MoreHeaderButton: HighlightableButtonNode { let animationNode = AnimationNode(animation: "anim_profilemore", colors: ["Point 2.Group 1.Fill 1": iconColor, "Point 3.Group 1.Fill 1": iconColor, "Point 1.Group 1.Fill 1": iconColor], scale: 1.0) - animationNode.frame = self.containerNode.bounds + let animationSize = CGSize(width: 22.0, height: 22.0) + animationNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - animationSize.width) / 2.0), y: floor((self.containerNode.bounds.height - animationSize.height) / 2.0)), size: animationSize) self.addSubnode(animationNode) self.animationNode = animationNode } @@ -423,10 +426,18 @@ private final class MoreHeaderButton: HighlightableButtonNode { self.content = content switch content { case let .image(image): + if let image = image { + self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) + } + self.iconNode.image = image self.iconNode.isHidden = false self.animationNode?.isHidden = true case let .more(image): + if let image = image { + self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) + } + self.iconNode.image = image self.iconNode.isHidden = false self.animationNode?.isHidden = false @@ -440,7 +451,7 @@ private final class MoreHeaderButton: HighlightableButtonNode { } override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { - return CGSize(width: wide ? 32.0 : 22.0, height: 22.0) + return CGSize(width: wide ? 32.0 : 22.0, height: 44.0) } func onLayout() { @@ -494,6 +505,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { private var requiresDownload = false private var item: UniversalVideoGalleryItem? + private var playbackRate: Double? + private let playbackRatePromise = ValuePromise() private let statusDisposable = MetaDisposable() private let moreButtonStateDisposable = MetaDisposable() @@ -511,6 +524,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { private let isInteractingPromise = ValuePromise(false, ignoreRepeated: true) private let controlsVisiblePromise = ValuePromise(true, ignoreRepeated: true) private let isShowingContextMenuPromise = ValuePromise(false, ignoreRepeated: true) + private let hasExpandedCaptionPromise = ValuePromise(false, ignoreRepeated: true) private var hideControlsDisposable: Disposable? var playbackCompleted: (() -> Void)? @@ -622,6 +636,10 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { self.footerContentNode.setPlayRate = { [weak self] rate in if let strongSelf = self, let videoNode = strongSelf.videoNode { videoNode.setBaseRate(rate) + + if let controller = strongSelf.galleryController() as? GalleryController { + controller.updateSharedPlaybackRate(rate) + } } } @@ -684,12 +702,12 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { self.titleContentView = GalleryTitleView(frame: CGRect()) self._titleView.set(.single(self.titleContentView)) - let shouldHideControlsSignal: Signal = combineLatest(self.isPlayingPromise.get(), self.isInteractingPromise.get(), self.controlsVisiblePromise.get(), self.isShowingContextMenuPromise.get()) - |> mapToSignal { isPlaying, isIntracting, controlsVisible, isShowingContextMenu -> Signal in - if isShowingContextMenu { + let shouldHideControlsSignal: Signal = combineLatest(self.isPlayingPromise.get(), self.isInteractingPromise.get(), self.controlsVisiblePromise.get(), self.isShowingContextMenuPromise.get(), self.hasExpandedCaptionPromise.get()) + |> mapToSignal { isPlaying, isInteracting, controlsVisible, isShowingContextMenu, hasExpandedCaptionPromise -> Signal in + if isShowingContextMenu || hasExpandedCaptionPromise { return .complete() } - if isPlaying && !isIntracting && controlsVisible { + if isPlaying && !isInteracting && controlsVisible { return .single(Void()) |> delay(4.0, queue: Queue.mainQueue()) } else { @@ -839,6 +857,10 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } else { strongSelf.videoNode?.playOnceWithSound(playAndRecord: false, actionAtEnd: isAnimated ? .loop : strongSelf.actionAtEnd) } + + if let playbackRate = strongSelf.playbackRate { + strongSelf.videoNode?.setBaseRate(playbackRate) + } } } } @@ -872,10 +894,15 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } else { let throttledSignal = videoNode.status |> mapToThrottled { next -> Signal in - return .single(next) |> then(.complete() |> delay(2.0, queue: Queue.concurrentDefaultQueue())) + return .single(next) |> then(.complete() |> delay(0.5, queue: Queue.concurrentDefaultQueue())) } - self.mediaPlaybackStateDisposable.set(throttledSignal.start(next: { status in + self.mediaPlaybackStateDisposable.set((throttledSignal + |> deliverOnMainQueue).start(next: { [weak self] status in + guard let strongSelf = self, let videoNode = strongSelf.videoNode, videoNode.ownsContentNode else { + return + } + if let status = status, status.duration >= 60.0 * 10.0 { var timestamp: Double? if status.timestamp > 5.0 && status.timestamp < status.duration - 5.0 { @@ -923,21 +950,18 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } self.moreButtonStateDisposable.set(combineLatest(queue: .mainQueue(), - videoNode.status, + self.playbackRatePromise.get(), self.isShowingContextMenuPromise.get() - ).start(next: { [weak self] status, isShowingContextMenu in + ).start(next: { [weak self] playbackRate, isShowingContextMenu in guard let strongSelf = self else { return } - guard let status = status else { - return - } let effectiveBaseRate: Double if isShowingContextMenu { effectiveBaseRate = 1.0 } else { - effectiveBaseRate = status.baseRate + effectiveBaseRate = playbackRate } if abs(effectiveBaseRate - strongSelf.moreBarButtonRate) > 0.01 { @@ -1113,14 +1137,12 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { if let contentInfo = item.contentInfo, case let .message(message) = contentInfo { var file: TelegramMediaFile? - var isWebpage = false for m in message.media { if let m = m as? TelegramMediaFile, m.isVideo { file = m break } else if let m = m as? TelegramMediaWebpage, case let .Loaded(content) = m.content, let f = content.file, f.isVideo { file = f - isWebpage = true break } } @@ -1128,7 +1150,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { var hasMoreButton = false if isEnhancedWebPlayer { hasMoreButton = true - } else if !isWebpage, let file = file, !file.isAnimated { + } else if let file = file, !file.isAnimated { hasMoreButton = true } @@ -1158,6 +1180,14 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } self.item = item + + if let _ = item.content as? NativeVideoContent { + self.playbackRate = item.playbackRate() + } else if let _ = item.content as? WebEmbedVideoContent { + self.playbackRate = item.playbackRate() + } + + self.playbackRatePromise.set(self.playbackRate ?? 1.0) if let contentInfo = item.contentInfo { switch contentInfo { @@ -1244,6 +1274,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } else if self.shouldAutoplayOnCentrality() { self.initiallyActivated = true videoNode.playOnceWithSound(playAndRecord: false, actionAtEnd: self.actionAtEnd) + + videoNode.setBaseRate(self.playbackRate ?? 1.0) } } else { if self.shouldAutoplayOnCentrality() { @@ -1314,24 +1346,21 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { var isAnimated = false var seek = MediaPlayerSeek.start - var playbackRate: Double? = nil if let item = self.item { if let content = item.content as? NativeVideoContent { isAnimated = content.fileReference.media.isAnimated if let time = item.timecode { seek = .timecode(time) } - playbackRate = item.playbackRate } else if let _ = item.content as? WebEmbedVideoContent { if let time = item.timecode { seek = .timecode(time) } - playbackRate = item.playbackRate } } - if let playbackRate = playbackRate { - videoNode.setBaseRate(playbackRate) - } + + videoNode.setBaseRate(self.playbackRate ?? 1.0) + if isAnimated { videoNode.seek(0.0) videoNode.play() @@ -1763,6 +1792,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { }, close: { [weak mediaManager] in mediaManager?.setOverlayVideoNode(nil) }) + + let playbackRate = self.playbackRate + expandImpl = { [weak overlayNode] in guard let contentInfo = item.contentInfo, let overlayNode = overlayNode else { return @@ -1770,7 +1802,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { switch contentInfo { case let .message(message): - let gallery = GalleryController(context: context, source: .peerMessagesAtId(messageId: message.id, chatLocation: .peer(message.id.peerId), chatLocationContextHolder: Atomic(value: nil)), replaceRootController: { controller, ready in + let gallery = GalleryController(context: context, source: .peerMessagesAtId(messageId: message.id, chatLocation: .peer(message.id.peerId), chatLocationContextHolder: Atomic(value: nil)), playbackRate: playbackRate, replaceRootController: { controller, ready in if let baseNavigationController = baseNavigationController { baseNavigationController.replaceTopController(controller, animated: false, ready: ready) } @@ -1865,6 +1897,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { }, close: { [weak mediaManager] in mediaManager?.setOverlayVideoNode(nil) }) + + let playbackRate = self.playbackRate + expandImpl = { [weak overlayNode] in guard let contentInfo = item.contentInfo, let overlayNode = overlayNode else { return @@ -1872,7 +1907,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { switch contentInfo { case let .message(message): - let gallery = GalleryController(context: context, source: .peerMessagesAtId(messageId: message.id, chatLocation: .peer(message.id.peerId), chatLocationContextHolder: Atomic(value: nil)), replaceRootController: { controller, ready in + let gallery = GalleryController(context: context, source: .peerMessagesAtId(messageId: message.id, chatLocation: .peer(message.id.peerId), chatLocationContextHolder: Atomic(value: nil)), playbackRate: playbackRate, replaceRootController: { controller, ready in if let baseNavigationController = baseNavigationController { baseNavigationController.replaceTopController(controller, animated: false, ready: ready) } @@ -2007,10 +2042,10 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } } - private func speedList() -> [(String, String, Double)] { + private func speedList(strings: PresentationStrings) -> [(String, String, Double)] { let speedList: [(String, String, Double)] = [ ("0.5x", "0.5x", 0.5), - ("Normal", "1x", 1.0), + (strings.PlaybackSpeed_Normal, "1x", 1.0), ("1.5x", "1.5x", 1.5), ("2x", "2x", 2.0) ] @@ -2033,9 +2068,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { var items: [ContextMenuItem] = [] - var speedValue: String = "Normal" + var speedValue: String = strongSelf.presentationData.strings.PlaybackSpeed_Normal var speedIconText: String = "1x" - for (text, iconText, speed) in strongSelf.speedList() { + for (text, iconText, speed) in strongSelf.speedList(strings: strongSelf.presentationData.strings) { if abs(speed - status.baseRate) < 0.01 { speedValue = text speedIconText = iconText @@ -2043,7 +2078,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } } - items.append(.action(ContextMenuActionItem(text: "Playback Speed", textLayout: .secondLineWithValue(speedValue), icon: { theme in + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PlaybackSpeed_Title, textLayout: .secondLineWithValue(speedValue), icon: { theme in return optionsRateImage(rate: speedIconText, isLarge: false, color: theme.contextMenu.primaryColor) }, action: { c, _ in guard let strongSelf = self else { @@ -2053,8 +2088,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { c.setItems(strongSelf.contextMenuSpeedItems()) }))) - if let (message, maybeFile, isWebpage) = strongSelf.contentInfo(), let file = maybeFile, !isWebpage { - items.append(.action(ContextMenuActionItem(text: "Save to Gallery", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Download"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in + if let (message, maybeFile, _) = strongSelf.contentInfo(), let file = maybeFile { + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Gallery_SaveToGallery, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Download"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in f(.default) if let strongSelf = self { @@ -2068,22 +2103,20 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { guard let controller = strongSelf.galleryController() else { return } - //TODO:localize - controller.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .mediaSaved(text: "Video Saved"), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root)) + controller.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .mediaSaved(text: strongSelf.presentationData.strings.Gallery_VideoSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root)) }) default: guard let controller = strongSelf.galleryController() else { return } - //TODO:localize - controller.present(textAlertController(context: strongSelf.context, title: nil, text: "Please wait for the video to be fully downloaded.", actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { + controller.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Gallery_WaitForVideoDownoad, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { })]), in: .window(.root)) } } }))) } if strongSelf.canDelete() { - items.append(.action(ContextMenuActionItem(text: "Delete", textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { _, f in + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Common_Delete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { _, f in f(.default) if let strongSelf = self { @@ -2111,7 +2144,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { var items: [ContextMenuItem] = [] - for (text, _, rate) in strongSelf.speedList() { + for (text, _, rate) in strongSelf.speedList(strings: strongSelf.presentationData.strings) { let isSelected = abs(status.baseRate - rate) < 0.01 items.append(.action(ContextMenuActionItem(text: text, icon: { theme in if isSelected { @@ -2127,6 +2160,10 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } videoNode.setBaseRate(rate) + + if let controller = strongSelf.galleryController() as? GalleryController { + controller.updateSharedPlaybackRate(rate) + } }))) } @@ -2198,6 +2235,16 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { override func footerContent() -> Signal<(GalleryFooterContentNode?, GalleryOverlayContentNode?), NoError> { return .single((self.footerContentNode, nil)) } + + func updatePlaybackRate(_ playbackRate: Double?) { + self.playbackRate = playbackRate + + if let playbackRate = self.playbackRate { + self.videoNode?.setBaseRate(playbackRate) + } + + self.playbackRatePromise.set(self.playbackRate ?? 1.0) + } } private final class HeaderContextReferenceContentSource: ContextReferenceContentSource { diff --git a/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift b/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift index 53dc555da1..b8d108ed4b 100644 --- a/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift +++ b/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift @@ -237,17 +237,8 @@ public final class SecretMediaPreviewController: ViewController { strongSelf._hiddenMedia.set(.single(nil)) let animatedOutNode = true - var animatedOutInterface = false - - let completion = { - if animatedOutNode && animatedOutInterface { - //self?.presentingViewController?.dismiss(animated: false, completion: nil) - } - } strongSelf.controllerNode.animateOut(animateContent: animatedOutNode, completion: { - animatedOutInterface = true - //completion() }) } } @@ -437,7 +428,7 @@ public final class SecretMediaPreviewController: ViewController { } } - guard let item = galleryItemForEntry(context: self.context, presentationData: self.presentationData, entry: MessageHistoryEntry(message: message, isRead: false, location: nil, monthLocation: nil, attributes: MutableMessageHistoryEntryAttributes(authorIsContact: false)), streamVideos: false, hideControls: true, isSecret: true, tempFilePath: tempFilePath, playbackCompleted: { [weak self] in + guard let item = galleryItemForEntry(context: self.context, presentationData: self.presentationData, entry: MessageHistoryEntry(message: message, isRead: false, location: nil, monthLocation: nil, attributes: MutableMessageHistoryEntryAttributes(authorIsContact: false)), streamVideos: false, hideControls: true, isSecret: true, playbackRate: { nil }, tempFilePath: tempFilePath, playbackCompleted: { [weak self] in self?.dismiss(forceAway: false) }, present: { _, _ in }) else { self._ready.set(.single(true)) diff --git a/submodules/GameUI/BUILD b/submodules/GameUI/BUILD index b12e125010..1ea06ab121 100644 --- a/submodules/GameUI/BUILD +++ b/submodules/GameUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/Geocoding/BUILD b/submodules/Geocoding/BUILD index d03fe14734..35ce472ddf 100644 --- a/submodules/Geocoding/BUILD +++ b/submodules/Geocoding/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", ], diff --git a/submodules/GlassButtonNode/BUILD b/submodules/GlassButtonNode/BUILD index 7a35f60ab4..bab4d6718e 100644 --- a/submodules/GlassButtonNode/BUILD +++ b/submodules/GlassButtonNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/GradientBackground/BUILD b/submodules/GradientBackground/BUILD index f216710644..a84790b04b 100644 --- a/submodules/GradientBackground/BUILD +++ b/submodules/GradientBackground/BUILD @@ -8,6 +8,7 @@ swift_library( ]), copts = [ "-O", + "-warnings-as-errors", ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/GraphCore/BUILD b/submodules/GraphCore/BUILD index 10c347161d..058b5faf80 100644 --- a/submodules/GraphCore/BUILD +++ b/submodules/GraphCore/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Display:Display", ], diff --git a/submodules/GraphCore/Sources/Charts/Controllers/Stacked Bars/BarsComponentController.swift b/submodules/GraphCore/Sources/Charts/Controllers/Stacked Bars/BarsComponentController.swift index f92dce8b73..fb586a4d37 100644 --- a/submodules/GraphCore/Sources/Charts/Controllers/Stacked Bars/BarsComponentController.swift +++ b/submodules/GraphCore/Sources/Charts/Controllers/Stacked Bars/BarsComponentController.swift @@ -216,7 +216,6 @@ class BarsComponentController: GeneralChartComponentController { } override func showDetailsView(at chartPosition: CGFloat, detailsViewPosition: CGFloat, dataIndex: Int, date: Date, animated: Bool, feedback: Bool) { - let rangeWithOffset = detailsViewPosition - barsWidth / currentHorizontalMainChartRange.distance * chartFrame().width / 2 + chartFrame().minX / 2 super.showDetailsView(at: chartPosition, detailsViewPosition: detailsViewPosition, dataIndex: dataIndex, date: date, animated: animated, feedback: feedback) mainBarsRenderer.setSelectedIndex(dataIndex, animated: true) } diff --git a/submodules/GraphCore/Sources/Charts/Renderes/BaseChartRenderer.swift b/submodules/GraphCore/Sources/Charts/Renderes/BaseChartRenderer.swift index fa9ce5bc4c..6c920496b2 100644 --- a/submodules/GraphCore/Sources/Charts/Renderes/BaseChartRenderer.swift +++ b/submodules/GraphCore/Sources/Charts/Renderes/BaseChartRenderer.swift @@ -13,7 +13,7 @@ import Cocoa import UIKit #endif -public protocol ChartViewRenderer: class { +public protocol ChartViewRenderer: AnyObject { var containerViews: [GView] { get set } func render(context: CGContext, bounds: CGRect, chartFrame: CGRect) } diff --git a/submodules/GraphCore/Sources/Charts/Renderes/LinesChartRenderer.swift b/submodules/GraphCore/Sources/Charts/Renderes/LinesChartRenderer.swift index b974f7ae51..949ffacc88 100644 --- a/submodules/GraphCore/Sources/Charts/Renderes/LinesChartRenderer.swift +++ b/submodules/GraphCore/Sources/Charts/Renderes/LinesChartRenderer.swift @@ -76,7 +76,6 @@ class LinesChartRenderer: BaseChartRenderer { if linesShapeAnimator.isAnimating { let animationOffset = linesShapeAnimator.current - let path = CGMutablePath() let fromPoints = fromLines.safeElement(at: index)?.points ?? [] let toPoints = toLines.safeElement(at: index)?.points ?? [] diff --git a/submodules/GraphCore/Sources/Charts/Renderes/PecentChartRenderer.swift b/submodules/GraphCore/Sources/Charts/Renderes/PecentChartRenderer.swift index 7c402941b2..c65487ec42 100644 --- a/submodules/GraphCore/Sources/Charts/Renderes/PecentChartRenderer.swift +++ b/submodules/GraphCore/Sources/Charts/Renderes/PecentChartRenderer.swift @@ -52,7 +52,7 @@ class PecentChartRenderer: BaseChartRenderer { let range = renderRange(bounds: bounds, chartFrame: chartFrame) - var paths: [CGMutablePath] = percentageData.components.map { _ in CGMutablePath() } + let paths: [CGMutablePath] = percentageData.components.map { _ in CGMutablePath() } var vertices: [CGFloat] = Array(repeating: 0, count: percentageData.components.count) if var locationIndex = percentageData.locations.firstIndex(where: { $0 > range.lowerBound }) { diff --git a/submodules/GraphCore/Sources/Charts/Renderes/PercentPieAnimationRenderer.swift b/submodules/GraphCore/Sources/Charts/Renderes/PercentPieAnimationRenderer.swift index dfa0ff3c97..23f11eb0af 100644 --- a/submodules/GraphCore/Sources/Charts/Renderes/PercentPieAnimationRenderer.swift +++ b/submodules/GraphCore/Sources/Charts/Renderes/PercentPieAnimationRenderer.swift @@ -125,7 +125,6 @@ class PercentPieAnimationRenderer: BaseChartRenderer { offset: animationFractionD) let startDestinationPoint = lineCenterPoint + CGPoint(x: destinationRadius, y: 0) - let centerDestinationPoint = lineCenterPoint + CGPoint(x: 0, y: destinationRadius) let endDestinationPoint = lineCenterPoint + CGPoint(x: -destinationRadius, y: 0) let initialStartDestinationAngle: CGFloat = 0 let initialCenterDestinationAngle: CGFloat = .pi / 2 diff --git a/submodules/GraphCore/Sources/Helpers/DisplayLinkService.swift b/submodules/GraphCore/Sources/Helpers/DisplayLinkService.swift index 89f98b23b2..e040b4b6e6 100644 --- a/submodules/GraphCore/Sources/Helpers/DisplayLinkService.swift +++ b/submodules/GraphCore/Sources/Helpers/DisplayLinkService.swift @@ -14,7 +14,7 @@ import UIKit #endif import CoreGraphics -public protocol DisplayLinkListner: class { +public protocol DisplayLinkListner: AnyObject { func update(delta: TimeInterval) } diff --git a/submodules/GraphUI/BUILD b/submodules/GraphUI/BUILD index 376406b82d..fc10bc1cb3 100644 --- a/submodules/GraphUI/BUILD +++ b/submodules/GraphUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/GridMessageSelectionNode/BUILD b/submodules/GridMessageSelectionNode/BUILD index 1d24313555..0f49aaa07e 100644 --- a/submodules/GridMessageSelectionNode/BUILD +++ b/submodules/GridMessageSelectionNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/HashtagSearchUI/BUILD b/submodules/HashtagSearchUI/BUILD index 76911cd41d..5a466af616 100644 --- a/submodules/HashtagSearchUI/BUILD +++ b/submodules/HashtagSearchUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift index 2976f02ba7..1e80d08b49 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift @@ -48,7 +48,7 @@ public final class HashtagSearchController: TelegramBaseController { return result.messages.map({ .message($0, RenderedPeer(message: $0), result.readStates[$0.id.peerId], chatListPresentationData, result.totalCount, nil, false) }) } let interaction = ChatListNodeInteraction(activateSearch: { - }, peerSelected: { _, _ in + }, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in diff --git a/submodules/HexColor/BUILD b/submodules/HexColor/BUILD index faf074d046..4dfd5a4ec7 100644 --- a/submodules/HexColor/BUILD +++ b/submodules/HexColor/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TextFormat:TextFormat", ], diff --git a/submodules/HorizontalPeerItem/BUILD b/submodules/HorizontalPeerItem/BUILD index 6fcfc15a69..1bce1aa70f 100644 --- a/submodules/HorizontalPeerItem/BUILD +++ b/submodules/HorizontalPeerItem/BUILD @@ -6,9 +6,11 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", - "//submodules/Postbox:Postbox", "//submodules/TelegramCore:TelegramCore", "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift b/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift index fdb191dd2e..cba0bc85fa 100644 --- a/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift +++ b/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift @@ -1,7 +1,6 @@ import Foundation import UIKit import Display -import Postbox import AsyncDisplayKit import TelegramCore import SwiftSignalKit @@ -24,15 +23,15 @@ public final class HorizontalPeerItem: ListViewItem { let strings: PresentationStrings let mode: HorizontalPeerItemMode let context: AccountContext - public let peer: Peer - let action: (Peer) -> Void - let contextAction: (Peer, ASDisplayNode, ContextGesture?) -> Void - let isPeerSelected: (PeerId) -> Bool + public let peer: EnginePeer + let action: (EnginePeer) -> Void + let contextAction: (EnginePeer, ASDisplayNode, ContextGesture?) -> Void + let isPeerSelected: (EnginePeer.Id) -> Bool let customWidth: CGFloat? - let presence: PeerPresence? + let presence: EnginePeer.Presence? let unreadBadge: (Int32, Bool)? - public init(theme: PresentationTheme, strings: PresentationStrings, mode: HorizontalPeerItemMode, context: AccountContext, peer: Peer, presence: PeerPresence?, unreadBadge: (Int32, Bool)?, action: @escaping (Peer) -> Void, contextAction: @escaping (Peer, ASDisplayNode, ContextGesture?) -> Void, isPeerSelected: @escaping (PeerId) -> Bool, customWidth: CGFloat?) { + public init(theme: PresentationTheme, strings: PresentationStrings, mode: HorizontalPeerItemMode, context: AccountContext, peer: EnginePeer, presence: EnginePeer.Presence?, unreadBadge: (Int32, Bool)?, action: @escaping (EnginePeer) -> Void, contextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?) -> Void, isPeerSelected: @escaping (EnginePeer.Id) -> Bool, customWidth: CGFloat?) { self.theme = theme self.strings = strings self.mode = mode @@ -163,7 +162,7 @@ public final class HorizontalPeerItemNode: ListViewItemNode { var online = false let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 - if let peer = item.peer as? TelegramUser, let presence = item.presence as? TelegramUserPresence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) { + if case let .user(peer) = item.peer, let presence = item.presence, !item.peer.isService, !peer.flags.contains(.isSupport) { let relativeStatus = relativeUserPresenceStatus(presence, relativeTo: Int32(timestamp)) if case .online = relativeStatus { online = true @@ -187,7 +186,7 @@ public final class HorizontalPeerItemNode: ListViewItemNode { if let strongSelf = self { strongSelf.item = item strongSelf.peerNode.theme = itemTheme - strongSelf.peerNode.setup(context: item.context, theme: item.theme, strings: item.strings, peer: RenderedPeer(peer: item.peer), numberOfLines: 1, synchronousLoad: synchronousLoads) + strongSelf.peerNode.setup(context: item.context, theme: item.theme, strings: item.strings, peer: EngineRenderedPeer(peer: item.peer), numberOfLines: 1, synchronousLoad: synchronousLoads) strongSelf.peerNode.frame = CGRect(origin: CGPoint(), size: itemLayout.size) strongSelf.peerNode.updateSelection(selected: item.isPeerSelected(item.peer.id), animated: false) diff --git a/submodules/ImageBlur/BUILD b/submodules/ImageBlur/BUILD index bb486d2964..08d7c3d40a 100644 --- a/submodules/ImageBlur/BUILD +++ b/submodules/ImageBlur/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/FastBlur:FastBlur", ], diff --git a/submodules/ImageCompression/BUILD b/submodules/ImageCompression/BUILD index 752daa198e..fe40c802c3 100644 --- a/submodules/ImageCompression/BUILD +++ b/submodules/ImageCompression/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/MozjpegBinding:MozjpegBinding", ], diff --git a/submodules/ImageTransparency/BUILD b/submodules/ImageTransparency/BUILD index acae43de49..19c5699fcb 100644 --- a/submodules/ImageTransparency/BUILD +++ b/submodules/ImageTransparency/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + #"-warnings-as-errors", + ], deps = [ "//submodules/Display:Display", ], diff --git a/submodules/ImageTransparency/Sources/ImageTransparency.swift b/submodules/ImageTransparency/Sources/ImageTransparency.swift index 3283567641..8f73dcbd9e 100644 --- a/submodules/ImageTransparency/Sources/ImageTransparency.swift +++ b/submodules/ImageTransparency/Sources/ImageTransparency.swift @@ -29,7 +29,7 @@ private func generateHistogram(cgImage: CGImage) -> ([[vImagePixelCount]], Int)? } assert(error == kvImageNoError) - let histogramBins = (0...3).map { _ in + let histogramBins: [[vImagePixelCount]] = (0...3).map { _ in return [vImagePixelCount](repeating: 0, count: 256) } var mutableHistogram: [UnsafeMutablePointer?] = histogramBins.map { diff --git a/submodules/ImportStickerPackUI/BUILD b/submodules/ImportStickerPackUI/BUILD index 38960140a3..0c44d12e08 100644 --- a/submodules/ImportStickerPackUI/BUILD +++ b/submodules/ImportStickerPackUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift b/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift index 5e0010ada7..e84e2685b1 100644 --- a/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift +++ b/submodules/ImportStickerPackUI/Sources/ImportStickerPackControllerNode.swift @@ -694,10 +694,7 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, UIScroll } } } - }, error: { [weak self] error in - if let strongSelf = self { - - } + }, error: { _ in })) } diff --git a/submodules/ImportStickerPackUI/Sources/ImportStickerPackTitleController.swift b/submodules/ImportStickerPackUI/Sources/ImportStickerPackTitleController.swift index 1b3ce7f95f..9ffc1207f0 100644 --- a/submodules/ImportStickerPackUI/Sources/ImportStickerPackTitleController.swift +++ b/submodules/ImportStickerPackUI/Sources/ImportStickerPackTitleController.swift @@ -105,7 +105,7 @@ private class TextField: UITextField, UIScrollViewDelegate { } func selectWhole() { - guard let scrollView = self.scrollView else { + guard let _ = self.scrollView else { return } // if scrollView.contentSize.width > scrollView.frame.width - scrollView.contentInset.left { @@ -813,7 +813,7 @@ func importStickerPackShortNameController(context: AccountContext, title: String contentNode?.inputFieldNode.updateTheme(presentationData.theme) }) let checkDisposable = MetaDisposable() - var value = value ?? "" + let value = value ?? "" contentNode.actionNodes.last?.actionEnabled = !value.isEmpty if !value.isEmpty { Queue.mainQueue().after(0.25) { diff --git a/submodules/InstantPageCache/BUILD b/submodules/InstantPageCache/BUILD index 1dd01739ff..101fd0f887 100644 --- a/submodules/InstantPageCache/BUILD +++ b/submodules/InstantPageCache/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramCore:TelegramCore", "//submodules/Postbox:Postbox", diff --git a/submodules/InstantPageUI/BUILD b/submodules/InstantPageUI/BUILD index 1a19aa0ed7..75a726f91c 100644 --- a/submodules/InstantPageUI/BUILD +++ b/submodules/InstantPageUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/InstantPageUI/Sources/InstantPageContentNode.swift b/submodules/InstantPageUI/Sources/InstantPageContentNode.swift index 30026bb0e6..ca5bf723df 100644 --- a/submodules/InstantPageUI/Sources/InstantPageContentNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageContentNode.swift @@ -179,7 +179,7 @@ final class InstantPageContentNode : ASDisplayNode { var itemNode = self.visibleItemsWithNodes[itemIndex] if let currentItemNode = itemNode { if !item.matchesNode(currentItemNode) { - (currentItemNode as! ASDisplayNode).removeFromSupernode() + currentItemNode.removeFromSupernode() self.visibleItemsWithNodes.removeValue(forKey: itemIndex) itemNode = nil } @@ -221,9 +221,9 @@ final class InstantPageContentNode : ASDisplayNode { } } } else { - if (itemNode as! ASDisplayNode).frame != itemFrame { - transition.updateFrame(node: (itemNode as! ASDisplayNode), frame: itemFrame) - itemNode?.updateLayout(size: itemFrame.size, transition: transition) + if let itemNode = itemNode, itemNode.frame != itemFrame { + transition.updateFrame(node: itemNode, frame: itemFrame) + itemNode.updateLayout(size: itemFrame.size, transition: transition) } } @@ -279,9 +279,9 @@ final class InstantPageContentNode : ASDisplayNode { for (index, itemNode) in self.visibleItemsWithNodes { if !visibleItemIndices.contains(index) { removeItemIndices.append(index) - (itemNode as! ASDisplayNode).removeFromSupernode() + itemNode.removeFromSupernode() } else { - var itemFrame = (itemNode as! ASDisplayNode).frame + var itemFrame = itemNode.frame let itemThreshold: CGFloat = 200.0 itemFrame.origin.y -= itemThreshold itemFrame.size.height += itemThreshold * 2.0 diff --git a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift index abd1348083..685cfd3e9d 100644 --- a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift @@ -544,7 +544,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { var itemNode = self.visibleItemsWithNodes[itemIndex] if let currentItemNode = itemNode { if !item.matchesNode(currentItemNode) { - (currentItemNode as! ASDisplayNode).removeFromSupernode() + currentItemNode.removeFromSupernode() self.visibleItemsWithNodes.removeValue(forKey: itemIndex) itemNode = nil } @@ -619,9 +619,9 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { } } } else { - if (itemNode as! ASDisplayNode).frame != itemFrame { - transition.updateFrame(node: (itemNode as! ASDisplayNode), frame: itemFrame) - itemNode?.updateLayout(size: itemFrame.size, transition: transition) + if let itemNode = itemNode, itemNode.frame != itemFrame { + transition.updateFrame(node: itemNode, frame: itemFrame) + itemNode.updateLayout(size: itemFrame.size, transition: transition) } } @@ -689,9 +689,9 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { for (index, itemNode) in self.visibleItemsWithNodes { if !visibleItemIndices.contains(index) { removeItemIndices.append(index) - (itemNode as! ASDisplayNode).removeFromSupernode() + itemNode.removeFromSupernode() } else { - var itemFrame = (itemNode as! ASDisplayNode).frame + var itemFrame = itemNode.frame let itemThreshold: CGFloat = 200.0 itemFrame.origin.y -= itemThreshold itemFrame.size.height += itemThreshold * 2.0 @@ -725,7 +725,6 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { } let bounds = self.scrollNode.view.bounds - let contentOffset = self.scrollNode.view.contentOffset let maxBarHeight: CGFloat let minBarHeight: CGFloat @@ -741,7 +740,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { minBarHeight = 20.0 } - var transition: ContainedViewLayoutTransition = .immediate + let transition: ContainedViewLayoutTransition = .immediate var navigationBarFrame = self.navigationBar.frame navigationBarFrame.size.width = bounds.size.width if navigationBarFrame.size.height.isZero { diff --git a/submodules/InstantPageUI/Sources/InstantPageGalleryController.swift b/submodules/InstantPageUI/Sources/InstantPageGalleryController.swift index 4ff0a69ed9..4c0e812516 100644 --- a/submodules/InstantPageUI/Sources/InstantPageGalleryController.swift +++ b/submodules/InstantPageUI/Sources/InstantPageGalleryController.swift @@ -112,7 +112,7 @@ public struct InstantPageGalleryEntry: Equatable { nativeId = .instantPage(self.pageId, file.fileId) } - return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: NativeVideoContent(id: nativeId, fileReference: .webPage(webPage: WebpageReference(webPage), media: file), streamVideo: isMediaStreamable(media: file) ? .conservative : .none), originData: nil, indexData: indexData, contentInfo: .webPage(webPage, file, nil), caption: caption, credit: credit, fromPlayingVideo: fromPlayingVideo, landscape: landscape, performAction: { _ in }, openActionOptions: { _, _ in }, storeMediaPlaybackState: { _, _, _ in }, present: { _, _ in }) + return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: NativeVideoContent(id: nativeId, fileReference: .webPage(webPage: WebpageReference(webPage), media: file), streamVideo: isMediaStreamable(media: file) ? .conservative : .none), originData: nil, indexData: indexData, contentInfo: .webPage(webPage, file, nil), caption: caption, credit: credit, fromPlayingVideo: fromPlayingVideo, landscape: landscape, playbackRate: { nil }, performAction: { _ in }, openActionOptions: { _, _ in }, storeMediaPlaybackState: { _, _, _ in }, present: { _, _ in }) } else { var representations: [TelegramMediaImageRepresentation] = [] representations.append(contentsOf: file.previewRepresentations) @@ -134,12 +134,12 @@ public struct InstantPageGalleryEntry: Equatable { present(gallery, InstantPageGalleryControllerPresentationArguments(transitionArguments: { entry -> GalleryTransitionArguments? in return makeArguments() })) - }), caption: NSAttributedString(string: ""), fromPlayingVideo: fromPlayingVideo, landscape: landscape, performAction: { _ in }, openActionOptions: { _, _ in }, storeMediaPlaybackState: { _, _, _ in }, present: { _, _ in }) + }), caption: NSAttributedString(string: ""), fromPlayingVideo: fromPlayingVideo, landscape: landscape, playbackRate: { nil }, performAction: { _ in }, openActionOptions: { _, _ in }, storeMediaPlaybackState: { _, _, _ in }, present: { _, _ in }) } else { if let content = WebEmbedVideoContent(webPage: embedWebpage, webpageContent: webpageContent, openUrl: { url in }) { - return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: nil, indexData: nil, contentInfo: .webPage(webPage, embedWebpage, nil), caption: NSAttributedString(string: ""), fromPlayingVideo: fromPlayingVideo, landscape: landscape, performAction: { _ in }, openActionOptions: { _, _ in }, storeMediaPlaybackState: { _, _, _ in }, present: { _, _ in }) + return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: nil, indexData: nil, contentInfo: .webPage(webPage, embedWebpage, nil), caption: NSAttributedString(string: ""), fromPlayingVideo: fromPlayingVideo, landscape: landscape, playbackRate: { nil }, performAction: { _ in }, openActionOptions: { _, _ in }, storeMediaPlaybackState: { _, _, _ in }, present: { _, _ in }) } else { preconditionFailure() } diff --git a/submodules/InstantPageUI/Sources/InstantPageLayout.swift b/submodules/InstantPageUI/Sources/InstantPageLayout.swift index a802fc10a2..4f157f5e4f 100644 --- a/submodules/InstantPageUI/Sources/InstantPageLayout.swift +++ b/submodules/InstantPageUI/Sources/InstantPageLayout.swift @@ -251,7 +251,7 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins let (textItem, textItems, textItemSize) = layoutTextItemWithString(attributedStringForRichText(text, styleStack: styleStack), boundingWidth: boundingWidth - horizontalInset * 2.0 - indexSpacing - maxIndexWidth, offset: CGPoint(x: horizontalInset + indexSpacing + maxIndexWidth, y: contentSize.height), media: media, webpage: webpage) contentSize.height += textItemSize.height - var indexItem = indexItems[i] + let indexItem = indexItems[i] var itemFrame = indexItem.frame var lineMidY: CGFloat = 0.0 @@ -286,7 +286,7 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins contentSize.height += subLayout.contentSize.height + spacing previousBlock = subBlock } - var indexItem = indexItems[i] + let indexItem = indexItems[i] var indexItemFrame = indexItem.frame if let textIndexItem = indexItem as? InstantPageTextItem, let line = textIndexItem.lines.first { indexItemFrame = indexItemFrame.offsetBy(dx: horizontalInset + maxIndexWidth - line.frame.width, dy: originY) @@ -412,7 +412,7 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins } else { return InstantPageLayout(origin: CGPoint(), contentSize: CGSize(), items: []) } - case let .video(id, caption, autoplay, loop): + case let .video(id, caption, autoplay, _): if let file = media[id] as? TelegramMediaFile, let dimensions = file.dimensions { let imageSize = dimensions var filledSize = imageSize.cgSize.aspectFitted(CGSize(width: boundingWidth - safeInset * 2.0, height: 1200.0)) @@ -489,7 +489,7 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins contentSize.height += captionSize.height return InstantPageLayout(origin: CGPoint(), contentSize: contentSize, items: items) - case let .postEmbed(url, webpageId, avatarId, author, date, blocks, caption): + case let .postEmbed(_, _, avatarId, author, date, blocks, caption): var contentSize = CGSize(width: boundingWidth, height: 0.0) let lineInset: CGFloat = 20.0 let verticalInset: CGFloat = 4.0 diff --git a/submodules/InstantPageUI/Sources/InstantPageScrollableNode.swift b/submodules/InstantPageUI/Sources/InstantPageScrollableNode.swift index ab221dac9f..30c1239ee8 100644 --- a/submodules/InstantPageUI/Sources/InstantPageScrollableNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageScrollableNode.swift @@ -6,7 +6,7 @@ import Postbox import Display import TelegramPresentationData -protocol InstantPageScrollableItem: class, InstantPageItem { +protocol InstantPageScrollableItem: AnyObject, InstantPageItem { var contentSize: CGSize { get } var horizontalInset: CGFloat { get } var isRTL: Bool { get } diff --git a/submodules/InstantPageUI/Sources/InstantPageSlideshowItemNode.swift b/submodules/InstantPageUI/Sources/InstantPageSlideshowItemNode.swift index 72b85c6bb2..a45ee2f1eb 100644 --- a/submodules/InstantPageUI/Sources/InstantPageSlideshowItemNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageSlideshowItemNode.swift @@ -183,7 +183,7 @@ private final class InstantPageSlideshowPagerNode: ASDisplayNode, UIScrollViewDe let contentNode: ASDisplayNode if let _ = media.media as? TelegramMediaImage { contentNode = InstantPageImageNode(context: self.context, sourcePeerType: self.sourcePeerType, theme: self.theme, webPage: self.webPage, media: media, attributes: [], interactive: true, roundCorners: false, fit: false, openMedia: self.openMedia, longPressMedia: self.longPressMedia, activatePinchPreview: nil, pinchPreviewFinished: nil) - } else if let file = media.media as? TelegramMediaFile { + } else if let _ = media.media as? TelegramMediaFile { contentNode = ASDisplayNode() } else { contentNode = ASDisplayNode() @@ -233,7 +233,7 @@ private final class InstantPageSlideshowPagerNode: ASDisplayNode, UIScrollViewDe if self.itemNodes.isEmpty { let node = self.makeNodeForItem(at: self.centralItemIndex ?? 0) node.frame = CGRect(origin: CGPoint(), size: scrollView.bounds.size) - if let containerLayout = self.containerLayout { + if let _ = self.containerLayout { //node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate) } self.addVisibleItemNode(node) @@ -248,7 +248,7 @@ private final class InstantPageSlideshowPagerNode: ASDisplayNode, UIScrollViewDe if self.visibleItemNode(at: centralItemIndex - 1) == nil { let node = self.makeNodeForItem(at: centralItemIndex - 1) node.frame = centralItemNode.frame.offsetBy(dx: -centralItemNode.frame.size.width - self.pageGap, dy: 0.0) - if let containerLayout = self.containerLayout { + if let _ = self.containerLayout { //node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate) } self.addVisibleItemNode(node) @@ -259,7 +259,7 @@ private final class InstantPageSlideshowPagerNode: ASDisplayNode, UIScrollViewDe if self.visibleItemNode(at: centralItemIndex + 1) == nil { let node = self.makeNodeForItem(at: centralItemIndex + 1) node.frame = centralItemNode.frame.offsetBy(dx: centralItemNode.frame.size.width + self.pageGap, dy: 0.0) - if let containerLayout = self.containerLayout { + if let _ = self.containerLayout { //node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate) } self.addVisibleItemNode(node) @@ -291,7 +291,7 @@ private final class InstantPageSlideshowPagerNode: ASDisplayNode, UIScrollViewDe if self.visibleItemNode(at: centralItemCandidateNode.index - 1) == nil { let node = self.makeNodeForItem(at: centralItemCandidateNode.index - 1) node.frame = centralItemCandidateNode.frame.offsetBy(dx: -centralItemCandidateNode.frame.size.width - self.pageGap, dy: 0.0) - if let containerLayout = self.containerLayout { + if let _ = self.containerLayout { //node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate) } self.addVisibleItemNode(node) @@ -302,7 +302,7 @@ private final class InstantPageSlideshowPagerNode: ASDisplayNode, UIScrollViewDe if self.visibleItemNode(at: centralItemCandidateNode.index + 1) == nil { let node = self.makeNodeForItem(at: centralItemCandidateNode.index + 1) node.frame = centralItemCandidateNode.frame.offsetBy(dx: centralItemCandidateNode.frame.size.width + self.pageGap, dy: 0.0) - if let containerLayout = self.containerLayout { + if let _ = self.containerLayout { //node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate) } self.addVisibleItemNode(node) diff --git a/submodules/InstantPageUI/Sources/InstantPageSubContentNode.swift b/submodules/InstantPageUI/Sources/InstantPageSubContentNode.swift index 15312dfe68..278a7d6875 100644 --- a/submodules/InstantPageUI/Sources/InstantPageSubContentNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageSubContentNode.swift @@ -179,7 +179,7 @@ final class InstantPageSubContentNode : ASDisplayNode { var itemNode = self.visibleItemsWithNodes[itemIndex] if let currentItemNode = itemNode { if !item.matchesNode(currentItemNode) { - (currentItemNode as! ASDisplayNode).removeFromSupernode() + currentItemNode.removeFromSupernode() self.visibleItemsWithNodes.removeValue(forKey: itemIndex) itemNode = nil } @@ -218,9 +218,9 @@ final class InstantPageSubContentNode : ASDisplayNode { } } } else { - if (itemNode as! ASDisplayNode).frame != itemFrame { - transition.updateFrame(node: (itemNode as! ASDisplayNode), frame: itemFrame) - itemNode?.updateLayout(size: itemFrame.size, transition: transition) + if let itemNode = itemNode, itemNode.frame != itemFrame { + transition.updateFrame(node: itemNode, frame: itemFrame) + itemNode.updateLayout(size: itemFrame.size, transition: transition) } } @@ -276,9 +276,9 @@ final class InstantPageSubContentNode : ASDisplayNode { for (index, itemNode) in self.visibleItemsWithNodes { if !visibleItemIndices.contains(index) { removeItemIndices.append(index) - (itemNode as! ASDisplayNode).removeFromSupernode() + itemNode.removeFromSupernode() } else { - var itemFrame = (itemNode as! ASDisplayNode).frame + var itemFrame = itemNode.frame let itemThreshold: CGFloat = 200.0 itemFrame.origin.y -= itemThreshold itemFrame.size.height += itemThreshold * 2.0 diff --git a/submodules/InviteLinksUI/BUILD b/submodules/InviteLinksUI/BUILD index e2a50b05fe..3e7f32d48a 100644 --- a/submodules/InviteLinksUI/BUILD +++ b/submodules/InviteLinksUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift b/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift index 702b661f9f..eeb6996255 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift @@ -736,7 +736,6 @@ public final class InviteLinkInviteController: ViewController { let rawControlsOffset = offset + listTopInset - controlsHeight let controlsOffset = max(layoutTopInset, rawControlsOffset) - let isOverscrolling = rawControlsOffset <= layoutTopInset let controlsFrame = CGRect(origin: CGPoint(x: 0.0, y: controlsOffset), size: CGSize(width: validLayout.size.width, height: controlsHeight)) let previousFrame = self.headerNode.frame diff --git a/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift b/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift index 7b744119e6..48214031d8 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift @@ -979,7 +979,6 @@ public final class InviteLinkViewController: ViewController { let rawControlsOffset = offset + listTopInset - controlsHeight let controlsOffset = max(layoutTopInset, rawControlsOffset) - let isOverscrolling = rawControlsOffset <= layoutTopInset let controlsFrame = CGRect(origin: CGPoint(x: 0.0, y: controlsOffset), size: CGSize(width: validLayout.size.width, height: controlsHeight)) let previousFrame = self.headerNode.frame diff --git a/submodules/InviteLinksUI/Sources/ItemListDatePickerItem.swift b/submodules/InviteLinksUI/Sources/ItemListDatePickerItem.swift index a67088f9a8..fb9fe12d82 100644 --- a/submodules/InviteLinksUI/Sources/ItemListDatePickerItem.swift +++ b/submodules/InviteLinksUI/Sources/ItemListDatePickerItem.swift @@ -123,7 +123,6 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode { let itemSeparatorColor: UIColor let leftInset = 16.0 + params.leftInset - let rightInset = 16.0 + params.rightInset let width = min(390.0, params.width - params.leftInset - params.rightInset) let cellSize = floor((width - 12.0 * 2.0) / 7.0) diff --git a/submodules/InviteLinksUI/Sources/ItemListPermanentInviteLinkItem.swift b/submodules/InviteLinksUI/Sources/ItemListPermanentInviteLinkItem.swift index 15c89afb94..a95c3d01c0 100644 --- a/submodules/InviteLinksUI/Sources/ItemListPermanentInviteLinkItem.swift +++ b/submodules/InviteLinksUI/Sources/ItemListPermanentInviteLinkItem.swift @@ -322,7 +322,7 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem let (invitedPeersLayout, invitedPeersApply) = makeInvitedPeersLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: subtitle, font: titleFont, textColor: subtitleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - 20.0 - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - let avatarsContent = avatarsContext.update(peers: item.peers, animated: false) + let avatarsContent = avatarsContext.update(peers: item.peers.map(EnginePeer.init), animated: false) let verticalInset: CGFloat = 16.0 let fieldHeight: CGFloat = 52.0 diff --git a/submodules/ItemListAddressItem/BUILD b/submodules/ItemListAddressItem/BUILD index 76423972ee..590b8aaa22 100644 --- a/submodules/ItemListAddressItem/BUILD +++ b/submodules/ItemListAddressItem/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/ItemListAvatarAndNameInfoItem/BUILD b/submodules/ItemListAvatarAndNameInfoItem/BUILD index 609528ece4..dbf1c6e68d 100644 --- a/submodules/ItemListAvatarAndNameInfoItem/BUILD +++ b/submodules/ItemListAvatarAndNameInfoItem/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift b/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift index 714ca9efdb..daff7c21dd 100644 --- a/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift +++ b/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift @@ -48,7 +48,7 @@ public enum ItemListAvatarAndNameInfoItemName: Equatable { public var composedTitle: String { switch self { - case let .personName(firstName, lastName, phone): + case let .personName(firstName, lastName, _): if !firstName.isEmpty && !lastName.isEmpty { return firstName + " " + lastName } else if !firstName.isEmpty { @@ -77,16 +77,6 @@ public enum ItemListAvatarAndNameInfoItemName: Equatable { } else { return strings.User_DeletedAccount } - - if !firstName.isEmpty && !lastName.isEmpty { - return firstName + " " + lastName - } else if !firstName.isEmpty { - return firstName - } else if !lastName.isEmpty { - return lastName - } else { - return strings.User_DeletedAccount - } case let .title(title, _): return title } @@ -676,7 +666,7 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo overrideImage = .deletedIcon } - strongSelf.avatarNode.setPeer(context: item.accountContext, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: ignoreEmpty ? nil : item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads) + strongSelf.avatarNode.setPeer(context: item.accountContext, theme: item.presentationData.theme, peer: EnginePeer(peer), overrideImage: overrideImage, emptyColor: ignoreEmpty ? nil : item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads) } let avatarFrame = CGRect(origin: CGPoint(x: params.leftInset + 15.0, y: floor((layout.contentSize.height - 66.0) / 2.0)), size: CGSize(width: 66.0, height: 66.0)) diff --git a/submodules/ItemListPeerActionItem/BUILD b/submodules/ItemListPeerActionItem/BUILD index 76e9eece2a..520700b146 100644 --- a/submodules/ItemListPeerActionItem/BUILD +++ b/submodules/ItemListPeerActionItem/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/ItemListPeerItem/BUILD b/submodules/ItemListPeerItem/BUILD index 9d3ee832e8..82417c0e2f 100644 --- a/submodules/ItemListPeerItem/BUILD +++ b/submodules/ItemListPeerItem/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift index 1e69a77038..477e89f4ce 100644 --- a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift +++ b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift @@ -1106,15 +1106,15 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo transition.updateFrame(node: strongSelf.avatarNode, frame: avatarFrame) if item.peer.id == item.context.account.peerId, case .threatSelfAsSaved = item.aliasHandling { - strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, overrideImage: .savedMessagesIcon, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad) + strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: EnginePeer(item.peer), overrideImage: .savedMessagesIcon, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad) } else if item.peer.id.isReplies { - strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, overrideImage: .repliesIcon, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad) + strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: EnginePeer(item.peer), overrideImage: .repliesIcon, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad) } else { var overrideImage: AvatarNodeImageOverride? if item.peer.isDeleted { overrideImage = .deletedIcon } - strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad) + strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: EnginePeer(item.peer), overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad) } strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: layout.contentSize.height + UIScreenPixel + UIScreenPixel)) diff --git a/submodules/ItemListStickerPackItem/BUILD b/submodules/ItemListStickerPackItem/BUILD index 254167a8b7..5752bd50bc 100644 --- a/submodules/ItemListStickerPackItem/BUILD +++ b/submodules/ItemListStickerPackItem/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift index 514e53971a..2b090b5ecc 100644 --- a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift +++ b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift @@ -643,7 +643,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode { strongSelf.selectionIconNode.image = image strongSelf.selectionIconNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - image.size.width - floor((44.0 - image.size.width) / 2.0), y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size) } - case let .check(checked): + case .check: strongSelf.installationActionNode.isHidden = true strongSelf.installationActionImageNode.isHidden = true strongSelf.selectionIconNode.isHidden = true diff --git a/submodules/ItemListUI/BUILD b/submodules/ItemListUI/BUILD index 9a38bf8d03..af5616e0d6 100644 --- a/submodules/ItemListUI/BUILD +++ b/submodules/ItemListUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/ItemListUI/Sources/ItemListControllerNode.swift b/submodules/ItemListUI/Sources/ItemListControllerNode.swift index 47124521f3..31aa4eda42 100644 --- a/submodules/ItemListUI/Sources/ItemListControllerNode.swift +++ b/submodules/ItemListUI/Sources/ItemListControllerNode.swift @@ -7,7 +7,7 @@ import TelegramCore import TelegramPresentationData import MergeLists -public protocol ItemListHeaderItemNode: class { +public protocol ItemListHeaderItemNode: AnyObject { func updateTheme(theme: PresentationTheme) } diff --git a/submodules/ItemListVenueItem/BUILD b/submodules/ItemListVenueItem/BUILD index 622c8270aa..27c0645443 100644 --- a/submodules/ItemListVenueItem/BUILD +++ b/submodules/ItemListVenueItem/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/JoinLinkPreviewUI/BUILD b/submodules/JoinLinkPreviewUI/BUILD index b94598cc1b..9524625964 100644 --- a/submodules/JoinLinkPreviewUI/BUILD +++ b/submodules/JoinLinkPreviewUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/JoinLinkPreviewUI/Sources/JoinLinkPreviewPeerContentNode.swift b/submodules/JoinLinkPreviewUI/Sources/JoinLinkPreviewPeerContentNode.swift index e9d6c6cd83..ce85fc870c 100644 --- a/submodules/JoinLinkPreviewUI/Sources/JoinLinkPreviewPeerContentNode.swift +++ b/submodules/JoinLinkPreviewUI/Sources/JoinLinkPreviewPeerContentNode.swift @@ -49,7 +49,7 @@ final class JoinLinkPreviewPeerContentNode: ASDisplayNode, ShareContentContainer self.peerNodes = members.map { peer in let node = SelectablePeerNode() - node.setup(context: context, theme: theme, strings: strings, peer: RenderedPeer(peer: peer), synchronousLoad: false) + node.setup(context: context, theme: theme, strings: strings, peer: EngineRenderedPeer(peer: EnginePeer(peer)), synchronousLoad: false) node.theme = itemTheme return node } @@ -65,7 +65,7 @@ final class JoinLinkPreviewPeerContentNode: ASDisplayNode, ShareContentContainer let peer = TelegramGroup(id: PeerId(0), title: title, photo: image.flatMap { [$0] } ?? [], participantCount: Int(memberCount), role: .member, membership: .Left, flags: [], defaultBannedRights: nil, migrationReference: nil, creationDate: 0, version: 0) self.addSubnode(self.avatarNode) - self.avatarNode.setPeer(context: context, theme: theme, peer: peer, emptyColor: theme.list.mediaPlaceholderColor) + self.avatarNode.setPeer(context: context, theme: theme, peer: EnginePeer(peer), emptyColor: theme.list.mediaPlaceholderColor) self.addSubnode(self.titleNode) self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(16.0), textColor: theme.actionSheet.primaryTextColor) diff --git a/submodules/LanguageLinkPreviewUI/BUILD b/submodules/LanguageLinkPreviewUI/BUILD index 4e08005b17..7dbed7278d 100644 --- a/submodules/LanguageLinkPreviewUI/BUILD +++ b/submodules/LanguageLinkPreviewUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/LanguageSuggestionUI/BUILD b/submodules/LanguageSuggestionUI/BUILD index 9b716eb7d0..b2e31d19ab 100644 --- a/submodules/LanguageSuggestionUI/BUILD +++ b/submodules/LanguageSuggestionUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/LanguageSuggestionUI/Sources/LanguageSuggestionController.swift b/submodules/LanguageSuggestionUI/Sources/LanguageSuggestionController.swift index 43607c18e5..06632d40d4 100644 --- a/submodules/LanguageSuggestionUI/Sources/LanguageSuggestionController.swift +++ b/submodules/LanguageSuggestionUI/Sources/LanguageSuggestionController.swift @@ -334,7 +334,6 @@ public func languageSuggestionController(context: AccountContext, suggestedLocal } let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let theme = context.sharedContext.presentationData let strings = LanguageSuggestionControllerStrings(localization: suggestedLocalization) guard let mainPath = getAppBundle().path(forResource: "en", ofType: "lproj") else { return nil diff --git a/submodules/LegacyComponents/BUILD b/submodules/LegacyComponents/BUILD index d1bfde2cc1..16ddc7c99f 100644 --- a/submodules/LegacyComponents/BUILD +++ b/submodules/LegacyComponents/BUILD @@ -30,6 +30,7 @@ objc_library( ]), copts = [ "-I{}/PublicHeaders/LegacyComponents".format(package_name()), + "-Werror", ], includes = [ "PublicHeaders", diff --git a/submodules/LegacyComponents/LegacyImages.xcassets/Camera/FlashOn.imageset/Contents.json b/submodules/LegacyComponents/LegacyImages.xcassets/Camera/Flash.imageset/Contents.json similarity index 73% rename from submodules/LegacyComponents/LegacyImages.xcassets/Camera/FlashOn.imageset/Contents.json rename to submodules/LegacyComponents/LegacyImages.xcassets/Camera/Flash.imageset/Contents.json index 8aacf1b5de..2ddf28fc77 100644 --- a/submodules/LegacyComponents/LegacyImages.xcassets/Camera/FlashOn.imageset/Contents.json +++ b/submodules/LegacyComponents/LegacyImages.xcassets/Camera/Flash.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "ic_cam_flashon.pdf", + "filename" : "ic_cam_flashon (1).pdf", "idiom" : "universal" } ], diff --git a/submodules/LegacyComponents/LegacyImages.xcassets/Camera/FlashOn.imageset/ic_cam_flashon.pdf b/submodules/LegacyComponents/LegacyImages.xcassets/Camera/Flash.imageset/ic_cam_flashon (1).pdf similarity index 72% rename from submodules/LegacyComponents/LegacyImages.xcassets/Camera/FlashOn.imageset/ic_cam_flashon.pdf rename to submodules/LegacyComponents/LegacyImages.xcassets/Camera/Flash.imageset/ic_cam_flashon (1).pdf index 25896a3a6e..0cd45d0c0e 100644 Binary files a/submodules/LegacyComponents/LegacyImages.xcassets/Camera/FlashOn.imageset/ic_cam_flashon.pdf and b/submodules/LegacyComponents/LegacyImages.xcassets/Camera/Flash.imageset/ic_cam_flashon (1).pdf differ diff --git a/submodules/LegacyComponents/LegacyImages.xcassets/Camera/FlashOff.imageset/Contents.json b/submodules/LegacyComponents/LegacyImages.xcassets/Camera/FlashOff.imageset/Contents.json deleted file mode 100644 index 3d7da8444b..0000000000 --- a/submodules/LegacyComponents/LegacyImages.xcassets/Camera/FlashOff.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "ic_cam_flashoff (1).pdf", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/submodules/LegacyComponents/LegacyImages.xcassets/Camera/FlashOff.imageset/ic_cam_flashoff (1).pdf b/submodules/LegacyComponents/LegacyImages.xcassets/Camera/FlashOff.imageset/ic_cam_flashoff (1).pdf deleted file mode 100644 index 883bcd2938..0000000000 Binary files a/submodules/LegacyComponents/LegacyImages.xcassets/Camera/FlashOff.imageset/ic_cam_flashoff (1).pdf and /dev/null differ diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCamera.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCamera.h index b6465ca803..16113759ff 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCamera.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCamera.h @@ -60,7 +60,7 @@ typedef enum @property (nonatomic, copy) void(^finishedModeChange)(void); @property (nonatomic, copy) void(^beganPositionChange)(bool targetPositionHasFlash, bool targetPositionHasZoom, void(^commitBlock)(void)); -@property (nonatomic, copy) void(^finishedPositionChange)(void); +@property (nonatomic, copy) void(^finishedPositionChange)(bool targetPositionHasZoom); @property (nonatomic, copy) void(^beganAdjustingFocus)(void); @property (nonatomic, copy) void(^finishedAdjustingFocus)(void); @@ -83,6 +83,13 @@ typedef enum @property (nonatomic, readonly) bool isZoomAvailable; @property (nonatomic, assign) CGFloat zoomLevel; +@property (nonatomic, readonly) CGFloat minZoomLevel; +@property (nonatomic, readonly) CGFloat maxZoomLevel; + +- (void)setZoomLevel:(CGFloat)zoomLevel animated:(bool)animated; + +@property (nonatomic, readonly) bool hasUltrawideCamera; +@property (nonatomic, readonly) bool hasTelephotoCamera; @property (nonatomic, assign) bool disableResultMirroring; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCameraCaptureSession.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCameraCaptureSession.h index 9269470d96..499ecdbae8 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCameraCaptureSession.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCameraCaptureSession.h @@ -24,14 +24,22 @@ @property (nonatomic, readonly) bool isZoomAvailable; @property (nonatomic, assign) CGFloat zoomLevel; +@property (nonatomic, readonly) CGFloat minZoomLevel; +@property (nonatomic, readonly) CGFloat maxZoomLevel; + +- (void)setZoomLevel:(CGFloat)zoomLevel animated:(bool)animated; + +@property (nonatomic, readonly) bool hasUltrawideCamera; +@property (nonatomic, readonly) bool hasTelephotoCamera; @property (nonatomic, readonly) CGPoint focusPoint; @property (nonatomic, copy) void(^outputSampleBuffer)(CMSampleBufferRef sampleBuffer, AVCaptureConnection *connection); -@property (nonatomic, copy) void(^changingPosition)(void); @property (nonatomic, copy) bool(^requestPreviewIsMirrored)(void); +@property (nonatomic, copy) void(^crossfadeNeeded)(void); + @property (nonatomic, copy) void(^recognizedQRCode)(NSString *value, AVMetadataMachineReadableCodeObject *object); @property (nonatomic, assign) bool compressVideo; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGAttachmentCarouselItemView.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGAttachmentCarouselItemView.h index ed3b3f3ae3..80103834ca 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGAttachmentCarouselItemView.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGAttachmentCarouselItemView.h @@ -10,14 +10,13 @@ @class TGViewController; @class TGAttachmentCameraView; @class TGVideoEditAdjustments; -@protocol TGModernGalleryTransitionHostScrollView; @protocol TGPhotoPaintStickersContext; @interface TGAttachmentCarouselCollectionView : UICollectionView @end -@interface TGAttachmentCarouselItemView : TGMenuSheetItemView +@interface TGAttachmentCarouselItemView : TGMenuSheetItemView @property (nonatomic, weak) TGViewController *parentController; @@ -45,7 +44,7 @@ @property (nonatomic, copy) void (^cameraPressed)(TGAttachmentCameraView *cameraView); @property (nonatomic, copy) void (^sendPressed)(TGMediaAsset *currentItem, bool asFiles, bool silentPosting, int32_t scheduleTime, bool isFromPicker); @property (nonatomic, copy) void (^avatarCompletionBlock)(UIImage *image); -@property (nonatomic, copy) void (^avatarVideoCompletionBlock)(UIImage *image, AVAsset *asset, TGVideoEditAdjustments *adjustments); +@property (nonatomic, copy) void (^avatarVideoCompletionBlock)(UIImage *image, id asset, TGVideoEditAdjustments *adjustments); @property (nonatomic, copy) void (^editorOpened)(void); @property (nonatomic, copy) void (^editorClosed)(void); diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraController.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraController.h index da8b0b9bf3..c08deb5ed3 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraController.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraController.h @@ -76,6 +76,6 @@ typedef enum { + (UIInterfaceOrientation)_interfaceOrientationForDeviceOrientation:(UIDeviceOrientation)orientation; -+ (bool)useLegacyCamera; ++ (UIImage *)startImage; @end diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraFlashControl.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraFlashControl.h index b914fd8eef..50b3dff6d0 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraFlashControl.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraFlashControl.h @@ -6,15 +6,13 @@ @property (nonatomic, assign) PGCameraFlashMode mode; @property (nonatomic, assign) UIInterfaceOrientation interfaceOrientation; -@property (nonatomic, copy) void(^becameActive)(void); @property (nonatomic, copy) void(^modeChanged)(PGCameraFlashMode mode); - (void)setFlashUnavailable:(bool)unavailable; +- (void)setFlashActive:(bool)active; - (void)setHidden:(bool)hidden animated:(bool)animated; -- (void)dismissAnimated:(bool)animated; - @end extern const CGFloat TGCameraFlashControlHeight; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraFlipButton.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraFlipButton.h index 6cc971ee6b..2eb65682dc 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraFlipButton.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraFlipButton.h @@ -6,6 +6,12 @@ @end +@interface TGCameraSmallFlipButton : TGModernButton + +- (void)setHidden:(bool)hidden animated:(bool)animated; + +@end + @interface TGCameraCancelButton : TGModernButton - (void)setHidden:(bool)hidden animated:(bool)animated; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraMainView.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraMainView.h index b6e31ff04b..7e023c677a 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraMainView.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraMainView.h @@ -18,6 +18,10 @@ @class TGMediaPickerGallerySelectedItemsModel; @class TGMediaEditingContext; +@interface TGCameraCornersView : UIImageView + +@end + @interface TGCameraMainView : UIView { UIInterfaceOrientation _interfaceOrientation; @@ -49,9 +53,11 @@ @property (nonatomic, copy) void(^focusPointChanged)(CGPoint point); @property (nonatomic, copy) void(^expositionChanged)(CGFloat value); +@property (nonatomic, copy) void(^zoomChanged)(CGFloat level, bool animated); @property (nonatomic, copy) void(^shutterPressed)(bool fromHardwareButton); @property (nonatomic, copy) void(^shutterReleased)(bool fromHardwareButton); +@property (nonatomic, copy) void(^shutterPanGesture)(UIPanGestureRecognizer *gesture); @property (nonatomic, copy) void(^cancelPressed)(void); @property (nonatomic, copy) void(^donePressed)(void); @property (nonatomic, copy) void(^resultPressed)(NSInteger index); @@ -61,7 +67,7 @@ @property (nonatomic, assign) CGRect previewViewFrame; -- (instancetype)initWithFrame:(CGRect)frame avatar:(bool)avatar; +- (instancetype)initWithFrame:(CGRect)frame avatar:(bool)avatar hasUltrawideCamera:(bool)hasUltrawideCamera hasTelephotoCamera:(bool)hasTelephotoCamera; - (void)setDocumentFrameHidden:(bool)hidden; - (void)setCameraMode:(PGCameraMode)mode; @@ -88,6 +94,7 @@ - (void)shutterButtonPressed; - (void)shutterButtonReleased; +- (void)shutterButtonPanGesture:(UIPanGestureRecognizer *)gestureRecognizer; - (void)flipButtonPressed; - (void)cancelButtonPressed; - (void)doneButtonPressed; @@ -106,8 +113,6 @@ - (UIInterfaceOrientation)interfaceOrientation; - (void)setInterfaceOrientation:(UIInterfaceOrientation)orientation animated:(bool)animated; -- (void)layoutPreviewRelativeViews; - - (void)photoCounterButtonPressed; @end diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraTimeCodeView.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraTimeCodeView.h index b5854a29ae..76e49f033e 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraTimeCodeView.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraTimeCodeView.h @@ -2,6 +2,8 @@ @interface TGCameraTimeCodeView : UIView +@property (nonatomic, assign) UIInterfaceOrientation interfaceOrientation; + @property (nonatomic, copy) NSTimeInterval(^requestedRecordingDuration)(void); - (void)startRecording; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraZoomView.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraZoomView.h index 502f5c8cfe..69a21795fb 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraZoomView.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraZoomView.h @@ -18,20 +18,33 @@ @interface TGCameraZoomModeView : UIView -@property (copy, nonatomic) void(^zoomChanged)(CGFloat zoomLevel, bool done); +@property (nonatomic, assign) UIInterfaceOrientation interfaceOrientation; + +@property (copy, nonatomic) void(^zoomChanged)(CGFloat zoomLevel, bool done, bool animated); @property (nonatomic, assign) CGFloat zoomLevel; - (void)setZoomLevel:(CGFloat)zoomLevel animated:(bool)animated; - (void)setHidden:(bool)hidden animated:(bool)animated; +- (void)panGesture:(UIPanGestureRecognizer *)gestureRecognizer; + +- (instancetype)initWithFrame:(CGRect)frame hasUltrawideCamera:(bool)hasUltrawideCamera hasTelephotoCamera:(bool)hasTelephotoCamera minZoomLevel:(CGFloat)minZoomLevel maxZoomLevel:(CGFloat)maxZoomLevel; + @end @interface TGCameraZoomWheelView : UIView +@property (nonatomic, assign) UIInterfaceOrientation interfaceOrientation; + +@property (copy, nonatomic) void(^panGesture)(UIPanGestureRecognizer *gestureRecognizer); + @property (nonatomic, assign) CGFloat zoomLevel; +- (void)setZoomLevel:(CGFloat)zoomLevel panning:(bool)panning; - (void)setHidden:(bool)hidden animated:(bool)animated; +- (instancetype)initWithFrame:(CGRect)frame hasUltrawideCamera:(bool)hasUltrawideCamera hasTelephotoCamera:(bool)hasTelephotoCamera; + @end diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGConversation.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGConversation.h index 1640dd7cc0..ab466872e0 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGConversation.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGConversation.h @@ -211,8 +211,8 @@ typedef enum { @property (nonatomic) NSData *chatPhotoFileReferenceSmall; @property (nonatomic) NSData *chatPhotoFileReferenceBig; -@property (nonatomic, strong) NSString *chatPhotoFullSmall; -@property (nonatomic, strong) NSString *chatPhotoFullBig; +@property (nonatomic, strong, readonly) NSString *chatPhotoFullSmall; +@property (nonatomic, strong, readonly) NSString *chatPhotoFullBig; @property (nonatomic) int chatParticipantCount; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGDateUtils.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGDateUtils.h index a4833b6a16..8087dcd8ff 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGDateUtils.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGDateUtils.h @@ -12,7 +12,6 @@ + (NSString *)stringForMonthOfYear:(int)date; + (NSString *)stringForPreciseDate:(int)date; + (NSString *)stringForMessageListDate:(int)date; -+ (NSString *)stringForLastSeen:(int)date; + (NSString *)stringForApproximateDate:(int)date; + (NSString *)stringForRelativeLastSeen:(int)date; + (NSString *)stringForRelativeUpdate:(int)date; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGHacks.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGHacks.h index 916a57346c..25e0484904 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGHacks.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGHacks.h @@ -14,8 +14,13 @@ void InjectInstanceMethodFromAnotherClass(Class toClass, Class fromClass, SEL fr } #endif +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + typedef void (^TGAlertHandler)(UIAlertView *alertView); +#pragma clang diagnostic pop + typedef enum { TGStatusBarAppearanceAnimationSlideDown = 1, TGStatusBarAppearanceAnimationSlideUp = 2, diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaAssetsController.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaAssetsController.h index 5d94419416..c94f239e0b 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaAssetsController.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaAssetsController.h @@ -95,7 +95,7 @@ typedef enum - (NSArray *)resultSignalsWithCurrentItem:(TGMediaAsset *)currentItem descriptionGenerator:(id (^)(id, NSString *, NSArray *, NSString *, NSString *))descriptionGenerator; - (void)completeWithAvatarImage:(UIImage *)image; -- (void)completeWithAvatarVideo:(AVAsset *)asset adjustments:(TGVideoEditAdjustments *)adjustments image:(UIImage *)image; +- (void)completeWithAvatarVideo:(id)asset adjustments:(TGVideoEditAdjustments *)adjustments image:(UIImage *)image; - (void)completeWithCurrentItem:(TGMediaAsset *)currentItem silentPosting:(bool)silentPosting scheduleTime:(int32_t)scheduleTime; - (void)dismiss; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGUser.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGUser.h index 7f26131184..09ba58b166 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGUser.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGUser.h @@ -81,8 +81,8 @@ typedef enum { @property (nonatomic) NSData *photoFileReferenceSmall; @property (nonatomic) NSData *photoFileReferenceBig; -@property (nonatomic) NSString *photoFullUrlSmall; -@property (nonatomic) NSString *photoFullUrlBig; +@property (nonatomic, strong, readonly) NSString *photoFullUrlSmall; +@property (nonatomic, strong, readonly) NSString *photoFullUrlBig; @property (nonatomic) TGUserPresence presence; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoMessageRingView.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoMessageRingView.h index b9c7004ee7..55253039fe 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoMessageRingView.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoMessageRingView.h @@ -1,5 +1,12 @@ #import +@interface TGVideoMessageShimmerView : UIView + +- (void)updateAbsoluteRect:(CGRect)absoluteRect containerSize:(CGSize)containerSize; + +@end + + @interface TGVideoMessageRingView : UIView @property (nonatomic, strong) UIColor *accentColor; diff --git a/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/CameraPlaceholder.jpg b/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/CameraPlaceholder.jpg new file mode 100644 index 0000000000..d7d326fa0e Binary files /dev/null and b/submodules/LegacyComponents/Resources/LegacyComponentsResources.bundle/CameraPlaceholder.jpg differ diff --git a/submodules/LegacyComponents/Sources/GPUImageContext.m b/submodules/LegacyComponents/Sources/GPUImageContext.m index 941c37601c..81acd833ae 100755 --- a/submodules/LegacyComponents/Sources/GPUImageContext.m +++ b/submodules/LegacyComponents/Sources/GPUImageContext.m @@ -259,7 +259,7 @@ static void *openGLESContextQueueKey; #if TARGET_IPHONE_SIMULATOR return NO; #else - return (CVOpenGLESTextureCacheCreate != NULL); + return true; #endif } diff --git a/submodules/LegacyComponents/Sources/GPUImageCropFilter.m b/submodules/LegacyComponents/Sources/GPUImageCropFilter.m index 401392a05f..84b162659c 100755 --- a/submodules/LegacyComponents/Sources/GPUImageCropFilter.m +++ b/submodules/LegacyComponents/Sources/GPUImageCropFilter.m @@ -153,6 +153,7 @@ NSString *const kGPUImageCropFragmentShaderString = SHADER_STRING cropTextureCoordinates[7] = minY; }; break; case kGPUImageFlipHorizonal: // Works for me + case kGPUImageRotate180FlipHorizontal: { cropTextureCoordinates[0] = maxX; // 1,0 cropTextureCoordinates[1] = minY; diff --git a/submodules/LegacyComponents/Sources/PGCamera.m b/submodules/LegacyComponents/Sources/PGCamera.m index d8088a1124..4ffeb5f002 100644 --- a/submodules/LegacyComponents/Sources/PGCamera.m +++ b/submodules/LegacyComponents/Sources/PGCamera.m @@ -690,9 +690,8 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus"; [strongSelf.captureSession setCurrentCameraPosition:targetCameraPosition]; if (strongSelf.finishedPositionChange != nil) - strongSelf.finishedPositionChange(); + strongSelf.finishedPositionChange([PGCameraCaptureSession _isZoomAvailableForDevice:targetDevice]); - [strongSelf setZoomLevel:0.0f]; [strongSelf _subscribeForCameraChanges]; }]; }; @@ -705,11 +704,27 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus"; #pragma mark - Zoom +- (bool)hasUltrawideCamera { + return self.captureSession.hasUltrawideCamera; +} + +- (bool)hasTelephotoCamera { + return self.captureSession.hasTelephotoCamera; +} + - (bool)isZoomAvailable { return self.captureSession.isZoomAvailable; } +- (CGFloat)minZoomLevel { + return self.captureSession.minZoomLevel; +} + +- (CGFloat)maxZoomLevel { + return self.captureSession.maxZoomLevel; +} + - (CGFloat)zoomLevel { return self.captureSession.zoomLevel; @@ -717,14 +732,20 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus"; - (void)setZoomLevel:(CGFloat)zoomLevel { - zoomLevel = MAX(0.0f, MIN(1.0f, zoomLevel)); - + [self setZoomLevel:zoomLevel animated:false]; +} + +- (void)setZoomLevel:(CGFloat)zoomLevel animated:(bool)animated +{ + if (self.cameraMode == PGCameraModeVideo) { + animated = false; + } [[PGCamera cameraQueue] dispatch:^ { if (self.disabled) return; - [self.captureSession setZoomLevel:zoomLevel]; + [self.captureSession setZoomLevel:zoomLevel animated:animated]; }]; } diff --git a/submodules/LegacyComponents/Sources/PGCameraCaptureSession.m b/submodules/LegacyComponents/Sources/PGCameraCaptureSession.m index 5e2b94dc19..2b015fd8fe 100644 --- a/submodules/LegacyComponents/Sources/PGCameraCaptureSession.m +++ b/submodules/LegacyComponents/Sources/PGCameraCaptureSession.m @@ -15,6 +15,8 @@ #import #import +#import "POPSpringAnimation.h" + const NSInteger PGCameraFrameRate = 30; @interface PGCameraCaptureSession () @@ -73,6 +75,10 @@ const NSInteger PGCameraFrameRate = 30; _preferredCameraPosition = position; _currentAudioSession = [[SMetaDisposable alloc] init]; + + self.automaticallyConfiguresApplicationAudioSession = false; + self.usesApplicationAudioSession = true; + } return self; } @@ -139,6 +145,8 @@ const NSInteger PGCameraFrameRate = 30; TGLegacyLog(@"ERROR: camera can't add still image output"); } + [self resetZoom]; + AVCaptureVideoDataOutput *videoOutput = [[AVCaptureVideoDataOutput alloc] init]; videoOutput.alwaysDiscardsLateVideoFrames = true; videoOutput.videoSettings = @{ (id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) }; @@ -234,8 +242,7 @@ const NSInteger PGCameraFrameRate = 30; [self commitConfiguration]; [self resetFocusPoint]; - - self.zoomLevel = 0.0f; + [self resetZoom]; } - (void)resetFlashMode @@ -292,6 +299,8 @@ const NSInteger PGCameraFrameRate = 30; [self _enableLowLightBoost]; [self _enableVideoStabilization]; + [self resetZoom]; + [self commitConfiguration]; if (mode == PGCameraModePhotoScan) { @@ -384,12 +393,8 @@ const NSInteger PGCameraFrameRate = 30; - (void)_enableVideoStabilization { AVCaptureConnection *videoConnection = [_videoOutput connectionWithMediaType:AVMediaTypeVideo]; - if (videoConnection.supportsVideoStabilization) - { - if ([videoConnection respondsToSelector:@selector(setPreferredVideoStabilizationMode:)]) - videoConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeStandard; - else - videoConnection.enablesVideoStabilizationWhenAvailable = true; + if (videoConnection.supportsVideoStabilization) { + videoConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeStandard; } } @@ -444,20 +449,90 @@ const NSInteger PGCameraFrameRate = 30; #pragma mark - Zoom -- (CGFloat)_maximumZoomFactor -{ - return MIN(5.0f, self.videoDevice.activeFormat.videoMaxZoomFactor); +- (bool)hasUltrawideCamera { + if (@available(iOS 13.0, *)) { + if (self.videoDevice.isVirtualDevice && self.videoDevice.constituentDevices.firstObject.deviceType == AVCaptureDeviceTypeBuiltInUltraWideCamera) { + return true; + } + } + return false; +} + +- (bool)hasTelephotoCamera { + if (@available(iOS 13.0, *)) { + if (self.videoDevice.isVirtualDevice && self.videoDevice.constituentDevices.lastObject.deviceType == AVCaptureDeviceTypeBuiltInTelephotoCamera) { + return true; + } + } + return false; } - (CGFloat)zoomLevel { if (![self.videoDevice respondsToSelector:@selector(videoZoomFactor)]) return 1.0f; + + if (@available(iOS 13.0, *)) { + if (self.videoDevice.isVirtualDevice) { + CGFloat backingLevel = self.videoDevice.videoZoomFactor; + CGFloat realLevel = backingLevel; + + NSArray *marks = self.videoDevice.virtualDeviceSwitchOverVideoZoomFactors; + if (marks.count == 2) { + CGFloat firstMark = [marks.firstObject floatValue]; + CGFloat secondMark = [marks.lastObject floatValue]; + + if (backingLevel < firstMark) { + realLevel = 0.5 + 0.5 * (backingLevel - 1.0) / (firstMark - 1.0); + } else if (backingLevel < secondMark) { + realLevel = 1.0 + 1.0 * (backingLevel - firstMark) / (secondMark - firstMark); + } else { + realLevel = 2.0 + 6.0 * (backingLevel - secondMark) / (self.maxZoomLevel - secondMark); + } + } else if (marks.count == 1) { + CGFloat mark = [marks.firstObject floatValue]; + if ([self hasTelephotoCamera]) { + if (backingLevel < mark) { + realLevel = 1.0 + 1.0 * (backingLevel - 1.0) / (mark - 1.0); + } else { + realLevel = 2.0 + 6.0 * (backingLevel - mark) / (self.maxZoomLevel - mark); + } + } else if ([self hasUltrawideCamera]) { + if (backingLevel < mark) { + realLevel = 0.5 + 0.5 * (backingLevel - 1.0) / (mark - 1.0); + } else { + realLevel = 1.0 + 7.0 * (backingLevel - mark) / (self.maxZoomLevel - mark); + } + } + } + + return realLevel; + } + } - return (self.videoDevice.videoZoomFactor - 1.0f) / ([self _maximumZoomFactor] - 1.0f); + return self.videoDevice.videoZoomFactor; } -- (void)setZoomLevel:(CGFloat)zoomLevel +- (CGFloat)minZoomLevel { + if (self.hasUltrawideCamera) { + return 0.5; + } + return 1.0; +} + +- (CGFloat)maxZoomLevel { + return MIN(16.0f, self.videoDevice.activeFormat.videoMaxZoomFactor); +} + +- (void)resetZoom { + [self setZoomLevel:1.0]; +} + +- (void)setZoomLevel:(CGFloat)zoomLevel { + [self setZoomLevel:zoomLevel animated:false]; +} + +- (void)setZoomLevel:(CGFloat)zoomLevel animated:(bool)animated { if (![self.videoDevice respondsToSelector:@selector(setVideoZoomFactor:)]) return; @@ -469,7 +544,78 @@ const NSInteger PGCameraFrameRate = 30; if (strongSelf == nil) return; - device.videoZoomFactor = MAX(1.0f, MIN([strongSelf _maximumZoomFactor], 1.0f + ([strongSelf _maximumZoomFactor] - 1.0f) * zoomLevel)); + CGFloat level = zoomLevel; + CGFloat backingLevel = zoomLevel; + if (@available(iOS 13.0, *)) { + if (device.isVirtualDevice) { + NSArray *marks = device.virtualDeviceSwitchOverVideoZoomFactors; + if (marks.count == 2) { + CGFloat firstMark = [marks.firstObject floatValue]; + CGFloat secondMark = [marks.lastObject floatValue]; + if (level < 1.0) { + level = MAX(0.5, level); + backingLevel = 1.0 + ((level - 0.5) / 0.5) * (firstMark - 1.0); + } else if (zoomLevel < 2.0) { + backingLevel = firstMark + ((level - 1.0) / 1.0) * (secondMark - firstMark); + } else { + backingLevel = secondMark + ((level - 2.0) / 6.0) * (self.maxZoomLevel - secondMark); + } + } else if (marks.count == 1) { + CGFloat mark = [marks.firstObject floatValue]; + if ([self hasTelephotoCamera]) { + if (zoomLevel < 2.0) { + backingLevel = 1.0 + ((level - 1.0) / 1.0) * (mark - 1.0); + } else { + backingLevel = mark + ((level - 2.0) / 6.0) * (self.maxZoomLevel - mark); + } + } else if ([self hasUltrawideCamera]) { + if (level < 1.0) { + level = MAX(0.5, level); + backingLevel = 1.0 + ((level - 0.5) / 0.5) * (mark - 1.0); + } else { + backingLevel = mark + ((level - 1.0) / 7.0) * (self.maxZoomLevel - mark); + } + } + } + } + } + CGFloat finalLevel = MAX(1.0, MIN([strongSelf maxZoomLevel], backingLevel)); + if (animated) { + bool zoomingIn = finalLevel > self.videoDevice.videoZoomFactor; + bool needsCrossfade = level >= 1.0; + POPSpringAnimation *animation = [POPSpringAnimation new]; + animation.property = [POPAnimatableProperty propertyWithName:@"zoom" initializer:^(POPMutableAnimatableProperty *prop) + { + prop.readBlock = ^(PGCameraCaptureSession *session, CGFloat values[]) + { + if (session != nil) { + values[0] = session.videoDevice.videoZoomFactor; + } + }; + + prop.writeBlock = ^(PGCameraCaptureSession *session, const CGFloat values[]) + { + if (session != nil) { + if ((zoomingIn && values[0] > finalLevel - 0.015) || (!zoomingIn && values[0] < finalLevel + 0.015)) { + if (needsCrossfade && session.crossfadeNeeded != nil) + session.crossfadeNeeded(); + } + [session _reconfigureDevice:session->_videoDevice withBlock:^(AVCaptureDevice *device) { + device.videoZoomFactor = values[0]; + }]; + } + }; + + prop.threshold = 0.03f; + }]; + animation.fromValue = @(self.videoDevice.videoZoomFactor); + animation.toValue = @(finalLevel); + animation.springSpeed = 14; + animation.springBounciness = 1; + [self pop_addAnimation:animation forKey:@"zoom"]; + } else { + device.videoZoomFactor = finalLevel; + } }]; } @@ -674,9 +820,6 @@ const NSInteger PGCameraFrameRate = 30; [self _addAudioInputRequestAudioSession:false]; } - if (self.changingPosition != nil) - self.changingPosition(); - [self commitConfiguration]; if (self.currentMode == PGCameraModeVideo || self.currentMode == PGCameraModeSquareVideo || self.currentMode == PGCameraModeSquareSwing) @@ -686,6 +829,7 @@ const NSInteger PGCameraFrameRate = 30; } _videoDevice = deviceForTargetPosition; + [self resetZoom]; [self setCurrentFlashMode:self.currentFlashMode]; @@ -700,16 +844,18 @@ const NSInteger PGCameraFrameRate = 30; + (AVCaptureDevice *)_deviceWithPosition:(AVCaptureDevicePosition)position { - if (iosMajorVersion() >= 10 && position == AVCaptureDevicePositionBack) { - AVCaptureDevice *device = nil; - if (iosMajorVersion() >= 13) { - device = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInTripleCamera mediaType:AVMediaTypeVideo position:position]; - } - if (device == nil) { - device = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInDualCamera mediaType:AVMediaTypeVideo position:position]; - } - if (device != nil) { - return device; + if (@available(iOS 13.0, *)) { + if (position != AVCaptureDevicePositionFront) { + AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInTripleCamera mediaType:AVMediaTypeVideo position:position]; + if (device == nil) { + device = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInDualCamera mediaType:AVMediaTypeVideo position:position]; + } + if (device == nil) { + device = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInDualWideCamera mediaType:AVMediaTypeVideo position:position]; + } + if (device != nil) { + return device; + } } } @@ -874,54 +1020,18 @@ const NSInteger PGCameraFrameRate = 30; { CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); CVPixelBufferLockBaseAddress(imageBuffer,0); - + size_t width = CVPixelBufferGetWidth(imageBuffer); size_t height = CVPixelBufferGetHeight(imageBuffer); - uint8_t *yBuffer = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0); - size_t yPitch = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0); - uint8_t *cbCrBuffer = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 1); - size_t cbCrPitch = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 1); - int bytesPerPixel = 4; - uint8_t *rgbBuffer = malloc(width * height * bytesPerPixel); - - for (size_t y = 0; y < height; y++) - { - uint8_t *rgbBufferLine = &rgbBuffer[y * width * bytesPerPixel]; - uint8_t *yBufferLine = &yBuffer[y * yPitch]; - uint8_t *cbCrBufferLine = &cbCrBuffer[(y >> 1) * cbCrPitch]; - - for (size_t x = 0; x < width; x++) - { - int16_t y = yBufferLine[x]; - int16_t cb = cbCrBufferLine[x & ~1] - 128; - int16_t cr = cbCrBufferLine[x | 1] - 128; - - uint8_t *rgbOutput = &rgbBufferLine[x * bytesPerPixel]; - - int16_t r = (int16_t)round( y + cr * 1.4 ); - int16_t g = (int16_t)round( y + cb * -0.343 + cr * -0.711 ); - int16_t b = (int16_t)round( y + cb * 1.765); - - rgbOutput[0] = 0xff; - rgbOutput[1] = clamp(b); - rgbOutput[2] = clamp(g); - rgbOutput[3] = clamp(r); - } - } - - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - CGContextRef context = CGBitmapContextCreate(rgbBuffer, width, height, 8, width * bytesPerPixel, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast); - CGImageRef quartzImage = CGBitmapContextCreateImage(context); - UIImage *image = [UIImage imageWithCGImage:quartzImage scale:1.0 orientation:orientation]; - - CGContextRelease(context); - CGColorSpaceRelease(colorSpace); + CIImage *coreImage = [CIImage imageWithCVPixelBuffer:imageBuffer]; + + CIContext *ciContext = [CIContext contextWithOptions:nil]; + CGImageRef quartzImage = [ciContext createCGImage:coreImage fromRect:CGRectMake(0, 0, width, height)]; + UIImage *image = [[UIImage alloc] initWithCGImage:quartzImage scale:1.0 orientation:orientation]; CGImageRelease(quartzImage); - free(rgbBuffer); CVPixelBufferUnlockBaseAddress(imageBuffer, 0); - return image; } @@ -990,8 +1100,6 @@ static UIImageOrientation TGSnapshotOrientationForVideoOrientation(bool mirrored if (!self.isRunning || self.currentMode != PGCameraModePhoto) return; - - if ([metadataObjects.firstObject isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) { AVMetadataMachineReadableCodeObject *object = (AVMetadataMachineReadableCodeObject *)metadataObjects.firstObject; diff --git a/submodules/LegacyComponents/Sources/POPAnimatableProperty.mm b/submodules/LegacyComponents/Sources/POPAnimatableProperty.mm index 8cd843659a..fef591ccdf 100644 --- a/submodules/LegacyComponents/Sources/POPAnimatableProperty.mm +++ b/submodules/LegacyComponents/Sources/POPAnimatableProperty.mm @@ -13,10 +13,6 @@ #import -#if SCENEKIT_SDK_AVAILABLE -#import -#endif - #import "POPLayerExtras.h" #if TARGET_OS_IPHONE @@ -542,284 +538,6 @@ static POPStaticAnimatablePropertyState _staticStates[] = }, 0.01 }, -#if SCENEKIT_SDK_AVAILABLE - - /* SceneKit */ - - {kPOPSCNNodePosition, - ^(SCNNode *obj, CGFloat values[]) { - values_from_vec3(values, obj.position); - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.position = values_to_vec3(values); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodePositionX, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.position.x; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.position = SCNVector3Make(values[0], obj.position.y, obj.position.z); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodePositionY, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.position.y; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.position = SCNVector3Make(obj.position.x, values[0], obj.position.z); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodePositionZ, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.position.z; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.position = SCNVector3Make(obj.position.x, obj.position.y, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeTranslation, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.transform.m41; - values[1] = obj.transform.m42; - values[2] = obj.transform.m43; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.transform = SCNMatrix4MakeTranslation(values[0], values[1], values[2]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeTranslationX, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.transform.m41; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.transform = SCNMatrix4MakeTranslation(values[0], obj.transform.m42, obj.transform.m43); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeTranslationY, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.transform.m42; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.transform = SCNMatrix4MakeTranslation(obj.transform.m41, values[0], obj.transform.m43); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeTranslationY, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.transform.m43; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.transform = SCNMatrix4MakeTranslation(obj.transform.m41, obj.transform.m42, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeRotation, - ^(SCNNode *obj, CGFloat values[]) { - values_from_vec4(values, obj.rotation); - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.rotation = values_to_vec4(values); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeRotationX, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.rotation.x; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.rotation = SCNVector4Make(1.0, obj.rotation.y, obj.rotation.z, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeRotationY, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.rotation.y; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.rotation = SCNVector4Make(obj.rotation.x, 1.0, obj.rotation.z, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeRotationZ, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.rotation.z; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.rotation = SCNVector4Make(obj.rotation.x, obj.rotation.y, 1.0, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeRotationW, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.rotation.w; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.rotation = SCNVector4Make(obj.rotation.x, obj.rotation.y, obj.rotation.z, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeEulerAngles, - ^(SCNNode *obj, CGFloat values[]) { - values_from_vec3(values, obj.eulerAngles); - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.eulerAngles = values_to_vec3(values); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeEulerAnglesX, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.eulerAngles.x; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.eulerAngles = SCNVector3Make(values[0], obj.eulerAngles.y, obj.eulerAngles.z); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeEulerAnglesY, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.eulerAngles.y; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.eulerAngles = SCNVector3Make(obj.eulerAngles.x, values[0], obj.eulerAngles.z); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeEulerAnglesZ, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.eulerAngles.z; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.eulerAngles = SCNVector3Make(obj.eulerAngles.x, obj.eulerAngles.y, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeOrientation, - ^(SCNNode *obj, CGFloat values[]) { - values_from_vec4(values, obj.orientation); - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.orientation = values_to_vec4(values); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeOrientationX, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.orientation.x; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.orientation = SCNVector4Make(values[0], obj.orientation.y, obj.orientation.z, obj.orientation.w); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeOrientationY, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.orientation.y; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.orientation = SCNVector4Make(obj.orientation.x, values[0], obj.orientation.z, obj.orientation.w); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeOrientationZ, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.orientation.z; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.orientation = SCNVector4Make(obj.orientation.x, obj.orientation.y, values[0], obj.orientation.w); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeOrientationW, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.orientation.w; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.orientation = SCNVector4Make(obj.orientation.x, obj.orientation.y, obj.orientation.z, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeScale, - ^(SCNNode *obj, CGFloat values[]) { - values_from_vec3(values, obj.scale); - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.scale = values_to_vec3(values); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeScaleX, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.scale.x; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.scale = SCNVector3Make(values[0], obj.scale.y, obj.scale.z); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeScaleY, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.scale.y; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.position = SCNVector3Make(obj.scale.x, values[0], obj.scale.z); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeScaleZ, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.scale.z; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.scale = SCNVector3Make(obj.scale.x, obj.scale.y, values[0]); - }, - kPOPThresholdScale - }, - - {kPOPSCNNodeScaleXY, - ^(SCNNode *obj, CGFloat values[]) { - values[0] = obj.scale.x; - values[1] = obj.scale.y; - }, - ^(SCNNode *obj, const CGFloat values[]) { - obj.scale = SCNVector3Make(values[0], values[1], obj.scale.z); - }, - kPOPThresholdScale - }, - -#endif #if TARGET_OS_IPHONE diff --git a/submodules/LegacyComponents/Sources/POPAnimationRuntime.mm b/submodules/LegacyComponents/Sources/POPAnimationRuntime.mm index 28412a0e8b..4e27a6e006 100644 --- a/submodules/LegacyComponents/Sources/POPAnimationRuntime.mm +++ b/submodules/LegacyComponents/Sources/POPAnimationRuntime.mm @@ -124,18 +124,10 @@ static bool FBCompareTypeEncoding(const char *objctype, POPValueType type) ); case kPOPValueSCNVector3: -#if SCENEKIT_SDK_AVAILABLE - return strcmp(objctype, @encode(SCNVector3)) == 0; -#else return false; -#endif case kPOPValueSCNVector4: -#if SCENEKIT_SDK_AVAILABLE - return strcmp(objctype, @encode(SCNVector4)) == 0; -#else return false; -#endif default: return false; @@ -229,16 +221,6 @@ id POPBox(VectorConstRef vec, POPValueType type, bool force) return (__bridge_transfer id)vec->cg_color(); break; } -#if SCENEKIT_SDK_AVAILABLE - case kPOPValueSCNVector3: { - return [NSValue valueWithSCNVector3:vec->scn_vector3()]; - break; - } - case kPOPValueSCNVector4: { - return [NSValue valueWithSCNVector4:vec->scn_vector4()]; - break; - } -#endif default: return force ? [NSValue valueWithCGPoint:vec->cg_point()] : nil; break; @@ -274,14 +256,6 @@ static VectorRef vectorize(id value, POPValueType type) case kPOPValueColor: vec = Vector::new_cg_color(POPCGColorWithColor(value)); break; -#if SCENEKIT_SDK_AVAILABLE - case kPOPValueSCNVector3: - vec = Vector::new_scn_vector3([value SCNVector3Value]); - break; - case kPOPValueSCNVector4: - vec = Vector::new_scn_vector4([value SCNVector4Value]); - break; -#endif default: break; } diff --git a/submodules/LegacyComponents/Sources/POPCGUtils.h b/submodules/LegacyComponents/Sources/POPCGUtils.h index a170750d07..f754d66140 100644 --- a/submodules/LegacyComponents/Sources/POPCGUtils.h +++ b/submodules/LegacyComponents/Sources/POPCGUtils.h @@ -13,10 +13,6 @@ #endif #import "POPDefines.h" -#if SCENEKIT_SDK_AVAILABLE -#import -#endif - #if TARGET_OS_IPHONE @class UIColor; #endif @@ -38,18 +34,6 @@ NS_INLINE CGRect values_to_rect(const CGFloat values[]) return CGRectMake(values[0], values[1], values[2], values[3]); } -#if SCENEKIT_SDK_AVAILABLE -NS_INLINE SCNVector3 values_to_vec3(const CGFloat values[]) -{ - return SCNVector3Make(values[0], values[1], values[2]); -} - -NS_INLINE SCNVector4 values_to_vec4(const CGFloat values[]) -{ - return SCNVector4Make(values[0], values[1], values[2], values[3]); -} -#endif - #if TARGET_OS_IPHONE NS_INLINE UIEdgeInsets values_to_edge_insets(const CGFloat values[]) @@ -79,23 +63,6 @@ NS_INLINE void values_from_rect(CGFloat values[], CGRect r) values[3] = r.size.height; } -#if SCENEKIT_SDK_AVAILABLE -NS_INLINE void values_from_vec3(CGFloat values[], SCNVector3 v) -{ - values[0] = v.x; - values[1] = v.y; - values[2] = v.z; -} - -NS_INLINE void values_from_vec4(CGFloat values[], SCNVector4 v) -{ - values[0] = v.x; - values[1] = v.y; - values[2] = v.z; - values[3] = v.w; -} -#endif - #if TARGET_OS_IPHONE NS_INLINE void values_from_edge_insets(CGFloat values[], UIEdgeInsets i) diff --git a/submodules/LegacyComponents/Sources/POPGeometry.mm b/submodules/LegacyComponents/Sources/POPGeometry.mm index 41998b10b2..64d11341c9 100644 --- a/submodules/LegacyComponents/Sources/POPGeometry.mm +++ b/submodules/LegacyComponents/Sources/POPGeometry.mm @@ -69,26 +69,4 @@ #if TARGET_OS_IPHONE #import "POPDefines.h" -#if SCENEKIT_SDK_AVAILABLE -#import - -/** - Dirty hacks because iOS is weird and decided to define both SCNVector3's and SCNVector4's objCType as "t". However @encode(SCNVector3) and @encode(SCNVector4) both return the proper definition ("{SCNVector3=fff}" and "{SCNVector4=ffff}" respectively) - - [[NSValue valueWithSCNVector3:SCNVector3Make(0.0, 0.0, 0.0)] objcType] returns "t", whereas it should return "{SCNVector3=fff}". - - *flips table* - */ -@implementation NSValue (SceneKitFixes) - -+ (NSValue *)valueWithSCNVector3:(SCNVector3)vec3 { - return [NSValue valueWithBytes:&vec3 objCType:@encode(SCNVector3)]; -} - -+ (NSValue *)valueWithSCNVector4:(SCNVector4)vec4 { - return [NSValue valueWithBytes:&vec4 objCType:@encode(SCNVector4)]; -} - -@end -#endif #endif diff --git a/submodules/LegacyComponents/Sources/POPVector.h b/submodules/LegacyComponents/Sources/POPVector.h index 630f17307b..48a0f72617 100644 --- a/submodules/LegacyComponents/Sources/POPVector.h +++ b/submodules/LegacyComponents/Sources/POPVector.h @@ -22,10 +22,6 @@ #import "POPMath.h" #import "POPDefines.h" -#if SCENEKIT_SDK_AVAILABLE -#import -#endif - namespace POP { /** Fixed two-size vector class */ @@ -342,16 +338,6 @@ namespace POP { // CGColorRef support CGColorRef cg_color() const CF_RETURNS_RETAINED; static Vector *new_cg_color(CGColorRef color); - -#if SCENEKIT_SDK_AVAILABLE - // SCNVector3 support - SCNVector3 scn_vector3() const; - static Vector *new_scn_vector3(const SCNVector3 &vec3); - - // SCNVector4 support - SCNVector4 scn_vector4() const; - static Vector *new_scn_vector4(const SCNVector4 &vec4); -#endif // operator overloads CGFloat &operator[](size_t i) const { diff --git a/submodules/LegacyComponents/Sources/POPVector.mm b/submodules/LegacyComponents/Sources/POPVector.mm index 98007ef172..481e5ebe75 100644 --- a/submodules/LegacyComponents/Sources/POPVector.mm +++ b/submodules/LegacyComponents/Sources/POPVector.mm @@ -250,37 +250,6 @@ namespace POP POPCGColorGetRGBAComponents(color, rgba); return new_vector(4, rgba); } - -#if SCENEKIT_SDK_AVAILABLE - SCNVector3 Vector::scn_vector3() const - { - return _count < 3 ? SCNVector3Make(0.0, 0.0, 0.0) : SCNVector3Make(_values[0], _values[1], _values[2]); - } - - Vector *Vector::new_scn_vector3(const SCNVector3 &vec3) - { - Vector *v = new Vector(3); - v->_values[0] = vec3.x; - v->_values[1] = vec3.y; - v->_values[2] = vec3.z; - return v; - } - - SCNVector4 Vector::scn_vector4() const - { - return _count < 4 ? SCNVector4Make(0.0, 0.0, 0.0, 0.0) : SCNVector4Make(_values[0], _values[1], _values[2], _values[3]); - } - - Vector *Vector::new_scn_vector4(const SCNVector4 &vec4) - { - Vector *v = new Vector(4); - v->_values[0] = vec4.x; - v->_values[1] = vec4.y; - v->_values[2] = vec4.z; - v->_values[3] = vec4.w; - return v; - } -#endif void Vector::subRound(CGFloat sub) { diff --git a/submodules/LegacyComponents/Sources/TGAlphacodePanelCell.m b/submodules/LegacyComponents/Sources/TGAlphacodePanelCell.m index 7845f7c863..792388471b 100644 --- a/submodules/LegacyComponents/Sources/TGAlphacodePanelCell.m +++ b/submodules/LegacyComponents/Sources/TGAlphacodePanelCell.m @@ -95,12 +95,18 @@ NSString *const TGAlphacodePanelCellKind = @"TGAlphacodePanelCell"; CGFloat leftInset = 11.0f; CGFloat rightInset = 6.0f; - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGSize titleSize = [_emojiLabel.text sizeWithFont:_emojiLabel.font]; +#pragma clang diagnostic pop titleSize.width = CGCeil(MIN((boundsSize.width - leftInset - rightInset) * 3.0f / 4.0f, titleSize.width)); titleSize.height = CGCeil(titleSize.height); - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGSize descriptionSize = [_descriptionLabel.text sizeWithFont:_descriptionLabel.font]; +#pragma clang diagnostic pop descriptionSize.width = CGCeil(MIN(boundsSize.width - leftInset - 40.0f, descriptionSize.width)); _emojiLabel.frame = CGRectMake(leftInset, CGFloor((boundsSize.height - titleSize.height) / 2.0f), titleSize.width, titleSize.height); diff --git a/submodules/LegacyComponents/Sources/TGAttachmentAssetCell.m b/submodules/LegacyComponents/Sources/TGAttachmentAssetCell.m index 51b23a8596..b50fa2fcdb 100644 --- a/submodules/LegacyComponents/Sources/TGAttachmentAssetCell.m +++ b/submodules/LegacyComponents/Sources/TGAttachmentAssetCell.m @@ -59,8 +59,7 @@ _gradientView.hidden = true; [self addSubview:_gradientView]; - if (iosMajorVersion() >= 11) - { + if (@available(iOS 11.0, *)) { _imageView.accessibilityIgnoresInvertColors = true; _gradientView.accessibilityIgnoresInvertColors = true; } @@ -87,8 +86,9 @@ [_checkButton addTarget:self action:@selector(checkButtonPressed) forControlEvents:UIControlEventTouchUpInside]; [self addSubview:_checkButton]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { _checkButton.accessibilityIgnoresInvertColors = true; + } } if (_itemSelectedDisposable == nil) diff --git a/submodules/LegacyComponents/Sources/TGAttachmentCameraView.m b/submodules/LegacyComponents/Sources/TGAttachmentCameraView.m index 30821b674e..2c0c95f510 100644 --- a/submodules/LegacyComponents/Sources/TGAttachmentCameraView.m +++ b/submodules/LegacyComponents/Sources/TGAttachmentCameraView.m @@ -4,6 +4,7 @@ #import #import "TGAttachmentMenuCell.h" +#import "TGCameraController.h" #import #import @@ -46,6 +47,8 @@ _camera = camera; _previewView = [[TGCameraPreviewView alloc] initWithFrame:CGRectMake(0, 0, 84.0f, 84.0f)]; + [_previewView fadeInAnimated:false]; + [_previewView beginTransitionWithSnapshotImage:[TGCameraController startImage] animated:false]; [_wrapperView addSubview:_previewView]; [camera attachPreviewView:_previewView]; @@ -101,8 +104,7 @@ _zoomedView.userInteractionEnabled = false; [self addSubview:_zoomedView]; - if (iosMajorVersion() >= 11) - { + if (@available(iOS 11.0, *)) { _fadeView.accessibilityIgnoresInvertColors = true; _iconView.accessibilityIgnoresInvertColors = true; } diff --git a/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m b/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m index cc036da905..420fe63797 100644 --- a/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m +++ b/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m @@ -246,8 +246,9 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500; } _collectionView = [[TGAttachmentCarouselCollectionView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, TGAttachmentZoomedPhotoHeight + TGAttachmentEdgeInset * 2) collectionViewLayout:_smallLayout]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { _collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + } _collectionView.backgroundColor = [UIColor clearColor]; _collectionView.dataSource = self; _collectionView.delegate = self; diff --git a/submodules/LegacyComponents/Sources/TGAttachmentVideoCell.m b/submodules/LegacyComponents/Sources/TGAttachmentVideoCell.m index 4feda8a385..1df73fed2f 100644 --- a/submodules/LegacyComponents/Sources/TGAttachmentVideoCell.m +++ b/submodules/LegacyComponents/Sources/TGAttachmentVideoCell.m @@ -39,8 +39,9 @@ NSString *const TGAttachmentVideoCellIdentifier = @"AttachmentVideoCell"; _adjustmentsDisposable = [[SMetaDisposable alloc] init]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { _durationLabel.accessibilityIgnoresInvertColors = true; + } } return self; } diff --git a/submodules/LegacyComponents/Sources/TGCache.m b/submodules/LegacyComponents/Sources/TGCache.m index e202bfe64e..6fc28ec3d4 100644 --- a/submodules/LegacyComponents/Sources/TGCache.m +++ b/submodules/LegacyComponents/Sources/TGCache.m @@ -449,11 +449,6 @@ static NSFileManager *cacheFileManager = nil; { cacheRecord.date = CFAbsoluteTimeGetCurrent(); - if (false) - { - dataImage = [[UIImage alloc] initWithData:cacheRecord.object]; - } - else { NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:true] forKey:(id)kCGImageSourceShouldCache]; CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)cacheRecord.object, nil); diff --git a/submodules/LegacyComponents/Sources/TGCameraController.m b/submodules/LegacyComponents/Sources/TGCameraController.m index 999bd80845..811813ec86 100644 --- a/submodules/LegacyComponents/Sources/TGCameraController.m +++ b/submodules/LegacyComponents/Sources/TGCameraController.m @@ -107,6 +107,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus UIView *_backgroundView; TGCameraPreviewView *_previewView; TGCameraMainView *_interfaceView; + TGCameraCornersView *_cornersView; UIView *_overlayView; TGCameraFocusCrosshairsControl *_focusControl; TGCameraRectangleView *_rectangleView; @@ -127,7 +128,6 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus NSTimer *_switchToVideoTimer; NSTimer *_startRecordingTimer; - bool _recordingByShutterHold; bool _stopRecordingOnRelease; bool _shownMicrophoneAlert; @@ -136,6 +136,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus bool _saveCapturedMedia; bool _shutterIsBusy; + bool _crossfadingForZoom; UIImpactFeedbackGenerator *_feedbackGenerator; @@ -302,12 +303,12 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { - _interfaceView = [[TGCameraMainPhoneView alloc] initWithFrame:screenBounds avatar:_intent == TGCameraControllerAvatarIntent]; + _interfaceView = [[TGCameraMainPhoneView alloc] initWithFrame:screenBounds avatar:_intent == TGCameraControllerAvatarIntent hasUltrawideCamera:_camera.hasUltrawideCamera hasTelephotoCamera:_camera.hasTelephotoCamera]; [_interfaceView setInterfaceOrientation:interfaceOrientation animated:false]; } else { - _interfaceView = [[TGCameraMainTabletView alloc] initWithFrame:screenBounds avatar:_intent == TGCameraControllerAvatarIntent]; + _interfaceView = [[TGCameraMainTabletView alloc] initWithFrame:screenBounds avatar:_intent == TGCameraControllerAvatarIntent hasUltrawideCamera:_camera.hasUltrawideCamera hasTelephotoCamera:_camera.hasTelephotoCamera]; [_interfaceView setInterfaceOrientation:interfaceOrientation animated:false]; CGSize referenceSize = [self referenceViewSizeForOrientation:interfaceOrientation]; @@ -317,6 +318,9 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus _interfaceView.transform = CGAffineTransformMakeRotation(TGRotationForInterfaceOrientation(interfaceOrientation)); _interfaceView.frame = CGRectMake(0, 0, referenceSize.width, referenceSize.height); } + + _cornersView = [[TGCameraCornersView alloc] init]; + if (_intent == TGCameraControllerPassportIdIntent) [_interfaceView setDocumentFrameHidden:false]; _selectedItemsModel = [[TGMediaPickerGallerySelectedItemsModel alloc] initWithSelectionContext:nil items:[_items copy]]; @@ -378,6 +382,15 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus [strongSelf->_camera setFlashMode:mode]; }; + _interfaceView.zoomChanged = ^(CGFloat level, bool animated) + { + __strong TGCameraController *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + [strongSelf->_camera setZoomLevel:level animated:animated]; + }; + _interfaceView.shutterPressed = ^(bool fromHardwareButton) { __strong TGCameraController *strongSelf = weakSelf; @@ -405,6 +418,14 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus [strongSelf shutterReleased]; }; + _interfaceView.shutterPanGesture = ^(UIPanGestureRecognizer *gesture) { + __strong TGCameraController *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + [strongSelf handleRamp:gesture]; + }; + _interfaceView.cancelPressed = ^ { __strong TGCameraController *strongSelf = weakSelf; @@ -433,15 +454,17 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus if (_intent != TGCameraControllerGenericIntent && _intent != TGCameraControllerAvatarIntent) [_interfaceView setHasModeControl:false]; - if (iosMajorVersion() >= 11) - { + if (@available(iOS 11.0, *)) { _backgroundView.accessibilityIgnoresInvertColors = true; _interfaceView.accessibilityIgnoresInvertColors = true; _focusControl.accessibilityIgnoresInvertColors = true; } [_autorotationCorrectionView addSubview:_interfaceView]; - + if ((int)self.view.frame.size.width > 320 || [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { + [_autorotationCorrectionView addSubview:_cornersView]; + } + _photoSwipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipe:)]; _photoSwipeGestureRecognizer.delegate = self; [_autorotationCorrectionView addGestureRecognizer:_photoSwipeGestureRecognizer]; @@ -555,8 +578,6 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus } else { - strongSelf->_camera.zoomLevel = 0.0f; - [strongSelf->_camera captureNextFrameCompletion:^(UIImage *image) { if (commitBlock != nil) @@ -580,8 +601,8 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus TGDispatchOnMainThread(^ { - [strongSelf->_previewView endTransitionAnimated:true]; - + [strongSelf->_interfaceView setZoomLevel:1.0f displayNeeded:false]; + if (!strongSelf->_dismissing) { strongSelf.view.userInteractionEnabled = true; @@ -613,6 +634,8 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus }; } } + + [strongSelf->_previewView endTransitionAnimated:true]; }); }; @@ -625,7 +648,9 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus [strongSelf->_focusControl reset]; [strongSelf->_interfaceView setHasFlash:targetPositionHasFlash]; - [strongSelf->_interfaceView setHasZoom:targetPositionHasZoom]; + if (!targetPositionHasZoom) { + [strongSelf->_interfaceView setHasZoom:targetPositionHasZoom]; + } strongSelf->_camera.zoomLevel = 0.0f; strongSelf.view.userInteractionEnabled = false; @@ -648,9 +673,15 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus }]; }); }]; + + if (@available(iOS 13.0, *)) { + [strongSelf->_feedbackGenerator impactOccurredWithIntensity:0.5]; + } else { + [strongSelf->_feedbackGenerator impactOccurred]; + } }; - _camera.finishedPositionChange = ^ + _camera.finishedPositionChange = ^(bool targetPositionHasZoom) { __strong TGCameraController *strongSelf = weakSelf; if (strongSelf == nil) @@ -659,8 +690,12 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus TGDispatchOnMainThread(^ { [strongSelf->_previewView endTransitionAnimated:true]; - [strongSelf->_interfaceView setZoomLevel:0.0f displayNeeded:false]; + [strongSelf->_interfaceView setZoomLevel:1.0f displayNeeded:false]; + if (targetPositionHasZoom) { + [strongSelf->_interfaceView setHasZoom:targetPositionHasZoom]; + } + if (strongSelf->_camera.hasFlash && strongSelf->_camera.flashActive) [strongSelf->_interfaceView setFlashActive:true]; @@ -698,8 +733,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus TGDispatchOnMainThread(^ { - if (!strongSelf->_camera.isRecordingVideo) - [strongSelf->_interfaceView setFlashActive:active]; + [strongSelf->_interfaceView setFlashActive:active]; }); }; @@ -763,6 +797,30 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus } } }; + + _camera.captureSession.crossfadeNeeded = ^{ + __strong TGCameraController *strongSelf = weakSelf; + if (strongSelf != nil) + { + if (strongSelf->_crossfadingForZoom) { + return; + } + strongSelf->_crossfadingForZoom = true; + + [strongSelf->_camera captureNextFrameCompletion:^(UIImage *image) + { + TGDispatchOnMainThread(^ + { + [strongSelf->_previewView beginTransitionWithSnapshotImage:image animated:false]; + + TGDispatchAfter(0.15, dispatch_get_main_queue(), ^{ + [strongSelf->_previewView endTransitionAnimated:true]; + strongSelf->_crossfadingForZoom = false; + }); + }); + }]; + }; + }; } #pragma mark - View Life Cycle @@ -866,12 +924,22 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus animation.duration = 0.2f; [_interfaceView.layer addAnimation:animation forKey:@"opacity"]; + CABasicAnimation *cornersAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; + cornersAnimation.fromValue = @(_cornersView.alpha); + cornersAnimation.toValue = @(hidden ? 0.0f : 1.0f); + cornersAnimation.duration = 0.2f; + [_cornersView.layer addAnimation:cornersAnimation forKey:@"opacity"]; + _interfaceView.alpha = hidden ? 0.0f : 1.0f; + _cornersView.alpha = hidden ? 0.0 : 1.0; } else { [_interfaceView.layer removeAllAnimations]; - _interfaceView.alpha = 0.0f; + _interfaceView.alpha = hidden ? 0.0 : 1.0; + + [_cornersView.layer removeAllAnimations]; + _cornersView.alpha = hidden ? 0.0 : 1.0; } } @@ -951,7 +1019,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus - (void)shutterPressed { - if (iosMajorVersion() >= 13.0) { + if (@available(iOS 13.0, *)) { [_feedbackGenerator impactOccurredWithIntensity:0.5]; } else { [_feedbackGenerator impactOccurred]; @@ -991,7 +1059,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus - (void)shutterReleased { - if (iosMajorVersion() >= 13.0) { + if (@available(iOS 13.0, *)) { [_feedbackGenerator impactOccurredWithIntensity:0.6]; } else { [_feedbackGenerator impactOccurred]; @@ -1033,7 +1101,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus __strong TGCameraController *strongSelf = weakSelf; if (strongSelf == nil) return; - + TGDispatchOnMainThread(^ { strongSelf->_shutterIsBusy = false; @@ -1051,6 +1119,10 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus strongSelf->_camera.disabled = false; } }); + + [[SQueue concurrentDefaultQueue] dispatch:^{ + [TGCameraController generateStartImageWithImage:result]; + }]; }]; } else if (cameraMode == PGCameraModeVideo || cameraMode == PGCameraModeSquareVideo || cameraMode == PGCameraModeSquareSwing) @@ -1092,8 +1164,12 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus { _stopRecordingOnRelease = false; - _camera.disabled = true; [_camera stopVideoRecording]; + TGDispatchAfter(0.3, dispatch_get_main_queue(), ^{ + [_camera setZoomLevel:1.0]; + [_interfaceView setZoomLevel:1.0 displayNeeded:false]; + _camera.disabled = true; + }); [_buttonHandler ignoreEventsFor:1.0f andDisable:[self willPresentResultController]]; } @@ -2118,10 +2194,10 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus [UIView animateWithDuration:0.3f delay:0.1f options:UIViewAnimationOptionCurveLinear animations:^ { _interfaceView.alpha = 1.0f; + _cornersView.alpha = 1.0; } completion:nil]; _interfaceView.previewViewFrame = _previewView.frame; - [_interfaceView layoutPreviewRelativeViews]; return targetFrame; } @@ -2136,11 +2212,13 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus _backgroundView.alpha = 0.0f; _interfaceView.alpha = 0.0f; + _cornersView.alpha = 0.0; [UIView animateWithDuration:0.3f animations:^ { _backgroundView.alpha = 1.0f; _interfaceView.alpha = 1.0f; + _cornersView.alpha = 1.0; }]; CGRect fromFrame = rect; @@ -2154,14 +2232,30 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus frameAnimation.springSpeed = 20; frameAnimation.springBounciness = 1; [_previewView pop_addAnimation:frameAnimation forKey:@"frame"]; + + POPSpringAnimation *cornersFrameAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPViewFrame]; + cornersFrameAnimation.fromValue = [NSValue valueWithCGRect:fromFrame]; + cornersFrameAnimation.toValue = [NSValue valueWithCGRect:toFrame]; + cornersFrameAnimation.springSpeed = 20; + cornersFrameAnimation.springBounciness = 1; + [_cornersView pop_addAnimation:cornersFrameAnimation forKey:@"frame"]; } else { _previewView.frame = toFrame; + _cornersView.frame = toFrame; } _interfaceView.previewViewFrame = toFrame; - [_interfaceView layoutPreviewRelativeViews]; +} + ++ (void)generateStartImageWithImage:(UIImage *)frameImage { + CGFloat minSize = MIN(frameImage.size.width, frameImage.size.height); + UIImage *image = TGPhotoEditorCrop(frameImage, nil, UIImageOrientationUp, 0.0f, CGRectMake((frameImage.size.width - minSize) / 2.0f, (frameImage.size.height - minSize) / 2.0f, minSize, minSize), false, CGSizeMake(240.0f, 240.0f), frameImage.size, true); + UIImage *startImage = TGSecretBlurredAttachmentImage(image, image.size, NULL, false, 0); + TGDispatchOnMainThread(^{ + [TGCameraController saveStartImage:startImage]; + }); } - (void)beginTransitionOutWithVelocity:(CGFloat)velocity @@ -2169,19 +2263,16 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus _dismissing = true; self.view.userInteractionEnabled = false; + _focusControl.active = false; _rectangleView.hidden = true; - [UIView animateWithDuration:0.3f animations:^ - { - //[_context setApplicationStatusBarAlpha:1.0f]; - }]; - [self setInterfaceHidden:true animated:true]; [UIView animateWithDuration:0.25f animations:^ { _backgroundView.alpha = 0.0f; + _cornersView.alpha = 0.0; }]; CGRect referenceFrame = CGRectZero; @@ -2189,6 +2280,11 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus referenceFrame = self.beginTransitionOut(); __weak TGCameraController *weakSelf = self; + [_camera captureNextFrameCompletion:^(UIImage *frameImage) { + [[SQueue concurrentDefaultQueue] dispatch:^{ + [TGCameraController generateStartImageWithImage:frameImage]; + }]; + }]; if (_standalone) { [self simpleTransitionOutWithVelocity:velocity completion:^ @@ -2236,6 +2332,13 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus [strongSelf dismiss]; }; [_previewView pop_addAnimation:frameAnimation forKey:@"frame"]; + + POPSpringAnimation *cornersAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPViewFrame]; + cornersAnimation.fromValue = [NSValue valueWithCGRect:_cornersView.frame]; + cornersAnimation.toValue = [NSValue valueWithCGRect:referenceFrame]; + cornersAnimation.springSpeed = 20; + cornersAnimation.springBounciness = 1; + [_cornersView pop_addAnimation:cornersAnimation forKey:@"frame"]; } else { @@ -2262,21 +2365,6 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus } [self dismiss]; }]; - - return; - - [UIView animateWithDuration:0.3 delay:0.0f options:(7 << 16) animations:^ - { - resultController.view.frame = CGRectOffset(resultController.view.frame, 0, resultController.view.frame.size.height); - } completion:^(__unused BOOL finished) - { - if (resultController.customDismissSelf) { - resultController.customDismissSelf(); - } else { - [resultController dismiss]; - } - [self dismiss]; - }]; } - (void)simpleTransitionOutWithVelocity:(CGFloat)velocity completion:(void (^)())completion @@ -2292,6 +2380,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus [UIView animateWithDuration:ABS(distance / velocity) animations:^ { _previewView.frame = targetFrame; + _cornersView.frame = targetFrame; } completion:^(__unused BOOL finished) { if (completion) @@ -2308,11 +2397,13 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus [UIView animateWithDuration:0.3 animations:^ { _previewView.frame = frame; + _cornersView.frame = frame; }]; } else { _previewView.frame = frame; + _cornersView.frame = frame; } } @@ -2349,12 +2440,12 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus { CGRect frame = [TGCameraController _cameraPreviewFrameForScreenSize:TGScreenSize() mode:mode]; _interfaceView.previewViewFrame = frame; - [_interfaceView layoutPreviewRelativeViews]; [_interfaceView updateForCameraModeChangeAfterResize]; [UIView animateWithDuration:0.3f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionLayoutSubviews animations:^ { _previewView.frame = frame; + _cornersView.frame = frame; _overlayView.frame = frame; } completion:nil]; } @@ -2514,8 +2605,11 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus { case UIGestureRecognizerStateChanged: { - CGFloat delta = (gestureRecognizer.scale - 1.0f) / 1.5f; - CGFloat value = MAX(0.0f, MIN(1.0f, _camera.zoomLevel + delta)); + CGFloat delta = (gestureRecognizer.scale - 1.0f) * 1.25; + if (_camera.zoomLevel > 2.0) { + delta *= 2.0; + } + CGFloat value = MAX(_camera.minZoomLevel, MIN(_camera.maxZoomLevel, _camera.zoomLevel + delta)); [_camera setZoomLevel:value]; [_interfaceView setZoomLevel:value displayNeeded:true]; @@ -2536,6 +2630,37 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus } } +- (void)handleRamp:(UIPanGestureRecognizer *)gestureRecognizer +{ + if (!_stopRecordingOnRelease) { + [gestureRecognizer setTranslation:CGPointZero inView:self.view]; + return; + } + switch (gestureRecognizer.state) + { + case UIGestureRecognizerStateChanged: + { + CGPoint translation = [gestureRecognizer translationInView:self.view]; + + CGFloat value = 1.0; + if (translation.y < 0.0) { + value = MIN(8.0, value + ABS(translation.y) / 60.0); + } + + [_camera setZoomLevel:value]; + [_interfaceView setZoomLevel:value displayNeeded:true]; + } + break; + case UIGestureRecognizerStateEnded: + { + [self shutterReleased]; + } + break; + default: + break; + } +} + - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { if (gestureRecognizer == _panGestureRecognizer) @@ -2558,7 +2683,9 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus { case PGCameraModeVideo: { - if (widescreenWidth == 896.0f) + if (widescreenWidth == 926.0f) + return CGRectMake(0, 82, screenSize.width, screenSize.height - 82 - 83); + else if (widescreenWidth == 896.0f) return CGRectMake(0, 77, screenSize.width, screenSize.height - 77 - 83); else if (widescreenWidth == 812.0f) return CGRectMake(0, 77, screenSize.width, screenSize.height - 77 - 68); @@ -2583,6 +2710,8 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus default: { + if (widescreenWidth == 926.0f) + return CGRectMake(0, 121, screenSize.width, screenSize.height - 121 - 234); if (widescreenWidth == 896.0f) return CGRectMake(0, 121, screenSize.width, screenSize.height - 121 - 223); else if (widescreenWidth == 844.0f) @@ -2631,11 +2760,6 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus } } -+ (bool)useLegacyCamera -{ - return false; -} - + (NSArray *)resultSignalsForSelectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext currentItem:(id)currentItem storeAssets:(bool)storeAssets saveEditedPhotos:(bool)saveEditedPhotos descriptionGenerator:(id (^)(id, NSString *, NSArray *, NSString *))descriptionGenerator { NSMutableArray *signals = [[NSMutableArray alloc] init]; @@ -3018,4 +3142,24 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus return value; } +#pragma mark - Start Image + +static UIImage *startImage = nil; + ++ (UIImage *)startImage +{ + if (startImage == nil) + startImage = TGComponentsImageNamed (@"CameraPlaceholder.jpg"); + + return startImage; +} + ++ (void)saveStartImage:(UIImage *)image +{ + if (image == nil) + return; + + startImage = image; +} + @end diff --git a/submodules/LegacyComponents/Sources/TGCameraFlashControl.m b/submodules/LegacyComponents/Sources/TGCameraFlashControl.m index 65bec10499..5841fa771f 100644 --- a/submodules/LegacyComponents/Sources/TGCameraFlashControl.m +++ b/submodules/LegacyComponents/Sources/TGCameraFlashControl.m @@ -2,21 +2,121 @@ #import "LegacyComponentsInternal.h" +#import "TGImageUtils.h" + #import "UIControl+HitTestEdgeInsets.h" #import "TGCameraInterfaceAssets.h" #import +#import "POPBasicAnimation.h" + const CGFloat TGCameraFlashControlHeight = 44.0f; +@interface TGCameraFlashIcon: UIView +{ + bool _active; + CGFloat _progress; + bool _on; +} +@end + +@implementation TGCameraFlashIcon + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self != nil) { + self.contentMode = UIViewContentModeRedraw; + self.opaque = false; + self.backgroundColor = [UIColor clearColor]; + } + return self; +} + +- (void)setOn:(bool)on animated:(bool)animated { + _on = on; + if (animated) { + POPBasicAnimation *animation = [POPBasicAnimation animation]; + animation.property = [POPAnimatableProperty propertyWithName:@"progress" initializer:^(POPMutableAnimatableProperty *prop) + { + prop.readBlock = ^(TGCameraFlashIcon *view, CGFloat values[]) + { + if (view != nil) { + values[0] = view->_progress; + } + }; + + prop.writeBlock = ^(TGCameraFlashIcon *view, const CGFloat values[]) + { + view->_progress = values[0]; + [view setNeedsDisplay]; + }; + + prop.threshold = 0.03f; + }]; + animation.fromValue = @(_progress); + animation.toValue = @(on ? 1.0 : 0.0); + animation.duration = 0.2; + animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + [self pop_addAnimation:animation forKey:@"progress"]; + } else { + _progress = on ? 1.0 : 0.0; + [self setNeedsDisplay]; + } +} + +- (void)setActive:(bool)active { + _active = active; + [self setNeedsDisplay]; +} + +- (void)drawRect:(CGRect)__unused rect +{ + CGContextRef context = UIGraphicsGetCurrentContext(); + CGRect bounds = CGRectMake(0, 0, rect.size.width, rect.size.height); + + CGContextClearRect(context, bounds); + + UIImage *iconImage = [UIImage imageNamed:@"Camera/Flash"]; + + if (_active && _on) { + CGContextSetFillColorWithColor(context, [TGCameraInterfaceAssets accentColor].CGColor); + CGContextFillEllipseInRect(context, CGRectInset(bounds, 2.5, 2.5)); + + [TGTintedImage(iconImage, [UIColor blackColor]) drawInRect:CGRectMake(0, 0, 30, 30)]; + } else { + CGContextSetLineWidth(context, 1.0); + CGContextSetStrokeColorWithColor(context, [UIColor colorWithWhite:1.0 alpha:0.5].CGColor); + CGContextStrokeEllipseInRect(context, CGRectInset(bounds, 3.0, 3.0)); + + [TGTintedImage(iconImage, [UIColor whiteColor]) drawInRect:CGRectMake(0, 0, 30, 30)]; + } + + CGFloat lineProgress = 1.0 - _progress; + + if (lineProgress > 0.0) { + CGMutablePathRef path = CGPathCreateMutable(); + CGPathMoveToPoint(path, NULL, 5, 5); + CGPathAddLineToPoint(path, NULL, 5 + (bounds.size.width - 10.0) * lineProgress, 5 + (bounds.size.height - 10.0) * lineProgress); + + CGPathRef strokedPath = CGPathCreateCopyByStrokingPath(path, NULL, 2.0f, kCGLineCapRound, kCGLineJoinMiter, 10); + CGContextAddPath(context, strokedPath); + CGPathRelease(strokedPath); + CGPathRelease(path); + + CGContextSetBlendMode(context, kCGBlendModeCopy); + CGContextSetStrokeColorWithColor(context, [UIColor clearColor].CGColor); + CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); + CGContextDrawPath(context, kCGPathFillStroke); + } +} + +@end + @interface TGCameraFlashControl () { - UIButton *_flashIconView; - UIButton *_autoButton; - UIButton *_onButton; - UIButton *_offButton; - - bool _active; + TGCameraFlashIcon *_icon; + UIButton *_button; } @end @@ -27,23 +127,22 @@ const CGFloat TGCameraFlashControlHeight = 44.0f; self = [super initWithFrame:frame]; if (self != nil) { + self.mode = PGCameraFlashModeOff; + self.hitTestEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10); - _flashIconView = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 44, 44)]; - _flashIconView.adjustsImageWhenHighlighted = false; - _flashIconView.contentMode = UIViewContentModeCenter; - _flashIconView.exclusiveTouch = true; - _flashIconView.hitTestEdgeInsets = UIEdgeInsetsMake(0, -10, 0, -10); - _flashIconView.tag = -1; - [_flashIconView setImage:[UIImage imageNamed:@"Camera/FlashOff"] forState:UIControlStateNormal]; - [_flashIconView addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside]; - [self addSubview:_flashIconView]; - - [UIView performWithoutAnimation:^ - { - self.mode = PGCameraFlashModeOff; - [self setActive:false animated:false]; - }]; + _icon = [[TGCameraFlashIcon alloc] initWithFrame:CGRectMake(7, 7, 30, 30)]; + _icon.userInteractionEnabled = false; + [self addSubview:_icon]; + + _button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 44, 44)]; + _button.adjustsImageWhenHighlighted = false; + _button.contentMode = UIViewContentModeCenter; + _button.exclusiveTouch = true; + _button.hitTestEdgeInsets = UIEdgeInsetsMake(0, -10, 0, -10); + _button.tag = -1; + [_button addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside]; + [self addSubview:_button]; } return self; } @@ -61,11 +160,11 @@ const CGFloat TGCameraFlashControlHeight = 44.0f; - (void)buttonPressed:(UIButton *)sender { if (_mode == PGCameraFlashModeOff) { - self.mode = PGCameraFlashModeOn; - [_flashIconView setImage:[UIImage imageNamed:@"Camera/FlashOn"] forState:UIControlStateNormal]; + self.mode = PGCameraFlashModeAuto; + [_icon setOn:true animated:true]; } else { self.mode = PGCameraFlashModeOff; - [_flashIconView setImage:[UIImage imageNamed:@"Camera/FlashOff"] forState:UIControlStateNormal]; + [_icon setOn:false animated:true]; } if (self.modeChanged != nil) @@ -75,35 +174,24 @@ const CGFloat TGCameraFlashControlHeight = 44.0f; - (void)setFlashUnavailable:(bool)unavailable { self.userInteractionEnabled = !unavailable; - [self setActive:false animated:false]; + self.alpha = unavailable ? 0.4 : 1.0; } -- (void)setActive:(bool)active animated:(bool)animated +- (void)setFlashActive:(bool)active { - return; + [_icon setActive:active]; } - (void)setMode:(PGCameraFlashMode)mode { _mode = mode; - - [self setActive:false animated:_active]; -} - -- (void)dismissAnimated:(bool)animated -{ - if (animated && _active) - [self setActive:false animated:animated]; - else - [self setActive:false animated:false]; + [_icon setOn:mode == PGCameraFlashModeAuto animated:true]; } - (void)setHidden:(BOOL)hidden { self.alpha = hidden ? 0.0f : 1.0f; super.hidden = hidden; - - [self setActive:false animated:false]; } - (void)setHidden:(bool)hidden animated:(bool)animated @@ -123,24 +211,18 @@ const CGFloat TGCameraFlashControlHeight = 44.0f; if (finished) self.hidden = hidden; - - [self setActive:false animated:false]; }]; } else { self.alpha = hidden ? 0.0f : 1.0f; super.hidden = hidden; - - [self setActive:false animated:false]; } } - (void)setInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { _interfaceOrientation = interfaceOrientation; - - [self setActive:false animated:false]; } @end diff --git a/submodules/LegacyComponents/Sources/TGCameraFlipButton.m b/submodules/LegacyComponents/Sources/TGCameraFlipButton.m index b4e05637cb..f2940f368f 100644 --- a/submodules/LegacyComponents/Sources/TGCameraFlipButton.m +++ b/submodules/LegacyComponents/Sources/TGCameraFlipButton.m @@ -11,6 +11,8 @@ self = [super initWithFrame:frame]; if (self != nil) { + self.adjustsImageWhenHighlighted = false; + self.modernHighlight = false; self.exclusiveTouch = true; self.backgroundColor = [TGCameraInterfaceAssets buttonColor]; self.layer.cornerRadius = 24.0; @@ -19,6 +21,63 @@ return self; } +- (void)_setHighligtedAnimated:(bool)highlighted animated:(bool)animated { + if (animated) { + [UIView animateWithDuration:0.3 animations:^{ + self.layer.sublayerTransform = highlighted ? CATransform3DMakeScale(0.9, 0.9, 1.0) : CATransform3DIdentity; + }]; + } else { + self.layer.sublayerTransform = highlighted ? CATransform3DMakeScale(0.9, 0.9, 1.0) : CATransform3DIdentity; + } +} + +- (void)setHidden:(BOOL)hidden +{ + self.alpha = hidden ? 0.0f : 1.0f; + super.hidden = hidden; +} + +- (void)setHidden:(bool)hidden animated:(bool)animated +{ + if (animated) + { + super.hidden = false; + self.userInteractionEnabled = false; + + [UIView animateWithDuration:0.25f animations:^ + { + self.alpha = hidden ? 0.0f : 1.0f; + } completion:^(BOOL finished) + { + self.userInteractionEnabled = true; + + if (finished) + self.hidden = hidden; + }]; + } + else + { + self.alpha = hidden ? 0.0f : 1.0f; + super.hidden = hidden; + } +} + +@end + +@implementation TGCameraSmallFlipButton + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self != nil) + { + self.exclusiveTouch = true; + UIImage *image = TGTintedImage(TGComponentsImageNamed(@"CameraFlipButton"), [UIColor whiteColor]); + [self setImage:image forState:UIControlStateNormal]; + } + return self; +} + - (void)setHidden:(BOOL)hidden { self.alpha = hidden ? 0.0f : 1.0f; @@ -60,6 +119,8 @@ self = [super initWithFrame:frame]; if (self != nil) { + self.adjustsImageWhenHighlighted = false; + self.modernHighlight = false; self.exclusiveTouch = true; self.backgroundColor = [TGCameraInterfaceAssets buttonColor]; self.layer.cornerRadius = 24.0; @@ -68,6 +129,16 @@ return self; } +- (void)_setHighligtedAnimated:(bool)highlighted animated:(bool)animated { + if (animated) { + [UIView animateWithDuration:0.3 animations:^{ + self.layer.sublayerTransform = highlighted ? CATransform3DMakeScale(0.9, 0.9, 1.0) : CATransform3DIdentity; + }]; + } else { + self.layer.sublayerTransform = highlighted ? CATransform3DMakeScale(0.9, 0.9, 1.0) : CATransform3DIdentity; + } +} + - (void)setHidden:(BOOL)hidden { self.alpha = hidden ? 0.0f : 1.0f; diff --git a/submodules/LegacyComponents/Sources/TGCameraInterfaceAssets.m b/submodules/LegacyComponents/Sources/TGCameraInterfaceAssets.m index f7a98b6e10..09e169d6c9 100644 --- a/submodules/LegacyComponents/Sources/TGCameraInterfaceAssets.m +++ b/submodules/LegacyComponents/Sources/TGCameraInterfaceAssets.m @@ -26,7 +26,7 @@ static NSString *TGCameraEncodeText(NSString *string, int key) + (UIColor *)accentColor { - return UIColorRGB(0xffd60a); + return UIColorRGB(0xf8d74a); } + (UIColor *)redColor diff --git a/submodules/LegacyComponents/Sources/TGCameraMainPhoneView.m b/submodules/LegacyComponents/Sources/TGCameraMainPhoneView.m index b61e0ff61f..c71d45d351 100644 --- a/submodules/LegacyComponents/Sources/TGCameraMainPhoneView.m +++ b/submodules/LegacyComponents/Sources/TGCameraMainPhoneView.m @@ -64,13 +64,9 @@ UIView *_videoLandscapePanelView; TGCameraFlashControl *_flashControl; - TGCameraFlashActiveView *_flashActiveView; - - TGCameraFlipButton *_topFlipButton; - - TGCameraZoomModeView *_zoomModeView; - TGCameraZoomWheelView *_zoomWheelView; + TGCameraSmallFlipButton *_topFlipButton; + bool _hasResults; CGFloat _topPanelOffset; @@ -87,6 +83,8 @@ bool _displayedTooltip; TGMenuContainerView *_tooltipContainerView; NSTimer *_tooltipTimer; + + int _dismissingWheelCounter; } @end @@ -103,23 +101,25 @@ @synthesize cancelPressed; @synthesize actionHandle = _actionHandle; -- (instancetype)initWithFrame:(CGRect)frame avatar:(bool)avatar +- (instancetype)initWithFrame:(CGRect)frame avatar:(bool)avatar hasUltrawideCamera:(bool)hasUltrawideCamera hasTelephotoCamera:(bool)hasTelephotoCamera { self = [super initWithFrame:frame]; if (self != nil) { _actionHandle = [[ASHandle alloc] initWithDelegate:self releaseOnMainThread:true]; + _dismissingWheelCounter = 0; + CGFloat shutterButtonWidth = 66.0f; CGSize screenSize = TGScreenSize(); CGFloat widescreenWidth = MAX(screenSize.width, screenSize.height); if (widescreenWidth == 926.0f) { - _topPanelOffset = 77.0f; - _topPanelHeight = 77.0f; + _topPanelOffset = 34.0f; + _topPanelHeight = 48.0f; _bottomPanelOffset = 94.0f; - _bottomPanelHeight = 155.0f; - _modeControlOffset = 6.0f; + _bottomPanelHeight = 140.0f; + _modeControlOffset = -2.0f; _modeControlHeight = 66.0f; _counterOffset = 7.0f; shutterButtonWidth = 72.0f; @@ -135,12 +135,12 @@ _counterOffset = 7.0f; shutterButtonWidth = 72.0f; } - if (widescreenWidth == 844.0f) + else if (widescreenWidth == 844.0f) { _topPanelOffset = 33.0f; _topPanelHeight = 44.0f; _bottomPanelOffset = 63.0f; - _bottomPanelHeight = 123.0f; + _bottomPanelHeight = 128.0f; _modeControlOffset = 3.0f; _modeControlHeight = 40.0f; _counterOffset = 7.0f; @@ -160,7 +160,7 @@ else if (widescreenWidth >= 736.0f - FLT_EPSILON) { _topPanelHeight = 44.0f; - _bottomPanelHeight = 140.0f; + _bottomPanelHeight = 129.0f; _modeControlHeight = 50.0f; _counterOffset = 8.0f; shutterButtonWidth = 70.0f; @@ -201,6 +201,64 @@ _topPanelBackgroundView.backgroundColor = [TGCameraInterfaceAssets transparentPanelBackgroundColor]; [_topPanelView addSubview:_topPanelBackgroundView]; + _zoomModeView = [[TGCameraZoomModeView alloc] initWithFrame:CGRectMake(floor((frame.size.width - 129.0) / 2.0), frame.size.height - _bottomPanelHeight - _bottomPanelOffset - 18 - 43, 129, 43) hasUltrawideCamera:hasUltrawideCamera hasTelephotoCamera:hasTelephotoCamera minZoomLevel:hasUltrawideCamera ? 0.5 : 1.0 maxZoomLevel:8.0]; + _zoomModeView.zoomChanged = ^(CGFloat zoomLevel, bool done, bool animated) { + __strong TGCameraMainPhoneView *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + if (done) { + [strongSelf->_zoomWheelView setZoomLevel:zoomLevel]; + [strongSelf->_zoomModeView setZoomLevel:zoomLevel animated:false]; + + if (!strongSelf->_zoomWheelView.isHidden) { + NSInteger counter = strongSelf->_dismissingWheelCounter + 1; + strongSelf->_dismissingWheelCounter = (int)counter; + + TGDispatchAfter(1.5, dispatch_get_main_queue(), ^{ + if (strongSelf->_dismissingWheelCounter == counter) { + [strongSelf->_zoomModeView setHidden:false animated:true]; + [strongSelf->_zoomWheelView setHidden:true animated:true]; + } + }); + } + } else { + NSInteger counter = strongSelf->_dismissingWheelCounter + 1; + strongSelf->_dismissingWheelCounter = (int)counter; + [strongSelf->_zoomWheelView setZoomLevel:zoomLevel panning:true]; + [strongSelf->_zoomModeView setHidden:true animated:true]; + [strongSelf->_zoomWheelView setHidden:false animated:true]; + } + + if (strongSelf.zoomChanged != nil) + strongSelf.zoomChanged(zoomLevel, animated); + }; + [_zoomModeView setZoomLevel:1.0]; + if (hasTelephotoCamera || hasUltrawideCamera) { + [self addSubview:_zoomModeView]; + } + + _zoomWheelView = [[TGCameraZoomWheelView alloc] initWithFrame:CGRectMake(0.0, frame.size.height - _bottomPanelHeight - _bottomPanelOffset - 132, frame.size.width, 132) hasUltrawideCamera:hasUltrawideCamera hasTelephotoCamera:hasTelephotoCamera]; + [_zoomWheelView setHidden:true animated:false]; + [_zoomWheelView setZoomLevel:1.0]; + _zoomWheelView.panGesture = ^(UIPanGestureRecognizer *gestureRecognizer) { + __strong TGCameraMainPhoneView *strongSelf = weakSelf; + if (strongSelf == nil) + return; + [strongSelf->_zoomModeView panGesture:gestureRecognizer]; + }; + if (hasTelephotoCamera || hasUltrawideCamera) { + [self addSubview:_zoomWheelView]; + } + + _zoomView = [[TGCameraZoomView alloc] initWithFrame:CGRectMake(10, frame.size.height - _bottomPanelHeight - _bottomPanelOffset - 18, frame.size.width - 20, 1.5f)]; + _zoomView.activityChanged = ^(bool active) + { + }; + if (!hasTelephotoCamera && !hasUltrawideCamera) { + [self addSubview:_zoomView]; + } + _bottomPanelView = [[UIView alloc] init]; [self addSubview:_bottomPanelView]; @@ -227,9 +285,11 @@ [_doneButton addTarget:self action:@selector(doneButtonPressed) forControlEvents:UIControlEventTouchUpInside]; [_bottomPanelView addSubview:_doneButton]; + UIPanGestureRecognizer *shutterPanGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(shutterButtonPanGesture:)]; _shutterButton = [[TGCameraShutterButton alloc] initWithFrame:CGRectMake((frame.size.width - shutterButtonWidth) / 2, 10, shutterButtonWidth, shutterButtonWidth)]; [_shutterButton addTarget:self action:@selector(shutterButtonReleased) forControlEvents:UIControlEventTouchUpInside]; [_shutterButton addTarget:self action:@selector(shutterButtonPressed) forControlEvents:UIControlEventTouchDown]; + [_shutterButton addGestureRecognizer:shutterPanGestureRecognizer]; [_bottomPanelView addSubview:_shutterButton]; _modeControl = [[TGCameraModeControl alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, _modeControlHeight) avatar:avatar]; @@ -242,13 +302,13 @@ _flashControl = [[TGCameraFlashControl alloc] initWithFrame:CGRectMake(3.0, 0, TGCameraFlashControlHeight, TGCameraFlashControlHeight)]; [_topPanelView addSubview:_flashControl]; - _topFlipButton = [[TGCameraFlipButton alloc] initWithFrame:CGRectMake(0, 0, 44, 44)]; + _topFlipButton = [[TGCameraSmallFlipButton alloc] initWithFrame:CGRectMake(0, 0, 44, 44)]; _topFlipButton.hidden = true; [_topFlipButton addTarget:self action:@selector(flipButtonPressed) forControlEvents:UIControlEventTouchUpInside]; -// [_topPanelView addSubview:_topFlipButton]; + [_topPanelView addSubview:_topFlipButton]; _timecodeView = [[TGCameraTimeCodeView alloc] initWithFrame:CGRectMake((frame.size.width - 120) / 2, 12, 120, 28)]; - _timecodeView.hidden = true; + _timecodeView.alpha = 0.0; _timecodeView.requestedRecordingDuration = ^NSTimeInterval { __strong TGCameraMainPhoneView *strongSelf = weakSelf; @@ -257,62 +317,18 @@ return strongSelf.requestedVideoRecordingDuration(); }; + _timecodeView.userInteractionEnabled = false; [_topPanelView addSubview:_timecodeView]; _videoLandscapePanelView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 274, 44)]; _videoLandscapePanelView.alpha = 0.0f; - _videoLandscapePanelView.backgroundColor = [TGCameraInterfaceAssets transparentPanelBackgroundColor]; _videoLandscapePanelView.hidden = true; - _videoLandscapePanelView.layer.cornerRadius = 3.5f; [self addSubview:_videoLandscapePanelView]; - _flashActiveView = [[TGCameraFlashActiveView alloc] initWithFrame:CGRectMake((frame.size.width - 40) / 2, frame.size.height - _bottomPanelHeight - 37, 40, 21)]; -// [self addSubview:_flashActiveView]; - _toastView = [[TGCameraToastView alloc] initWithFrame:CGRectMake(0, frame.size.height - _bottomPanelHeight - 42, frame.size.width, 32)]; _toastView.userInteractionEnabled = false; [self addSubview:_toastView]; - _zoomView = [[TGCameraZoomView alloc] initWithFrame:CGRectMake(10, frame.size.height - _bottomPanelHeight - _bottomPanelOffset - 18, frame.size.width - 20, 1.5f)]; - _zoomView.activityChanged = ^(bool active) - { - __strong TGCameraMainPhoneView *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - [UIView animateWithDuration:0.3f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut animations:^ - { - [strongSelf _layoutFlashActiveViewForInterfaceOrientation:strongSelf->_interfaceOrientation zoomViewHidden:!active]; - } completion:nil]; - }; -// [self addSubview:_zoomView]; - - _zoomModeView = [[TGCameraZoomModeView alloc] initWithFrame:CGRectMake(floor((frame.size.width - 129.0) / 2.0), frame.size.height - _bottomPanelHeight - _bottomPanelOffset - 18 - 43, 129, 43)]; - _zoomModeView.zoomChanged = ^(CGFloat zoomLevel, bool done) { - __strong TGCameraMainPhoneView *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - if (!done) { - [strongSelf->_zoomWheelView setZoomLevel:zoomLevel]; - [strongSelf->_zoomModeView setHidden:true animated:true]; - [strongSelf->_zoomWheelView setHidden:false animated:true]; - } else { - [strongSelf->_zoomWheelView setZoomLevel:zoomLevel]; - [strongSelf->_zoomModeView setZoomLevel:zoomLevel animated:false]; - [strongSelf->_zoomModeView setHidden:false animated:true]; - [strongSelf->_zoomWheelView setHidden:true animated:true]; - } - }; - [_zoomModeView setZoomLevel:1.0]; - [self addSubview:_zoomModeView]; - - _zoomWheelView = [[TGCameraZoomWheelView alloc] initWithFrame:CGRectMake(0.0, frame.size.height - _bottomPanelHeight - _bottomPanelOffset - 132, frame.size.width, 132)]; - [_zoomWheelView setHidden:true animated:false]; - [_zoomWheelView setZoomLevel:1.0]; - _zoomWheelView.userInteractionEnabled = false; - [self addSubview:_zoomWheelView]; - _flashControl.modeChanged = ^(PGCameraFlashMode mode) { __strong TGCameraMainPhoneView *strongSelf = weakSelf; @@ -457,7 +473,12 @@ { UIView *view = [super hitTest:point withEvent:event]; - if ([view isDescendantOfView:_topPanelView] || [view isDescendantOfView:_bottomPanelView] || [view isDescendantOfView:_videoLandscapePanelView] || [view isDescendantOfView:_tooltipContainerView] || [view isDescendantOfView:_selectedPhotosView] || [view isDescendantOfView:_zoomModeView] || view == _zoomModeView) + if (!_zoomModeView.isHidden && CGRectContainsPoint(_zoomModeView.frame, point)) { + CGPoint zoomPoint = [self convertPoint:point toView:_zoomModeView]; + return [_zoomModeView hitTest:zoomPoint withEvent:event]; + } + + if ([view isDescendantOfView:_topPanelView] || [view isDescendantOfView:_bottomPanelView] || [view isDescendantOfView:_videoLandscapePanelView] || [view isDescendantOfView:_tooltipContainerView] || [view isDescendantOfView:_selectedPhotosView] || [view isDescendantOfView:_zoomModeView] || view == _zoomModeView || (view == _zoomWheelView && !_zoomWheelView.isHidden)) return view; return nil; @@ -465,13 +486,6 @@ #pragma mark - Actions -- (void)shutterButtonReleased -{ - [super shutterButtonReleased]; - - [_flashControl dismissAnimated:true]; -} - - (void)updateForCameraModeChangeWithPreviousMode:(PGCameraMode)previousMode { [super updateForCameraModeChangeWithPreviousMode:previousMode]; @@ -479,20 +493,31 @@ UIInterfaceOrientation orientation = _interfaceOrientation; PGCameraMode cameraMode = _modeControl.cameraMode; - if (UIInterfaceOrientationIsLandscape(orientation) && !((cameraMode == PGCameraModePhoto && previousMode == PGCameraModeSquarePhoto) || (cameraMode == PGCameraModeSquarePhoto && previousMode == PGCameraModePhoto))) - { - if (cameraMode == PGCameraModeVideo) - _timecodeView.hidden = true; - + if (previousMode == PGCameraModePhoto && cameraMode == PGCameraModeVideo) { + [UIView animateWithDuration:0.25f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^ + { + _timecodeView.alpha = 1.0; + _bottomPanelBackgroundView.alpha = 0.0; + } completion:nil]; + } else if (previousMode == PGCameraModeVideo && cameraMode == PGCameraModePhoto) { + [UIView animateWithDuration:0.25f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^ + { + _timecodeView.alpha = 0.0; + } completion:nil]; + [UIView animateWithDuration:0.25f delay:1.5f options:UIViewAnimationOptionCurveLinear animations:^ + { + _bottomPanelBackgroundView.alpha = 1.0; + } completion:nil]; + } + if (UIInterfaceOrientationIsLandscape(orientation) && !((cameraMode == PGCameraModePhoto && previousMode == PGCameraModeSquarePhoto) || (cameraMode == PGCameraModeSquarePhoto && previousMode == PGCameraModePhoto))) + { [UIView animateWithDuration:0.25f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^ { - _topPanelView.alpha = 0.0f; _videoLandscapePanelView.alpha = 0.0f; } completion:^(__unused BOOL finished) { if (cameraMode == PGCameraModeVideo) { - _timecodeView.hidden = false; _flashControl.transform = CGAffineTransformIdentity; _flashControl.interfaceOrientation = UIInterfaceOrientationPortrait; [self _layoutTopPanelViewForInterfaceOrientation:orientation]; @@ -510,14 +535,11 @@ [self _attachControlsToTopPanel]; [self _layoutTopPanelSubviewsForInterfaceOrientation:orientation]; - [_flashControl dismissAnimated:false]; [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut animations:^ { if (cameraMode == PGCameraModeVideo) _videoLandscapePanelView.alpha = 1.0f; - else - _topPanelView.alpha = 1.0f; } completion:nil]; }]; } @@ -532,7 +554,7 @@ - (void)setFlashActive:(bool)active { - [_flashActiveView setActive:active animated:true]; + [_flashControl setFlashActive:active]; } - (void)setFlashUnavailable:(bool)unavailable @@ -542,9 +564,6 @@ - (void)setHasFlash:(bool)hasFlash { - if (!hasFlash) - [_flashActiveView setActive:false animated:true]; - [_flashControl setHidden:!hasFlash animated:true]; } @@ -580,6 +599,8 @@ { bool hasDoneButton = _hasResults; + _zoomWheelView.clipsToBounds = !hidden; + if (animated) { if (!hidden) @@ -588,10 +609,14 @@ _cancelButton.hidden = false; _flashControl.hidden = false; _flipButton.hidden = hasDoneButton; - _bottomPanelBackgroundView.hidden = false; _topFlipButton.hidden = !hasDoneButton; } + [UIView animateWithDuration:0.2 delay:0.0 options:7 << 16 animations:^{ + CGFloat offset = hidden ? 19 : 18 + 43; + _zoomModeView.frame = CGRectMake(floor((self.bounds.size.width - 129.0) / 2.0), self.bounds.size.height - _bottomPanelHeight - _bottomPanelOffset - offset, 129, 43); + } completion:nil]; + [UIView animateWithDuration:0.25 animations:^ { CGFloat alpha = hidden ? 0.0f : 1.0f; @@ -600,7 +625,6 @@ _flashControl.alpha = alpha; _flipButton.alpha = alpha; _topFlipButton.alpha = alpha; - _bottomPanelBackgroundView.alpha = alpha; if (hasDoneButton) _doneButton.alpha = alpha; @@ -613,7 +637,6 @@ _flashControl.hidden = hidden; _flipButton.hidden = hidden || hasDoneButton; _topFlipButton.hidden = hidden || !hasDoneButton; - _bottomPanelBackgroundView.hidden = hidden; if (hasDoneButton) _doneButton.hidden = hidden; @@ -635,8 +658,9 @@ _flipButton.alpha = alpha; _topFlipButton.hidden = hidden || !hasDoneButton; _topFlipButton.alpha = alpha; - _bottomPanelBackgroundView.hidden = hidden; - _bottomPanelBackgroundView.alpha = alpha; + + CGFloat offset = hidden ? 19 : 18 + 43; + _zoomModeView.frame = CGRectMake(floor((self.bounds.size.width - 129.0) / 2.0), self.bounds.size.height - _bottomPanelHeight - _bottomPanelOffset - offset, 129, 43); if (hasDoneButton) { @@ -664,116 +688,51 @@ { [UIView animateWithDuration:0.25 delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^ { - _flashActiveView.alpha = 0.0f; - if (_modeControl.cameraMode == PGCameraModeVideo) { - _topPanelView.alpha = 0.0f; _videoLandscapePanelView.alpha = 0.0f; } - else - { - _flashControl.alpha = 0.0f; - } - _topFlipButton.transform = CGAffineTransformMakeRotation(TGRotationForInterfaceOrientation(orientation)); _flipButton.transform = CGAffineTransformMakeRotation(TGRotationForInterfaceOrientation(orientation)); + _flashControl.transform = CGAffineTransformMakeRotation(TGRotationForInterfaceOrientation(orientation)); + _zoomModeView.interfaceOrientation = orientation; + _timecodeView.interfaceOrientation = orientation; + _zoomWheelView.interfaceOrientation = orientation; + _topFlipButton.transform = CGAffineTransformMakeRotation(TGRotationForInterfaceOrientation(orientation)); } completion:^(__unused BOOL finished) { - [self _layoutFlashActiveViewForInterfaceOrientation:orientation zoomViewHidden:!_zoomView.isActive]; - if (_modeControl.cameraMode == PGCameraModeVideo) { - _flashControl.transform = CGAffineTransformIdentity; - _flashControl.interfaceOrientation = UIInterfaceOrientationPortrait; - [self _layoutTopPanelViewForInterfaceOrientation:orientation]; if (UIInterfaceOrientationIsLandscape(orientation)) [self _attachControlsToLandscapePanel]; else [self _attachControlsToTopPanel]; - - _timecodeView.hidden = false; - } - else - { - _flashControl.transform = CGAffineTransformMakeRotation(TGRotationForInterfaceOrientation(orientation)); - _flashControl.interfaceOrientation = orientation; } [self _layoutTopPanelSubviewsForInterfaceOrientation:orientation]; - - [_flashControl dismissAnimated:false]; [UIView animateWithDuration:0.2 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^ { - _flashActiveView.alpha = 1.0f; - if (_modeControl.cameraMode == PGCameraModeVideo) { if (UIInterfaceOrientationIsLandscape(orientation)) _videoLandscapePanelView.alpha = 1.0f; - else - _topPanelView.alpha = 1.0f; - } - else - { - _flashControl.alpha = 1.0f; } } completion:nil]; }]; } else { - [_flashControl dismissAnimated:false]; - - _topFlipButton.transform = CGAffineTransformMakeRotation(TGRotationForInterfaceOrientation(orientation)); _flipButton.transform = CGAffineTransformMakeRotation(TGRotationForInterfaceOrientation(orientation)); _flashControl.transform = CGAffineTransformMakeRotation(TGRotationForInterfaceOrientation(orientation)); - _flashControl.interfaceOrientation = orientation; + _zoomModeView.interfaceOrientation = orientation; + _timecodeView.interfaceOrientation = orientation; + _zoomWheelView.interfaceOrientation = orientation; + _topFlipButton.transform = CGAffineTransformMakeRotation(TGRotationForInterfaceOrientation(orientation)); [self _layoutTopPanelSubviewsForInterfaceOrientation:orientation]; - - [self _layoutFlashActiveViewForInterfaceOrientation:orientation zoomViewHidden:!_zoomView.isActive]; - - if (_modeControl.cameraMode == PGCameraModeVideo) - _timecodeView.hidden = false; - } -} - -- (void)_layoutFlashActiveViewForInterfaceOrientation:(UIInterfaceOrientation)orientation zoomViewHidden:(bool)zoomViewHidden -{ - CGFloat zoomOffset = 0; - if (!zoomViewHidden) - zoomOffset -= 23; - - _flashActiveView.transform = CGAffineTransformMakeRotation(TGRotationForInterfaceOrientation(orientation)); - switch (orientation) - { - case UIInterfaceOrientationPortraitUpsideDown: - { - _flashActiveView.frame = CGRectMake((self.frame.size.width - 40) / 2, _topPanelHeight + 16, 40, 21); - } - break; - - case UIInterfaceOrientationLandscapeLeft: - { - _flashActiveView.frame = CGRectMake(self.frame.size.width - 37, _topPanelHeight + (self.frame.size.height - _topPanelHeight - _bottomPanelHeight - _bottomPanelOffset - 40) / 2, 21, 40); - } - break; - - case UIInterfaceOrientationLandscapeRight: - { - _flashActiveView.frame = CGRectMake(16, _topPanelHeight + (self.frame.size.height - _topPanelHeight - _bottomPanelHeight - _bottomPanelOffset - 40) / 2, 21, 40); - } - break; - - default: - { - _flashActiveView.frame = CGRectMake((self.frame.size.width - 40) / 2, self.frame.size.height - _bottomPanelHeight - _bottomPanelOffset - 37 + zoomOffset, 40, 21); - } - break; } } @@ -786,7 +745,6 @@ case UIInterfaceOrientationLandscapeLeft: { _videoLandscapePanelView.hidden = false; - _topPanelView.hidden = true; _videoLandscapePanelView.transform = transform; _videoLandscapePanelView.frame = CGRectMake(3, (self.frame.size.height - _videoLandscapePanelView.frame.size.height) / 2, _videoLandscapePanelView.frame.size.width, _videoLandscapePanelView.frame.size.height); @@ -795,7 +753,6 @@ case UIInterfaceOrientationLandscapeRight: { _videoLandscapePanelView.hidden = false; - _topPanelView.hidden = true; _videoLandscapePanelView.transform = transform; _videoLandscapePanelView.frame = CGRectMake(self.frame.size.width - _videoLandscapePanelView.frame.size.width - 3, (self.frame.size.height - _videoLandscapePanelView.frame.size.height) / 2, _videoLandscapePanelView.frame.size.width, _videoLandscapePanelView.frame.size.height); @@ -805,20 +762,18 @@ case UIInterfaceOrientationPortraitUpsideDown: { _videoLandscapePanelView.hidden = true; - _topPanelView.hidden = false; _topPanelView.transform = transform; - _topPanelView.frame = CGRectMake(0, 0, _topPanelView.frame.size.width, _topPanelView.frame.size.height); + _topPanelView.frame = CGRectMake(0, _topPanelOffset, _topPanelView.frame.size.width, _topPanelView.frame.size.height); } break; default: { _videoLandscapePanelView.hidden = true; - _topPanelView.hidden = false; _topPanelView.transform = transform; - _topPanelView.frame = CGRectMake(0, 0, _topPanelView.frame.size.width, _topPanelView.frame.size.height); + _topPanelView.frame = CGRectMake(0, _topPanelOffset, _topPanelView.frame.size.width, _topPanelView.frame.size.height); } break; } @@ -826,42 +781,25 @@ - (void)_attachControlsToTopPanel { - [_topPanelView addSubview:_flashControl]; [_topPanelView addSubview:_timecodeView]; } - (void)_attachControlsToLandscapePanel { - [_videoLandscapePanelView addSubview:_flashControl]; [_videoLandscapePanelView addSubview:_timecodeView]; } - (void)_layoutTopPanelSubviewsForInterfaceOrientation:(UIInterfaceOrientation)orientation { - UIView *superview = _flashControl.superview; + UIView *superview = _timecodeView.superview; CGSize superviewSize = superview.frame.size; if (superview == _videoLandscapePanelView && superviewSize.width < superviewSize.height) superviewSize = CGSizeMake(superviewSize.height, superviewSize.width); -// if (UIInterfaceOrientationIsLandscape(orientation) && _flashControl.interfaceOrientation == orientation && _flashControl.superview == _topPanelView) -// { -// if (orientation == UIInterfaceOrientationLandscapeLeft) -// _flashControl.frame = CGRectMake(7, 0, TGCameraFlashControlHeight, 370); -// else if (orientation == UIInterfaceOrientationLandscapeRight) -// _flashControl.frame = CGRectMake(7, 0, TGCameraFlashControlHeight, 370); -// } -// else -// { -// _flashControl.frame = CGRectMake(0, (superviewSize.height - TGCameraFlashControlHeight) / 2, superviewSize.width, TGCameraFlashControlHeight); -// } _timecodeView.frame = CGRectMake((superviewSize.width - 120) / 2, (superviewSize.height - 28) / 2, 120, 28); } -- (void)layoutPreviewRelativeViews -{ -} - - (void)layoutSubviews { _topPanelView.frame = CGRectMake(0, _topPanelOffset, self.frame.size.width, _topPanelHeight); diff --git a/submodules/LegacyComponents/Sources/TGCameraMainTabletView.m b/submodules/LegacyComponents/Sources/TGCameraMainTabletView.m index a52645e56e..653a06ebc3 100644 --- a/submodules/LegacyComponents/Sources/TGCameraMainTabletView.m +++ b/submodules/LegacyComponents/Sources/TGCameraMainTabletView.m @@ -42,7 +42,7 @@ const CGFloat TGCameraTabletPanelViewWidth = 102.0f; @synthesize shutterReleased; @synthesize cancelPressed; -- (instancetype)initWithFrame:(CGRect)frame avatar:(bool)avatar +- (instancetype)initWithFrame:(CGRect)frame avatar:(bool)avatar hasUltrawideCamera:(bool)hasUltrawideCamera hasTelephotoCamera:(bool)hasTelephotoCamera { self = [super initWithFrame:frame]; if (self != nil) @@ -100,7 +100,7 @@ const CGFloat TGCameraTabletPanelViewWidth = 102.0f; }; [_panelView addSubview:_timecodeView]; - _flipButton = [[TGCameraFlipButton alloc] initWithFrame:CGRectMake(0, 0, 44, 44)]; + _flipButton = [[TGCameraFlipButton alloc] initWithFrame:CGRectMake(0, 20, 44, 44)]; [_flipButton addTarget:self action:@selector(flipButtonPressed) forControlEvents:UIControlEventTouchUpInside]; [_panelView addSubview:_flipButton]; @@ -277,6 +277,10 @@ const CGFloat TGCameraTabletPanelViewWidth = 102.0f; (_panelView.frame.size.height - _shutterButton.frame.size.height) / 2, _shutterButton.frame.size.width, _shutterButton.frame.size.height); + _flipButton.frame = CGRectMake((_panelView.frame.size.width - _shutterButton.frame.size.width) / 2, + _shutterButton.frame.origin.y + _shutterButton.frame.size.height + 20.0, + _flipButton.frame.size.width, _flipButton.frame.size.height); + CGFloat flipButtonPosition = 0.0f; CGFloat cancelButtonPosition = _panelView.frame.size.height - _cancelButton.frame.size.height - 7; if (!_doneButton.hidden) @@ -284,7 +288,6 @@ const CGFloat TGCameraTabletPanelViewWidth = 102.0f; flipButtonPosition = _panelView.frame.size.height / 8.0f - _flipButton.frame.size.height / 2.0f; cancelButtonPosition = 7.0f; } - _flipButton.frame = CGRectMake((_panelView.frame.size.width - _flipButton.frame.size.width) / 2, flipButtonPosition, _flipButton.frame.size.width, _flipButton.frame.size.height); _doneButton.frame = CGRectMake((_panelView.frame.size.width - _doneButton.frame.size.width) / 2, _panelView.frame.size.height - _doneButton.frame.size.height - 7, _doneButton.frame.size.width, _doneButton.frame.size.height); diff --git a/submodules/LegacyComponents/Sources/TGCameraMainView.m b/submodules/LegacyComponents/Sources/TGCameraMainView.m index c214a2b76c..f5a363e0dd 100644 --- a/submodules/LegacyComponents/Sources/TGCameraMainView.m +++ b/submodules/LegacyComponents/Sources/TGCameraMainView.m @@ -14,6 +14,51 @@ #import "TGMediaPickerPhotoCounterButton.h" #import "TGMediaPickerPhotoStripView.h" +@implementation TGCameraCornersView + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self != nil) { + self.contentMode = UIViewContentModeScaleToFill; + + static UIImage *cornersImage = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + CGSize size = CGSizeMake(50.0, 50.0); + UIGraphicsBeginImageContextWithOptions(CGSizeMake(50.0, 50.0), false, 0.0f); + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGContextSetAlpha(context, 0.5); + CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); + CGContextSetBlendMode(context, kCGBlendModeCopy); + + CGFloat width = 1.0; + CGFloat length = 24.0; + CGContextFillRect(context, CGRectMake(0, 0, length, width)); + CGContextFillRect(context, CGRectMake(0, 0, width, length)); + + CGContextFillRect(context, CGRectMake(size.width - length, 0, length, width)); + CGContextFillRect(context, CGRectMake(size.width - width, 0, width, length)); + + CGContextFillRect(context, CGRectMake(0, size.height - width, length, width)); + CGContextFillRect(context, CGRectMake(0, size.height - length, width, length)); + + CGContextFillRect(context, CGRectMake(size.width - length, size.height - width, length, width)); + CGContextFillRect(context, CGRectMake(size.width - width, size.height - length, width, length)); + + cornersImage = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:25 topCapHeight:25]; + UIGraphicsEndImageContext(); + }); + + self.image = cornersImage; + self.userInteractionEnabled = false; + } + return self; +} + +@end + @interface TGCameraMainView () { @@ -25,6 +70,16 @@ @dynamic thumbnailSignalForItem; @dynamic editingContext; +- (instancetype)initWithFrame:(CGRect)frame avatar:(bool)avatar hasUltrawideCamera:(bool)hasUltrawideCamera hasTelephotoCamera:(bool)hasTelephotoCamera { + self = [super init]; + if (self != nil) { + } + return self; +} + +- (void)setResults:(NSArray *)__unused results { +} + #pragma mark - Mode - (void)setInterfaceHiddenForVideoRecording:(bool)__unused hidden animated:(bool)__unused animated @@ -156,6 +211,11 @@ self.shutterReleased(false); } +- (void)shutterButtonPanGesture:(UIPanGestureRecognizer *)gestureRecognizer { + if (self.shutterPanGesture != nil) + self.shutterPanGesture(gestureRecognizer); +} + - (void)cancelButtonPressed { if (self.cancelPressed != nil) @@ -182,7 +242,8 @@ - (void)setZoomLevel:(CGFloat)zoomLevel displayNeeded:(bool)displayNeeded { [_zoomView setZoomLevel:zoomLevel displayNeeded:displayNeeded]; - [_zoomModeView setZoomLevel:zoomLevel]; + [_zoomModeView setZoomLevel:zoomLevel animated:true]; + [_zoomWheelView setZoomLevel:zoomLevel]; } - (void)zoomChangingEnded @@ -194,6 +255,8 @@ { if (!hasZoom) [_zoomView hideAnimated:true]; + + [_zoomModeView setHidden:!hasZoom animated:true]; } #pragma mark - Video @@ -233,11 +296,6 @@ completion(true); } -- (void)layoutPreviewRelativeViews -{ - -} - #pragma mark - - (void)setDocumentFrameHidden:(bool)hidden diff --git a/submodules/LegacyComponents/Sources/TGCameraPreviewView.m b/submodules/LegacyComponents/Sources/TGCameraPreviewView.m index b49624e7a9..4d54e51c3d 100644 --- a/submodules/LegacyComponents/Sources/TGCameraPreviewView.m +++ b/submodules/LegacyComponents/Sources/TGCameraPreviewView.m @@ -59,11 +59,8 @@ { self.backgroundColor = [UIColor blackColor]; self.clipsToBounds = true; - - if (false && iosMajorVersion() >= 8) - _wrapperView = [[TGCameraPreviewLayerWrapperView alloc] init]; - else - _wrapperView = [[TGCameraLegacyPreviewLayerWrapperView alloc] init]; + + _wrapperView = [[TGCameraLegacyPreviewLayerWrapperView alloc] init]; [self addSubview:_wrapperView]; _wrapperView.videoGravity = AVLayerVideoGravityResizeAspectFill; @@ -74,8 +71,9 @@ _fadeView.userInteractionEnabled = false; [self addSubview:_fadeView]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { _fadeView.accessibilityIgnoresInvertColors = true; + } #if TARGET_IPHONE_SIMULATOR _fadeView.backgroundColor = [UIColor redColor]; @@ -129,10 +127,15 @@ if (strongSelf == nil) return; - if (resume) + if (resume) { [strongSelf endResetTransitionAnimated:true]; - else - [strongSelf fadeInAnimated:true]; + } else { + if (strongSelf->_snapshotView != nil) { + [strongSelf endTransitionAnimated:true]; + } else { + [strongSelf fadeInAnimated:true]; + } + } }; camera.captureStopped = ^(bool pause) @@ -225,8 +228,9 @@ snapshotView.image = image; [self insertSubview:snapshotView aboveSubview:_wrapperView]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { snapshotView.accessibilityIgnoresInvertColors = true; + } _snapshotView = snapshotView; @@ -262,6 +266,10 @@ } } +- (bool)hasTransitionSnapshot { + return _snapshotView != nil; +} + - (void)beginResetTransitionAnimated:(bool)animated { if (iosMajorVersion() < 7) @@ -319,7 +327,12 @@ if (_snapshotView != nil) { - CGSize size = TGScaleToFill(_snapshotView.frame.size, _wrapperView.frame.size); + CGSize imageSize = _snapshotView.frame.size; + if ([_snapshotView isKindOfClass:[UIImageView class]]) { + imageSize = ((UIImageView *)_snapshotView).image.size; + } + + CGSize size = TGScaleToFill(imageSize, _wrapperView.frame.size); _snapshotView.frame = CGRectMake(floor((self.frame.size.width - size.width) / 2.0f), floor((self.frame.size.height - size.height) / 2.0f), size.width, size.height); } } diff --git a/submodules/LegacyComponents/Sources/TGCameraTimeCodeView.m b/submodules/LegacyComponents/Sources/TGCameraTimeCodeView.m index 1fbea1fea1..2160cc398f 100644 --- a/submodules/LegacyComponents/Sources/TGCameraTimeCodeView.m +++ b/submodules/LegacyComponents/Sources/TGCameraTimeCodeView.m @@ -8,6 +8,7 @@ @interface TGCameraTimeCodeView () { UIView *_backgroundView; + UIView *_recordingBackgroundView; UILabel *_timeLabel; NSUInteger _recordingDurationSeconds; @@ -25,10 +26,17 @@ _backgroundView = [[UIView alloc] init]; _backgroundView.clipsToBounds = true; _backgroundView.layer.cornerRadius = 4.0; - _backgroundView.backgroundColor = [TGCameraInterfaceAssets redColor]; + _backgroundView.backgroundColor = [TGCameraInterfaceAssets transparentPanelBackgroundColor]; _backgroundView.alpha = 0.0; [self addSubview:_backgroundView]; + _recordingBackgroundView = [[UIView alloc] init]; + _recordingBackgroundView.clipsToBounds = true; + _recordingBackgroundView.layer.cornerRadius = 4.0; + _recordingBackgroundView.backgroundColor = [TGCameraInterfaceAssets redColor]; + _recordingBackgroundView.alpha = 0.0; + [self addSubview:_recordingBackgroundView]; + _timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)]; _timeLabel.backgroundColor = [UIColor clearColor]; _timeLabel.font = [TGCameraInterfaceAssets regularFontOfSize:21]; @@ -45,6 +53,12 @@ [self stopRecording]; } +- (void)setInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { + _interfaceOrientation = interfaceOrientation; + + _backgroundView.alpha = UIInterfaceOrientationIsLandscape(interfaceOrientation) ? 1.0 : 0.0; +} + - (void)_updateRecordingTime { if (_recordingDurationSeconds > 60 * 60) { @@ -57,6 +71,7 @@ CGFloat inset = 8.0f; CGFloat backgroundWidth = _timeLabel.frame.size.width + inset * 2.0; _backgroundView.frame = CGRectMake(floor((self.frame.size.width - backgroundWidth) / 2.0), 0.0, backgroundWidth, 28.0); + _recordingBackgroundView.frame = _backgroundView.frame; _timeLabel.frame = CGRectMake(floor((self.frame.size.width - _timeLabel.frame.size.width) / 2.0), floor((28 - _timeLabel.frame.size.height) / 2.0), _timeLabel.frame.size.width, _timeLabel.frame.size.height); } @@ -66,12 +81,20 @@ [self reset]; _recordingTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(recordingTimerEvent) interval:1.0 repeat:false]; + + [UIView animateWithDuration:0.2 animations:^{ + _recordingBackgroundView.alpha = 1.0; + }]; } - (void)stopRecording { [_recordingTimer invalidate]; _recordingTimer = nil; + + [UIView animateWithDuration:0.2 animations:^{ + _recordingBackgroundView.alpha = 0.0; + }]; } - (void)reset diff --git a/submodules/LegacyComponents/Sources/TGCameraZoomView.m b/submodules/LegacyComponents/Sources/TGCameraZoomView.m index 6f3127c54f..f673cff9bb 100644 --- a/submodules/LegacyComponents/Sources/TGCameraZoomView.m +++ b/submodules/LegacyComponents/Sources/TGCameraZoomView.m @@ -54,7 +54,7 @@ CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetStrokeColorWithColor(context, [TGCameraInterfaceAssets accentColor].CGColor); - CGContextSetLineWidth(context, 1.5f); + CGContextSetLineWidth(context, 1.0f); CGContextStrokeEllipseInRect(context, CGRectMake(0.75f, 0.75f, 12.5f - 1.5f, 12.5f - 1.5f)); knobImage = UIGraphicsGetImageFromCurrentImageContext(); @@ -170,7 +170,11 @@ { _clipView.frame = CGRectMake(22, (self.frame.size.height - 12.5f) / 2, self.frame.size.width - 44, 12.5f); - CGFloat position = (_clipView.frame.size.width - _knobView.frame.size.width) * self.zoomLevel; + CGFloat zoomLevel = self.zoomLevel; + zoomLevel = MAX(1.0, zoomLevel); + CGFloat factor = zoomLevel / 8.0; + + CGFloat position = (_clipView.frame.size.width - _knobView.frame.size.width) * factor; if (self.zoomLevel < 1.0f - FLT_EPSILON) position = CGFloor(position); @@ -196,6 +200,7 @@ _label = [[UILabel alloc] initWithFrame:self.bounds]; _label.textAlignment = NSTextAlignmentCenter; + _label.font = [TGCameraInterfaceAssets boldFontOfSize:13.0]; [self addSubview:_backgroundView]; [self addSubview:_label]; @@ -209,7 +214,6 @@ _label.text = value; _label.textColor = selected ? [TGCameraInterfaceAssets accentColor] : [UIColor whiteColor]; - _label.font = [TGCameraInterfaceAssets boldFontOfSize:13.0]; if (animated) { [UIView animateWithDuration:0.3f animations:^ @@ -225,23 +229,38 @@ @end -@interface TGCameraZoomModeView () +@interface TGCameraZoomModeView () { + CGFloat _minZoomLevel; + CGFloat _maxZoomLevel; + UIView *_backgroundView; + + bool _hasUltrawideCamera; + bool _hasTelephotoCamera; + + bool _beganFromPress; TGCameraZoomModeItemView *_leftItem; TGCameraZoomModeItemView *_centerItem; TGCameraZoomModeItemView *_rightItem; + + bool _lockedOn; } @end @implementation TGCameraZoomModeView -- (instancetype)initWithFrame:(CGRect)frame +- (instancetype)initWithFrame:(CGRect)frame hasUltrawideCamera:(bool)hasUltrawideCamera hasTelephotoCamera:(bool)hasTelephotoCamera minZoomLevel:(CGFloat)minZoomLevel maxZoomLevel:(CGFloat)maxZoomLevel { self = [super initWithFrame:frame]; if (self != nil) - { + { + _hasUltrawideCamera = hasUltrawideCamera; + _hasTelephotoCamera = hasTelephotoCamera; + _minZoomLevel = minZoomLevel; + _maxZoomLevel = maxZoomLevel; + _backgroundView = [[UIView alloc] initWithFrame:self.bounds]; _backgroundView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.15]; _backgroundView.layer.cornerRadius = self.bounds.size.height / 2.0; @@ -256,58 +275,141 @@ [_rightItem addTarget:self action:@selector(rightPressed) forControlEvents:UIControlEventTouchUpInside]; [self addSubview:_backgroundView]; - [self addSubview:_leftItem]; [self addSubview:_centerItem]; - [self addSubview:_rightItem]; + if (hasTelephotoCamera && hasUltrawideCamera) { + [self addSubview:_leftItem]; + [self addSubview:_rightItem]; + } - UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGesture:)]; - [self addGestureRecognizer:gestureRecognizer]; + UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGesture:)]; + panGestureRecognizer.delegate = self; + [self addGestureRecognizer:panGestureRecognizer]; + + UILongPressGestureRecognizer *pressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(pressGesture:)]; + pressGestureRecognizer.delegate = self; + [self addGestureRecognizer:pressGestureRecognizer]; } return self; } +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { + if (gestureRecognizer.view == self && otherGestureRecognizer.view == self) { + return true; + } else { + return false; + } +} + +- (void)pressGesture:(UILongPressGestureRecognizer *)gestureRecognizer { + switch (gestureRecognizer.state) { + case UIGestureRecognizerStateBegan: + _beganFromPress = true; + self.zoomChanged(_zoomLevel, false, false); + break; + case UIGestureRecognizerStateEnded: + self.zoomChanged(_zoomLevel, true, false); + break; + case UIGestureRecognizerStateCancelled: + self.zoomChanged(_zoomLevel, true, false); + break; + default: + break; + } +} - (void)panGesture:(UIPanGestureRecognizer *)gestureRecognizer { CGPoint translation = [gestureRecognizer translationInView:self]; switch (gestureRecognizer.state) { - case UIGestureRecognizerStateBegan: - self.zoomChanged(_zoomLevel, false); - break; - case UIGestureRecognizerStateChanged: - _zoomLevel = MAX(0.5, MIN(10.0, _zoomLevel - translation.x / 100.0)); - self.zoomChanged(_zoomLevel, false); - break; + { + if (_lockedOn) { + if (ABS(translation.x) > 8.0) { + _lockedOn = false; + [gestureRecognizer setTranslation:CGPointZero inView:self]; + + CGFloat delta = translation.x > 0 ? -0.06 : 0.06; + CGFloat newLevel = MAX(_minZoomLevel, MIN(_maxZoomLevel, _zoomLevel + delta)); + _zoomLevel = newLevel; + self.zoomChanged(newLevel, false, false); + return; + } else { + return; + } + } + CGFloat previousLevel = _zoomLevel; + + CGFloat delta = -translation.x / 60.0; + if (_zoomLevel > 2.0) { + delta *= 3.5; + } + CGFloat newLevel = MAX(_minZoomLevel, MIN(_maxZoomLevel, _zoomLevel + delta)); + + CGFloat near = floor(newLevel); + if (near <= 2.0 && ABS(newLevel - near) < 0.05 && previousLevel != near && translation.x < 15.0) { + newLevel = near; + _lockedOn = true; + + [gestureRecognizer setTranslation:CGPointZero inView:self]; + } + + _zoomLevel = newLevel; + self.zoomChanged(newLevel, false, false); + } + break; case UIGestureRecognizerStateEnded: - self.zoomChanged(_zoomLevel, true); - break; - case UIGestureRecognizerStateCancelled: - self.zoomChanged(_zoomLevel, true); + { + if (gestureRecognizer.view != self || !_beganFromPress) { + self.zoomChanged(_zoomLevel, true, false); + } + _beganFromPress = false; + } break; - default: break; } - [gestureRecognizer setTranslation:CGPointZero inView:self]; + if (!_lockedOn) { + [gestureRecognizer setTranslation:CGPointZero inView:self]; + } } - (void)leftPressed { - [self setZoomLevel:0.5 animated:true]; - self.zoomChanged(0.5, true); + if (_zoomLevel != 0.5) { + [self setZoomLevel:0.5 animated:true]; + self.zoomChanged(0.5, true, true); + } } - (void)centerPressed { - [self setZoomLevel:1.0 animated:true]; - self.zoomChanged(1.0, true); + if (!(_hasTelephotoCamera && _hasUltrawideCamera)) { + if (_zoomLevel == 1.0) { + if (_hasUltrawideCamera) { + [self setZoomLevel:0.5 animated:true]; + self.zoomChanged(0.5, true, true); + } else if (_hasTelephotoCamera) { + [self setZoomLevel:2.0 animated:true]; + self.zoomChanged(2.0, true, true); + } + } else { + [self setZoomLevel:1.0 animated:true]; + self.zoomChanged(1.0, true, true); + } + } else { + if (_zoomLevel != 1.0) { + [self setZoomLevel:1.0 animated:true]; + self.zoomChanged(1.0, true, true); + } + } } - (void)rightPressed { - [self setZoomLevel:2.0 animated:true]; - self.zoomChanged(2.0, true); + if (_zoomLevel != 2.0) { + [self setZoomLevel:2.0 animated:true]; + self.zoomChanged(2.0, true, true); + } } - (void)setZoomLevel:(CGFloat)zoomLevel { @@ -318,26 +420,44 @@ { _zoomLevel = zoomLevel; if (zoomLevel < 1.0) { - [_leftItem setValue:[NSString stringWithFormat:@"%.1fx", zoomLevel] selected:true animated:animated]; - [_centerItem setValue:@"1" selected:false animated:animated]; + NSString *value = [NSString stringWithFormat:@"%.1f×", zoomLevel]; + value = [value stringByReplacingOccurrencesOfString:@"." withString:@","]; + if ([value isEqual:@"1,0×"] || [value isEqual:@"1×"]) { + value = @"0,9×"; + } + if (_leftItem.superview != nil) { + [_leftItem setValue:value selected:true animated:animated]; + [_centerItem setValue:@"1" selected:false animated:animated]; + } else { + [_centerItem setValue:value selected:false animated:animated]; + } [_rightItem setValue:@"2" selected:false animated:animated]; } else if (zoomLevel < 2.0) { - [_leftItem setValue:@"0.5" selected:false animated:animated]; - if ((zoomLevel - 1.0) < FLT_EPSILON) { - [_centerItem setValue:@"1x" selected:true animated:animated]; + [_leftItem setValue:@"0,5" selected:false animated:animated]; + bool selected = _hasTelephotoCamera && _hasUltrawideCamera; + if ((zoomLevel - 1.0) < 0.025) { + [_centerItem setValue:@"1×" selected:true animated:animated]; } else { - [_centerItem setValue:[NSString stringWithFormat:@"%.1fx", zoomLevel] selected:true animated:animated]; + NSString *value = [NSString stringWithFormat:@"%.1f×", zoomLevel]; + value = [value stringByReplacingOccurrencesOfString:@"." withString:@","]; + value = [value stringByReplacingOccurrencesOfString:@",0×" withString:@"×"]; + if ([value isEqual:@"2×"]) { + value = @"1,9×"; + } + [_centerItem setValue:value selected:selected animated:animated]; } [_rightItem setValue:@"2" selected:false animated:animated]; } else { - [_leftItem setValue:@"0.5" selected:false animated:animated]; - [_centerItem setValue:@"1" selected:false animated:animated]; + [_leftItem setValue:@"0,5" selected:false animated:animated]; + + NSString *value = [[NSString stringWithFormat:@"%.1f×", zoomLevel] stringByReplacingOccurrencesOfString:@"." withString:@","]; + value = [value stringByReplacingOccurrencesOfString:@",0×" withString:@"×"]; - CGFloat near = round(zoomLevel); - if (ABS(zoomLevel - near) < FLT_EPSILON) { - [_rightItem setValue:[NSString stringWithFormat:@"%d", (int)zoomLevel] selected:true animated:animated]; + if (_rightItem.superview != nil) { + [_centerItem setValue:@"1" selected:false animated:animated]; + [_rightItem setValue:value selected:true animated:animated]; } else { - [_rightItem setValue:[NSString stringWithFormat:@"%.1fx", zoomLevel] selected:true animated:animated]; + [_centerItem setValue:value selected:true animated:animated]; } } } @@ -375,8 +495,16 @@ - (void)layoutSubviews { - if (_leftItem.isHidden) { - + if (_leftItem.superview == nil && _rightItem.superview == nil) { + _backgroundView.frame = CGRectMake(43, 0, 43, 43); + } else if (_leftItem.superview != nil && _rightItem.superview == nil) { + _backgroundView.frame = CGRectMake(21 + TGScreenPixel, 0, 86, 43); + _leftItem.frame = CGRectMake(21 + TGScreenPixel, 0, 43, 43); + _centerItem.frame = CGRectMake(21 + TGScreenPixel + 43, 0, 43, 43); + } else if (_leftItem.superview == nil && _rightItem.superview != nil) { + _backgroundView.frame = CGRectMake(21 + TGScreenPixel, 0, 86, 43); + _centerItem.frame = CGRectMake(21 + TGScreenPixel, 0, 43, 43); + _rightItem.frame = CGRectMake(21 + TGScreenPixel + 43, 0, 43, 43); } else { _leftItem.frame = CGRectMake(0, 0, 43, 43.0); _centerItem.frame = CGRectMake(43, 0, 43, 43.0); @@ -384,49 +512,406 @@ } } +- (void)setInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + _interfaceOrientation = interfaceOrientation; + _leftItem.transform = CGAffineTransformMakeRotation(TGRotationForInterfaceOrientation(interfaceOrientation)); + _centerItem.transform = CGAffineTransformMakeRotation(TGRotationForInterfaceOrientation(interfaceOrientation)); + _rightItem.transform = CGAffineTransformMakeRotation(TGRotationForInterfaceOrientation(interfaceOrientation)); +} + @end @interface TGCameraZoomWheelView () { + bool _hasUltrawideCamera; + bool _hasTelephotoCamera; + UIView *_containerView; UIImageView *_backgroundView; + UIImageView *_scaleView; + UIImageView *_maskView; + UIImageView *_arrowView; + + UILabel *_valueLabel; + UILabel *_05Label; + UILabel *_1Label; + UILabel *_2Label; + UILabel *_8Label; + + UIPanGestureRecognizer *_gestureRecognizer; + + UISelectionFeedbackGenerator *_feedbackGenerator; } @end @implementation TGCameraZoomWheelView -- (instancetype)initWithFrame:(CGRect)frame +- (void)_drawLineInContext:(CGContextRef)context side:(CGFloat)side atAngle:(CGFloat)angle lineLength:(CGFloat)lineLength lineWidth:(CGFloat)lineWidth opaque:(bool)opaque { + CGContextSaveGState(context); + + CGContextTranslateCTM(context, side / 2.0, side / 2.0); + CGContextRotateCTM(context, angle); + CGContextTranslateCTM(context, -side / 2.0, -side / 2.0); + + CGContextSetLineWidth(context, lineWidth); + CGContextSetStrokeColorWithColor(context, [UIColor colorWithWhite:1.0 alpha:opaque ? 1.0 : 0.5].CGColor); + CGContextMoveToPoint(context, side / 2.0, 4.0); + CGContextAddLineToPoint(context, side / 2.0, 4.0 + lineLength); + CGContextStrokePath(context); + + CGContextRestoreGState(context); +} + +- (NSArray *)ultraLines { + return @[ + @[@0.5, @-19.6, @3], + @[@0.6, @-14.4, @1], + @[@0.7, @-10.0, @1], + @[@0.8, @-6.3, @1], + @[@0.9, @-3.0, @1] + ]; +} + +- (NSArray *)lines { + return @[ + @[@1.0, @0.0, @3], + + @[@1.1, @2.7, @1], + @[@1.2, @5.2, @1], + @[@1.3, @7.4, @1], + @[@1.4, @9.6, @1], + @[@1.5, @11.5, @1], + @[@1.6, @13.3, @1], + @[@1.7, @15.0, @1], + @[@1.8, @16.7, @1], + @[@1.9, @18.2, @1], + @[@2.0, @19.6, _hasTelephotoCamera ? @3 : @2], + + @[@2.1, @21.0, @1], + @[@2.2, @22.4, @1], + @[@2.3, @23.7, @1], + @[@2.4, @24.8, @1], + @[@2.5, @26.0, @1], + @[@2.6, @27.1, @1], + @[@2.7, @28.2, @1], + @[@2.8, @29.2, @1], + @[@2.9, @30.2, @1], + @[@3.0, @31.1, @2], + + @[@3.1, @32.0, @1], + @[@3.2, @32.9, @1], + @[@3.3, @33.8, @1], + @[@3.4, @34.7, @1], + @[@3.5, @35.5, @1], + @[@3.6, @36.34, @1], + @[@3.7, @37.1, @1], + @[@3.8, @37.85, @1], + @[@3.9, @38.55, @1], + @[@4.0, @39.3, @2], + + @[@4.1, @40.0, @1], + @[@4.2, @40.77, @1], + @[@4.3, @41.4, @1], + @[@4.4, @42.05, @1], + @[@4.5, @42.63, @1], + @[@4.6, @43.3, @1], + @[@4.7, @43.89, @1], + @[@4.8, @44.42, @1], + @[@4.9, @45.05, @1], + @[@5.0, @45.6, @2], + + @[@5.1, @46.17, @1], + @[@5.2, @46.77, @1], + @[@5.3, @47.31, @1], + @[@5.4, @47.78, @1], + @[@5.5, @48.34, @1], + @[@5.6, @48.8, @1], + @[@5.7, @49.31, @1], + @[@5.8, @49.85, @1], + @[@5.9, @50.3, @1], + @[@6.0, @50.8, @2], + + @[@6.1, @51.25, @1], + @[@6.2, @51.7, @1], + @[@6.3, @52.18, @1], + @[@6.4, @52.63, @1], + @[@6.5, @53.12, @1], + @[@6.6, @53.49, @1], + @[@6.7, @53.88, @1], + @[@6.8, @54.28, @1], + @[@6.9, @54.71, @1], + @[@7.0, @55.15, @2], + + @[@7.1, @55.53, @1], + @[@7.2, @55.91, @1], + @[@7.3, @56.36, @1], + @[@7.4, @56.74, @1], + @[@7.5, @57.09, @1], + @[@7.6, @57.52, @1], + @[@7.7, @57.89, @1], + @[@7.8, @58.19, @1], + @[@7.9, @58.56, @1], + @[@8.0, @58.93, @3], + ]; +} + +- (instancetype)initWithFrame:(CGRect)frame hasUltrawideCamera:(bool)hasUltrawideCamera hasTelephotoCamera:(bool)hasTelephotoCamera { self = [super initWithFrame:frame]; if (self != nil) { - self.clipsToBounds = true; + TGIsRetina(); - _backgroundView = [[UIImageView alloc] initWithFrame:CGRectMake(-28.0, 0.0, 446.0, 446.0)]; - _backgroundView.alpha = 0.75; - + if (iosMajorVersion() >= 10) { + _feedbackGenerator = [[UISelectionFeedbackGenerator alloc] init]; + } + + _hasUltrawideCamera = hasUltrawideCamera; + _hasTelephotoCamera = hasTelephotoCamera; + + CGFloat side = floor(frame.size.width * 1.1435); + CGFloat length = 17.0; + CGFloat smallWidth = MAX(0.5, 1.0 - TGScreenPixel); + CGFloat mediumWidth = smallWidth; + CGFloat bigWidth = 1.0; + + _backgroundView = [[UIImageView alloc] initWithImage:TGCircleImage(side, [UIColor colorWithWhite:0.0 alpha:0.5])]; + _backgroundView.frame = CGRectMake(TGScreenPixelFloor((frame.size.width - side) / 2.0), 0.0, side, side); [self addSubview:_backgroundView]; + + UIGraphicsBeginImageContextWithOptions(CGSizeMake(side, side), false, 0.0f); + CGContextRef context = UIGraphicsGetCurrentContext(); + + NSArray *ultraLines = [self ultraLines]; + NSArray *lines = [self lines]; + + if (_hasUltrawideCamera) { + for (NSArray *values in ultraLines) { + CGFloat angle = [values[1] floatValue]; + CGFloat width = [values[2] intValue]; + + CGFloat lineWidth = smallWidth; + if (width == 2) { + lineWidth = mediumWidth; + } else if (width == 3) { + lineWidth = bigWidth; + } + [self _drawLineInContext:context side:side atAngle:TGDegreesToRadians(angle) lineLength:length lineWidth:lineWidth opaque:width > 1]; + } + } + for (NSArray *values in lines) { + CGFloat angle = [values[1] floatValue]; + CGFloat width = [values[2] intValue]; + + CGFloat lineWidth = smallWidth; + if (width == 2) { + lineWidth = mediumWidth; + } else if (width == 3) { + lineWidth = bigWidth; + } + + [self _drawLineInContext:context side:side atAngle:TGDegreesToRadians(angle) lineLength:length lineWidth:lineWidth opaque:width > 1]; + } + + UIImage *scaleImage = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:25 topCapHeight:25]; + UIGraphicsEndImageContext(); + + _containerView = [[UIView alloc] initWithFrame:CGRectMake(TGScreenPixelFloor((frame.size.width - side) / 2.0), 0.0, side, frame.size.height)]; + _containerView.userInteractionEnabled = false; + [self addSubview:_containerView]; + + UIGraphicsBeginImageContextWithOptions(CGSizeMake(side, frame.size.height), false, 0.0f); + context = UIGraphicsGetCurrentContext(); + + CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); + CGContextFillEllipseInRect(context, CGRectMake(0, 0, side, side)); + + CGContextSetBlendMode(context, kCGBlendModeClear); + CGContextMoveToPoint(context, side / 2.0 - 7.0, 0); + CGContextAddLineToPoint(context, side / 2.0 + 7.0, 0); + CGContextAddLineToPoint(context, side / 2.0 + 2.0, 22); + CGContextAddLineToPoint(context, side / 2.0 - 2.0, 22); + CGContextClosePath(context); + CGContextFillPath(context); + + CGContextFillRect(context, CGRectMake(side / 2.0 - 1.0, 20, 2.0, 7.0)); + CGContextFillEllipseInRect(context, CGRectMake(side / 2.0 - 17.0, 21.0, 34.0, 34.0)); + + UIImage *maskImage = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:25 topCapHeight:25]; + UIGraphicsEndImageContext(); + + _maskView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, side, frame.size.height)]; + _maskView.image = maskImage; + _containerView.maskView = _maskView; + + _scaleView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, side, side)]; + _scaleView.image = scaleImage; + [_containerView addSubview:_scaleView]; + + UIGraphicsBeginImageContextWithOptions(CGSizeMake(4, 10), false, 0.0f); + context = UIGraphicsGetCurrentContext(); + + CGContextSetFillColorWithColor(context, [TGCameraInterfaceAssets accentColor].CGColor); + CGContextMoveToPoint(context, 0, 0); + CGContextAddLineToPoint(context, 4, 0); + CGContextAddLineToPoint(context, 2 + TGScreenPixel, 10); + CGContextAddLineToPoint(context, 2 - TGScreenPixel, 10); + CGContextClosePath(context); + CGContextFillPath(context); + + UIImage *arrowImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + _arrowView = [[UIImageView alloc] initWithFrame:CGRectMake(floor((frame.size.width - 4) / 2.0), 4, 4, 10)]; + _arrowView.image = arrowImage; + _arrowView.userInteractionEnabled = false; + [self addSubview:_arrowView]; + + _valueLabel = [[UILabel alloc] init]; + _valueLabel.font = [TGCameraInterfaceAssets boldFontOfSize:13.0]; + _valueLabel.textColor = [TGCameraInterfaceAssets accentColor]; + _valueLabel.userInteractionEnabled = false; + [self addSubview:_valueLabel]; + + CGFloat radius = side / 2.0; + if (_hasUltrawideCamera) { + _05Label = [[UILabel alloc] init]; + _05Label.text = @"0,5"; + _05Label.font = [TGCameraInterfaceAssets boldFontOfSize:13.0]; + _05Label.textColor = [UIColor whiteColor]; + [_05Label sizeToFit]; + [_scaleView addSubview:_05Label]; + + _05Label.center = CGPointMake(radius - sin(TGDegreesToRadians(19.6)) * (radius - 38.0), radius - cos(TGDegreesToRadians(19.6)) * (radius - 38.0)); + _05Label.transform = CGAffineTransformMakeRotation(TGDegreesToRadians(-19.6)); + } + + _1Label = [[UILabel alloc] init]; + _1Label.text = @"1"; + _1Label.font = [TGCameraInterfaceAssets boldFontOfSize:13.0]; + _1Label.textColor = [UIColor whiteColor]; + [_1Label sizeToFit]; + _1Label.frame = CGRectMake(TGScreenPixelFloor((_scaleView.bounds.size.width - _1Label.frame.size.width) / 2.0), 30.0, _1Label.frame.size.width, _1Label.frame.size.height); + [_scaleView addSubview:_1Label]; + + if (_hasTelephotoCamera) { + _2Label = [[UILabel alloc] init]; + _2Label.text = @"2"; + _2Label.font = [TGCameraInterfaceAssets boldFontOfSize:13.0]; + _2Label.textColor = [UIColor whiteColor]; + [_2Label sizeToFit]; + [_scaleView addSubview:_2Label]; + + _2Label.center = CGPointMake(radius - sin(TGDegreesToRadians(-19.6)) * (radius - 38.0), radius - cos(TGDegreesToRadians(-19.6)) * (radius - 38.0)); + _2Label.transform = CGAffineTransformMakeRotation(TGDegreesToRadians(19.6)); + } + + _8Label = [[UILabel alloc] init]; + _8Label.text = @"8"; + _8Label.font = [TGCameraInterfaceAssets boldFontOfSize:13.0]; + _8Label.textColor = [UIColor whiteColor]; + [_8Label sizeToFit]; + [_scaleView addSubview:_8Label]; + + _8Label.center = CGPointMake(radius - sin(TGDegreesToRadians(-58.93)) * (radius - 38.0), radius - cos(TGDegreesToRadians(-58.93)) * (radius - 38.0)); + _8Label.transform = CGAffineTransformMakeRotation(TGDegreesToRadians(58.93)); + + _gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGesture:)]; + [self addGestureRecognizer:_gestureRecognizer]; } return self; } +- (void)panGesture:(UIPanGestureRecognizer *)gestureRecognizer { + if (self.panGesture != nil) { + self.panGesture(gestureRecognizer); + } +} + + - (void)setZoomLevel:(CGFloat)zoomLevel { + [self setZoomLevel:zoomLevel panning:false]; +} + +- (void)setZoomLevel:(CGFloat)zoomLevel panning:(bool)panning { zoomLevel = MAX(0.5, zoomLevel); _zoomLevel = zoomLevel; - CGFloat angle = 0.0; - if (zoomLevel < 1.0) { - CGFloat delta = (zoomLevel - 0.5) / 0.5; - angle = TGDegreesToRadians(20.8) * (1.0 - delta); - } else if (zoomLevel < 2.0) { - CGFloat delta = zoomLevel - 1.0; - angle = TGDegreesToRadians(-22.0) * delta; - } else if (zoomLevel < 10.0) { - CGFloat delta = (zoomLevel - 2.0) / 8.0; - angle = TGDegreesToRadians(-22.0) + TGDegreesToRadians(-68.0) * delta; + NSArray *ultraLines = [self ultraLines]; + NSArray *lines = [self lines]; + + CGFloat finalAngle = 0.0; + NSArray *allLines = [ultraLines arrayByAddingObjectsFromArray:lines]; + NSArray *previous = nil; + for (NSArray *values in allLines) { + CGFloat value = [values[0] floatValue]; + CGFloat angle = [values[1] floatValue]; + + if (previous == nil && zoomLevel <= value) { + finalAngle = angle; + break; + } + + if (previous != nil && zoomLevel <= value) { + if (zoomLevel == value) { + finalAngle = angle; + break; + } else { + CGFloat previousValue = [previous[0] floatValue]; + CGFloat previousAngle = [previous[1] floatValue]; + + if (zoomLevel > previousValue) { + CGFloat factor = (zoomLevel - previousValue) / (value - previousValue); + finalAngle = previousAngle + (angle - previousAngle) * factor; + break; + } + } + } + previous = values; + } + finalAngle = -TGDegreesToRadians(finalAngle); + + _scaleView.transform = CGAffineTransformMakeRotation(finalAngle); + + NSString *value = [NSString stringWithFormat:@"%.1f×", zoomLevel]; + value = [value stringByReplacingOccurrencesOfString:@"." withString:@","]; + value = [value stringByReplacingOccurrencesOfString:@",0×" withString:@"×"]; + + NSString *previousValue = _valueLabel.text; + _valueLabel.text = value; + [_valueLabel sizeToFit]; + + if (panning && ![previousValue isEqualToString:value] && ([value isEqualToString:@"0,5×"] || ![value containsString:@","])) { + [_feedbackGenerator selectionChanged]; } - _backgroundView.transform = CGAffineTransformMakeRotation(angle); + CGRect valueLabelFrame = CGRectMake(TGScreenPixelFloor((self.frame.size.width - _valueLabel.bounds.size.width) / 2.0), 30.0, _valueLabel.bounds.size.width, _valueLabel.bounds.size.height); + _valueLabel.bounds = CGRectMake(0, 0, valueLabelFrame.size.width, valueLabelFrame.size.height); + _valueLabel.center = CGPointMake(valueLabelFrame.origin.x + valueLabelFrame.size.width / 2.0, valueLabelFrame.origin.y + valueLabelFrame.size.height / 2.0); +} + +- (void)setInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { + _interfaceOrientation = interfaceOrientation; + + CGFloat delta = 0.0f; + switch (interfaceOrientation) { + case UIInterfaceOrientationLandscapeLeft: + delta = -90.0f; + break; + case UIInterfaceOrientationLandscapeRight: + delta = 90.0f; + break; + case UIInterfaceOrientationPortraitUpsideDown: + delta = 180.0f; + default: + break; + } + _valueLabel.transform = CGAffineTransformMakeRotation(TGDegreesToRadians(delta)); + _05Label.transform = CGAffineTransformMakeRotation(TGDegreesToRadians(-19.6 + delta)); + _1Label.transform = CGAffineTransformMakeRotation(TGDegreesToRadians(delta)); + _2Label.transform = CGAffineTransformMakeRotation(TGDegreesToRadians(19.6 + delta)); + _8Label.transform = CGAffineTransformMakeRotation(TGDegreesToRadians(58.93 + delta)); } - (void)setHidden:(BOOL)hidden diff --git a/submodules/LegacyComponents/Sources/TGClipboardPreviewItemView.m b/submodules/LegacyComponents/Sources/TGClipboardPreviewItemView.m index b36c74226d..c51cd1f32e 100644 --- a/submodules/LegacyComponents/Sources/TGClipboardPreviewItemView.m +++ b/submodules/LegacyComponents/Sources/TGClipboardPreviewItemView.m @@ -50,8 +50,9 @@ const CGFloat TGClipboardPreviewEdgeInset = 8.0f; _collectionLayout.minimumLineSpacing = 8.0f; _collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, TGClipboardPreviewCellHeight + TGClipboardPreviewEdgeInset * 2) collectionViewLayout:_collectionLayout]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { _collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + } _collectionView.backgroundColor = [UIColor whiteColor]; _collectionView.dataSource = self; _collectionView.delegate = self; diff --git a/submodules/LegacyComponents/Sources/TGConversation.m b/submodules/LegacyComponents/Sources/TGConversation.m index 6bdd274cb8..b250f6d810 100644 --- a/submodules/LegacyComponents/Sources/TGConversation.m +++ b/submodules/LegacyComponents/Sources/TGConversation.m @@ -1367,8 +1367,7 @@ return TGPeerIdIsAd(_conversationId); } -- (NSString *)chatPhotoFullSmall -{ +- (NSString *)chatPhotoFullSmall { NSString *finalAvatarUrl = self.chatPhotoSmall; if (finalAvatarUrl.length == 0) return finalAvatarUrl; @@ -1389,8 +1388,7 @@ return finalAvatarUrl; } -- (NSString *)chatPhotoFullBig -{ +- (NSString *)chatPhotoFullBig { NSString *finalAvatarUrl = self.chatPhotoBig; if (finalAvatarUrl.length == 0) return finalAvatarUrl; diff --git a/submodules/LegacyComponents/Sources/TGGradientLabel.m b/submodules/LegacyComponents/Sources/TGGradientLabel.m index ca5f4212dd..23dc4e2943 100644 --- a/submodules/LegacyComponents/Sources/TGGradientLabel.m +++ b/submodules/LegacyComponents/Sources/TGGradientLabel.m @@ -31,12 +31,8 @@ { if (_text == nil || _font == nil) return; - - if (iosMajorVersion() >= 7) { - _textSize = [self.text boundingRectWithSize:CGSizeMake(1000.0f, 1000.0f) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: _font} context:nil].size; - } else { - _textSize = [self.text sizeWithFont:_font]; - } + + _textSize = [self.text boundingRectWithSize:CGSizeMake(1000.0f, 1000.0f) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: _font} context:nil].size; CGRect frame = self.frame; frame.size = _textSize; @@ -126,98 +122,20 @@ { CGPoint drawingOffset = CGPointMake(CGFloor((bounds.size.width - _textSize.width) / 2.0f), CGFloor((bounds.size.height - _textSize.height) / 2.0f)); - if (false) - { - memset(_offscreenMemory, 0x00, (int)(_offscreenContextStride * _offscreenContextHeight)); - UIGraphicsPushContext(_offscreenContext); - [_text drawAtPoint:CGPointMake(2.0f, 2.0f) withFont:_font]; - UIGraphicsPopContext(); - - int minX = _offscreenContextWidth; - int minY = _offscreenContextHeight; - int maxX = 0; - int maxY = 0; - - const void *offscreenMemory = _offscreenMemory; - const int offscreenStride = _offscreenContextStride; - for (int y = 0; y < _offscreenContextHeight; y++) - { - for (int x = 0; x < _offscreenContextWidth; x++) - { - if ((*((uint32_t *)&offscreenMemory[y * offscreenStride + x * 4]) & 0xff000000) == 0xff000000) - { - if (x < minX) - minX = x; - if (y < minY) - minY = y; - if (x > maxX) - maxX = x; - if (y > maxY) - maxY = y; - } - } - } - - const int halfY = (maxY + minY) / 2; - const int halfX = (maxX + minX) / 2; - - int topHalf = 0; - int bottomHalf = 0; - int rightHalf = 0; - int leftHalf = 0; - for (int y = minY; y <= maxY; y++) - { - for (int x = minX; x <= maxX; x++) - { - if ((*((uint32_t *)&offscreenMemory[y * offscreenStride + x * 4]) & 0xff000000) == 0xff000000) - { - if (x < halfX) - leftHalf++; - else - rightHalf++; - - if (y < halfY) - topHalf++; - else - bottomHalf++; - } - } - } - - CGFloat topOffset = 0.0f; - CGFloat leftOffset = 0.0f; - - if (topHalf != 0 && bottomHalf != 0 && leftHalf != 0 && rightHalf != 0) - { - topOffset = topHalf / (CGFloat)bottomHalf - 1.0f; - leftOffset = leftHalf / (CGFloat)rightHalf - 1.0f; - } - - minY = _offscreenContextHeight - minY; - maxY = _offscreenContextHeight - maxY; - int tmp = maxY; - maxY = minY; - minY = tmp; - - CGSize realSize = CGSizeMake(maxX - minX, maxY - minY); - CGPoint realOffset = CGPointMake(minX - 2.0f, minY - 2.0f); - - if (realSize.width > FLT_EPSILON && realSize.height > FLT_EPSILON) - { - drawingOffset = CGPointMake(CGFloor((bounds.size.width - realSize.width) / 2.0f) - realOffset.x, CGFloor((bounds.size.height - realSize.height) / 2.0f) - realOffset.y); - //drawingOffset.x += leftOffset; - //drawingOffset.y += topOffset; - } - } - CGContextSetFillColorWithColor(context, _textColor.CGColor); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [_text drawAtPoint:drawingOffset withFont:_font]; +#pragma clang diagnostic pop } else { CGContextSetTextDrawingMode(context, kCGTextClip); - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [_text drawAtPoint:CGPointMake(CGFloor((bounds.size.width - _textSize.width) / 2.0f), CGFloor((bounds.size.height - _textSize.height) / 2.0f)) withFont:_font]; +#pragma clang diagnostic pop CGColorRef colors[2] = { CGColorRetain(UIColorRGB(_topColor).CGColor), diff --git a/submodules/LegacyComponents/Sources/TGHashtagPanelCell.m b/submodules/LegacyComponents/Sources/TGHashtagPanelCell.m index eaf52ced26..ea19909e4c 100644 --- a/submodules/LegacyComponents/Sources/TGHashtagPanelCell.m +++ b/submodules/LegacyComponents/Sources/TGHashtagPanelCell.m @@ -103,7 +103,10 @@ NSString *const TGHashtagPanelCellKind = @"TGHashtagPanelCell"; [super layoutSubviews]; CGFloat inset = 15.0f; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGSize labelSize = [_label.text sizeWithFont:_label.font]; +#pragma clang diagnostic pop labelSize.width = CGCeil(MIN(labelSize.width, self.frame.size.width - inset * 2.0f)); labelSize.height = CGCeil(labelSize.height); _label.frame = CGRectMake(inset, CGFloor((self.frame.size.height - labelSize.height) / 2.0f), labelSize.width, labelSize.height); diff --git a/submodules/LegacyComponents/Sources/TGImageBlur.m b/submodules/LegacyComponents/Sources/TGImageBlur.m index df6d285dd8..2b0017d0ad 100644 --- a/submodules/LegacyComponents/Sources/TGImageBlur.m +++ b/submodules/LegacyComponents/Sources/TGImageBlur.m @@ -1706,6 +1706,9 @@ UIImage *TGBlurredAlphaImage(UIImage *source, CGSize size) UIImage *TGBlurredRectangularImage(UIImage *source, bool more, CGSize size, CGSize renderSize, uint32_t *averageColor, void (^pixelProcessingBlock)(void *, int, int, int)) { CGSize fittedSize = fitSize(size, CGSizeMake(90, 90)); + if ((int)(fittedSize.width) % 2 != 0) { + fittedSize.width += 1.0; + } CGSize fittedRenderSize = CGSizeMake(fittedSize.width / size.width * renderSize.width, fittedSize.height / size.height * renderSize.height); const struct { int width, height; } blurredContextSize = { (int)fittedSize.width, (int)fittedSize.height }; @@ -2288,12 +2291,12 @@ UIImage *TGCropBackdropImage(UIImage *source, CGSize size) UIImage *TGCameraPositionSwitchImage(UIImage *source, CGSize size) { - return TGBlurredRectangularImage(source, false, size, size, NULL, nil); + return TGBlurredRectangularImage(source, true, size, size, NULL, nil); } UIImage *TGCameraModeSwitchImage(UIImage *source, CGSize size) { - return TGBlurredRectangularImage(source, false, size, size, NULL, nil); + return TGBlurredRectangularImage(source, true, size, size, NULL, nil); } UIImage *TGScaleAndCropImageToPixelSize(UIImage *source, CGSize size, CGSize renderSize, uint32_t *averageColor, void (^pixelProcessingBlock)(void *, int, int, int)) diff --git a/submodules/LegacyComponents/Sources/TGImageUtils.mm b/submodules/LegacyComponents/Sources/TGImageUtils.mm index c10be6021b..0734f8b066 100644 --- a/submodules/LegacyComponents/Sources/TGImageUtils.mm +++ b/submodules/LegacyComponents/Sources/TGImageUtils.mm @@ -229,22 +229,6 @@ UIImage *TGScaleAndBlurImage(NSData *data, __unused CGSize size, __autoreleasing return returnImage; } -static void matrixMul(CGFloat *a, CGFloat *b, CGFloat *result) -{ - for (int i = 0; i != 4; ++i) - { - for (int j = 0; j != 4; ++j) - { - CGFloat sum = 0; - for (int k = 0; k != 4; ++k) - { - sum += a[i + k * 4] * b[k + j * 4]; - } - result[i + j * 4] = sum; - } - } -} - UIImage *TGScaleImageToPixelSize(UIImage *image, CGSize size) { UIGraphicsBeginImageContextWithOptions(size, true, 1.0f); diff --git a/submodules/LegacyComponents/Sources/TGLetteredAvatarView.m b/submodules/LegacyComponents/Sources/TGLetteredAvatarView.m index daf1f0c3fa..fcdc4ea406 100644 --- a/submodules/LegacyComponents/Sources/TGLetteredAvatarView.m +++ b/submodules/LegacyComponents/Sources/TGLetteredAvatarView.m @@ -27,8 +27,9 @@ self = [super initWithFrame:frame]; if (self != nil) { - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { self.accessibilityIgnoresInvertColors = true; + } _label = [[TGGradientLabel alloc] init]; _label.backgroundColor = [UIColor clearColor]; diff --git a/submodules/LegacyComponents/Sources/TGMediaAssetModernImageSignals.m b/submodules/LegacyComponents/Sources/TGMediaAssetModernImageSignals.m index e63a2daeb3..003bf452f9 100644 --- a/submodules/LegacyComponents/Sources/TGMediaAssetModernImageSignals.m +++ b/submodules/LegacyComponents/Sources/TGMediaAssetModernImageSignals.m @@ -311,7 +311,10 @@ { fileUrl = tmpURL; dataUTI = @"public.jpeg"; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" imageData = [[NSData alloc] initWithContentsOfMappedFile:fileUrl.path]; +#pragma clang diagnostic pop NSString *lowcaseString = [fileName lowercaseString]; NSRange range = [lowcaseString rangeOfString:@".heic"]; diff --git a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m index 37058dca22..b3e558fba3 100644 --- a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m +++ b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m @@ -583,12 +583,15 @@ hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON; } } - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGFloat inset = [TGViewController safeAreaInsetForOrientation:self.interfaceOrientation hasOnScreenNavigation:hasOnScreenNavigation].bottom; _toolbarView = [[TGMediaPickerToolbarView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - TGMediaPickerToolbarHeight - inset, self.view.frame.size.width, TGMediaPickerToolbarHeight + inset)]; if (_pallete != nil) _toolbarView.pallete = _pallete; _toolbarView.safeAreaInset = [TGViewController safeAreaInsetForOrientation:self.interfaceOrientation hasOnScreenNavigation:hasOnScreenNavigation]; +#pragma clang diagnostic pop _toolbarView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; if ((_intent != TGMediaAssetsControllerSendFileIntent && _intent != TGMediaAssetsControllerSendMediaIntent && _intent != TGMediaAssetsControllerPassportMultipleIntent) || _selectionContext == nil) [_toolbarView setRightButtonHidden:true]; @@ -609,19 +612,24 @@ }; } [self.view addSubview:_toolbarView]; - - if (iosMajorVersion() >= 14 && [PHPhotoLibrary authorizationStatusForAccessLevel:PHAccessLevelReadWrite] == PHAuthorizationStatusLimited) { - _accessView = [[TGMediaPickerAccessView alloc] init]; - _accessView.autoresizingMask = UIViewAutoresizingFlexibleWidth; - _accessView.safeAreaInset = [TGViewController safeAreaInsetForOrientation:self.interfaceOrientation hasOnScreenNavigation:hasOnScreenNavigation]; - [_accessView setPallete:_pallete]; - _accessView.pressed = ^{ - __strong TGMediaAssetsController *strongSelf = weakSelf; - if (strongSelf != nil) { - [strongSelf manageAccess]; - } - }; - [self.view addSubview:_accessView]; + + if (@available(iOS 14.0, *)) { + if ([PHPhotoLibrary authorizationStatusForAccessLevel:PHAccessLevelReadWrite] == PHAuthorizationStatusLimited) { + _accessView = [[TGMediaPickerAccessView alloc] init]; + _accessView.autoresizingMask = UIViewAutoresizingFlexibleWidth; + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + _accessView.safeAreaInset = [TGViewController safeAreaInsetForOrientation:self.interfaceOrientation hasOnScreenNavigation:hasOnScreenNavigation]; + #pragma clang diagnostic pop + [_accessView setPallete:_pallete]; + _accessView.pressed = ^{ + __strong TGMediaAssetsController *strongSelf = weakSelf; + if (strongSelf != nil) { + [strongSelf manageAccess]; + } + }; + [self.view addSubview:_accessView]; + } } } diff --git a/submodules/LegacyComponents/Sources/TGMediaAssetsGifCell.m b/submodules/LegacyComponents/Sources/TGMediaAssetsGifCell.m index ca3015a6e4..2052108aa3 100644 --- a/submodules/LegacyComponents/Sources/TGMediaAssetsGifCell.m +++ b/submodules/LegacyComponents/Sources/TGMediaAssetsGifCell.m @@ -64,8 +64,7 @@ NSString *const TGMediaAssetsGifCellKind = @"TGMediaAssetsGifCellKind"; [_typeLabel sizeToFit]; [self addSubview:_typeLabel]; - if (iosMajorVersion() >= 11) - { + if (@available(iOS 11.0, *)) { _shadowView.accessibilityIgnoresInvertColors = true; _typeLabel.accessibilityIgnoresInvertColors = true; } diff --git a/submodules/LegacyComponents/Sources/TGMediaAssetsVideoCell.m b/submodules/LegacyComponents/Sources/TGMediaAssetsVideoCell.m index 2e1eab68a8..51953e671a 100644 --- a/submodules/LegacyComponents/Sources/TGMediaAssetsVideoCell.m +++ b/submodules/LegacyComponents/Sources/TGMediaAssetsVideoCell.m @@ -74,8 +74,7 @@ NSString *const TGMediaAssetsVideoCellKind = @"TGMediaAssetsVideoCellKind"; _adjustmentsDisposable = [[SMetaDisposable alloc] init]; - if (iosMajorVersion() >= 11) - { + if (@available(iOS 11.0, *)) { _shadowView.accessibilityIgnoresInvertColors = true; _durationLabel.accessibilityIgnoresInvertColors = true; } diff --git a/submodules/LegacyComponents/Sources/TGMediaAvatarEditorTransition.m b/submodules/LegacyComponents/Sources/TGMediaAvatarEditorTransition.m index e23598e047..256b51d0f6 100644 --- a/submodules/LegacyComponents/Sources/TGMediaAvatarEditorTransition.m +++ b/submodules/LegacyComponents/Sources/TGMediaAvatarEditorTransition.m @@ -76,8 +76,9 @@ orientation = UIInterfaceOrientationPortrait; bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { hasOnScreenNavigation = _controller.view.safeAreaInsets.bottom > FLT_EPSILON; + } CGSize referenceViewSize = [_controller referenceViewSizeForOrientation:orientation]; CGRect containerFrame = [TGPhotoEditorTabController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceViewSize.width, referenceViewSize.height) toolbarLandscapeSize:_controller.toolbarLandscapeSize orientation:orientation panelSize:0.0f hasOnScreenNavigation:hasOnScreenNavigation]; diff --git a/submodules/LegacyComponents/Sources/TGMediaAvatarMenuMixin.m b/submodules/LegacyComponents/Sources/TGMediaAvatarMenuMixin.m index 12b4f56a8a..191aac1ce9 100644 --- a/submodules/LegacyComponents/Sources/TGMediaAvatarMenuMixin.m +++ b/submodules/LegacyComponents/Sources/TGMediaAvatarMenuMixin.m @@ -53,7 +53,7 @@ _hasSearchButton = hasSearchButton; _hasDeleteButton = hasDeleteButton; _hasViewButton = hasViewButton; - _personalPhoto = ![TGCameraController useLegacyCamera] ? personalPhoto : false; + _personalPhoto = personalPhoto; _isVideo = isVideo; _signup = signup; } @@ -63,11 +63,8 @@ - (TGMenuSheetController *)present { [_parentController.view endEditing:true]; - - if (true || [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) - return [self _presentAvatarMenu]; - else - return [self _presentLegacyAvatarMenu]; + + return [self _presentAvatarMenu]; } - (TGMenuSheetController *)_presentAvatarMenu diff --git a/submodules/LegacyComponents/Sources/TGMediaGroupCell.m b/submodules/LegacyComponents/Sources/TGMediaGroupCell.m index cc458c004c..0da21877b6 100644 --- a/submodules/LegacyComponents/Sources/TGMediaGroupCell.m +++ b/submodules/LegacyComponents/Sources/TGMediaGroupCell.m @@ -124,8 +124,7 @@ const CGFloat TGMediaGroupCellHeight = 86.0f; _iconView.contentMode = UIViewContentModeCenter; [self.contentView addSubview:_iconView]; - if (iosMajorVersion() >= 11) - { + if (@available(iOS 11.0, *)) { _shadowView.accessibilityIgnoresInvertColors = true; _iconView.accessibilityIgnoresInvertColors = true; } @@ -227,8 +226,9 @@ const CGFloat TGMediaGroupCellHeight = 86.0f; imageType:TGMediaAssetImageTypeThumbnail size:CGSizeMake(138, 138)]]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { imageView.accessibilityIgnoresInvertColors = true; + } } else { @@ -237,8 +237,9 @@ const CGFloat TGMediaGroupCellHeight = 86.0f; [imageView reset]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { imageView.accessibilityIgnoresInvertColors = false; + } } } } @@ -254,8 +255,9 @@ const CGFloat TGMediaGroupCellHeight = 86.0f; [imageView reset]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { imageView.accessibilityIgnoresInvertColors = false; + } } [(TGImageView *)_imageViews.firstObject setImage:TGComponentsImageNamed(@"ModernMediaEmptyAlbumIcon")]; @@ -345,8 +347,11 @@ const CGFloat TGMediaGroupCellHeight = 86.0f; CGSize titleSize = [_nameLabel sizeThatFits:CGSizeMake(self.contentView.frame.size.width - _nameLabel.frame.origin.x - 20, _nameLabel.frame.size.height)]; _nameLabel.frame = CGRectMake(_nameLabel.frame.origin.x, y, ceil(titleSize.width), ceil(titleSize.height)); - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGSize countSize = [_countLabel.text sizeWithFont:_countLabel.font]; +#pragma clang diagnostic pop _countLabel.frame = (CGRect){ _countLabel.frame.origin, { ceil(countSize.width), ceil(countSize.height) } }; } diff --git a/submodules/LegacyComponents/Sources/TGMediaGroupsController.m b/submodules/LegacyComponents/Sources/TGMediaGroupsController.m index c079d3ce7d..6a748cc4f3 100644 --- a/submodules/LegacyComponents/Sources/TGMediaGroupsController.m +++ b/submodules/LegacyComponents/Sources/TGMediaGroupsController.m @@ -50,8 +50,9 @@ self.view.backgroundColor = self.pallete != nil ? self.pallete.backgroundColor : [UIColor whiteColor]; _tableView = [[UITableView alloc] initWithFrame:self.view.bounds]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { _tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + } _tableView.alwaysBounceVertical = true; _tableView.backgroundColor = self.view.backgroundColor; _tableView.delaysContentTouches = true; diff --git a/submodules/LegacyComponents/Sources/TGMediaOriginInfo.m b/submodules/LegacyComponents/Sources/TGMediaOriginInfo.m index a49ea56a8a..0a62731f5f 100644 --- a/submodules/LegacyComponents/Sources/TGMediaOriginInfo.m +++ b/submodules/LegacyComponents/Sources/TGMediaOriginInfo.m @@ -114,8 +114,11 @@ case TGMediaOriginTypeWebpage: { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" NSString *url = (__bridge_transfer NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL, (CFStringRef)keyComponents[1], CFSTR(""), kCFStringEncodingUTF8); info->_webpageUrl = url; +#pragma clang diagnostic pop } break; diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerCaptionInputPanel.m b/submodules/LegacyComponents/Sources/TGMediaPickerCaptionInputPanel.m index c253a45589..446bccd311 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerCaptionInputPanel.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerCaptionInputPanel.m @@ -92,14 +92,20 @@ static void setViewFrame(UIView *view, CGRect frame) localizationPlaceholderText = TGLocalized(@"MediaPicker.AddCaption"); NSString *placeholderText = TGLocalized(@"MediaPicker.AddCaption"); UIFont *placeholderFont = TGSystemFontOfSize(17); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGSize placeholderSize = [placeholderText sizeWithFont:placeholderFont]; +#pragma clang diagnostic pop placeholderSize.width += 2.0f; placeholderSize.height += 2.0f; UIGraphicsBeginImageContextWithOptions(placeholderSize, false, 0.0f); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(context, UIColorRGB(0xffffff).CGColor); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [placeholderText drawAtPoint:CGPointMake(1.0f, 1.0f) withFont:placeholderFont]; +#pragma clang diagnostic pop placeholderImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); } diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerCell.m b/submodules/LegacyComponents/Sources/TGMediaPickerCell.m index 6e4ae434c8..ff25ee6009 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerCell.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerCell.m @@ -31,8 +31,9 @@ _imageView.clipsToBounds = true; [self addSubview:_imageView]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { _imageView.accessibilityIgnoresInvertColors = true; + } _typeIconView = [[UIImageView alloc] init]; _typeIconView.contentMode = UIViewContentModeCenter; @@ -69,8 +70,9 @@ [_checkButton addTarget:self action:@selector(checkButtonPressed) forControlEvents:UIControlEventTouchUpInside]; [self addSubview:_checkButton]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { _checkButton.accessibilityIgnoresInvertColors = true; + } } if (_itemSelectedDisposable == nil) diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerController.m b/submodules/LegacyComponents/Sources/TGMediaPickerController.m index 7e90bc9fd0..903335590e 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerController.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerController.m @@ -62,8 +62,9 @@ [self.view addSubview:_wrapperView]; _collectionView = [[[self _collectionViewClass] alloc] initWithFrame:_wrapperView.bounds collectionViewLayout:[self _collectionLayout]]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { _collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + } _collectionView.alwaysBounceVertical = true; _collectionView.backgroundColor = self.view.backgroundColor; _collectionView.delaysContentTouches = true; @@ -101,8 +102,9 @@ [super viewDidLoad]; bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON; + } UIInterfaceOrientation orientation = UIInterfaceOrientationPortrait; if (self.view.frame.size.width > self.view.frame.size.height) @@ -267,12 +269,16 @@ if (!self.isViewLoaded) { return; } - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" UIEdgeInsets contentInset = [self controllerInsetForInterfaceOrientation:self.interfaceOrientation]; +#pragma clang diagnostic pop bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON; + } CGPoint contentOffset = CGPointMake(0, _collectionView.contentSize.height - _collectionView.frame.size.height + contentInset.bottom); if (contentOffset.y < -contentInset.top) @@ -308,8 +314,9 @@ orientation = UIInterfaceOrientationLandscapeLeft; bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON; + } UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:hasOnScreenNavigation]; @@ -326,7 +333,10 @@ } else if (lastOffset < -_collectionView.contentInset.top + 2) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" UIEdgeInsets contentInset = [self controllerInsetForInterfaceOrientation:self.interfaceOrientation]; +#pragma clang diagnostic pop CGPoint contentOffset = CGPointMake(0, -contentInset.top); [_collectionView setContentOffset:contentOffset animated:false]; diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m index 079ea3250d..d0b13eb90d 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m @@ -1701,7 +1701,7 @@ } - (CGRect)doneButtonFrame { - if (UIDeviceOrientationIsPortrait([self interfaceOrientation])) { + if (UIDeviceOrientationIsPortrait((UIDeviceOrientation)[self interfaceOrientation])) { return [_portraitToolbarView.doneButton convertRect:_portraitToolbarView.doneButton.bounds toView:nil]; } else { return [_landscapeToolbarView.doneButton convertRect:_landscapeToolbarView.doneButton.bounds toView:nil]; diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryModel.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryModel.m index 8da4a30f77..745859dc26 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryModel.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryModel.m @@ -278,8 +278,9 @@ }]; }; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { _interfaceView.accessibilityIgnoresInvertColors = true; + } } return _interfaceView; } @@ -564,8 +565,9 @@ [_context setStatusBarHidden:false withAnimation:UIStatusBarAnimationNone]; } - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { [strongSelf.controller setNeedsUpdateOfScreenEdgesDeferringSystemGestures]; + } }; controller.requestThumbnailImage = ^SSignal *(id editableItem) diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGallerySelectedItemsModel.m b/submodules/LegacyComponents/Sources/TGMediaPickerGallerySelectedItemsModel.m index da3b641321..ffa3a3abd9 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGallerySelectedItemsModel.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGallerySelectedItemsModel.m @@ -97,8 +97,6 @@ - (void)clear { - NSInteger index = 0; - [_items enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id item, NSUInteger index, __unused BOOL *stop) { if (self.selectionUpdated != nil) diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m index dfdea64019..24c07a0634 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoItemView.m @@ -794,17 +794,12 @@ CGSize rotatedCropSize = cropRect.size; if (orientation == UIImageOrientationLeft || orientation == UIImageOrientationRight) rotatedCropSize = CGSizeMake(rotatedCropSize.height, rotatedCropSize.width); - - CGSize containerSize = _imageView.frame.size; - CGSize fittedSize = TGScaleToSize(rotatedCropSize, containerSize); - CGRect previewFrame = CGRectMake((containerSize.width - fittedSize.width) / 2, (containerSize.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height); CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(TGRotationForOrientation(orientation)); _contentView.transform = rotationTransform; _contentView.frame = _imageView.frame; CGSize fittedContentSize = [TGPhotoPaintController fittedContentSize:cropRect orientation:orientation originalSize:originalSize]; - CGRect fittedCropRect = [TGPhotoPaintController fittedCropRect:cropRect originalSize:originalSize keepOriginalSize:false]; _contentWrapperView.frame = CGRectMake(0.0f, 0.0f, fittedContentSize.width, fittedContentSize.height); CGFloat contentScale = ratio; @@ -817,13 +812,7 @@ CGSize fittedOriginalSize = TGScaleToSize(originalSize, [TGPhotoPaintController maximumPaintingSize]); CGSize rotatedSize = TGRotatedContentSize(fittedOriginalSize, 0.0); - CGPoint centerPoint = CGPointMake(rotatedSize.width / 2.0f, rotatedSize.height / 2.0f); - - CGFloat scale = fittedOriginalSize.width / originalSize.width; - CGPoint offset = TGPaintSubtractPoints(centerPoint, [TGPhotoPaintController fittedCropRect:cropRect centerScale:scale]); - - CGPoint boundsCenter = TGPaintCenterOfRect(_contentWrapperView.bounds); -// _entitiesContainerView.center = TGPaintAddPoints(boundsCenter, CGPointMake(offset.x / contentScale, offset.y / contentScale)); + __unused CGPoint centerPoint = CGPointMake(rotatedSize.width / 2.0f, rotatedSize.height / 2.0f); } - (TGPhotoEntitiesContainerView *)entitiesView { @@ -1423,7 +1412,7 @@ TGVideoEditAdjustments *adjustments = (TGVideoEditAdjustments *)[self.item.editingContext adjustmentsForItem:self.item.editableMediaItem]; if ([self itemIsLivePhoto]) { if (adjustments.sendAsGif) { - return [[TGCameraCapturedVideo alloc] initWithAsset:self.item.editableMediaItem livePhoto:true]; + return [[TGCameraCapturedVideo alloc] initWithAsset:(TGMediaAsset *)self.item.editableMediaItem livePhoto:true]; } else { return self.item.editableMediaItem; } @@ -1595,8 +1584,7 @@ { if (timestamps.count == 0) return; - - SSignal *avAsset = self.item.avAsset ?: [SSignal single:_player.currentItem.asset]; + TGMediaEditingContext *editingContext = self.item.editingContext; id editableItem = self.editableMediaItem; diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerModernGalleryMixin.m b/submodules/LegacyComponents/Sources/TGMediaPickerModernGalleryMixin.m index 702201f77c..ed80d1c179 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerModernGalleryMixin.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerModernGalleryMixin.m @@ -370,7 +370,7 @@ { case TGMediaAssetVideoType: { - galleryItem = [[TGMediaPickerGalleryVideoItem alloc] initWithAsset:asset]; + galleryItem = [[TGMediaPickerGalleryVideoItem alloc] initWithAsset:(id)asset]; } break; @@ -386,7 +386,7 @@ // if (asset.subtypes & TGMediaAssetSubtypePhotoLive) // galleryItem = [[TGMediaPickerGalleryVideoItem alloc] initWithAsset:asset]; // else - galleryItem = [[TGMediaPickerGalleryPhotoItem alloc] initWithAsset:asset]; + galleryItem = [[TGMediaPickerGalleryPhotoItem alloc] initWithAsset:(id)asset]; } break; } diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m b/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m index 8a94b4c4e2..68bce634d9 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerPhotoCounterButton.m @@ -83,8 +83,12 @@ const CGFloat TGPhotoCounterButtonMaskFade = 18; CGFloat maskWidth = 50.0f; if (iosMajorVersion() >= 7) maskWidth += CGCeil([TGLocalized(@"MediaPicker.Processing") sizeWithAttributes:@{ NSFontAttributeName:TGSystemFontOfSize(16) }].width); - else + else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" maskWidth += CGCeil([TGLocalized(@"MediaPicker.Processing") sizeWithFont:TGSystemFontOfSize(16)].width); +#pragma clang diagnostic pop + } _processingMaskView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, maskWidth, 38)]; [_wrapperView addSubview:_processingMaskView]; diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerPhotoStripView.m b/submodules/LegacyComponents/Sources/TGMediaPickerPhotoStripView.m index 610595961d..a87b7b010f 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerPhotoStripView.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerPhotoStripView.m @@ -82,8 +82,9 @@ _collectionViewLayout.minimumLineSpacing = 4.0f; _collectionView = [[TGDraggableCollectionView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height) collectionViewLayout:_collectionViewLayout]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { _collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + } _collectionView.alwaysBounceHorizontal = false; _collectionView.alwaysBounceVertical = false; _collectionView.backgroundColor = [UIColor clearColor]; diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerToolbarView.m b/submodules/LegacyComponents/Sources/TGMediaPickerToolbarView.m index 2a855cccd6..b1e1ed3127 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerToolbarView.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerToolbarView.m @@ -267,6 +267,8 @@ const CGFloat TGMediaPickerToolbarHeight = 44.0f; - (void)setCenterButtonImage:(UIImage *)centerButtonImage { + _centerButtonImage = centerButtonImage; + if (_centerButton == nil) { _centerButton = [[TGModernButton alloc] initWithFrame:CGRectMake(round((self.frame.size.width - 60.0f) / 2.0f), 0, 60, 44)]; @@ -281,6 +283,8 @@ const CGFloat TGMediaPickerToolbarHeight = 44.0f; - (void)setCenterButtonSelectedImage:(UIImage *)centerButtonSelectedImage { + _centerButtonSelectedImage = centerButtonSelectedImage; + [_centerButton setImage:centerButtonSelectedImage forState:UIControlStateSelected]; [_centerButton setImage:centerButtonSelectedImage forState:UIControlStateSelected | UIControlStateHighlighted]; } diff --git a/submodules/LegacyComponents/Sources/TGMediaSelectionContext.m b/submodules/LegacyComponents/Sources/TGMediaSelectionContext.m index bc24af169c..9881c0d674 100644 --- a/submodules/LegacyComponents/Sources/TGMediaSelectionContext.m +++ b/submodules/LegacyComponents/Sources/TGMediaSelectionContext.m @@ -276,7 +276,7 @@ + (instancetype)changeWithItem:(id)item selected:(bool)selected animated:(bool)animated sender:(id)sender { TGMediaSelectionChange *change = [[TGMediaSelectionChange alloc] init]; - change->_item = item; + change->_item = (NSObject *)item; change->_selected = selected; change->_animated = animated; change->_sender = sender; diff --git a/submodules/LegacyComponents/Sources/TGMentionPanelCell.m b/submodules/LegacyComponents/Sources/TGMentionPanelCell.m index c316003e8a..2d75e3c26f 100644 --- a/submodules/LegacyComponents/Sources/TGMentionPanelCell.m +++ b/submodules/LegacyComponents/Sources/TGMentionPanelCell.m @@ -154,12 +154,18 @@ NSString *const TGMentionPanelCellKind = @"TGMentionPanelCell"; CGFloat leftInset = 51.0f; CGFloat spacing = 6.0f; CGFloat rightInset = 6.0f; - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGSize nameSize = [_nameLabel.text sizeWithFont:_nameLabel.font]; +#pragma clang diagnostic pop nameSize.width = CGCeil(MIN((boundsSize.width - leftInset - rightInset) * 3.0f / 4.0f, nameSize.width)); nameSize.height = CGCeil(nameSize.height); - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGSize usernameSize = [_usernameLabel.text sizeWithFont:_usernameLabel.font]; +#pragma clang diagnostic pop usernameSize.width = CGCeil(MIN(boundsSize.width - leftInset - rightInset - nameSize.width - spacing, usernameSize.width)); _nameLabel.frame = CGRectMake(leftInset, CGFloor((boundsSize.height - nameSize.height) / 2.0f), nameSize.width, nameSize.height); diff --git a/submodules/LegacyComponents/Sources/TGMenuSheetButtonItemView.m b/submodules/LegacyComponents/Sources/TGMenuSheetButtonItemView.m index db4462f238..8b5d049437 100644 --- a/submodules/LegacyComponents/Sources/TGMenuSheetButtonItemView.m +++ b/submodules/LegacyComponents/Sources/TGMenuSheetButtonItemView.m @@ -95,8 +95,9 @@ const CGFloat TGMenuSheetButtonItemViewHeight = 57.0f; _button.highlightBackgroundColor = nil; [self _updateForType:_buttonType]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { self.accessibilityIgnoresInvertColors = true; + } } - (void)setPallete:(TGMenuSheetPallete *)pallete @@ -176,6 +177,8 @@ const CGFloat TGMenuSheetButtonItemViewHeight = 57.0f; - (void)setThickDivider:(bool)thickDivider { + _thickDivider = thickDivider; + if (thickDivider && _customDivider == nil) { _customDivider = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.bounds.size.width, TGScreenPixel)]; diff --git a/submodules/LegacyComponents/Sources/TGMenuSheetController.m b/submodules/LegacyComponents/Sources/TGMenuSheetController.m index 8e10f799dc..465792e80c 100644 --- a/submodules/LegacyComponents/Sources/TGMenuSheetController.m +++ b/submodules/LegacyComponents/Sources/TGMenuSheetController.m @@ -45,7 +45,7 @@ typedef enum @end -@interface TGMenuSheetController () +@interface TGMenuSheetController () { bool _dark; @@ -75,8 +75,11 @@ typedef enum bool _checked3dTouch; NSDictionary *_3dTouchHandlers; - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" UIPopoverController *_popoverController; +#pragma clang diagnostic pop id _context; } @@ -99,8 +102,11 @@ typedef enum self.pallete = [[LegacyComponentsGlobals provider] darkMenuSheetPallete]; else if (!dark && [[LegacyComponentsGlobals provider] respondsToSelector:@selector(menuSheetPallete)]) self.pallete = [[LegacyComponentsGlobals provider] menuSheetPallete]; - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" self.wantsFullScreenLayout = true; +#pragma clang diagnostic pop } return self; } @@ -373,6 +379,8 @@ typedef enum *rect = self.sourceRect(); } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" - (void)popoverControllerDidDismissPopover:(UIPopoverController *)__unused popoverController { _popoverController = nil; @@ -383,6 +391,7 @@ typedef enum if (self.sourceRect != nil) *rect = self.sourceRect(); } +#pragma clang diagnostic pop #pragma mark - @@ -417,7 +426,10 @@ typedef enum } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" _popoverController = [[UIPopoverController alloc] initWithContentViewController:self]; +#pragma clang diagnostic pop UIColor *backgroundColor = self.pallete != nil ? self.pallete.backgroundColor : [UIColor whiteColor]; if ([_popoverController respondsToSelector:@selector(setBackgroundColor:)]) @@ -574,6 +586,8 @@ typedef enum if (animated) { self.view.userInteractionEnabled = false; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [self animateSheetViewToPosition:_sheetView.menuHeight + [self safeAreaInsetForOrientation:self.interfaceOrientation].bottom velocity:0 type:TGMenuSheetAnimationDismiss completion:^ { [self.view removeFromSuperview]; @@ -586,6 +600,7 @@ typedef enum if (completion != nil) completion(); }]; +#pragma clang diagnostic pop } else { @@ -807,10 +822,13 @@ typedef enum if (velocity > 200.0f && allowDismissal) { [self setDimViewHidden:true animated:true]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [self animateSheetViewToPosition:_sheetView.menuHeight + [self safeAreaInsetForOrientation:self.interfaceOrientation].bottom velocity:velocity type:TGMenuSheetAnimationDismiss completion:^ { [self dismissAnimated:false]; }]; +#pragma clang diagnostic pop } else { @@ -924,7 +942,10 @@ typedef enum _containerView.frame = CGRectMake(_containerView.frame.origin.x, _containerView.frame.origin.y, viewWidth, self.view.frame.size.height); _dimView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" _sheetView.safeAreaInset = [self safeAreaInsetForOrientation:self.interfaceOrientation]; +#pragma clang diagnostic pop CGFloat minSide = MIN(referenceSize.width, referenceSize.height); _sheetView.narrowInLandscape = self.narrowInLandscape; @@ -943,8 +964,9 @@ typedef enum - (UIEdgeInsets)safeAreaInsetForOrientation:(UIInterfaceOrientation)orientation { bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON; + } UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:hasOnScreenNavigation]; if (safeAreaInset.bottom > FLT_EPSILON) @@ -955,14 +977,19 @@ typedef enum - (UIEdgeInsets)safeAreaInset { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" return [self safeAreaInsetForOrientation:self.interfaceOrientation]; +#pragma clang diagnostic pop } - (void)repositionMenuWithReferenceSize:(CGSize)referenceSize { if ([self sizeClass] == UIUserInterfaceSizeClassRegular && !_forceFullScreen) return; - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" UIEdgeInsets safeAreaInset = [self safeAreaInsetForOrientation:self.interfaceOrientation]; if (_keyboardOffset > FLT_EPSILON) safeAreaInset.bottom = 0.0f; @@ -970,6 +997,7 @@ typedef enum CGFloat defaultStatusBarHeight = TGMenuSheetDefaultStatusBarHeight; if (!TGIsPad() && iosMajorVersion() >= 11 && UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) defaultStatusBarHeight = 0.0; +#pragma clang diagnostic pop CGFloat statusBarHeight = !UIEdgeInsetsEqualToEdgeInsets(safeAreaInset, UIEdgeInsetsZero) ? safeAreaInset.top : defaultStatusBarHeight; referenceSize.height = referenceSize.height + statusBarHeight - [self statusBarHeight]; @@ -995,7 +1023,9 @@ typedef enum CGSize statusBarSize = [_context statusBarFrame].size; CGFloat statusBarHeight = MIN(statusBarSize.width, statusBarSize.height); statusBarHeight = MAX(TGMenuSheetDefaultStatusBarHeight, statusBarHeight); - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" if (!TGIsPad() && iosMajorVersion() >= 11 && UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) { return 0.0f; @@ -1006,6 +1036,7 @@ typedef enum if (!UIEdgeInsetsEqualToEdgeInsets(safeAreaInset, UIEdgeInsetsZero)) statusBarHeight = 44.0f; } +#pragma clang diagnostic pop return statusBarHeight; } diff --git a/submodules/LegacyComponents/Sources/TGMenuSheetDimView.m b/submodules/LegacyComponents/Sources/TGMenuSheetDimView.m index b1b844ac19..0f8cbb75c1 100644 --- a/submodules/LegacyComponents/Sources/TGMenuSheetDimView.m +++ b/submodules/LegacyComponents/Sources/TGMenuSheetDimView.m @@ -111,8 +111,9 @@ setupView(_topView); } - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { self.accessibilityIgnoresInvertColors = true; + } } return self; } diff --git a/submodules/LegacyComponents/Sources/TGMenuSheetTitleItemView.m b/submodules/LegacyComponents/Sources/TGMenuSheetTitleItemView.m index 8f7208edc5..6eea101534 100644 --- a/submodules/LegacyComponents/Sources/TGMenuSheetTitleItemView.m +++ b/submodules/LegacyComponents/Sources/TGMenuSheetTitleItemView.m @@ -57,8 +57,9 @@ _subtitleLabel.backgroundColor = [UIColor clearColor]; _subtitleLabel.textColor = UIColorRGB(0x777777); - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { self.accessibilityIgnoresInvertColors = true; + } } - (void)setPallete:(TGMenuSheetPallete *)pallete diff --git a/submodules/LegacyComponents/Sources/TGMenuSheetView.m b/submodules/LegacyComponents/Sources/TGMenuSheetView.m index 7691d17acd..f54332f2ba 100644 --- a/submodules/LegacyComponents/Sources/TGMenuSheetView.m +++ b/submodules/LegacyComponents/Sources/TGMenuSheetView.m @@ -63,8 +63,9 @@ const CGFloat TGMenuSheetInterSectionSpacing = 8.0f; _effectView.frame = self.bounds; [self addSubview:_effectView]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { _effectView.accessibilityIgnoresInvertColors = true; + } } else { @@ -211,8 +212,9 @@ const CGFloat TGMenuSheetInterSectionSpacing = 8.0f; _mainBackgroundView.backgroundColor = _pallete.backgroundColor; _scrollView = [[TGMenuSheetScrollView alloc] initWithFrame:CGRectZero]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { _scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + } _scrollView.delegate = self; [_mainBackgroundView addSubview:_scrollView]; } diff --git a/submodules/LegacyComponents/Sources/TGMenuView.m b/submodules/LegacyComponents/Sources/TGMenuView.m index 04be557c73..6797ac4953 100644 --- a/submodules/LegacyComponents/Sources/TGMenuView.m +++ b/submodules/LegacyComponents/Sources/TGMenuView.m @@ -169,12 +169,18 @@ static UIImage *pagerLeftButtonHighlightedImage() { { if (self.isMultiline) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGSize size = [title sizeWithFont:self.titleLabel.font constrainedToSize:CGSizeMake(self.maxWidth - 18.0f, FLT_MAX) lineBreakMode:NSLineBreakByWordWrapping]; +#pragma clang diagnostic pop self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, ceil(size.width) + 18, MAX(41.0f, ceil(size.height) + 20.0f)); } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, [title sizeWithFont:self.titleLabel.font].width + 34, 41); +#pragma clang diagnostic pop } } else diff --git a/submodules/LegacyComponents/Sources/TGMessage.mm b/submodules/LegacyComponents/Sources/TGMessage.mm index d480477a9f..0a48ed23d4 100644 --- a/submodules/LegacyComponents/Sources/TGMessage.mm +++ b/submodules/LegacyComponents/Sources/TGMessage.mm @@ -409,7 +409,10 @@ typedef enum { NSString *link = [text substringWithRange:entity.range]; NSURL *url = [NSURL URLWithString:link]; if (url == nil) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" url = [NSURL URLWithString:[link stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; +#pragma clang diagnostic pop } [textCheckingResults addObject:[NSTextCheckingResult linkCheckingResultWithRange:entity.range URL:url]]; } else if ([entity isKindOfClass:[TGMessageEntityCashtag class]]) { @@ -876,7 +879,10 @@ typedef enum { NSString *link = [_text substringWithRange:entity.range]; NSURL *url = [NSURL URLWithString:link]; if (url == nil) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" url = [NSURL URLWithString:[link stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; +#pragma clang diagnostic pop } [textCheckingResults addObject:[NSTextCheckingResult linkCheckingResultWithRange:entity.range URL:url]]; } else if ([entity isKindOfClass:[TGMessageEntityCashtag class]]) { @@ -1359,7 +1365,7 @@ typedef enum { - (NSUInteger)hash { if (_cachedHash == 0) - _cachedHash = (int)(((_itemId >> 32) ^ _itemId & 0xffffffff) + (int)_type); + _cachedHash = (int)((((_itemId >> 32) ^ _itemId) & 0xffffffff) + (int)_type); return _cachedHash; } diff --git a/submodules/LegacyComponents/Sources/TGMessageImageViewOverlayView.m b/submodules/LegacyComponents/Sources/TGMessageImageViewOverlayView.m index 7d02043b76..6343d6fc6e 100644 --- a/submodules/LegacyComponents/Sources/TGMessageImageViewOverlayView.m +++ b/submodules/LegacyComponents/Sources/TGMessageImageViewOverlayView.m @@ -490,18 +490,10 @@ const NSInteger TGMessageImageViewOverlayParticlesCount = 40; if (_overlayStyle == TGMessageImageViewOverlayStyleDefault) CGContextSetStrokeColorWithColor(context, TGColorWithHexAndAlpha(0xff000000, 0.55f).CGColor); else if (_overlayStyle == TGMessageImageViewOverlayStyleIncoming) { - if (true || ABS(diameter - 37.0f) < 0.1) { - CGContextSetStrokeColorWithColor(context, [UIColor clearColor].CGColor); - } else { - CGContextSetStrokeColorWithColor(context, TGColorWithHex(0x4f9ef3).CGColor); - } + CGContextSetStrokeColorWithColor(context, [UIColor clearColor].CGColor); } else if (_overlayStyle == TGMessageImageViewOverlayStyleOutgoing) { - if (true || ABS(diameter - 37.0f) < 0.1) { - CGContextSetStrokeColorWithColor(context, [UIColor clearColor].CGColor); - } else { - CGContextSetStrokeColorWithColor(context, TGColorWithHex(0x64b15e).CGColor); - } + CGContextSetStrokeColorWithColor(context, [UIColor clearColor].CGColor); } else CGContextSetStrokeColorWithColor(context, TGAccentColor().CGColor); @@ -566,20 +558,12 @@ const NSInteger TGMessageImageViewOverlayParticlesCount = 40; } else if (_overlayStyle == TGMessageImageViewOverlayStyleIncoming) { - if (true || ABS(diameter - 37.0f) < 0.1) { - CGContextSetFillColorWithColor(context, incomingButtonColor.CGColor); - } else { - CGContextSetFillColorWithColor(context, TGColorWithHexAndAlpha(0x85baf2, 0.15f).CGColor); - } + CGContextSetFillColorWithColor(context, incomingButtonColor.CGColor); CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter)); } else if (_overlayStyle == TGMessageImageViewOverlayStyleOutgoing) { - if (true || ABS(diameter - 37.0f) < 0.1) { - CGContextSetFillColorWithColor(context, outgoingButtonColor.CGColor); - } else { - CGContextSetFillColorWithColor(context, TGColorWithHexAndAlpha(0x4fb212, 0.15f).CGColor); - } + CGContextSetFillColorWithColor(context, outgoingButtonColor.CGColor); CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter)); } else if (_overlayStyle == TGMessageImageViewOverlayStyleAccent) @@ -602,18 +586,10 @@ const NSInteger TGMessageImageViewOverlayParticlesCount = 40; if (_overlayStyle == TGMessageImageViewOverlayStyleDefault) CGContextSetStrokeColorWithColor(context, [UIColor clearColor].CGColor); else if (_overlayStyle == TGMessageImageViewOverlayStyleIncoming) { - if (true || ABS(diameter - 37.0f) < 0.1) { - CGContextSetStrokeColorWithColor(context, incomingIconColor.CGColor); - } else { - CGContextSetStrokeColorWithColor(context, TGColorWithHex(0x4f9ef3).CGColor); - } + CGContextSetStrokeColorWithColor(context, incomingIconColor.CGColor); } else if (_overlayStyle == TGMessageImageViewOverlayStyleOutgoing) { - if (true || ABS(diameter - 37.0f) < 0.1) { - CGContextSetStrokeColorWithColor(context, outgoingIconColor.CGColor); - } else { - CGContextSetStrokeColorWithColor(context, TGColorWithHex(0x64b15e).CGColor); - } + CGContextSetStrokeColorWithColor(context, outgoingIconColor.CGColor); } else CGContextSetStrokeColorWithColor(context, TGAccentColor().CGColor); @@ -644,18 +620,10 @@ const NSInteger TGMessageImageViewOverlayParticlesCount = 40; if (_overlayStyle == TGMessageImageViewOverlayStyleDefault) CGContextSetStrokeColorWithColor(context, [UIColor clearColor].CGColor); else if (_overlayStyle == TGMessageImageViewOverlayStyleIncoming) { - if (true || ABS(diameter - 37.0f) < 0.1) { - CGContextSetStrokeColorWithColor(context, incomingIconColor.CGColor); - } else { - CGContextSetStrokeColorWithColor(context, TGColorWithHex(0x4f9ef3).CGColor); - } + CGContextSetStrokeColorWithColor(context, incomingIconColor.CGColor); } else if (_overlayStyle == TGMessageImageViewOverlayStyleOutgoing) { - if (true || ABS(diameter - 37.0f) < 0.1) { - CGContextSetStrokeColorWithColor(context, outgoingIconColor.CGColor); - } else { - CGContextSetStrokeColorWithColor(context, TGColorWithHex(0x64b15e).CGColor); - } + CGContextSetStrokeColorWithColor(context, outgoingIconColor.CGColor); } else CGContextSetStrokeColorWithColor(context, TGAccentColor().CGColor); @@ -697,8 +665,6 @@ const NSInteger TGMessageImageViewOverlayParticlesCount = 40; case TGMessageImageViewOverlayViewTypePlay: { const CGFloat diameter = self.radius; - CGFloat width = round(diameter * 0.4); - CGFloat height = round(width * 1.2f); CGFloat offset = round(diameter * 0.06f); CGFloat verticalOffset = 0.0f; CGFloat alpha = 0.8f; @@ -925,18 +891,10 @@ const NSInteger TGMessageImageViewOverlayParticlesCount = 40; if (_overlayStyle == TGMessageImageViewOverlayStyleDefault) CGContextSetStrokeColorWithColor(context, [UIColor clearColor].CGColor); else if (_overlayStyle == TGMessageImageViewOverlayStyleIncoming) { - if (true || ABS(diameter - 37.0f) < 0.1) { - CGContextSetStrokeColorWithColor(context, incomingIconColor.CGColor); - } else { - CGContextSetStrokeColorWithColor(context, TGColorWithHex(0x4f9ef3).CGColor); - } + CGContextSetStrokeColorWithColor(context, incomingIconColor.CGColor); } else if (_overlayStyle == TGMessageImageViewOverlayStyleOutgoing) { - if (true || ABS(diameter - 37.0f) < 0.1) { - CGContextSetStrokeColorWithColor(context, outgoingIconColor.CGColor); - } else { - CGContextSetStrokeColorWithColor(context, TGColorWithHex(0x64b15e).CGColor); - } + CGContextSetStrokeColorWithColor(context, outgoingIconColor.CGColor); } else CGContextSetStrokeColorWithColor(context, TGAccentColor().CGColor); diff --git a/submodules/LegacyComponents/Sources/TGModernConversationHashtagsAssociatedPanel.m b/submodules/LegacyComponents/Sources/TGModernConversationHashtagsAssociatedPanel.m index e180a926fb..969b62ee10 100644 --- a/submodules/LegacyComponents/Sources/TGModernConversationHashtagsAssociatedPanel.m +++ b/submodules/LegacyComponents/Sources/TGModernConversationHashtagsAssociatedPanel.m @@ -75,8 +75,9 @@ [self addSubview:_bottomView]; _tableView = [[UITableView alloc] init]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { _tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + } _tableView.delegate = self; _tableView.dataSource = self; _tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine; diff --git a/submodules/LegacyComponents/Sources/TGModernConversationMentionsAssociatedPanel.m b/submodules/LegacyComponents/Sources/TGModernConversationMentionsAssociatedPanel.m index 7524af1b24..1deb889455 100644 --- a/submodules/LegacyComponents/Sources/TGModernConversationMentionsAssociatedPanel.m +++ b/submodules/LegacyComponents/Sources/TGModernConversationMentionsAssociatedPanel.m @@ -87,8 +87,9 @@ [self addSubview:_tableViewBackground]; _tableView = [[UITableView alloc] init]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { _tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + } _tableView.delegate = self; _tableView.dataSource = self; _tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine; diff --git a/submodules/LegacyComponents/Sources/TGModernGalleryController.m b/submodules/LegacyComponents/Sources/TGModernGalleryController.m index c9bb0800e3..d2bd0a5a07 100644 --- a/submodules/LegacyComponents/Sources/TGModernGalleryController.m +++ b/submodules/LegacyComponents/Sources/TGModernGalleryController.m @@ -307,8 +307,9 @@ { [_view showHideInterface]; - if ([self respondsToSelector:@selector(setNeedsUpdateOfHomeIndicatorAutoHidden)]) + if (@available(iOS 11.0, *)) { [self setNeedsUpdateOfHomeIndicatorAutoHidden]; + } } - (void)itemViewDidRequestGalleryDismissal:(TGModernGalleryItemView *)__unused itemView animated:(bool)animated diff --git a/submodules/LegacyComponents/Sources/TGModernGalleryDefaultInterfaceView.m b/submodules/LegacyComponents/Sources/TGModernGalleryDefaultInterfaceView.m index 24f07ca128..7f1720a598 100644 --- a/submodules/LegacyComponents/Sources/TGModernGalleryDefaultInterfaceView.m +++ b/submodules/LegacyComponents/Sources/TGModernGalleryDefaultInterfaceView.m @@ -79,8 +79,9 @@ _closeButton.frame = [self closeButtonFrameForSize:frame.size]; [_navigationBarView addSubview:_closeButton]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { self.accessibilityIgnoresInvertColors = true; + } } return self; } diff --git a/submodules/LegacyComponents/Sources/TGModernGalleryVideoView.m b/submodules/LegacyComponents/Sources/TGModernGalleryVideoView.m index f7ae948ab0..8915c81ec1 100644 --- a/submodules/LegacyComponents/Sources/TGModernGalleryVideoView.m +++ b/submodules/LegacyComponents/Sources/TGModernGalleryVideoView.m @@ -53,8 +53,9 @@ _key = key; self.playerLayer.player = player; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { self.accessibilityIgnoresInvertColors = true; + } } return self; } diff --git a/submodules/LegacyComponents/Sources/TGModernGalleryView.m b/submodules/LegacyComponents/Sources/TGModernGalleryView.m index b9ccd8f6f8..4c6d3e8dad 100644 --- a/submodules/LegacyComponents/Sources/TGModernGalleryView.m +++ b/submodules/LegacyComponents/Sources/TGModernGalleryView.m @@ -50,8 +50,9 @@ static const CGFloat swipeDistanceThreshold = 128.0f; _context = context; _itemPadding = itemPadding; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { self.accessibilityIgnoresInvertColors = true; + } self.opaque = false; self.backgroundColor = UIColorRGBA(0x000000, 1.0f); @@ -62,8 +63,9 @@ static const CGFloat swipeDistanceThreshold = 128.0f; [self addSubview:_scrollViewContainer]; _scrollView = [[TGModernGalleryScrollView alloc] initWithFrame:CGRectMake(-_itemPadding, 0.0f, frame.size.width + itemPadding * 2.0f, frame.size.height)]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { _scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + } [_scrollViewContainer addSubview:_scrollView]; _interfaceView = interfaceView; diff --git a/submodules/LegacyComponents/Sources/TGModernMediaListItemView.m b/submodules/LegacyComponents/Sources/TGModernMediaListItemView.m index 0e124599e2..a3a7c27945 100644 --- a/submodules/LegacyComponents/Sources/TGModernMediaListItemView.m +++ b/submodules/LegacyComponents/Sources/TGModernMediaListItemView.m @@ -7,6 +7,8 @@ - (void)prepareForReuse { [self _recycleItemContentView]; + + [super prepareForReuse]; } - (void)_recycleItemContentView diff --git a/submodules/LegacyComponents/Sources/TGNavigationBar.m b/submodules/LegacyComponents/Sources/TGNavigationBar.m index 57a2ba7d6c..fb338ba368 100644 --- a/submodules/LegacyComponents/Sources/TGNavigationBar.m +++ b/submodules/LegacyComponents/Sources/TGNavigationBar.m @@ -133,10 +133,11 @@ static id _musicPlayerProvider; self.tintColor = pallete.tintColor; NSMutableDictionary *attributes = [[NSMutableDictionary alloc] init]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" attributes[UITextAttributeTextColor] = pallete.titleColor; attributes[UITextAttributeTextShadowColor] = [UIColor clearColor]; - if (iosMajorVersion() < 7) - attributes[UITextAttributeFont] = TGBoldSystemFontOfSize(17.0f); +#pragma clang diagnostic pop [self setTitleTextAttributes:attributes]; } @@ -228,8 +229,10 @@ static id _musicPlayerProvider; if (_backgroundContainerView != nil) { CGFloat backgroundOverflow = iosMajorVersion() >= 7 ? 20.0f : 0.0f; - if (iosMajorVersion() >= 11 && self.superview.safeAreaInsets.top > FLT_EPSILON) - backgroundOverflow = self.superview.safeAreaInsets.top; + if (@available(iOS 11.0, *)) { + if (self.superview.safeAreaInsets.top > FLT_EPSILON) + backgroundOverflow = self.superview.safeAreaInsets.top; + } CGFloat heightAddition = 0.0; if (iosMajorVersion() < 11) { diff --git a/submodules/LegacyComponents/Sources/TGNavigationController.m b/submodules/LegacyComponents/Sources/TGNavigationController.m index d3bb25fb1b..ff761925af 100644 --- a/submodules/LegacyComponents/Sources/TGNavigationController.m +++ b/submodules/LegacyComponents/Sources/TGNavigationController.m @@ -130,7 +130,7 @@ { [super loadView]; - if (iosMajorVersion() >= 11) { + if (@available(iOS 11.0, *)) { self.navigationBar.prefersLargeTitles = false; } @@ -493,7 +493,7 @@ { UIBarStyle barStyle = UIBarStyleDefault; bool navigationBarShouldBeHidden = false; - UIStatusBarStyle statusBarStyle = UIStatusBarStyleBlackOpaque; + UIStatusBarStyle statusBarStyle = UIStatusBarStyleLightContent; bool statusBarShouldBeHidden = false; if ([viewController conformsToProtocol:@protocol(TGViewControllerNavigationBarAppearance)]) @@ -830,7 +830,7 @@ TGNavigationController *findNavigationController() return self.topViewController.preferredStatusBarStyle; } -- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)())completion { +- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion { if (_customDismissSelf) { _customDismissSelf(); if (completion) { diff --git a/submodules/LegacyComponents/Sources/TGPassportICloud.m b/submodules/LegacyComponents/Sources/TGPassportICloud.m index 08228489a2..cb46ac0813 100644 --- a/submodules/LegacyComponents/Sources/TGPassportICloud.m +++ b/submodules/LegacyComponents/Sources/TGPassportICloud.m @@ -21,7 +21,10 @@ return nil; NSError *error; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" NSString *urlData = [[url bookmarkDataWithOptions:NSURLBookmarkCreationSuitableForBookmarkFile includingResourceValuesForKeys:nil relativeToURL:nil error:&error] base64Encoding]; +#pragma clang diagnostic pop if (error != nil || urlData == nil) return nil; @@ -118,7 +121,10 @@ { return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" NSData *urlData = [[NSData alloc] initWithBase64Encoding:description.urlData]; +#pragma clang diagnostic pop if (urlData == nil) { [subscriber putCompletion]; diff --git a/submodules/LegacyComponents/Sources/TGPassportOCR.mm b/submodules/LegacyComponents/Sources/TGPassportOCR.mm index 17e14f9a8f..37d3b733fa 100644 --- a/submodules/LegacyComponents/Sources/TGPassportOCR.mm +++ b/submodules/LegacyComponents/Sources/TGPassportOCR.mm @@ -40,33 +40,37 @@ + (SSignal *)recognizeBarcodeInImage:(UIImage *)image { - return [[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { - VNDetectBarcodesRequest *barcodeRequest = [[VNDetectBarcodesRequest alloc] initWithCompletionHandler:^(VNRequest * _Nonnull request, NSError * _Nullable error) - { - TGPassportMRZ *mrz = nil; - NSArray *results = request.results; - for (VNBarcodeObservation *barcode in results) + if (@available(iOS 11.0, *)) { + return [[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { + VNDetectBarcodesRequest *barcodeRequest = [[VNDetectBarcodesRequest alloc] initWithCompletionHandler:^(VNRequest * _Nonnull request, NSError * _Nullable error) { - if (![barcode isKindOfClass:[VNBarcodeObservation class]]) - continue; - - if (barcode.symbology != VNBarcodeSymbologyPDF417) - continue; - - NSString *payload = barcode.payloadStringValue; - mrz = [TGPassportMRZ parseBarcodePayload:payload]; - } - - [subscriber putNext:mrz]; - [subscriber putCompletion]; - }]; - - NSError *error; - VNImageRequestHandler *handler = [[VNImageRequestHandler alloc] initWithCGImage:image.CGImage options:@{}]; - [handler performRequests:@[barcodeRequest] error:&error]; - - return nil; - }] startOn:[SQueue concurrentDefaultQueue]]; + TGPassportMRZ *mrz = nil; + NSArray *results = request.results; + for (VNBarcodeObservation *barcode in results) + { + if (![barcode isKindOfClass:[VNBarcodeObservation class]]) + continue; + + if (barcode.symbology != VNBarcodeSymbologyPDF417) + continue; + + NSString *payload = barcode.payloadStringValue; + mrz = [TGPassportMRZ parseBarcodePayload:payload]; + } + + [subscriber putNext:mrz]; + [subscriber putCompletion]; + }]; + + NSError *error; + VNImageRequestHandler *handler = [[VNImageRequestHandler alloc] initWithCGImage:image.CGImage options:@{}]; + [handler performRequests:@[barcodeRequest] error:&error]; + + return nil; + }] startOn:[SQueue concurrentDefaultQueue]]; + } else { + return [SSignal complete]; + } } @end diff --git a/submodules/LegacyComponents/Sources/TGPhotoAvatarPreviewController.m b/submodules/LegacyComponents/Sources/TGPhotoAvatarPreviewController.m index aa54d305f6..7b2ef48fdf 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoAvatarPreviewController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoAvatarPreviewController.m @@ -715,7 +715,10 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel - (void)updateToolViews { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" UIInterfaceOrientation orientation = self.interfaceOrientation; +#pragma clang diagnostic pop if ([self inFormSheet] || TGIsPad()) { _landscapeToolsWrapperView.hidden = true; diff --git a/submodules/LegacyComponents/Sources/TGPhotoCropController.m b/submodules/LegacyComponents/Sources/TGPhotoCropController.m index e64c049c3d..5fd4e04506 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoCropController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoCropController.m @@ -345,8 +345,9 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original"; { CGSize referenceSize = [self referenceViewSize]; bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON; + } CGRect containerFrame = [TGPhotoCropController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation hasArbitraryRotation:_cropView.hasArbitraryRotation hasOnScreenNavigation:hasOnScreenNavigation]; containerFrame = CGRectInset(containerFrame, TGPhotoCropAreaInsetSize.width, TGPhotoCropAreaInsetSize.height); @@ -761,8 +762,9 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original"; _wrapperView.frame = CGRectMake((referenceSize.width - screenSide) / 2, (referenceSize.height - screenSide) / 2, screenSide, screenSide); bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON; + } UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:hasOnScreenNavigation]; UIEdgeInsets screenEdges = UIEdgeInsetsMake((screenSide - referenceSize.height) / 2 , (screenSide - referenceSize.width) / 2, (screenSide + referenceSize.height) / 2, (screenSide + referenceSize.width) / 2); diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorBlurAreaView.m b/submodules/LegacyComponents/Sources/TGPhotoEditorBlurAreaView.m index 6ba6fa788c..77e0efbfd5 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoEditorBlurAreaView.m +++ b/submodules/LegacyComponents/Sources/TGPhotoEditorBlurAreaView.m @@ -169,4 +169,6 @@ } } +@synthesize interactionBegan; + @end diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorController.m b/submodules/LegacyComponents/Sources/TGPhotoEditorController.m index 166715d56b..cda57e13a9 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoEditorController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoEditorController.m @@ -208,8 +208,9 @@ self.view.frame = (CGRect){ CGPointZero, [self referenceViewSize]}; self.view.clipsToBounds = true; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { self.view.accessibilityIgnoresInvertColors = true; + } if ([self presentedForAvatarCreation] && ![self presentedFromCamera]) self.view.backgroundColor = [UIColor blackColor]; @@ -888,9 +889,11 @@ self.navigationController.interactivePopGestureRecognizer.enabled = true; } - - if ([self respondsToSelector:@selector(setNeedsUpdateOfScreenEdgesDeferringSystemGestures)]) - [self setNeedsUpdateOfScreenEdgesDeferringSystemGestures]; + + if (@available(iOS 11.0, *)) { + if ([self respondsToSelector:@selector(setNeedsUpdateOfScreenEdgesDeferringSystemGestures)]) + [self setNeedsUpdateOfScreenEdgesDeferringSystemGestures]; + } [super viewWillDisappear:animated]; } @@ -1274,6 +1277,8 @@ if ([self presentedFromCamera] && [self presentedForAvatarCreation]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft) { transitionReferenceFrame = CGRectMake(self.view.frame.size.width - transitionReferenceFrame.size.height - transitionReferenceFrame.origin.y, @@ -1286,6 +1291,7 @@ self.view.frame.size.height - transitionReferenceFrame.size.width - transitionReferenceFrame.origin.x, transitionReferenceFrame.size.height, transitionReferenceFrame.size.width); } +#pragma clang diagnostic pop } if ([self presentedForAvatarCreation] && ![self presentedFromCamera]) @@ -1719,9 +1725,11 @@ [_landscapeToolbarView setDoneButtonType:doneButtonType]; [self updateEditorButtons]; - - if ([self respondsToSelector:@selector(setNeedsUpdateOfScreenEdgesDeferringSystemGestures)]) - [self setNeedsUpdateOfScreenEdgesDeferringSystemGestures]; + + if (@available(iOS 11.0, *)) { + if ([self respondsToSelector:@selector(setNeedsUpdateOfScreenEdgesDeferringSystemGestures)]) + [self setNeedsUpdateOfScreenEdgesDeferringSystemGestures]; + } } - (void)updatePreviewView:(bool)full @@ -2352,13 +2360,17 @@ - (bool)hasOnScreenNavigation { bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON; + } return hasOnScreenNavigation; } - (UIInterfaceOrientation)effectiveOrientation { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" return [self effectiveOrientation:self.interfaceOrientation]; +#pragma clang diagnostic pop } - (UIInterfaceOrientation)effectiveOrientation:(UIInterfaceOrientation)orientation { diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorCurvesHistogramView.m b/submodules/LegacyComponents/Sources/TGPhotoEditorCurvesHistogramView.m index 678074367e..d727abbf0d 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoEditorCurvesHistogramView.m +++ b/submodules/LegacyComponents/Sources/TGPhotoEditorCurvesHistogramView.m @@ -228,4 +228,6 @@ }]]; } +@synthesize interactionBegan; + @end diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorInterfaceAssets.m b/submodules/LegacyComponents/Sources/TGPhotoEditorInterfaceAssets.m index 0f24694816..778a56f6c6 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoEditorInterfaceAssets.m +++ b/submodules/LegacyComponents/Sources/TGPhotoEditorInterfaceAssets.m @@ -261,9 +261,12 @@ CGContextStrokePath(context); UIFont *font = [TGFont roundedFontOfSize:11]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGSize textSize = [label sizeWithFont:font]; [[UIColor whiteColor] setFill]; [label drawInRect:CGRectMake((size.width - textSize.width) / 2.0f + TGScreenPixel, 4.0f, textSize.width, textSize.height) withFont:font]; +#pragma clang diagnostic pop UIImage *result = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); @@ -294,8 +297,11 @@ NSString *label = [NSString stringWithFormat:@"%ld", value]; UIFont *font = [TGFont roundedFontOfSize:11]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGSize size = [label sizeWithFont:font]; [label drawInRect:CGRectMake(floor(background.size.width - size.width) / 2.0f, 9.0f, size.width, size.height) withFont:font]; +#pragma clang diagnostic pop UIImage *result = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorSliderView.m b/submodules/LegacyComponents/Sources/TGPhotoEditorSliderView.m index 96f847a378..acfe8a1e31 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoEditorSliderView.m +++ b/submodules/LegacyComponents/Sources/TGPhotoEditorSliderView.m @@ -447,6 +447,7 @@ const CGFloat TGPhotoEditorSliderViewInternalMargin = 7.0f; } - (void)setEnablePanHandling:(bool)enablePanHandling { + _enablePanHandling = enablePanHandling; _panGestureRecognizer.enabled = enablePanHandling; } diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorTabController.m b/submodules/LegacyComponents/Sources/TGPhotoEditorTabController.m index be182acce2..3a9492964d 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoEditorTabController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoEditorTabController.m @@ -84,13 +84,17 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f; - (bool)hasOnScreenNavigation { bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON; + } return hasOnScreenNavigation; } - (UIInterfaceOrientation)effectiveOrientation { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" return [self effectiveOrientation:self.interfaceOrientation]; +#pragma clang diagnostic pop } - (UIInterfaceOrientation)effectiveOrientation:(UIInterfaceOrientation)orientation { @@ -227,6 +231,8 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f; if (self.intent & TGPhotoEditorControllerFromCameraIntent && self.intent & (TGPhotoEditorControllerAvatarIntent | TGPhotoEditorControllerSignupAvatarIntent)) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft) { referenceFrame = CGRectMake(referenceSize.height - referenceFrame.size.height - referenceFrame.origin.y, @@ -239,6 +245,7 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f; referenceFrame.origin.x, referenceFrame.size.height, referenceFrame.size.width); } +#pragma clang diagnostic pop } if (self.beginTransitionOut != nil) @@ -249,6 +256,8 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f; if (self.intent & TGPhotoEditorControllerFromCameraIntent && self.intent & (TGPhotoEditorControllerAvatarIntent | TGPhotoEditorControllerSignupAvatarIntent)) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft) { referenceFrame = CGRectMake(referenceSize.width - referenceFrame.size.height - referenceFrame.origin.y, @@ -261,6 +270,7 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f; referenceSize.height - referenceFrame.size.width - referenceFrame.origin.x, referenceFrame.size.height, referenceFrame.size.width); } +#pragma clang diagnostic pop } if (saving) @@ -343,8 +353,9 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f; - (CGRect)transitionOutSourceFrameForReferenceFrame:(CGRect)referenceFrame orientation:(UIInterfaceOrientation)orientation { bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON; + } CGRect containerFrame = [TGPhotoEditorTabController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoEditorPanelSize hasOnScreenNavigation:hasOnScreenNavigation]; CGSize fittedSize = TGScaleToSize(referenceFrame.size, containerFrame.size); @@ -367,8 +378,9 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f; UIInterfaceOrientation orientation = self.effectiveOrientation; bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON; + } CGRect containerFrame = [TGPhotoEditorTabController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoEditorPanelSize hasOnScreenNavigation:hasOnScreenNavigation]; CGSize fittedSize = TGScaleToSize(fromFrame.size, containerFrame.size); diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorToolButtonsView.m b/submodules/LegacyComponents/Sources/TGPhotoEditorToolButtonsView.m index de088068e8..6fa8aaf7c2 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoEditorToolButtonsView.m +++ b/submodules/LegacyComponents/Sources/TGPhotoEditorToolButtonsView.m @@ -106,10 +106,7 @@ const CGFloat TGPhotoEditorToolButtonsViewSize = 53; for (NSString *title in possibleButtonTitles) { CGFloat width = 0.0f; - if ([title respondsToSelector:@selector(sizeWithAttributes:)]) - width = CGCeil([title sizeWithAttributes:@{ NSFontAttributeName:TGSystemFontOfSize(17) }].width - 1); - else - width = CGCeil([title sizeWithFont:TGSystemFontOfSize(17)].width - 1); + width = CGCeil([title sizeWithAttributes:@{ NSFontAttributeName:TGSystemFontOfSize(17) }].width - 1); if (width > maxWidth) maxWidth = width; diff --git a/submodules/LegacyComponents/Sources/TGPhotoPaintColorPicker.m b/submodules/LegacyComponents/Sources/TGPhotoPaintColorPicker.m index 1bceed2d44..71812d67a3 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoPaintColorPicker.m +++ b/submodules/LegacyComponents/Sources/TGPhotoPaintColorPicker.m @@ -11,8 +11,8 @@ const CGFloat TGPhotoPaintColorWeightGestureRange = 320.0f; const CGFloat TGPhotoPaintVerticalThreshold = 5.0f; const CGFloat TGPhotoPaintPreviewOffset = -70.0f; const CGFloat TGPhotoPaintPreviewScale = 2.0f; -const CGFloat TGPhotoPaintDefaultBrushWeight = 0.22f; -const CGFloat TGPhotoPaintDefaultColorLocation = 1.0f; +const CGFloat TGPhotoPaintDefaultBrushWeight = 0.08f; +const CGFloat TGPhotoPaintDefaultColorLocation = 0.0f; @interface TGPhotoPaintColorPickerKnobCircleView : UIView { @@ -97,7 +97,7 @@ const CGFloat TGPhotoPaintDefaultColorLocation = 1.0f; [self addGestureRecognizer:_tapGestureRecognizer]; _location = [self restoreLastColorLocation]; - _weight = 0.08f; + _weight = TGPhotoPaintDefaultBrushWeight; } return self; } @@ -107,7 +107,7 @@ const CGFloat TGPhotoPaintDefaultColorLocation = 1.0f; NSNumber *lastColor = [[NSUserDefaults standardUserDefaults] objectForKey:@"TG_paintLastColorLocation_v0"]; if (lastColor != nil) return [lastColor floatValue]; - + return TGPhotoPaintDefaultColorLocation; } diff --git a/submodules/LegacyComponents/Sources/TGPhotoPaintController.m b/submodules/LegacyComponents/Sources/TGPhotoPaintController.m index fa503a47a3..8a5fa607e9 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoPaintController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoPaintController.m @@ -194,7 +194,7 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f; self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; _scrollView = [[TGModernGalleryZoomableScrollView alloc] initWithFrame:self.view.bounds hasDoubleTap:false]; - if (iosMajorVersion() >= 11) { + if (@available(iOS 11.0, *)) { _scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; } _scrollView.contentInset = UIEdgeInsetsZero; @@ -1414,7 +1414,8 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f; - (CGFloat)_brushWeightForSize:(CGFloat)size { - return ([self _brushBaseWeightForCurrentPainting] + [self _brushWeightRangeForCurrentPainting] * size) / _scrollView.zoomScale; + CGFloat scale = MAX(0.001, _scrollView.zoomScale); + return ([self _brushBaseWeightForCurrentPainting] + [self _brushWeightRangeForCurrentPainting] * size) / scale; } + (CGSize)maximumPaintingSize diff --git a/submodules/LegacyComponents/Sources/TGPhotoQualityController.m b/submodules/LegacyComponents/Sources/TGPhotoQualityController.m index ad9327dbb4..30e065f806 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoQualityController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoQualityController.m @@ -222,7 +222,9 @@ const NSTimeInterval TGPhotoQualityPreviewDuration = 15.0f; _portraitToolsWrapperView.alpha = 1.0f; _landscapeToolsWrapperView.alpha = 1.0f; }]; - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)) _portraitToolControlView.layer.shouldRasterize = true; else @@ -259,6 +261,7 @@ const NSTimeInterval TGPhotoQualityPreviewDuration = 15.0f; else _landscapeToolsWrapperView.frame = toolTargetFrame; }; +#pragma clang diagnostic pop [UIView animateWithDuration:0.3f animations:^ { @@ -280,6 +283,8 @@ const NSTimeInterval TGPhotoQualityPreviewDuration = 15.0f; - (void)transitionOutSwitching:(bool)__unused switching completion:(void (^)(void))completion { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" _wrapperView.backgroundColor = [UIColor clearColor]; if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)) @@ -347,6 +352,7 @@ const NSTimeInterval TGPhotoQualityPreviewDuration = 15.0f; if (completion != nil) completion(); }]; +#pragma clang diagnostic pop } - (void)_animatePreviewViewTransitionOutToFrame:(CGRect)targetFrame saving:(bool)saving parentView:(UIView *)__unused parentView completion:(void (^)(void))completion @@ -446,8 +452,9 @@ const NSTimeInterval TGPhotoQualityPreviewDuration = 15.0f; - (CGRect)transitionOutSourceFrameForReferenceFrame:(CGRect)referenceFrame orientation:(UIInterfaceOrientation)orientation { bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON; + } CGRect containerFrame = [TGPhotoQualityController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoEditorQualityPanelSize hasOnScreenNavigation:hasOnScreenNavigation]; CGSize fittedSize = TGScaleToSize(referenceFrame.size, containerFrame.size); @@ -459,14 +466,18 @@ const NSTimeInterval TGPhotoQualityPreviewDuration = 15.0f; - (CGRect)_targetFrameForTransitionInFromFrame:(CGRect)fromFrame { CGSize referenceSize = [self referenceViewSize]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" UIInterfaceOrientation orientation = self.interfaceOrientation; +#pragma clang diagnostic pop if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) orientation = UIInterfaceOrientationPortrait; bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON; + } CGRect containerFrame = [TGPhotoQualityController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoEditorQualityPanelSize hasOnScreenNavigation:hasOnScreenNavigation]; CGSize fittedSize = TGScaleToSize(fromFrame.size, containerFrame.size); @@ -519,8 +530,9 @@ const NSTimeInterval TGPhotoQualityPreviewDuration = 15.0f; CGFloat panelToolbarLandscapeSize = panelToolbarPortraitSize; bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON; + } UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:hasOnScreenNavigation]; UIEdgeInsets screenEdges = UIEdgeInsetsMake((screenSide - referenceSize.height) / 2 , (screenSide - referenceSize.width) / 2, (screenSide + referenceSize.height) / 2, (screenSide + referenceSize.width) / 2); @@ -741,8 +753,11 @@ const NSTimeInterval TGPhotoQualityPreviewDuration = 15.0f; [strongSelf.view insertSubview:strongSelf->_videoView belowSubview:belowView]; [strongSelf->_player play]; - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [strongSelf updateLayout:strongSelf.interfaceOrientation]; +#pragma clang diagnostic pop strongSelf->_overlayView.hidden = true; [strongSelf->_overlayView setProgress:0.03f cancelEnabled:false animated:true]; diff --git a/submodules/LegacyComponents/Sources/TGPhotoTextEntityView.m b/submodules/LegacyComponents/Sources/TGPhotoTextEntityView.m index 28446ca58d..cdce772876 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoTextEntityView.m +++ b/submodules/LegacyComponents/Sources/TGPhotoTextEntityView.m @@ -640,7 +640,7 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f; _path = nil; [self.rectArray removeAllObjects]; - [self enumerateLineFragmentsForGlyphRange:NSMakeRange(0, self.textStorage.string) usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer * _Nonnull textContainer, NSRange glyphRange, BOOL * _Nonnull stop) { + [self enumerateLineFragmentsForGlyphRange:NSMakeRange(0, self.textStorage.string.length) usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer * _Nonnull textContainer, NSRange glyphRange, BOOL * _Nonnull stop) { bool ignoreRange = false; NSRange characterRange = [self characterRangeForGlyphRange:glyphRange actualGlyphRange:nil]; NSString *substring = [[self.textStorage string] substringWithRange:characterRange]; @@ -662,9 +662,6 @@ const CGFloat TGPhotoTextSelectionViewHandleSide = 30.0f; // [super drawBackgroundForGlyphRange:glyphsToShow atPoint:origin]; if (self.frameColor != nil) { - NSRange range = [self characterRangeForGlyphRange:glyphsToShow actualGlyphRange:NULL]; - NSRange glyphRange = [self glyphRangeForCharacterRange:range actualCharacterRange:NULL]; - CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSaveGState(context); CGContextTranslateCTM(context, origin.x, origin.y); diff --git a/submodules/LegacyComponents/Sources/TGPhotoToolCell.m b/submodules/LegacyComponents/Sources/TGPhotoToolCell.m index dc42d9922f..9147522e4e 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoToolCell.m +++ b/submodules/LegacyComponents/Sources/TGPhotoToolCell.m @@ -29,6 +29,8 @@ NSString * const TGPhotoToolCellKind = @"TGPhotoToolCellKind"; { [_toolView removeFromSuperview]; _toolView = nil; + + [super prepareForReuse]; } - (void)setPhotoTool:(PGPhotoTool *)photoTool landscape:(bool)landscape nameWidth:(CGFloat)nameWidth changeBlock:(void (^)(PGPhotoTool *, id, bool))changeBlock interactionBegan:(void (^)(void))interactionBegan interactionEnded:(void (^)(void))interactionEnded diff --git a/submodules/LegacyComponents/Sources/TGPhotoToolsController.m b/submodules/LegacyComponents/Sources/TGPhotoToolsController.m index ac04824727..778c836b45 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoToolsController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoToolsController.m @@ -250,10 +250,7 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize { NSString *title = tool.title; CGFloat width = 0.0f; - if ([title respondsToSelector:@selector(sizeWithAttributes:)]) - width = CGCeil([title sizeWithAttributes:@{ NSFontAttributeName:[TGPhotoEditorInterfaceAssets editorItemTitleFont] }].width); - else - width = CGCeil([title sizeWithFont:[TGPhotoEditorInterfaceAssets editorItemTitleFont]].width); + width = CGCeil([title sizeWithAttributes:@{ NSFontAttributeName:[TGPhotoEditorInterfaceAssets editorItemTitleFont] }].width); if (width > maxTitleWidth) maxTitleWidth = width; @@ -844,7 +841,10 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize - (void)updateToolViews { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" UIInterfaceOrientation orientation = self.interfaceOrientation; +#pragma clang diagnostic pop if ([self inFormSheet] || TGIsPad()) { _landscapeToolsWrapperView.hidden = true; diff --git a/submodules/LegacyComponents/Sources/TGProgressWindow.m b/submodules/LegacyComponents/Sources/TGProgressWindow.m index a0f31d94a4..09e78ad1d1 100644 --- a/submodules/LegacyComponents/Sources/TGProgressWindow.m +++ b/submodules/LegacyComponents/Sources/TGProgressWindow.m @@ -121,7 +121,7 @@ static bool TGProgressWindowIsLight = true; [self dismiss:animated completion:nil]; } -- (void)dismiss:(bool)animated completion:(void (^)())completion +- (void)dismiss:(bool)animated completion:(void (^)(void))completion { TGProgressWindow *window = (TGProgressWindow *)_weakWindow; diff --git a/submodules/LegacyComponents/Sources/TGStringUtils.mm b/submodules/LegacyComponents/Sources/TGStringUtils.mm index c01d95a906..872fdd1411 100644 --- a/submodules/LegacyComponents/Sources/TGStringUtils.mm +++ b/submodules/LegacyComponents/Sources/TGStringUtils.mm @@ -306,23 +306,29 @@ static HTMLEscapeMap gAsciiHTMLEscapeMap[] = { + (NSString *)stringByEscapingForURL:(NSString *)string { static NSString * const kAFLegalCharactersToBeEscaped = @"?!@#$^&%*+=,.:;'\"`<>()[]{}/\\|~ "; - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" NSString *unescapedString = [string stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; if (unescapedString == nil) unescapedString = string; return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)unescapedString, NULL, (CFStringRef)kAFLegalCharactersToBeEscaped, CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding)); +#pragma clang diagnostic pop } + (NSString *)stringByEscapingForActorURL:(NSString *)string { static NSString * const kAFLegalCharactersToBeEscaped = @"?!@#$^&%*+=,:;'\"`<>()[]{}/\\|~ "; - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" NSString *unescapedString = [string stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; if (unescapedString == nil) unescapedString = string; return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)unescapedString, NULL, (CFStringRef)kAFLegalCharactersToBeEscaped, CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding)); +#pragma clang diagnostic pop } + (NSString *)stringByEncodingInBase64:(NSData *)data @@ -503,9 +509,11 @@ static HTMLEscapeMap gAsciiHTMLEscapeMap[] = { { NSRange equalsSignRange = [keyValuePair rangeOfString:@"="]; if (equalsSignRange.location != NSNotFound) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" NSString *key = [keyValuePair substringToIndex:equalsSignRange.location]; NSString *value = [[[keyValuePair substringFromIndex:equalsSignRange.location + equalsSignRange.length] stringByReplacingOccurrencesOfString:@"+" withString:@" "] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; - +#pragma clang diagnostic pop [queryStringDictionary setObject:value forKey:key]; } @@ -1389,7 +1397,7 @@ static unsigned char strToChar (char a, char b) buf[1] = [hex characterAtIndex:i+1]; char *b2 = NULL; *bp++ = strtol(buf, &b2, 16); - NSAssert(b2 == buf + 2, @"String should be all hex digits: %@ (bad digit around %d)", hex, i); + NSAssert(b2 == buf + 2, @"String should be all hex digits: %@ (bad digit around %d)", hex, (int)i); } return [NSData dataWithBytesNoCopy:bytes length:[hex length]/2 freeWhenDone:YES]; diff --git a/submodules/LegacyComponents/Sources/TGTextField.m b/submodules/LegacyComponents/Sources/TGTextField.m index 48b0c62e1e..1f5a5d67b4 100644 --- a/submodules/LegacyComponents/Sources/TGTextField.m +++ b/submodules/LegacyComponents/Sources/TGTextField.m @@ -12,8 +12,11 @@ else { CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), _placeholderColor.CGColor); - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGSize placeholderSize = [self.placeholder sizeWithFont:_placeholderFont]; +#pragma clang diagnostic pop CGPoint placeholderOrigin = CGPointMake(0.0f, CGFloor((rect.size.height - placeholderSize.height) / 2.0f) - TGRetinaPixel); if (self.textAlignment == NSTextAlignmentCenter) @@ -22,8 +25,11 @@ placeholderOrigin.x = rect.size.width - placeholderSize.width; placeholderOrigin.y += TGScreenPixel + _placeholderOffset; - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [self.placeholder drawAtPoint:placeholderOrigin withFont:_placeholderFont]; +#pragma clang diagnostic pop } } diff --git a/submodules/LegacyComponents/Sources/TGTooltipView.m b/submodules/LegacyComponents/Sources/TGTooltipView.m index d052801764..9c9e3c23c6 100644 --- a/submodules/LegacyComponents/Sources/TGTooltipView.m +++ b/submodules/LegacyComponents/Sources/TGTooltipView.m @@ -91,7 +91,10 @@ if (_numberOfLines == 1) { [_textLabel sizeToFit]; } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGSize textSize = [_textLabel.text sizeWithFont:_textLabel.font constrainedToSize:CGSizeMake(_maxWidth - 20.0f, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping]; +#pragma clang diagnostic pop textSize.width = CGCeil(textSize.width); textSize.height = CGCeil(textSize.height); _textLabel.frame = CGRectMake(_textLabel.frame.origin.x, _textLabel.frame.origin.y, textSize.width, textSize.height); @@ -110,7 +113,10 @@ if (_numberOfLines == 1) { [_textLabel sizeToFit]; } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGSize textSize = [_textLabel.text sizeWithFont:_textLabel.font constrainedToSize:CGSizeMake(maxWidth - 20.0f, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping]; +#pragma clang diagnostic pop textSize.width = CGCeil(textSize.width); textSize.height = CGCeil(textSize.height); _textLabel.frame = CGRectMake(_textLabel.frame.origin.x, _textLabel.frame.origin.y, textSize.width, textSize.height); diff --git a/submodules/LegacyComponents/Sources/TGVideoCameraPipeline.h b/submodules/LegacyComponents/Sources/TGVideoCameraPipeline.h index 796a49c812..63e37576a6 100644 --- a/submodules/LegacyComponents/Sources/TGVideoCameraPipeline.h +++ b/submodules/LegacyComponents/Sources/TGVideoCameraPipeline.h @@ -17,6 +17,7 @@ @property (nonatomic, readonly) bool isZoomAvailable; @property (nonatomic, assign) CGFloat zoomLevel; +- (void)cancelZoom; - (instancetype)initWithDelegate:(id)delegate position:(AVCaptureDevicePosition)position callbackQueue:(dispatch_queue_t)queue liveUploadInterface:(id)liveUploadInterface; diff --git a/submodules/LegacyComponents/Sources/TGVideoCameraPipeline.m b/submodules/LegacyComponents/Sources/TGVideoCameraPipeline.m index fde3cd666c..1334fe08f1 100644 --- a/submodules/LegacyComponents/Sources/TGVideoCameraPipeline.m +++ b/submodules/LegacyComponents/Sources/TGVideoCameraPipeline.m @@ -257,19 +257,20 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16; { if ([notification.name isEqualToString:AVCaptureSessionWasInterruptedNotification]) { - [self captureSessionDidStopRunning]; + NSInteger reason = [notification.userInfo[AVCaptureSessionInterruptionReasonKey] integerValue]; + if (reason == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableInBackground) { + if (_running) + _startCaptureSessionOnEnteringForeground = true; + } else { + [self captureSessionDidStopRunning]; + } } else if ([notification.name isEqualToString:AVCaptureSessionRuntimeErrorNotification]) { [self captureSessionDidStopRunning]; NSError *error = notification.userInfo[AVCaptureSessionErrorKey]; - if (error.code == AVErrorDeviceIsNotAvailableInBackground) - { - if (_running) - _startCaptureSessionOnEnteringForeground = true; - } - else if (error.code == AVErrorMediaServicesWereReset) + if (error.code == AVErrorMediaServicesWereReset) { [self handleRecoverableCaptureSessionRuntimeError:error]; } @@ -894,6 +895,20 @@ static CGFloat angleOffsetFromPortraitOrientationToOrientation(AVCaptureVideoOri }]; } +- (void)cancelZoom { + __weak TGVideoCameraPipeline *weakSelf = self; + [[TGVideoCameraPipeline cameraQueue] dispatch:^ + { + __strong TGVideoCameraPipeline *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + [self _reconfigureDevice:_videoDevice withBlock:^(AVCaptureDevice *device) { + [device rampToVideoZoomFactor:1.0 withRate:8.0]; + }]; + }]; +} + - (bool)isZoomAvailable { return [TGVideoCameraPipeline _isZoomAvailableForDevice:_videoDevice]; @@ -980,12 +995,8 @@ static CGFloat angleOffsetFromPortraitOrientationToOrientation(AVCaptureVideoOri - (void)_enableVideoStabilization { AVCaptureConnection *videoConnection = [_videoOutput connectionWithMediaType:AVMediaTypeVideo]; - if (videoConnection.supportsVideoStabilization) - { - if ([videoConnection respondsToSelector:@selector(setPreferredVideoStabilizationMode:)]) - videoConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeStandard; - else - videoConnection.enablesVideoStabilizationWhenAvailable = true; + if (videoConnection.supportsVideoStabilization) { + videoConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeStandard; } } diff --git a/submodules/LegacyComponents/Sources/TGVideoMessageCaptureController.m b/submodules/LegacyComponents/Sources/TGVideoMessageCaptureController.m index 56825822cb..34f9280861 100644 --- a/submodules/LegacyComponents/Sources/TGVideoMessageCaptureController.m +++ b/submodules/LegacyComponents/Sources/TGVideoMessageCaptureController.m @@ -60,7 +60,7 @@ typedef enum @end -@interface TGVideoMessageCaptureController () +@interface TGVideoMessageCaptureController () { SQueue *_queue; @@ -96,6 +96,7 @@ typedef enum UIView *_separatorView; UIImageView *_placeholderView; + TGVideoMessageShimmerView *_shimmerView; bool _automaticDismiss; NSTimeInterval _startTimestamp; @@ -300,7 +301,7 @@ typedef enum } CGFloat minSide = MIN(_wrapperView.frame.size.width, _wrapperView.frame.size.height); - CGFloat diameter = MIN(404.0, minSide - 24.0f); + CGFloat diameter = minSide == 320.0 ? 216.0 : MIN(404.0, minSide - 24.0f); CGFloat shadowSize = 21.0f; CGFloat circleWrapperViewLength = diameter + shadowSize * 2.0; @@ -329,8 +330,11 @@ typedef enum _placeholderView.image = [TGVideoMessageCaptureController startImage]; [_circleView addSubview:_placeholderView]; - if (iosMajorVersion() >= 11) - { + _shimmerView = [[TGVideoMessageShimmerView alloc] initWithFrame:_circleView.bounds]; + [_shimmerView updateAbsoluteRect:_circleView.bounds containerSize:_circleView.bounds.size]; + [_circleView addSubview:_shimmerView]; + + if (@available(iOS 11.0, *)) { _shadowView.accessibilityIgnoresInvertColors = true; _placeholderView.accessibilityIgnoresInvertColors = true; } @@ -450,6 +454,10 @@ typedef enum gestureRecognizer.scale = 1.0f; } break; + case UIGestureRecognizerStateEnded: + case UIGestureRecognizerStateCancelled: + [_capturePipeline cancelZoom]; + break; default: break; } @@ -478,8 +486,9 @@ typedef enum _previewView = [[TGVideoCameraGLView alloc] initWithFrame:_circleView.bounds]; [_circleView insertSubview:_previewView belowSubview:_placeholderView]; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { _previewView.accessibilityIgnoresInvertColors = true; + } [self captureStarted]; } @@ -584,6 +593,8 @@ typedef enum .x = _wrapperView.frame.size.width / 2.0f, .y = _wrapperView.frame.size.height / 2.0f - _controlsView.frame.size.height }; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" switch (self.interfaceOrientation) { case UIInterfaceOrientationLandscapeLeft: @@ -597,6 +608,7 @@ typedef enum targetPosition.y = MAX(_circleWrapperView.bounds.size.height / 2.0f + 40.0f, targetPosition.y); break; } +#pragma clang diagnostic pop if (TGIsPad()) { _circleWrapperView.center = targetPosition; @@ -762,8 +774,6 @@ typedef enum [_generator impactOccurred]; } - bool effectiveHasSchedule = true; - TGMediaPickerSendActionSheetController *controller = [[TGMediaPickerSendActionSheetController alloc] initWithContext:_context isDark:self.pallete.isDark sendButtonFrame:[_controlsView convertRect:[_controlsView frameForSendButton] toView:nil] canSendSilently:_canSendSilently canSchedule:_canSchedule reminder:_reminder hasTimer:false]; __weak TGVideoMessageCaptureController *weakSelf = self; controller.send = ^{ @@ -1178,9 +1188,11 @@ typedef enum [UIView animateWithDuration:0.3 delay:delay options:kNilOptions animations:^ { _placeholderView.alpha = 0.0f; + _shimmerView.alpha = 0.0f; _switchButton.alpha = 1.0f; } completion:^(__unused BOOL finished) { + _shimmerView.hidden = true; _placeholderView.hidden = true; _placeholderView.alpha = 1.0f; }]; @@ -1202,7 +1214,10 @@ typedef enum - (void)configureCamera { _capturePipeline = [[TGVideoCameraPipeline alloc] initWithDelegate:self position:_preferredPosition callbackQueue:dispatch_get_main_queue() liveUploadInterface:_liveUploadInterface]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" _capturePipeline.orientation = (AVCaptureVideoOrientation)self.interfaceOrientation; +#pragma clang diagnostic pop __weak TGVideoMessageCaptureController *weakSelf = self; _capturePipeline.micLevel = ^(CGFloat level) @@ -1537,12 +1552,15 @@ static UIImage *startImage = nil; if (cropOrientation != NULL) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)) *cropOrientation = UIImageOrientationUp; else if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft) *cropOrientation = UIImageOrientationRight; else if (self.interfaceOrientation == UIInterfaceOrientationLandscapeRight) *cropOrientation = UIImageOrientationLeft; +#pragma clang diagnostic pop } if (cropMirrored != NULL) diff --git a/submodules/LegacyComponents/Sources/TGVideoMessageControls.m b/submodules/LegacyComponents/Sources/TGVideoMessageControls.m index d7e68bb320..40bd7ad5c8 100644 --- a/submodules/LegacyComponents/Sources/TGVideoMessageControls.m +++ b/submodules/LegacyComponents/Sources/TGVideoMessageControls.m @@ -300,9 +300,11 @@ static CGRect viewFrame(UIView *view) static CGFloat freeOffsetLimit = 35.0f; static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^ - { + dispatch_once(&onceToken, ^{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGFloat labelWidth = [TGLocalized(@"Conversation.SlideToCancel") sizeWithFont:TGSystemFontOfSize(14.0f)].width; +#pragma clang diagnostic pop CGFloat arrowOrigin = CGFloor((TGScreenSize().width - labelWidth) / 2.0f) - 9.0f - 6.0f; CGFloat timerWidth = 90.0f; diff --git a/submodules/LegacyComponents/Sources/TGVideoMessageRingView.m b/submodules/LegacyComponents/Sources/TGVideoMessageRingView.m index e2e84b919a..38bf502dc5 100644 --- a/submodules/LegacyComponents/Sources/TGVideoMessageRingView.m +++ b/submodules/LegacyComponents/Sources/TGVideoMessageRingView.m @@ -2,6 +2,168 @@ #import "TGColor.h" +#import "LegacyComponentsInternal.h" + +@interface TGVideoMessageShimmerEffectForegroundView : UIView +{ + UIView *_imageContainerView; + UIView *_imageView; + + CGFloat _size; + bool _hasContainerSize; + CGRect _absoluteRect; + CGSize _containerSize; +} + +- (instancetype)initWithSize:(CGFloat)size alpha:(CGFloat)alpha; + +@end + +@implementation TGVideoMessageShimmerEffectForegroundView + +- (instancetype)initWithSize:(CGFloat)size alpha:(CGFloat)alpha { + self = [super initWithFrame:CGRectZero]; + if (self != nil) { + _size = size; + + _imageContainerView = [[UIView alloc] init]; + _imageView = [[UIView alloc] init]; + + self.clipsToBounds = true; + + [_imageContainerView addSubview:_imageView]; + [self addSubview:_imageContainerView]; + + UIGraphicsBeginImageContextWithOptions(CGSizeMake(size, 16), false, 0.0f); + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGRect bounds = CGRectMake(0, 0, size, 16); + CGContextClearRect(context, bounds); + CGContextClipToRect(context, bounds); + + UIColor *transparentColor = [UIColor colorWithWhite:1.0 alpha:0.0]; + UIColor *peakColor = [UIColor colorWithWhite:1.0 alpha:alpha]; + + CGColorRef colors[3] = { + CGColorRetain(transparentColor.CGColor), + CGColorRetain(peakColor.CGColor), + CGColorRetain(transparentColor.CGColor) + }; + + CFArrayRef colorsArray = CFArrayCreate(kCFAllocatorDefault, (const void **)&colors, 3, NULL); + CGFloat locations[3] = {0.0f, 0.5, 1.0}; + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, colorsArray, (CGFloat const *)&locations); + + CGContextDrawLinearGradient(context, gradient, CGPointMake(0, 0), CGPointMake(size, 0), kNilOptions); + + CFRelease(colorsArray); + CFRelease(colors[0]); + CFRelease(colors[1]); + + CGColorSpaceRelease(colorSpace); + CFRelease(gradient); + + UIImage *image = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:25 topCapHeight:25]; + UIGraphicsEndImageContext(); + + _imageView.backgroundColor = [UIColor colorWithPatternImage:image]; + } + return self; +} + +- (void)updateAbsoluteRect:(CGRect)absoluteRect containerSize:(CGSize)containerSize { + _hasContainerSize = true; + + CGRect previousAbsoluteRect = _absoluteRect; + CGSize previousContainerSize = _containerSize; + _absoluteRect = absoluteRect; + _containerSize = containerSize; + + if (!CGSizeEqualToSize(previousContainerSize, containerSize)) { + [self setupAnimation]; + } + + if (!CGRectEqualToRect(previousAbsoluteRect, absoluteRect)) { + _imageContainerView.frame = CGRectMake(-absoluteRect.origin.x, -absoluteRect.origin.y, containerSize.width, containerSize.height); + } +} + + +- (void)setupAnimation { + if (!_hasContainerSize) { + return; + } + + CGFloat gradientHeight = _size; + _imageView.frame = CGRectMake(-gradientHeight, 0, gradientHeight, _containerSize.height); + + CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.x"]; + animation.fromValue = @(_imageView.center.x); + animation.toValue = @(_imageView.center.x + _containerSize.width + gradientHeight); + animation.duration = 1.3f; + animation.repeatCount = INFINITY; + animation.beginTime = 1.0; + [_imageView.layer addAnimation:animation forKey:@"position"]; +} + +@end + +@interface TGVideoMessageShimmerView () +{ + TGVideoMessageShimmerEffectForegroundView *_effectView; + UIImageView *_imageView; + + UIView *_borderView; + UIView *_borderMaskView; + TGVideoMessageShimmerEffectForegroundView *_borderEffectView; +} +@end + +@implementation TGVideoMessageShimmerView + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self != nil) { + self.clipsToBounds = true; + self.layer.cornerRadius = frame.size.width / 2.0f; + if (@available(iOS 13.0, *)) { + self.layer.cornerCurve = kCACornerCurveCircular; + } + + _effectView = [[TGVideoMessageShimmerEffectForegroundView alloc] initWithSize:320 alpha:0.3]; + _effectView.layer.compositingFilter = @"screenBlendMode"; + _effectView.frame = self.bounds; + + _borderView = [[UIView alloc] initWithFrame:self.bounds]; + _borderMaskView = [[UIView alloc] initWithFrame:self.bounds]; + _borderMaskView.layer.borderWidth = 1.0; + _borderMaskView.layer.borderColor = [UIColor whiteColor].CGColor; + _borderMaskView.layer.cornerRadius = frame.size.width / 2.0f; + if (@available(iOS 13.0, *)) { + _borderMaskView.layer.cornerCurve = kCACornerCurveCircular; + } + _borderView.maskView = _borderMaskView; + + _borderEffectView = [[TGVideoMessageShimmerEffectForegroundView alloc] initWithSize:400 alpha:0.45]; + _borderEffectView.layer.compositingFilter = @"screenBlendMode"; + _borderEffectView.frame = self.bounds; + + [self addSubview:_effectView]; + [self addSubview:_borderView]; + [_borderView addSubview:_borderEffectView]; + } + return self; +} + +- (void)updateAbsoluteRect:(CGRect)absoluteRect containerSize:(CGSize)containerSize { + [_effectView updateAbsoluteRect:absoluteRect containerSize:containerSize]; + [_borderEffectView updateAbsoluteRect:absoluteRect containerSize:containerSize]; +} + +@end + @interface TGVideoMessageRingView () { CGFloat _value; diff --git a/submodules/LegacyComponents/Sources/TGViewController.mm b/submodules/LegacyComponents/Sources/TGViewController.mm index 0a35d3fb26..41ded68f86 100644 --- a/submodules/LegacyComponents/Sources/TGViewController.mm +++ b/submodules/LegacyComponents/Sources/TGViewController.mm @@ -375,12 +375,20 @@ static id _defaultContext = nil; return self; } +- (id)initWithCoder:(NSCoder *)coder { + return [self init]; +} + - (void)_commonViewControllerInit:(id)context { assert(context != nil); _context = context; - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" self.wantsFullScreenLayout = true; +#pragma clang diagnostic pop + self.automaticallyManageScrollViewInsets = true; self.autoManageStatusBarBackground = true; __block bool initializedSizeClass = false; @@ -467,7 +475,10 @@ static id _defaultContext = nil; - (bool)shouldIgnoreStatusBar { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" return [self shouldIgnoreStatusBarInOrientation:self.interfaceOrientation]; +#pragma clang diagnostic pop } - (bool)shouldIgnoreNavigationBar @@ -625,15 +636,21 @@ static id _defaultContext = nil; if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad && iosMajorVersion() < 7) { CGSize size = CGSizeMake(320, 491); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" self.contentSizeForViewInPopover = size; +#pragma clang diagnostic pop } if ([self.navigationController isKindOfClass:[TGNavigationController class]]) [(TGNavigationController *)self.navigationController setupNavigationBarForController:self animated:animated]; - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [self _updateControllerInsetForOrientation:self.interfaceOrientation force:false notify:true]; [self adjustToInterfaceOrientation:self.interfaceOrientation]; +#pragma clang diagnostic pop [super viewWillAppear:animated]; @@ -808,12 +825,18 @@ static id _defaultContext = nil; CGFloat minStatusBarHeight = [self prefersStatusBarHidden] ? 0.0f : 20.0f; CGFloat statusBarHeight = MAX(minStatusBarHeight, MIN(statusBarFrame.size.width, statusBarFrame.size.height)); statusBarHeight = MIN(40.0f, statusBarHeight + _additionalStatusBarHeight); - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGFloat keyboardHeight = [self _currentKeyboardHeight:self.interfaceOrientation]; +#pragma clang diagnostic pop [UIView animateWithDuration:0.35 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^ { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [self _updateControllerInsetForOrientation:self.interfaceOrientation statusBarHeight:statusBarHeight keyboardHeight:keyboardHeight force:false notify:true]; +#pragma clang diagnostic pop } completion:nil]; } } @@ -854,14 +877,20 @@ static id _defaultContext = nil; { [UIView performWithoutAnimation:^ { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [self _updateControllerInsetForOrientation:self.interfaceOrientation statusBarHeight:statusBarHeight keyboardHeight:keyboardHeight force:false notify:true]; +#pragma clang diagnostic pop }]; } else { [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^ { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [self _updateControllerInsetForOrientation:self.interfaceOrientation statusBarHeight:statusBarHeight keyboardHeight:keyboardHeight force:false notify:true]; +#pragma clang diagnostic pop } completion:nil]; } } @@ -884,14 +913,20 @@ static id _defaultContext = nil; { [UIView performWithoutAnimation:^ { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [self _updateControllerInsetForOrientation:self.interfaceOrientation statusBarHeight:statusBarHeight keyboardHeight:keyboardHeight force:false notify:true]; +#pragma clang diagnostic pop }]; } else { [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^ { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" [self _updateControllerInsetForOrientation:self.interfaceOrientation statusBarHeight:statusBarHeight keyboardHeight:keyboardHeight force:false notify:true]; +#pragma clang diagnostic pop } completion:nil]; } } @@ -982,9 +1017,12 @@ static id _defaultContext = nil; _additionalNavigationBarHeight = additionalNavigationBarHeight; CGFloat statusBarHeight = [self _currentStatusBarHeight]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGFloat keyboardHeight = [self _currentKeyboardHeight:self.interfaceOrientation]; [self _updateControllerInsetForOrientation:self.interfaceOrientation statusBarHeight:statusBarHeight keyboardHeight:keyboardHeight force:false notify:true]; +#pragma clang diagnostic pop } - (void)setAdditionalStatusBarHeight:(CGFloat)additionalStatusBarHeight @@ -992,9 +1030,12 @@ static id _defaultContext = nil; _additionalStatusBarHeight = additionalStatusBarHeight; CGFloat statusBarHeight = [self _currentStatusBarHeight]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGFloat keyboardHeight = [self _currentKeyboardHeight:self.interfaceOrientation]; [self _updateControllerInsetForOrientation:self.interfaceOrientation statusBarHeight:statusBarHeight keyboardHeight:keyboardHeight force:false notify:true]; +#pragma clang diagnostic pop } - (void)setExplicitTableInset:(UIEdgeInsets)explicitTableInset scrollIndicatorInset:(UIEdgeInsets)scrollIndicatorInset @@ -1003,14 +1044,20 @@ static id _defaultContext = nil; _explicitScrollIndicatorInset = scrollIndicatorInset; CGFloat statusBarHeight = [self _currentStatusBarHeight]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGFloat keyboardHeight = [self _currentKeyboardHeight:self.interfaceOrientation]; [self _updateControllerInsetForOrientation:self.interfaceOrientation statusBarHeight:statusBarHeight keyboardHeight:keyboardHeight force:false notify:true]; +#pragma clang diagnostic pop } - (bool)_updateControllerInset:(bool)force { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" return [self _updateControllerInsetForOrientation:self.interfaceOrientation force:force notify:true]; +#pragma clang diagnostic pop } - (bool)_updateControllerInsetForOrientation:(UIInterfaceOrientation)orientation force:(bool)force notify:(bool)notify @@ -1060,8 +1107,9 @@ static id _defaultContext = nil; orientation = UIInterfaceOrientationLandscapeLeft; bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON; + } return [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:hasOnScreenNavigation]; } @@ -1093,8 +1141,9 @@ static id _defaultContext = nil; - (bool)_updateControllerInsetForOrientation:(UIInterfaceOrientation)orientation statusBarHeight:(CGFloat)statusBarHeight keyboardHeight:(CGFloat)keyboardHeight force:(bool)force notify:(bool)notify { bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) + if (@available(iOS 11.0, *)) { hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON; + } UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:hasOnScreenNavigation]; CGFloat navigationBarHeight = ([self navigationBarShouldBeHidden] || [self shouldIgnoreNavigationBar]) ? 0 : [self navigationBarHeightForInterfaceOrientation:orientation]; @@ -1260,12 +1309,15 @@ static id _defaultContext = nil; { if (navigationBarHidden != self.navigationController.navigationBarHidden) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGFloat barHeight = [self navigationBarHeightForInterfaceOrientation:self.interfaceOrientation]; CGFloat statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height; if ([self shouldIgnoreStatusBarInOrientation:self.interfaceOrientation]) statusBarHeight = 0.0f; CGSize screenSize = [TGViewController screenSizeForInterfaceOrientation:self.interfaceOrientation]; +#pragma clang diagnostic pop if (!navigationBarHidden) { diff --git a/submodules/LegacyComponents/Sources/TGWebDocument.m b/submodules/LegacyComponents/Sources/TGWebDocument.m index eb312f0e1a..c5e13250c5 100644 --- a/submodules/LegacyComponents/Sources/TGWebDocument.m +++ b/submodules/LegacyComponents/Sources/TGWebDocument.m @@ -31,7 +31,7 @@ - (instancetype)initWithString:(NSString *)string { if ([string hasPrefix:@"webdoc"]) { - NSData *data = iosMajorVersion() >= 7 ? [[NSData alloc] initWithBase64EncodedString:[string substringFromIndex:6] options:NSDataBase64DecodingIgnoreUnknownCharacters] : [[NSData alloc] initWithBase64Encoding:[string substringFromIndex:6]]; + NSData *data = [[NSData alloc] initWithBase64EncodedString:[string substringFromIndex:6] options:NSDataBase64DecodingIgnoreUnknownCharacters]; if (data != nil) { PSKeyValueDecoder *decoder = [[PSKeyValueDecoder alloc] initWithData:data]; return [[TGWebDocumentReference alloc] initWithKeyValueCoder:decoder]; @@ -46,7 +46,7 @@ - (NSString *)toString { PSKeyValueEncoder *encoder = [[PSKeyValueEncoder alloc] init]; [self encodeWithKeyValueCoder:encoder]; - return [@"webdoc" stringByAppendingString:[[encoder data] base64Encoding]]; + return [@"webdoc" stringByAppendingString:[[encoder data] base64EncodedStringWithOptions:0]]; } @end diff --git a/submodules/LegacyComponents/Sources/YUGPUImageHighPassSkinSmoothingFilter.m b/submodules/LegacyComponents/Sources/YUGPUImageHighPassSkinSmoothingFilter.m index 683c2c9ade..734535e414 100644 --- a/submodules/LegacyComponents/Sources/YUGPUImageHighPassSkinSmoothingFilter.m +++ b/submodules/LegacyComponents/Sources/YUGPUImageHighPassSkinSmoothingFilter.m @@ -127,7 +127,9 @@ SHADER_STRING return self; } + - (void)setHighPassRadiusInPixels:(CGFloat)highPassRadiusInPixels { + _highPassRadiusInPixels = highPassRadiusInPixels; self.highPassFilter.radiusInPixels = highPassRadiusInPixels; } diff --git a/submodules/LegacyComponents/Sources/ocr.mm b/submodules/LegacyComponents/Sources/ocr.mm index 4e6e8940e1..b81744efe8 100755 --- a/submodules/LegacyComponents/Sources/ocr.mm +++ b/submodules/LegacyComponents/Sources/ocr.mm @@ -669,7 +669,7 @@ UIImage *normalizeImage(UIImage *image) if (image.imageOrientation == UIImageOrientationUp) return image; UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale); - [image drawInRect:(CGRect){0, 0, image.size}]; + [image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)]; UIImage *normalizedImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return normalizedImage; diff --git a/submodules/LegacyMediaPickerUI/BUILD b/submodules/LegacyMediaPickerUI/BUILD index db58fe1bdc..ddc196895b 100644 --- a/submodules/LegacyMediaPickerUI/BUILD +++ b/submodules/LegacyMediaPickerUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift index 66f5c574bd..31b075faef 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift @@ -102,11 +102,15 @@ public func legacyMediaEditor(context: AccountContext, peer: Peer, media: AnyMed present(legacyController, nil) - TGPhotoVideoEditor.present(with: legacyController.context, controller: emptyController, caption: initialCaption, entities: [], withItem: item, paint: true, recipientName: recipientName, stickersContext: paintStickersContext, snapshots: snapshots as? [Any], immediate: transitionCompletion != nil, appeared: { + TGPhotoVideoEditor.present(with: legacyController.context, controller: emptyController, caption: initialCaption, entities: [], withItem: item, paint: true, recipientName: recipientName, stickersContext: paintStickersContext, snapshots: snapshots as [Any], immediate: transitionCompletion != nil, appeared: { transitionCompletion?() }, completion: { result, editingContext in let nativeGenerator = legacyAssetPickerItemGenerator() - let signals = TGCameraController.resultSignals(for: nil, editingContext: editingContext, currentItem: result as! TGMediaSelectableItem, storeAssets: false, saveEditedPhotos: false, descriptionGenerator: { _1, _2, _3, _4 in + var selectableResult: TGMediaSelectableItem? + if let result = result { + selectableResult = unsafeDowncast(result, to: TGMediaSelectableItem.self) + } + let signals = TGCameraController.resultSignals(for: nil, editingContext: editingContext, currentItem: selectableResult, storeAssets: false, saveEditedPhotos: false, descriptionGenerator: { _1, _2, _3, _4 in nativeGenerator(_1, _2, _3, _4, nil) }) sendMessagesWithSignals(signals, false, 0) @@ -319,7 +323,11 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, chatLocati TGPhotoVideoEditor.present(with: legacyController.context, controller: emptyController, caption: "", entities: [], withItem: item, paint: false, recipientName: recipientName, stickersContext: paintStickersContext, snapshots: [], immediate: false, appeared: { }, completion: { result, editingContext in let nativeGenerator = legacyAssetPickerItemGenerator() - let signals = TGCameraController.resultSignals(for: nil, editingContext: editingContext, currentItem: result as! TGMediaSelectableItem, storeAssets: false, saveEditedPhotos: false, descriptionGenerator: { _1, _2, _3, _4 in + var selectableResult: TGMediaSelectableItem? + if let result = result { + selectableResult = unsafeDowncast(result, to: TGMediaSelectableItem.self) + } + let signals = TGCameraController.resultSignals(for: nil, editingContext: editingContext, currentItem: selectableResult, storeAssets: false, saveEditedPhotos: false, descriptionGenerator: { _1, _2, _3, _4 in nativeGenerator(_1, _2, _3, _4, nil) }) sendMessagesWithSignals(signals, false, 0, { _ in nil}, {}) diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift index a5ba83eb74..ac2e9e55b6 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift @@ -394,7 +394,7 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) - attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil)) } - var text = caption ?? "" + let text = caption ?? "" messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text, attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId, correlationId: nil), uniqueId: item.uniqueId)) } } @@ -504,7 +504,7 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) - finalDuration = adjustments.trimEndValue - adjustments.trimStartValue } - let adjustmentsData = MemoryBuffer(data: NSKeyedArchiver.archivedData(withRootObject: adjustments.dictionary())) + let adjustmentsData = MemoryBuffer(data: NSKeyedArchiver.archivedData(withRootObject: adjustments.dictionary()!)) let digest = MemoryBuffer(data: adjustmentsData.md5Digest()) resourceAdjustments = VideoMediaResourceAdjustments(data: adjustmentsData, digest: digest) } diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift index e5cf7c86b8..aa9c8c692a 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift @@ -26,11 +26,17 @@ private func render(width: Int, height: Int, bytesPerRow: Int, data: Data, type: let image = generateImagePixel(CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, pixelGenerator: { _, pixelData, bytesPerRow in switch type { case .yuva: - data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in + data.withUnsafeBytes { buffer -> Void in + guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } decodeYUVAToRGBA(bytes, pixelData, Int32(width), Int32(height), Int32(bytesPerRow)) } case .argb: - data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in + data.withUnsafeBytes { buffer -> Void in + guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } memcpy(pixelData, bytes, data.count) } } diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacySuggestionContext.swift b/submodules/LegacyMediaPickerUI/Sources/LegacySuggestionContext.swift index e96473f58f..d2803cf30e 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacySuggestionContext.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacySuggestionContext.swift @@ -29,16 +29,16 @@ public func legacySuggestionContext(context: AccountContext, peerId: PeerId, cha } } - subscriber?.putNext(users) - subscriber?.putCompletion() + subscriber.putNext(users) + subscriber.putCompletion() }) return SBlockDisposable { disposable.dispose() } } else { - subscriber?.putNext(NSArray()) - subscriber?.putCompletion() + subscriber.putNext(NSArray()) + subscriber.putCompletion() return nil } } @@ -59,8 +59,8 @@ public func legacySuggestionContext(context: AccountContext, peerId: PeerId, cha } |> take(1) |> deliverOnMainQueue).start(next: { hashtags in - subscriber?.putNext(hashtags) - subscriber?.putCompletion() + subscriber.putNext(hashtags) + subscriber.putCompletion() }) return SBlockDisposable { @@ -83,10 +83,10 @@ public func legacySuggestionContext(context: AccountContext, peerId: PeerId, cha } return result }).start(next: { result in - subscriber?.putNext(result) - subscriber?.putCompletion() + subscriber.putNext(result) + subscriber.putCompletion() }, error: nil, completed: { - subscriber?.putCompletion() + subscriber.putCompletion() }) return SBlockDisposable { diff --git a/submodules/LegacyUI/BUILD b/submodules/LegacyUI/BUILD index 6ea7f61ce4..d398f76aed 100644 --- a/submodules/LegacyUI/BUILD +++ b/submodules/LegacyUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/Postbox:Postbox", diff --git a/submodules/LegacyUI/Sources/LegacyController.swift b/submodules/LegacyUI/Sources/LegacyController.swift index 5fdeaa4f3a..d0f0bd4dae 100644 --- a/submodules/LegacyUI/Sources/LegacyController.swift +++ b/submodules/LegacyUI/Sources/LegacyController.swift @@ -332,7 +332,7 @@ open class LegacyController: ViewController, PresentableController { private let sizeClass: SVariable = SVariable() public var enableSizeClassSignal: Bool = false public var sizeClassSignal: SSignal { - return self.sizeClass.signal()! + return self.sizeClass.signal() } private var enableContainerLayoutUpdates = false diff --git a/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift b/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift index 37a4682a61..91974df539 100644 --- a/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift +++ b/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift @@ -187,7 +187,7 @@ private final class LegacyComponentsGlobalsProviderImpl: NSObject, LegacyCompone convertedType = .play } let disposable = legacyContext.sharedContext.mediaManager.audioSession.push(audioSessionType: convertedType, once: true, activate: { _ in - }, deactivate: { + }, deactivate: { _ in interrupted?() return .complete() }) diff --git a/submodules/LightweightAccountData/BUILD b/submodules/LightweightAccountData/BUILD index 2531e648ad..eeda5cc3e0 100644 --- a/submodules/LightweightAccountData/BUILD +++ b/submodules/LightweightAccountData/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], visibility = [ "//visibility:public", ], diff --git a/submodules/ListMessageItem/BUILD b/submodules/ListMessageItem/BUILD index 79b42b21cd..a7dd3fd0d5 100644 --- a/submodules/ListMessageItem/BUILD +++ b/submodules/ListMessageItem/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/ListSectionHeaderNode/BUILD b/submodules/ListSectionHeaderNode/BUILD index ebfba2dc44..38262bee99 100644 --- a/submodules/ListSectionHeaderNode/BUILD +++ b/submodules/ListSectionHeaderNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/LiveLocationManager/BUILD b/submodules/LiveLocationManager/BUILD index 48ab44440e..9af918155c 100644 --- a/submodules/LiveLocationManager/BUILD +++ b/submodules/LiveLocationManager/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/TelegramCore:TelegramCore", diff --git a/submodules/LiveLocationPositionNode/BUILD b/submodules/LiveLocationPositionNode/BUILD index aeec919f5c..a962112384 100644 --- a/submodules/LiveLocationPositionNode/BUILD +++ b/submodules/LiveLocationPositionNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/LiveLocationPositionNode/Sources/ChatMessageLiveLocationPositionNode.swift b/submodules/LiveLocationPositionNode/Sources/ChatMessageLiveLocationPositionNode.swift index 27c50023a5..45d7a5907b 100644 --- a/submodules/LiveLocationPositionNode/Sources/ChatMessageLiveLocationPositionNode.swift +++ b/submodules/LiveLocationPositionNode/Sources/ChatMessageLiveLocationPositionNode.swift @@ -3,7 +3,6 @@ import UIKit import Display import AsyncDisplayKit import TelegramCore -import Postbox import TelegramPresentationData import AvatarNode import LocationResources @@ -77,7 +76,7 @@ private func generateHeadingArrowImage() -> UIImage? { public final class ChatMessageLiveLocationPositionNode: ASDisplayNode { public enum Mode { - case liveLocation(peer: Peer, active: Bool, latitude: Double, longitude: Double, heading: Int32?) + case liveLocation(peer: EnginePeer, active: Bool, latitude: Double, longitude: Double, heading: Int32?) case location(TelegramMediaMap?) } diff --git a/submodules/LiveLocationTimerNode/BUILD b/submodules/LiveLocationTimerNode/BUILD index 068104c566..46781af862 100644 --- a/submodules/LiveLocationTimerNode/BUILD +++ b/submodules/LiveLocationTimerNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/LocalAuth/BUILD b/submodules/LocalAuth/BUILD index 59d5f7737f..73e432ce8e 100644 --- a/submodules/LocalAuth/BUILD +++ b/submodules/LocalAuth/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", ], diff --git a/submodules/LocalMediaResources/BUILD b/submodules/LocalMediaResources/BUILD index 25bf57a82a..297f8b73d8 100644 --- a/submodules/LocalMediaResources/BUILD +++ b/submodules/LocalMediaResources/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/Postbox:Postbox", diff --git a/submodules/LocalizedPeerData/BUILD b/submodules/LocalizedPeerData/BUILD index 8b68782d66..4ab6122d50 100644 --- a/submodules/LocalizedPeerData/BUILD +++ b/submodules/LocalizedPeerData/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramCore:TelegramCore", "//submodules/Postbox:Postbox", diff --git a/submodules/LocationResources/BUILD b/submodules/LocationResources/BUILD index 90aa14c29d..beedfc749f 100644 --- a/submodules/LocationResources/BUILD +++ b/submodules/LocationResources/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramCore:TelegramCore", "//submodules/Postbox:Postbox", diff --git a/submodules/LocationResources/Sources/MapResources.swift b/submodules/LocationResources/Sources/MapResources.swift index beaaedc854..1e699daacc 100644 --- a/submodules/LocationResources/Sources/MapResources.swift +++ b/submodules/LocationResources/Sources/MapResources.swift @@ -160,7 +160,6 @@ public func chatMapSnapshotImage(account: Account, resource: MapSnapshotMediaRes let context = DrawingContext(size: arguments.drawingSize, clear: true) var fullSizeImage: CGImage? - var imageOrientation: UIImage.Orientation = .up if let fullSizeData = fullSizeData { let options = NSMutableDictionary() options[kCGImageSourceShouldCache as NSString] = false as NSNumber diff --git a/submodules/LocationUI/BUILD b/submodules/LocationUI/BUILD index b004206519..ace157cd67 100644 --- a/submodules/LocationUI/BUILD +++ b/submodules/LocationUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/LocationUI/Sources/LocationAnnotation.swift b/submodules/LocationUI/Sources/LocationAnnotation.swift index 24d2a7a949..e58361dab1 100644 --- a/submodules/LocationUI/Sources/LocationAnnotation.swift +++ b/submodules/LocationUI/Sources/LocationAnnotation.swift @@ -554,7 +554,7 @@ class LocationPinAnnotationView: MKAnnotationView { if self.previousPeerId != peer.id { self.previousPeerId = peer.id - avatarNode.setPeer(context: context, theme: theme, peer: peer) + avatarNode.setPeer(context: context, theme: theme, peer: EnginePeer(peer)) } } diff --git a/submodules/LocationUI/Sources/LocationDistancePickerScreen.swift b/submodules/LocationUI/Sources/LocationDistancePickerScreen.swift index 05024a4b27..a10f77bf3a 100644 --- a/submodules/LocationUI/Sources/LocationDistancePickerScreen.swift +++ b/submodules/LocationUI/Sources/LocationDistancePickerScreen.swift @@ -90,7 +90,7 @@ final class LocationDistancePickerScreen: ViewController { self?.dismiss() } - self.controllerNode.update() + let _ = self.controllerNode.update() } override public func loadView() { @@ -463,7 +463,7 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD pickerView.selectRow(1, inComponent: 1, animated: true) } self.updateDoneButtonTitle() - self.update() + let _ = self.update() } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { @@ -601,7 +601,7 @@ class LocationDistancePickerScreenNode: ViewControllerTracingNode, UIScrollViewD } func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { - var hadValidLayout = self.containerLayout != nil + let hadValidLayout = self.containerLayout != nil self.containerLayout = (layout, navigationBarHeight) var insets = layout.insets(options: [.statusBar, .input]) diff --git a/submodules/LocationUI/Sources/LocationInfoListItem.swift b/submodules/LocationUI/Sources/LocationInfoListItem.swift index e8dc93c045..6dc9cee3f0 100644 --- a/submodules/LocationUI/Sources/LocationInfoListItem.swift +++ b/submodules/LocationUI/Sources/LocationInfoListItem.swift @@ -230,8 +230,6 @@ final class LocationInfoListItemNode: ListViewItemNode { let subtitleFrame = CGRect(origin: CGPoint(x: leftInset, y: verticalInset + titleLayout.size.height + titleSpacing), size: subtitleLayout.size) subtitleNode.frame = subtitleFrame - - let separatorHeight = UIScreenPixel let iconNodeFrame = CGRect(origin: CGPoint(x: params.leftInset + inset, y: 10.0), size: CGSize(width: iconSize, height: iconSize)) strongSelf.venueIconNode.frame = iconNodeFrame diff --git a/submodules/LocationUI/Sources/LocationLiveListItem.swift b/submodules/LocationUI/Sources/LocationLiveListItem.swift index f803c4cb66..e6ef55b5d8 100644 --- a/submodules/LocationUI/Sources/LocationLiveListItem.swift +++ b/submodules/LocationUI/Sources/LocationLiveListItem.swift @@ -228,7 +228,7 @@ final class LocationLiveListItemNode: ListViewItemNode { let avatarSize: CGFloat = 40.0 if let peer = item.message.author { - strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: nil, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: false) + strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: EnginePeer(peer), overrideImage: nil, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: false) } strongSelf.avatarNode.frame = CGRect(origin: CGPoint(x: params.leftInset + 15.0, y: floorToScreenPixels((contentSize.height - avatarSize) / 2.0)), size: CGSize(width: avatarSize, height: avatarSize)) diff --git a/submodules/LocationUI/Sources/LocationMapNode.swift b/submodules/LocationUI/Sources/LocationMapNode.swift index 7b77bd64bf..5bdf3a7ef7 100644 --- a/submodules/LocationUI/Sources/LocationMapNode.swift +++ b/submodules/LocationUI/Sources/LocationMapNode.swift @@ -178,7 +178,7 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate { var proximityDimView = UIImageView() var proximityIndicatorRadius: Double? { didSet { - if let radius = self.proximityIndicatorRadius, let mapView = self.mapView { + if let _ = self.proximityIndicatorRadius, let mapView = self.mapView { if self.proximityDimView.image == nil { proximityDimView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } diff --git a/submodules/LocationUI/Sources/LocationPickerController.swift b/submodules/LocationUI/Sources/LocationPickerController.swift index f72261d9ab..9219dbf559 100644 --- a/submodules/LocationUI/Sources/LocationPickerController.swift +++ b/submodules/LocationUI/Sources/LocationPickerController.swift @@ -119,7 +119,7 @@ public final class LocationPickerController: ViewController { strongSelf.present(c, in: .window(.root), with: a) }, openSettings: { strongSelf.context.sharedContext.applicationBindings.openSettings() - }) { [weak self] authorized in + }, { [weak self] authorized in guard let strongSelf = self, authorized else { return } @@ -160,7 +160,7 @@ public final class LocationPickerController: ViewController { ]) ]) strongSelf.present(controller, in: .window(.root)) - } + }) }, sendVenue: { [weak self] venue in guard let strongSelf = self else { return diff --git a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift index 6d0d0b47a4..2787eb5fdf 100644 --- a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift +++ b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift @@ -514,7 +514,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM entries.append(.attribution(presentationData.theme, attribution)) } } else { - for i in 0 ..< 8 { + for _ in 0 ..< 8 { entries.append(.venue(presentationData.theme, nil, index)) index += 1 } diff --git a/submodules/LocationUI/Sources/LocationViewController.swift b/submodules/LocationUI/Sources/LocationViewController.swift index 0b45ccfc07..8f212aeed8 100644 --- a/submodules/LocationUI/Sources/LocationViewController.swift +++ b/submodules/LocationUI/Sources/LocationViewController.swift @@ -226,7 +226,7 @@ public final class LocationViewController: ViewController { strongSelf.present(c, in: .window(.root), with: a) }, openSettings: { context.sharedContext.applicationBindings.openSettings() - }) { [weak self] authorized in + }, { [weak self] authorized in guard let strongSelf = self, authorized else { return } @@ -311,7 +311,7 @@ public final class LocationViewController: ViewController { }) strongSelf.present(controller, in: .window(.root)) }) - } + }) } }, updateSendActionHighlight: { [weak self] highlighted in guard let strongSelf = self else { @@ -326,7 +326,7 @@ public final class LocationViewController: ViewController { strongSelf.present(c, in: .window(.root), with: a) }, openSettings: { context.sharedContext.applicationBindings.openSettings() - }) { [weak self] authorized in + }, { [weak self] authorized in guard let strongSelf = self, authorized else { return } @@ -415,7 +415,7 @@ public final class LocationViewController: ViewController { strongSelf.present(controller, in: .window(.root)) }) } - } + }) }, stopLiveLocation: { [weak self] in params.stopLiveLocation(nil) self?.dismiss() diff --git a/submodules/ManagedAnimationNode/BUILD b/submodules/ManagedAnimationNode/BUILD index a8e059252e..77b05b16f6 100644 --- a/submodules/ManagedAnimationNode/BUILD +++ b/submodules/ManagedAnimationNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Display:Display", "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", diff --git a/submodules/MapResourceToAvatarSizes/BUILD b/submodules/MapResourceToAvatarSizes/BUILD index 0aa34305f4..0db6335885 100644 --- a/submodules/MapResourceToAvatarSizes/BUILD +++ b/submodules/MapResourceToAvatarSizes/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/Postbox:Postbox", diff --git a/submodules/Markdown/BUILD b/submodules/Markdown/BUILD index 0faac980c6..f206072b93 100644 --- a/submodules/Markdown/BUILD +++ b/submodules/Markdown/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Source/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], visibility = [ "//visibility:public", ], diff --git a/submodules/MediaPlayer/BUILD b/submodules/MediaPlayer/BUILD index 8a6d60408d..a13978b14e 100644 --- a/submodules/MediaPlayer/BUILD +++ b/submodules/MediaPlayer/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramCore:TelegramCore", "//submodules/Postbox:Postbox", diff --git a/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift b/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift index 7663542c92..fd7e2af44a 100644 --- a/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift +++ b/submodules/MediaPlayer/Sources/FFMpegMediaFrameSourceContext.swift @@ -336,13 +336,6 @@ final class FFMpegMediaFrameSourceContext: NSObject { self.fetchAutomatically = fetchAutomatically self.maximumFetchSize = maximumFetchSize - var preferSoftwareAudioDecoding = false - if case let .media(media, _) = resourceReference, let file = media.media as? TelegramMediaFile { - if file.isInstantVideo { - preferSoftwareAudioDecoding = true - } - } - if self.tempFilePath == nil { self.keepDataDisposable.set(postbox.mediaBox.keepResource(id: resourceReference.resource.id).start()) } diff --git a/submodules/MediaPlayer/Sources/MediaPlayer.swift b/submodules/MediaPlayer/Sources/MediaPlayer.swift index cea3926c49..c3a4693eb0 100644 --- a/submodules/MediaPlayer/Sources/MediaPlayer.swift +++ b/submodules/MediaPlayer/Sources/MediaPlayer.swift @@ -281,7 +281,6 @@ private final class MediaPlayerContext { CMTimebaseSetRate(loadedState.controlTimebase.timebase, rate: 0.0) } } - let currentTimestamp = CMTimeGetSeconds(CMTimebaseGetTime(loadedState.controlTimebase.timebase)) var duration: Double = 0.0 if let videoTrackFrameBuffer = loadedState.mediaBuffers.videoBuffer { duration = max(duration, CMTimeGetSeconds(videoTrackFrameBuffer.duration)) diff --git a/submodules/MediaPlayer/Sources/MediaPlayerAudioRenderer.swift b/submodules/MediaPlayer/Sources/MediaPlayerAudioRenderer.swift index 185b87a7a1..9e6774bc75 100644 --- a/submodules/MediaPlayer/Sources/MediaPlayerAudioRenderer.swift +++ b/submodules/MediaPlayer/Sources/MediaPlayerAudioRenderer.swift @@ -515,7 +515,7 @@ private final class AudioPlayerRendererContext { switch self.audioSession { case let .manager(manager): - self.audioSessionDisposable.set(manager.push(audioSessionType: self.ambient ? .ambient : (self.playAndRecord ? .playWithPossiblePortOverride : .play), outputMode: self.forceAudioToSpeaker ? .speakerIfNoHeadphones : .system, once: true, manualActivate: { [weak self] control in + self.audioSessionDisposable.set(manager.push(audioSessionType: self.ambient ? .ambient : (self.playAndRecord ? .playWithPossiblePortOverride : .play), outputMode: self.forceAudioToSpeaker ? .speakerIfNoHeadphones : .system, once: self.ambient, manualActivate: { [weak self] control in audioPlayerRendererQueue.async { if let strongSelf = self { strongSelf.audioSessionControl = control @@ -532,13 +532,15 @@ private final class AudioPlayerRendererContext { } } } - }, deactivate: { [weak self] in + }, deactivate: { [weak self] temporary in return Signal { subscriber in audioPlayerRendererQueue.async { if let strongSelf = self { strongSelf.audioSessionControl = nil - strongSelf.audioPaused() - strongSelf.stop() + if !temporary { + strongSelf.audioPaused() + strongSelf.stop() + } subscriber.putCompletion() } } @@ -688,7 +690,10 @@ private final class AudioPlayerRendererContext { strongSelf.bufferContext.with { context in let copyOffset = context.overflowData.count context.overflowData.count += dataLength - takeLength - context.overflowData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + context.overflowData.withUnsafeMutableBytes { buffer -> Void in + guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } CMBlockBufferCopyDataBytes(dataBuffer, atOffset: takeLength, dataLength: dataLength - takeLength, destination: bytes.advanced(by: copyOffset)) } } @@ -725,7 +730,10 @@ private final class AudioPlayerRendererContext { self.bufferContext.with { context in let bytesToCopy = min(context.buffer.size - context.buffer.availableBytes, data.count) - data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in + data.withUnsafeBytes { buffer -> Void in + guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } let _ = context.buffer.enqueue(UnsafeRawPointer(bytes), count: bytesToCopy) context.bufferMaxChannelSampleIndex = sampleIndex + Int64(data.count / (2 * 2)) } diff --git a/submodules/MediaPlayer/Sources/MediaPlayerNode.swift b/submodules/MediaPlayer/Sources/MediaPlayerNode.swift index d56c56aee9..fb7212b121 100644 --- a/submodules/MediaPlayer/Sources/MediaPlayerNode.swift +++ b/submodules/MediaPlayer/Sources/MediaPlayerNode.swift @@ -147,9 +147,8 @@ public final class MediaPlayerNode: ASDisplayNode { } private func poll(completion: @escaping (PollStatus) -> Void) { - if let (takeFrameQueue, takeFrame) = self.takeFrameAndQueue, let videoLayer = self.videoLayer, let (timebase, _, _, _) = self.state { + if let (takeFrameQueue, takeFrame) = self.takeFrameAndQueue, let _ = self.videoLayer, let (timebase, _, _, _) = self.state { let layerTime = CMTimeGetSeconds(CMTimebaseGetTime(timebase)) - let rate = CMTimebaseGetRate(timebase) struct PollState { var numFrames: Int @@ -403,7 +402,7 @@ public final class MediaPlayerNode: ASDisplayNode { assert(Queue.mainQueue().isCurrent()) self.videoLayer?.removeFromSuperlayer() - if let (takeFrameQueue, _) = self.takeFrameAndQueue { + if let _ = self.takeFrameAndQueue { if let videoLayer = self.videoLayer { videoLayer.flushAndRemoveImage() diff --git a/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift b/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift index b97a260cb9..166d6a1b89 100644 --- a/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift +++ b/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift @@ -53,7 +53,7 @@ private final class MediaPlayerScrubbingNodeButton: ASDisplayNode, UIGestureReco } override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { - guard let gestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer else { + guard let _ = gestureRecognizer as? UIPanGestureRecognizer else { return !self.verticalPanEnabled } return true @@ -570,7 +570,7 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode { } handleNodeContainer.updateMultiplier = { [weak self] multiplier in if let strongSelf = self { - if let statusValue = strongSelf.statusValue, let scrubbingBeginTimestamp = strongSelf.scrubbingBeginTimestamp, Double(0.0).isLess(than: statusValue.duration) { + if let statusValue = strongSelf.statusValue, let _ = strongSelf.scrubbingBeginTimestamp, Double(0.0).isLess(than: statusValue.duration) { strongSelf.scrubbingBeginTimestamp = strongSelf.scrubbingTimestampValue } } diff --git a/submodules/MediaPlayer/Sources/RingByteBuffer.swift b/submodules/MediaPlayer/Sources/RingByteBuffer.swift index 7093ce6878..293a91acbf 100644 --- a/submodules/MediaPlayer/Sources/RingByteBuffer.swift +++ b/submodules/MediaPlayer/Sources/RingByteBuffer.swift @@ -17,7 +17,10 @@ public final class RingByteBuffer { } public func enqueue(data: Data) -> Bool { - return data.withUnsafeBytes { (bytes: UnsafePointer) -> Bool in + return data.withUnsafeBytes { buffer -> Bool in + guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return false + } return TPCircularBufferProduceBytes(&self.buffer, UnsafeRawPointer(bytes), Int32(data.count)) } } diff --git a/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift b/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift index 27a0a3f1c6..7317f7fb2e 100644 --- a/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift +++ b/submodules/MediaPlayer/Sources/UniversalSoftwareVideoSource.swift @@ -48,7 +48,10 @@ private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: Unsa fetchDisposable.dispose() if let fetchedData = fetchedData { - fetchedData.withUnsafeBytes { (bytes: UnsafePointer) -> Void in + fetchedData.withUnsafeBytes { byteBuffer -> Void in + guard let bytes = byteBuffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } memcpy(buffer, bytes, fetchedData.count) } let fetchedCount = Int32(fetchedData.count) diff --git a/submodules/MediaResources/BUILD b/submodules/MediaResources/BUILD index 9bfc65e6e5..16e9f6ac37 100644 --- a/submodules/MediaResources/BUILD +++ b/submodules/MediaResources/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramCore:TelegramCore", "//submodules/Postbox:Postbox", diff --git a/submodules/MergeLists/BUILD b/submodules/MergeLists/BUILD index b19a43d51f..642b19176d 100644 --- a/submodules/MergeLists/BUILD +++ b/submodules/MergeLists/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], visibility = [ "//visibility:public", ], diff --git a/submodules/MosaicLayout/BUILD b/submodules/MosaicLayout/BUILD index 27e18bcc11..76cc9fbe05 100644 --- a/submodules/MosaicLayout/BUILD +++ b/submodules/MosaicLayout/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], visibility = [ "//visibility:public", ], diff --git a/submodules/MosaicLayout/Sources/ChatMessageBubbleMosaicLayout.swift b/submodules/MosaicLayout/Sources/ChatMessageBubbleMosaicLayout.swift index 02cb7925b7..a96c94f818 100644 --- a/submodules/MosaicLayout/Sources/ChatMessageBubbleMosaicLayout.swift +++ b/submodules/MosaicLayout/Sources/ChatMessageBubbleMosaicLayout.swift @@ -8,7 +8,6 @@ public struct MosaicItemPosition: OptionSet { self.rawValue = rawValue } - public static let none = MosaicItemPosition(rawValue: 0) public static let top = MosaicItemPosition(rawValue: 1) public static let bottom = MosaicItemPosition(rawValue: 2) public static let left = MosaicItemPosition(rawValue: 4) diff --git a/submodules/MtProtoKit/BUILD b/submodules/MtProtoKit/BUILD index b51410879d..db9e8f2755 100644 --- a/submodules/MtProtoKit/BUILD +++ b/submodules/MtProtoKit/BUILD @@ -7,6 +7,9 @@ objc_library( "Sources/**/*.m", "Sources/**/*.h", ]), + copts = [ + "-Werror", + ], hdrs = glob([ "PublicHeaders/**/*.h", ]), diff --git a/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTApiEnvironment.h b/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTApiEnvironment.h index 2180bd63b2..c35dfbcb9e 100644 --- a/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTApiEnvironment.h +++ b/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTApiEnvironment.h @@ -33,13 +33,13 @@ @interface MTSocksProxySettings : NSObject -@property (nonatomic, strong, readonly) NSString *ip; +@property (nonatomic, strong, readonly) NSString * _Nonnull ip; @property (nonatomic, readonly) uint16_t port; -@property (nonatomic, strong, readonly) NSString *username; -@property (nonatomic, strong, readonly) NSString *password; -@property (nonatomic, strong, readonly) NSData *secret; +@property (nonatomic, strong, readonly) NSString * _Nullable username; +@property (nonatomic, strong, readonly) NSString * _Nullable password; +@property (nonatomic, strong, readonly) NSData * _Nullable secret; -- (instancetype)initWithIp:(NSString *)ip port:(uint16_t)port username:(NSString *)username password:(NSString *)password secret:(NSData *)secret; +- (instancetype _Nonnull)initWithIp:(NSString * _Nonnull )ip port:(uint16_t)port username:(NSString * _Nullable)username password:(NSString * _Nullable)password secret:(NSData * _Nullable)secret; @end @@ -47,38 +47,38 @@ @property (nonatomic, readonly) bool reducedBackupDiscoveryTimeout; -- (instancetype)initWithReducedBackupDiscoveryTimeout:(bool)reducedBackupDiscoveryTimeout; +- (instancetype _Nonnull)initWithReducedBackupDiscoveryTimeout:(bool)reducedBackupDiscoveryTimeout; @end @interface MTApiEnvironment : NSObject @property (nonatomic) int32_t apiId; -@property (nonatomic, strong, readonly) NSString *deviceModel; -@property (nonatomic, strong, readonly) NSString *systemVersion; -@property (nonatomic, strong) NSString *appVersion; -@property (nonatomic, strong, readonly) NSString *systemLangCode; -@property (nonatomic, strong) NSNumber *layer; -@property (nonatomic, strong, readonly) NSData *systemCode; +@property (nonatomic, strong, readonly) NSString * _Nullable deviceModel; +@property (nonatomic, strong, readonly) NSString * _Nullable systemVersion; +@property (nonatomic, strong) NSString * _Nullable appVersion; +@property (nonatomic, strong, readonly) NSString * _Nullable systemLangCode; +@property (nonatomic, strong) NSNumber * _Nullable layer; +@property (nonatomic, strong, readonly) NSData * _Nullable systemCode; -@property (nonatomic, strong) NSString *langPack; -@property (nonatomic, strong, readonly) NSString *langPackCode; +@property (nonatomic, strong) NSString * _Nullable langPack; +@property (nonatomic, strong, readonly) NSString * _Nullable langPackCode; -@property (nonatomic, strong, readonly) NSString *apiInitializationHash; +@property (nonatomic, strong, readonly) NSString * _Nullable apiInitializationHash; @property (nonatomic) bool disableUpdates; -@property (nonatomic) NSData *tcpPayloadPrefix; -@property (nonatomic) NSDictionary *datacenterAddressOverrides; -@property (nonatomic) NSString *accessHostOverride; +@property (nonatomic) NSData * _Nullable tcpPayloadPrefix; +@property (nonatomic) NSDictionary * _Nullable datacenterAddressOverrides; +@property (nonatomic) NSString * _Nullable accessHostOverride; -@property (nonatomic, strong, readonly) MTSocksProxySettings *socksProxySettings; -@property (nonatomic, strong, readonly) MTNetworkSettings *networkSettings; +@property (nonatomic, strong, readonly) MTSocksProxySettings * _Nullable socksProxySettings; +@property (nonatomic, strong, readonly) MTNetworkSettings * _Nullable networkSettings; -@property (nonatomic, copy) void (^passwordInputHandler)(void); +@property (nonatomic, copy) void (^ _Nullable passwordInputHandler)(void); -- (MTApiEnvironment *)withUpdatedLangPackCode:(NSString *)langPackCode; -- (MTApiEnvironment *)withUpdatedSocksProxySettings:(MTSocksProxySettings *)socksProxySettings; -- (MTApiEnvironment *)withUpdatedNetworkSettings:(MTNetworkSettings *)networkSettings; -- (MTApiEnvironment *)withUpdatedSystemCode:(NSData *)systemCode; +- (MTApiEnvironment * _Nonnull)withUpdatedLangPackCode:(NSString * _Nullable)langPackCode; +- (MTApiEnvironment * _Nonnull)withUpdatedSocksProxySettings:(MTSocksProxySettings * _Nullable)socksProxySettings; +- (MTApiEnvironment * _Nonnull)withUpdatedNetworkSettings:(MTNetworkSettings * _Nullable)networkSettings; +- (MTApiEnvironment * _Nonnull)withUpdatedSystemCode:(NSData * _Nullable)systemCode; @end diff --git a/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTContext.h b/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTContext.h index c4898b32db..9f687b8774 100644 --- a/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTContext.h +++ b/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTContext.h @@ -19,34 +19,34 @@ @optional -- (void)contextDatacenterAddressSetUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId addressSet:(MTDatacenterAddressSet *)addressSet; -- (void)contextDatacenterAuthInfoUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)authInfo selector:(MTDatacenterAuthInfoSelector)selector; -- (void)contextDatacenterAuthTokenUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId authToken:(id)authToken; -- (void)contextDatacenterTransportSchemesUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId shouldReset:(bool)shouldReset; -- (void)contextIsPasswordRequiredUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId; -- (void)contextDatacenterPublicKeysUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId publicKeys:(NSArray *)publicKeys; -- (MTSignal *)fetchContextDatacenterPublicKeys:(MTContext *)context datacenterId:(NSInteger)datacenterId; -- (void)contextApiEnvironmentUpdated:(MTContext *)context apiEnvironment:(MTApiEnvironment *)apiEnvironment; -- (MTSignal *)isContextNetworkAccessAllowed:(MTContext *)context; -- (void)contextLoggedOut:(MTContext *)context; +- (void)contextDatacenterAddressSetUpdated:(MTContext * _Nonnull)context datacenterId:(NSInteger)datacenterId addressSet:(MTDatacenterAddressSet * _Nonnull)addressSet; +- (void)contextDatacenterAuthInfoUpdated:(MTContext * _Nonnull)context datacenterId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo * _Nonnull)authInfo selector:(MTDatacenterAuthInfoSelector)selector; +- (void)contextDatacenterAuthTokenUpdated:(MTContext * _Nonnull)context datacenterId:(NSInteger)datacenterId authToken:(id _Nullable)authToken; +- (void)contextDatacenterTransportSchemesUpdated:(MTContext * _Nonnull)context datacenterId:(NSInteger)datacenterId shouldReset:(bool)shouldReset; +- (void)contextIsPasswordRequiredUpdated:(MTContext * _Nonnull)context datacenterId:(NSInteger)datacenterId; +- (void)contextDatacenterPublicKeysUpdated:(MTContext * _Nonnull)context datacenterId:(NSInteger)datacenterId publicKeys:(NSArray * _Nonnull)publicKeys; +- (MTSignal * _Nonnull)fetchContextDatacenterPublicKeys:(MTContext * _Nonnull)context datacenterId:(NSInteger)datacenterId; +- (void)contextApiEnvironmentUpdated:(MTContext * _Nonnull)context apiEnvironment:(MTApiEnvironment * _Nonnull)apiEnvironment; +- (MTSignal * _Nonnull)isContextNetworkAccessAllowed:(MTContext * _Nonnull)context; +- (void)contextLoggedOut:(MTContext * _Nonnull)context; @end @interface MTContextBlockChangeListener : NSObject -@property (nonatomic, copy) void (^contextIsPasswordRequiredUpdated)(MTContext *, NSInteger); -@property (nonatomic, copy) MTSignal *(^fetchContextDatacenterPublicKeys)(MTContext *, NSInteger); -@property (nonatomic, copy) MTSignal *(^isContextNetworkAccessAllowed)(MTContext *); +@property (nonatomic, copy) void (^ _Nullable contextIsPasswordRequiredUpdated)(MTContext * _Nonnull, NSInteger); +@property (nonatomic, copy) MTSignal * _Nonnull (^ _Nullable fetchContextDatacenterPublicKeys)(MTContext * _Nonnull, NSInteger); +@property (nonatomic, copy) MTSignal * _Nonnull(^ _Nullable isContextNetworkAccessAllowed)(MTContext * _Nonnull); @end @interface MTContext : NSObject -@property (nonatomic, strong) id keychain; +@property (nonatomic, strong) id _Nonnull keychain; -@property (nonatomic, strong, readonly) id serialization; -@property (nonatomic, strong) id encryptionProvider; -@property (nonatomic, strong, readonly) MTApiEnvironment *apiEnvironment; +@property (nonatomic, strong, readonly) id _Nonnull serialization; +@property (nonatomic, strong) id _Nonnull encryptionProvider; +@property (nonatomic, strong, readonly) MTApiEnvironment * _Nonnull apiEnvironment; @property (nonatomic, readonly) bool isTestingEnvironment; @property (nonatomic, readonly) bool useTempAuthKeys; @property (nonatomic) int32_t tempKeyExpiration; @@ -54,66 +54,66 @@ + (int32_t)fixedTimeDifference; + (void)setFixedTimeDifference:(int32_t)fixedTimeDifference; -+ (MTQueue *)contextQueue; ++ (MTQueue * _Nonnull)contextQueue; -- (instancetype)initWithSerialization:(id)serialization encryptionProvider:(id)encryptionProvider apiEnvironment:(MTApiEnvironment *)apiEnvironment isTestingEnvironment:(bool)isTestingEnvironment useTempAuthKeys:(bool)useTempAuthKeys; +- (instancetype _Nonnull)initWithSerialization:(id _Nonnull)serialization encryptionProvider:(id _Nonnull)encryptionProvider apiEnvironment:(MTApiEnvironment * _Nonnull)apiEnvironment isTestingEnvironment:(bool)isTestingEnvironment useTempAuthKeys:(bool)useTempAuthKeys; -- (void)performBatchUpdates:(void (^)())block; +- (void)performBatchUpdates:(void (^ _Nonnull)())block; -- (void)addChangeListener:(id)changeListener; -- (void)removeChangeListener:(id)changeListener; +- (void)addChangeListener:(id _Nonnull)changeListener; +- (void)removeChangeListener:(id _Nonnull)changeListener; -- (void)setDiscoverBackupAddressListSignal:(MTSignal *)signal; +- (void)setDiscoverBackupAddressListSignal:(MTSignal * _Nonnull)signal; - (NSTimeInterval)globalTime; - (NSTimeInterval)globalTimeDifference; - (NSTimeInterval)globalTimeOffsetFromUTC; - (void)setGlobalTimeDifference:(NSTimeInterval)globalTimeDifference; -- (void)setSeedAddressSetForDatacenterWithId:(NSInteger)datacenterId seedAddressSet:(MTDatacenterAddressSet *)seedAddressSet; -- (void)updateAddressSetForDatacenterWithId:(NSInteger)datacenterId addressSet:(MTDatacenterAddressSet *)addressSet forceUpdateSchemes:(bool)forceUpdateSchemes; -- (void)addAddressForDatacenterWithId:(NSInteger)datacenterId address:(MTDatacenterAddress *)address; -- (void)updateTransportSchemeForDatacenterWithId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme media:(bool)media isProxy:(bool)isProxy; -- (void)updateAuthInfoForDatacenterWithId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)authInfo selector:(MTDatacenterAuthInfoSelector)selector; +- (void)setSeedAddressSetForDatacenterWithId:(NSInteger)datacenterId seedAddressSet:(MTDatacenterAddressSet * _Nonnull)seedAddressSet; +- (void)updateAddressSetForDatacenterWithId:(NSInteger)datacenterId addressSet:(MTDatacenterAddressSet * _Nonnull)addressSet forceUpdateSchemes:(bool)forceUpdateSchemes; +- (void)addAddressForDatacenterWithId:(NSInteger)datacenterId address:(MTDatacenterAddress * _Nonnull)address; +- (void)updateTransportSchemeForDatacenterWithId:(NSInteger)datacenterId transportScheme:(MTTransportScheme * _Nonnull)transportScheme media:(bool)media isProxy:(bool)isProxy; +- (void)updateAuthInfoForDatacenterWithId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo * _Nullable)authInfo selector:(MTDatacenterAuthInfoSelector)selector; - (bool)isPasswordInputRequiredForDatacenterWithId:(NSInteger)datacenterId; - (bool)updatePasswordInputRequiredForDatacenterWithId:(NSInteger)datacenterId required:(bool)required; -- (void)scheduleSessionCleanupForAuthKeyId:(int64_t)authKeyId sessionInfo:(MTSessionInfo *)sessionInfo; -- (void)collectSessionIdsForCleanupWithAuthKeyId:(int64_t)authKeyId completion:(void (^)(NSArray *sessionIds))completion; -- (void)sessionIdsDeletedForAuthKeyId:(int64_t)authKeyId sessionIds:(NSArray *)sessionIds; +- (void)scheduleSessionCleanupForAuthKeyId:(int64_t)authKeyId sessionInfo:(MTSessionInfo * _Nonnull)sessionInfo; +- (void)collectSessionIdsForCleanupWithAuthKeyId:(int64_t)authKeyId completion:(void (^ _Nonnull)(NSArray * _Nonnull sessionIds))completion; +- (void)sessionIdsDeletedForAuthKeyId:(int64_t)authKeyId sessionIds:(NSArray * _Nonnull)sessionIds; -- (NSArray *)knownDatacenterIds; -- (void)enumerateAddressSetsForDatacenters:(void (^)(NSInteger datacenterId, MTDatacenterAddressSet *addressSet, BOOL *stop))block; +- (NSArray * _Nonnull)knownDatacenterIds; +- (void)enumerateAddressSetsForDatacenters:(void (^ _Nonnull)(NSInteger datacenterId, MTDatacenterAddressSet * _Nonnull addressSet, BOOL * _Nullable stop))block; -- (MTDatacenterAddressSet *)addressSetForDatacenterWithId:(NSInteger)datacenterId; -- (void)reportTransportSchemeFailureForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme; -- (void)reportTransportSchemeSuccessForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme; +- (MTDatacenterAddressSet * _Nonnull)addressSetForDatacenterWithId:(NSInteger)datacenterId; +- (void)reportTransportSchemeFailureForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme * _Nonnull)transportScheme; +- (void)reportTransportSchemeSuccessForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme * _Nonnull)transportScheme; - (void)invalidateTransportSchemesForDatacenterIds:(NSArray * _Nonnull)datacenterIds; - (void)invalidateTransportSchemesForKnownDatacenterIds; - (MTTransportScheme * _Nullable)chooseTransportSchemeForConnectionToDatacenterId:(NSInteger)datacenterId schemes:(NSArray * _Nonnull)schemes; - (NSArray * _Nonnull)transportSchemesForDatacenterWithId:(NSInteger)datacenterId media:(bool)media enforceMedia:(bool)enforceMedia isProxy:(bool)isProxy; - (void)transportSchemeForDatacenterWithIdRequired:(NSInteger)datacenterId media:(bool)media; -- (void)invalidateTransportSchemeForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme isProbablyHttp:(bool)isProbablyHttp media:(bool)media; -- (void)revalidateTransportSchemeForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme media:(bool)media; -- (MTDatacenterAuthInfo *)authInfoForDatacenterWithId:(NSInteger)datacenterId selector:(MTDatacenterAuthInfoSelector)selector; +- (void)invalidateTransportSchemeForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme * _Nonnull)transportScheme isProbablyHttp:(bool)isProbablyHttp media:(bool)media; +- (void)revalidateTransportSchemeForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme * _Nonnull)transportScheme media:(bool)media; +- (MTDatacenterAuthInfo * _Nullable)authInfoForDatacenterWithId:(NSInteger)datacenterId selector:(MTDatacenterAuthInfoSelector)selector; -- (NSArray *)publicKeysForDatacenterWithId:(NSInteger)datacenterId; -- (void)updatePublicKeysForDatacenterWithId:(NSInteger)datacenterId publicKeys:(NSArray *)publicKeys; +- (NSArray * _Nonnull)publicKeysForDatacenterWithId:(NSInteger)datacenterId; +- (void)updatePublicKeysForDatacenterWithId:(NSInteger)datacenterId publicKeys:(NSArray * _Nonnull)publicKeys; - (void)publicKeysForDatacenterWithIdRequired:(NSInteger)datacenterId; - (void)removeAllAuthTokens; - (void)removeTokenForDatacenterWithId:(NSInteger)datacenterId; -- (id)authTokenForDatacenterWithId:(NSInteger)datacenterId; -- (void)updateAuthTokenForDatacenterWithId:(NSInteger)datacenterId authToken:(id)authToken; +- (id _Nullable)authTokenForDatacenterWithId:(NSInteger)datacenterId; +- (void)updateAuthTokenForDatacenterWithId:(NSInteger)datacenterId authToken:(id _Nullable)authToken; - (void)addressSetForDatacenterWithIdRequired:(NSInteger)datacenterId; - (void)authInfoForDatacenterWithIdRequired:(NSInteger)datacenterId isCdn:(bool)isCdn selector:(MTDatacenterAuthInfoSelector)selector; -- (void)authTokenForDatacenterWithIdRequired:(NSInteger)datacenterId authToken:(id)authToken masterDatacenterId:(NSInteger)masterDatacenterId; +- (void)authTokenForDatacenterWithIdRequired:(NSInteger)datacenterId authToken:(id _Nullable)authToken masterDatacenterId:(NSInteger)masterDatacenterId; -- (void)reportProblemsWithDatacenterAddressForId:(NSInteger)datacenterId address:(MTDatacenterAddress *)address; +- (void)reportProblemsWithDatacenterAddressForId:(NSInteger)datacenterId address:(MTDatacenterAddress * _Nonnull)address; -- (void)updateApiEnvironment:(MTApiEnvironment *(^)(MTApiEnvironment *))f; +- (void)updateApiEnvironment:(MTApiEnvironment * _Nullable (^ _Nonnull)(MTApiEnvironment * _Nullable))f; - (void)beginExplicitBackupAddressDiscovery; diff --git a/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTDatacenterAddress.h b/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTDatacenterAddress.h index b984d825dd..85d5b4ac0a 100644 --- a/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTDatacenterAddress.h +++ b/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTDatacenterAddress.h @@ -4,18 +4,18 @@ @interface MTDatacenterAddress : NSObject -@property (nonatomic, strong, readonly) NSString *host; -@property (nonatomic, strong, readonly) NSString *ip; +@property (nonatomic, strong, readonly) NSString * _Nullable host; +@property (nonatomic, strong, readonly) NSString * _Nullable ip; @property (nonatomic, readonly) uint16_t port; @property (nonatomic, readonly) bool preferForMedia; @property (nonatomic, readonly) bool restrictToTcp; @property (nonatomic, readonly) bool cdn; @property (nonatomic, readonly) bool preferForProxy; -@property (nonatomic, readonly) NSData *secret; +@property (nonatomic, readonly) NSData * _Nullable secret; -- (instancetype)initWithIp:(NSString *)ip port:(uint16_t)port preferForMedia:(bool)preferForMedia restrictToTcp:(bool)restrictToTcp cdn:(bool)cdn preferForProxy:(bool)preferForProxy secret:(NSData *)secret; +- (instancetype _Nonnull)initWithIp:(NSString * _Nonnull)ip port:(uint16_t)port preferForMedia:(bool)preferForMedia restrictToTcp:(bool)restrictToTcp cdn:(bool)cdn preferForProxy:(bool)preferForProxy secret:(NSData * _Nullable)secret; -- (BOOL)isEqualToAddress:(MTDatacenterAddress *)other; +- (BOOL)isEqualToAddress:(MTDatacenterAddress * _Nonnull)other; - (BOOL)isIpv6; @end diff --git a/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTEncryption.h b/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTEncryption.h index e197db45d1..2274923ffb 100644 --- a/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTEncryption.h +++ b/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTEncryption.h @@ -9,66 +9,66 @@ extern "C" { #endif -NSData *MTSha1(NSData *data); -NSData *MTSubdataSha1(NSData *data, NSUInteger offset, NSUInteger length); +NSData * _Nonnull MTSha1(NSData * _Nonnull data); +NSData * _Nonnull MTSubdataSha1(NSData * _Nonnull data, NSUInteger offset, NSUInteger length); -NSData *MTSha256(NSData *data); +NSData * _Nonnull MTSha256(NSData * _Nonnull data); -void MTRawSha1(void const *inData, NSUInteger length, void *outData); -void MTRawSha256(void const *inData, NSUInteger length, void *outData); +void MTRawSha1(void const * _Nonnull inData, NSUInteger length, void * _Nonnull outData); +void MTRawSha256(void const * _Nonnull inData, NSUInteger length, void * _Nonnull outData); -int32_t MTMurMurHash32(const void *bytes, int length); +int32_t MTMurMurHash32(const void * _Nonnull bytes, int length); -void MTAesEncryptInplace(NSMutableData *data, NSData *key, NSData *iv); -void MTAesEncryptInplaceAndModifyIv(NSMutableData *data, NSData *key, NSMutableData *iv); -void MTAesEncryptBytesInplaceAndModifyIv(void *data, NSInteger length, NSData *key, void *iv); -void MTAesEncryptRaw(void const *data, void *outData, NSInteger length, void const *key, void const *iv); -void MTAesDecryptRaw(void const *data, void *outData, NSInteger length, void const *key, void const *iv); -void MTAesDecryptInplaceAndModifyIv(NSMutableData *data, NSData *key, NSMutableData *iv); -void MTAesDecryptBytesInplaceAndModifyIv(void *data, NSInteger length, NSData *key, void *iv); -NSData *MTAesEncrypt(NSData *data, NSData *key, NSData *iv); -NSData *MTAesDecrypt(NSData *data, NSData *key, NSData *iv); -NSData *MTRsaEncrypt(id provider, NSString *publicKey, NSData *data); -NSData *MTExp(id provider, NSData *base, NSData *exp, NSData *modulus); -NSData *MTModSub(id provider, NSData *a, NSData *b, NSData *modulus); -NSData *MTModMul(id provider, NSData *a, NSData *b, NSData *modulus); -NSData *MTMul(id provider, NSData *a, NSData *b); -NSData *MTAdd(id provider, NSData *a, NSData *b); -bool MTFactorize(uint64_t what, uint64_t *resA, uint64_t *resB); -bool MTIsZero(id provider, NSData *value); +void MTAesEncryptInplace(NSMutableData * _Nonnull data, NSData * _Nonnull key, NSData * _Nonnull iv); +void MTAesEncryptInplaceAndModifyIv(NSMutableData * _Nonnull data, NSData * _Nonnull key, NSMutableData * _Nonnull iv); +void MTAesEncryptBytesInplaceAndModifyIv(void * _Nonnull data, NSInteger length, NSData * _Nonnull key, void * _Nonnull iv); +void MTAesEncryptRaw(void const * _Nonnull data, void * _Nonnull outData, NSInteger length, void const * _Nonnull key, void const * _Nonnull iv); +void MTAesDecryptRaw(void const * _Nonnull data, void * _Nonnull outData, NSInteger length, void const * _Nonnull key, void const * _Nonnull iv); +void MTAesDecryptInplaceAndModifyIv(NSMutableData * _Nonnull data, NSData * _Nonnull key, NSMutableData * _Nonnull iv); +void MTAesDecryptBytesInplaceAndModifyIv(void * _Nonnull data, NSInteger length, NSData * _Nonnull key, void * _Nonnull iv); +NSData * _Nullable MTAesEncrypt(NSData * _Nonnull data, NSData * _Nonnull key, NSData * _Nonnull iv); +NSData * _Nullable MTAesDecrypt(NSData * _Nonnull data, NSData * _Nonnull key, NSData * _Nonnull iv); +NSData * _Nullable MTRsaEncrypt(id _Nonnull provider, NSString * _Nonnull publicKey, NSData * _Nonnull data); +NSData * _Nullable MTExp(id _Nonnull provider, NSData * _Nonnull base, NSData * _Nonnull exp, NSData * _Nonnull modulus); +NSData * _Nullable MTModSub(id _Nonnull provider, NSData * _Nonnull a, NSData * _Nonnull b, NSData * _Nonnull modulus); +NSData * _Nullable MTModMul(id _Nonnull provider, NSData * _Nonnull a, NSData * _Nonnull b, NSData * _Nonnull modulus); +NSData * _Nullable MTMul(id _Nonnull provider, NSData * _Nonnull a, NSData * _Nonnull b); +NSData * _Nullable MTAdd(id _Nonnull provider, NSData * _Nonnull a, NSData * _Nonnull b); +bool MTFactorize(uint64_t what, uint64_t * _Nonnull resA, uint64_t * _Nonnull resB); +bool MTIsZero(id _Nonnull provider, NSData * _Nonnull value); -NSData *MTAesCtrDecrypt(NSData *data, NSData *key, NSData *iv); +NSData * _Nullable MTAesCtrDecrypt(NSData * _Nonnull data, NSData * _Nonnull key, NSData * _Nonnull iv); @protocol MTKeychain; bool MTCheckIsSafeG(unsigned int g); -bool MTCheckIsSafeB(id provider, NSData *b, NSData *p); -bool MTCheckIsSafePrime(id provider, NSData *numberBytes, id keychain); -bool MTCheckIsSafeGAOrB(id provider, NSData *gAOrB, NSData *p); -bool MTCheckMod(id provider, NSData *numberBytes, unsigned int g, id keychain); +bool MTCheckIsSafeB(id _Nonnull provider, NSData * _Nonnull b, NSData * _Nonnull p); +bool MTCheckIsSafePrime(id _Nonnull provider, NSData * _Nonnull numberBytes, id _Nonnull keychain); +bool MTCheckIsSafeGAOrB(id _Nonnull provider, NSData * _Nonnull gAOrB, NSData * _Nonnull p); +bool MTCheckMod(id _Nonnull provider, NSData * _Nonnull numberBytes, unsigned int g, id _Nonnull keychain); @interface MTAesCtr : NSObject -- (instancetype)initWithKey:(const void *)key keyLength:(int)keyLength iv:(const void *)iv decrypt:(bool)decrypt; -- (instancetype)initWithKey:(const void *)key keyLength:(int)keyLength iv:(const void *)iv ecount:(void *)ecount num:(uint32_t)num; +- (instancetype _Nonnull)initWithKey:(const void * _Nonnull)key keyLength:(int)keyLength iv:(const void * _Nonnull)iv decrypt:(bool)decrypt; +- (instancetype _Nonnull)initWithKey:(const void * _Nonnull)key keyLength:(int)keyLength iv:(const void * _Nonnull)iv ecount:(void * _Nonnull)ecount num:(uint32_t)num; - (uint32_t)num; -- (void *)ecount; -- (void)getIv:(void *)iv; +- (void * _Nonnull)ecount; +- (void)getIv:(void * _Nonnull)iv; -- (void)encryptIn:(const unsigned char *)in out:(unsigned char *)out len:(size_t)len; +- (void)encryptIn:(const unsigned char * _Nonnull)in out:(unsigned char * _Nonnull)out len:(size_t)len; @end -uint64_t MTRsaFingerprint(id provider, NSString *key); +uint64_t MTRsaFingerprint(id _Nonnull provider, NSString * _Nonnull key); -NSData *MTRsaEncryptPKCS1OAEP(id provider, NSString *key, NSData *data); +NSData * _Nullable MTRsaEncryptPKCS1OAEP(id _Nonnull provider, NSString * _Nonnull key, NSData * _Nonnull data); @interface MTBackupDatacenterAddress : NSObject @property (nonatomic, readonly) int32_t datacenterId; -@property (nonatomic, strong, readonly) NSString *ip; +@property (nonatomic, strong, readonly) NSString * _Nonnull ip; @property (nonatomic, readonly) int32_t port; -@property (nonatomic, strong, readonly) NSData *secret; +@property (nonatomic, strong, readonly) NSData * _Nullable secret; @end @@ -76,11 +76,11 @@ NSData *MTRsaEncryptPKCS1OAEP(id provider, NSString *key, NS @property (nonatomic, readonly) int32_t timestamp; @property (nonatomic, readonly) int32_t expirationDate; -@property (nonatomic, strong, readonly) NSArray *addressList; +@property (nonatomic, strong, readonly) NSArray * _Nonnull addressList; @end -MTBackupDatacenterData *MTIPDataDecode(id provider, NSData *data, NSString *phoneNumber); +MTBackupDatacenterData * _Nullable MTIPDataDecode(id _Nonnull provider, NSData * _Nonnull data, NSString * _Nonnull phoneNumber); NSData * _Nullable MTPBKDF2(NSData * _Nonnull data, NSData * _Nonnull salt, int rounds); diff --git a/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTTransport.h b/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTTransport.h index fcc2d4b241..c1e8923493 100644 --- a/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTTransport.h +++ b/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTTransport.h @@ -19,34 +19,34 @@ @optional -- (void)transportNetworkAvailabilityChanged:(MTTransport *)transport isNetworkAvailable:(bool)isNetworkAvailable; -- (void)transportConnectionStateChanged:(MTTransport *)transport isConnected:(bool)isConnected proxySettings:(MTSocksProxySettings *)proxySettings; -- (void)transportConnectionFailed:(MTTransport *)transport scheme:(MTTransportScheme *)scheme; -- (void)transportConnectionContextUpdateStateChanged:(MTTransport *)transport isUpdatingConnectionContext:(bool)isUpdatingConnectionContext; -- (void)transportConnectionProblemsStatusChanged:(MTTransport *)transport scheme:(MTTransportScheme *)scheme hasConnectionProblems:(bool)hasConnectionProblems isProbablyHttp:(bool)isProbablyHttp; +- (void)transportNetworkAvailabilityChanged:(MTTransport * _Nonnull)transport isNetworkAvailable:(bool)isNetworkAvailable; +- (void)transportConnectionStateChanged:(MTTransport * _Nonnull)transport isConnected:(bool)isConnected proxySettings:(MTSocksProxySettings * _Nullable)proxySettings; +- (void)transportConnectionFailed:(MTTransport * _Nonnull)transport scheme:(MTTransportScheme * _Nonnull)scheme; +- (void)transportConnectionContextUpdateStateChanged:(MTTransport * _Nonnull)transport isUpdatingConnectionContext:(bool)isUpdatingConnectionContext; +- (void)transportConnectionProblemsStatusChanged:(MTTransport * _Nonnull)transport scheme:(MTTransportScheme * _Nonnull)scheme hasConnectionProblems:(bool)hasConnectionProblems isProbablyHttp:(bool)isProbablyHttp; -- (void)transportReadyForTransaction:(MTTransport *)transport scheme:(MTTransportScheme *)scheme transportSpecificTransaction:(MTMessageTransaction *)transportSpecificTransaction forceConfirmations:(bool)forceConfirmations transactionReady:(void (^)(NSArray *))transactionReady; -- (void)transportHasIncomingData:(MTTransport *)transport scheme:(MTTransportScheme *)scheme data:(NSData *)data transactionId:(id)transactionId requestTransactionAfterProcessing:(bool)requestTransactionAfterProcessing decodeResult:(void (^)(id transactionId, bool success))decodeResult; -- (void)transportTransactionsMayHaveFailed:(MTTransport *)transport transactionIds:(NSArray *)transactionIds; -- (void)transportReceivedQuickAck:(MTTransport *)transport quickAckId:(int32_t)quickAckId; -- (void)transportDecodeProgressToken:(MTTransport *)transport scheme:(MTTransportScheme *)scheme data:(NSData *)data token:(int64_t)token completion:(void (^)(int64_t token, id progressToken))completion; -- (void)transportUpdatedDataReceiveProgress:(MTTransport *)transport progressToken:(id)progressToken packetLength:(NSInteger)packetLength progress:(float)progress; +- (void)transportReadyForTransaction:(MTTransport * _Nonnull)transport scheme:(MTTransportScheme * _Nonnull)scheme transportSpecificTransaction:(MTMessageTransaction * _Nonnull)transportSpecificTransaction forceConfirmations:(bool)forceConfirmations transactionReady:(void (^ _Nonnull)(NSArray * _Nonnull))transactionReady; +- (void)transportHasIncomingData:(MTTransport * _Nonnull)transport scheme:(MTTransportScheme * _Nonnull)scheme data:(NSData * _Nonnull)data transactionId:(id _Nonnull)transactionId requestTransactionAfterProcessing:(bool)requestTransactionAfterProcessing decodeResult:(void (^ _Nonnull)(id _Nonnull transactionId, bool success))decodeResult; +- (void)transportTransactionsMayHaveFailed:(MTTransport * _Nonnull)transport transactionIds:(NSArray * _Nonnull)transactionIds; +- (void)transportReceivedQuickAck:(MTTransport * _Nonnull)transport quickAckId:(int32_t)quickAckId; +- (void)transportDecodeProgressToken:(MTTransport * _Nonnull)transport scheme:(MTTransportScheme * _Nonnull)scheme data:(NSData * _Nonnull)data token:(int64_t)token completion:(void (^ _Nonnull)(int64_t token, id _Nonnull progressToken))completion; +- (void)transportUpdatedDataReceiveProgress:(MTTransport * _Nonnull)transport progressToken:(id _Nonnull)progressToken packetLength:(NSInteger)packetLength progress:(float)progress; @end @interface MTTransport : NSObject -@property (nonatomic, weak) id delegate; +@property (nonatomic, weak) id _Nullable delegate; -@property (nonatomic, strong, readonly) MTContext *context; +@property (nonatomic, strong, readonly) MTContext * _Nullable context; @property (nonatomic, readonly) NSInteger datacenterId; -@property (nonatomic, strong, readonly) MTSocksProxySettings *proxySettings; +@property (nonatomic, strong, readonly) MTSocksProxySettings * _Nullable proxySettings; @property (nonatomic) bool simultaneousTransactionsEnabled; @property (nonatomic) bool reportTransportConnectionContextUpdateStates; -- (instancetype)initWithDelegate:(id)delegate context:(MTContext *)context datacenterId:(NSInteger)datacenterId schemes:(NSArray * _Nonnull)schemes proxySettings:(MTSocksProxySettings *)proxySettings usageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo; +- (instancetype _Nonnull)initWithDelegate:(id _Nullable)delegate context:(MTContext * _Nonnull)context datacenterId:(NSInteger)datacenterId schemes:(NSArray * _Nonnull)schemes proxySettings:(MTSocksProxySettings * _Null_unspecified)proxySettings usageCalculationInfo:(MTNetworkUsageCalculationInfo * _Nullable)usageCalculationInfo; -- (void)setUsageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo; +- (void)setUsageCalculationInfo:(MTNetworkUsageCalculationInfo * _Null_unspecified)usageCalculationInfo; - (bool)needsParityCorrection; @@ -54,10 +54,10 @@ - (void)stop; - (void)updateConnectionState; - (void)setDelegateNeedsTransaction; -- (void)_processIncomingData:(NSData *)data scheme:(MTTransportScheme *)scheme transactionId:(id)transactionId requestTransactionAfterProcessing:(bool)requestTransactionAfterProcessing decodeResult:(void (^)(id transactionId, bool success))decodeResult; +- (void)_processIncomingData:(NSData * _Nonnull)data scheme:(MTTransportScheme * _Nonnull)scheme transactionId:(id _Nonnull)transactionId requestTransactionAfterProcessing:(bool)requestTransactionAfterProcessing decodeResult:(void (^ _Nonnull)(id _Nonnull transactionId, bool success))decodeResult; - (void)_networkAvailabilityChanged:(bool)networkAvailable; -- (void)activeTransactionIds:(void (^)(NSArray *activeTransactionId))completion; +- (void)activeTransactionIds:(void (^ _Nonnull)(NSArray * _Nonnull activeTransactionId))completion; - (void)updateSchemes:(NSArray * _Nonnull)schemes; diff --git a/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTTransportScheme.h b/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTTransportScheme.h index 333c2a018f..46b82fd544 100644 --- a/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTTransportScheme.h +++ b/submodules/MtProtoKit/PublicHeaders/MtProtoKit/MTTransportScheme.h @@ -10,14 +10,14 @@ @interface MTTransportScheme : NSObject -@property (nonatomic, strong, readonly) Class transportClass; -@property (nonatomic, strong, readonly) MTDatacenterAddress *address; +@property (nonatomic, strong, readonly) Class _Nonnull transportClass; +@property (nonatomic, strong, readonly) MTDatacenterAddress * _Nonnull address; @property (nonatomic, readonly) bool media; -- (instancetype)initWithTransportClass:(Class)transportClass address:(MTDatacenterAddress *)address media:(bool)media; +- (instancetype _Nonnull)initWithTransportClass:(Class _Nonnull)transportClass address:(MTDatacenterAddress * _Nonnull)address media:(bool)media; -- (BOOL)isEqualToScheme:(MTTransportScheme *)other; +- (BOOL)isEqualToScheme:(MTTransportScheme * _Nonnull)other; - (BOOL)isOptimal; -- (NSComparisonResult)compareToScheme:(MTTransportScheme *)other; +- (NSComparisonResult)compareToScheme:(MTTransportScheme * _Nonnull)other; @end diff --git a/submodules/MtProtoKit/Sources/AFHTTPRequestOperation.m b/submodules/MtProtoKit/Sources/AFHTTPRequestOperation.m index ca1590360f..3fb45e3f85 100644 --- a/submodules/MtProtoKit/Sources/AFHTTPRequestOperation.m +++ b/submodules/MtProtoKit/Sources/AFHTTPRequestOperation.m @@ -92,7 +92,7 @@ static NSString * AFStringFromIndexSet(NSIndexSet *indexSet) { if (self.response && !self.HTTPError) { if (![self hasAcceptableStatusCode]) { NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; - [userInfo setValue:[NSString stringWithFormat:NSLocalizedString(@"Expected status code in (%@), got %d", nil), AFStringFromIndexSet(self.acceptableStatusCodes), [self.response statusCode]] forKey:NSLocalizedDescriptionKey]; + [userInfo setValue:[NSString stringWithFormat:NSLocalizedString(@"Expected status code in (%@), got %ld", nil), AFStringFromIndexSet(self.acceptableStatusCodes), (long)[self.response statusCode]] forKey:NSLocalizedDescriptionKey]; [userInfo setValue:[self.request URL] forKey:NSURLErrorFailingURLErrorKey]; self.HTTPError = [[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorBadServerResponse userInfo:userInfo]; @@ -176,21 +176,27 @@ static NSString * AFStringFromIndexSet(NSIndexSet *indexSet) { - (void)setCompletionBlockWithSuccess:(void (^)(NSOperation *operation, id responseObject))success failure:(void (^)(NSOperation *operation, NSError *error))failure { + __weak typeof(self) weakSelf = self; self.completionBlock = ^ { - if ([self isCancelled]) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { + return; + } + + if ([strongSelf isCancelled]) { return; } - if (self.error) { + if (strongSelf.error) { if (failure) { - dispatch_group_async(self.dispatchGroup, self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{ - failure(self, self.error); + dispatch_group_async(strongSelf.dispatchGroup, strongSelf.failureCallbackQueue ? strongSelf.failureCallbackQueue : dispatch_get_main_queue(), ^{ + failure(strongSelf, strongSelf.error); }); } } else { if (success) { - dispatch_group_async(self.dispatchGroup, self.successCallbackQueue ? self.successCallbackQueue : dispatch_get_main_queue(), ^{ - success(self, self.responseData); + dispatch_group_async(strongSelf.dispatchGroup, strongSelf.successCallbackQueue ? strongSelf.successCallbackQueue : dispatch_get_main_queue(), ^{ + success(strongSelf, strongSelf.responseData); }); } } diff --git a/submodules/MtProtoKit/Sources/MTContext.m b/submodules/MtProtoKit/Sources/MTContext.m index bde013a54e..8216fe97b7 100644 --- a/submodules/MtProtoKit/Sources/MTContext.m +++ b/submodules/MtProtoKit/Sources/MTContext.m @@ -414,11 +414,11 @@ static int32_t fixedTimeDifferenceValue = 0; _cleanupSessionIdsByAuthKeyId = [[NSMutableDictionary alloc] initWithDictionary:cleanupSessionIdsByAuthKeyId]; if (MTLogEnabled()) { - MTLog(@"[MTContext#%x: received keychain globalTimeDifference:%f datacenterAuthInfoById:%@]", (int)self, _globalTimeDifference, _datacenterAuthInfoById); + MTLog(@"[MTContext#%llx: received keychain globalTimeDifference:%f datacenterAuthInfoById:%@]", (intptr_t)self, _globalTimeDifference, _datacenterAuthInfoById); } } else { if (MTLogEnabled()) { - MTLog(@"[MTContext#%x: received keychain nil]", (int)self); + MTLog(@"[MTContext#%llx: received keychain nil]", (intptr_t)self); } } }]; @@ -477,7 +477,7 @@ static int32_t fixedTimeDifferenceValue = 0; _globalTimeDifference = globalTimeDifference; if (MTLogEnabled()) { - MTLog(@"[MTContext#%x: global time difference changed: %.1fs]", (int)self, globalTimeDifference); + MTLog(@"[MTContext#%llx: global time difference changed: %.1fs]", (intptr_t)self, globalTimeDifference); } [_keychain setObject:@(_globalTimeDifference) forKey:@"globalTimeDifference" group:@"temp"]; @@ -499,7 +499,7 @@ static int32_t fixedTimeDifferenceValue = 0; if (addressSet != nil && datacenterId != 0) { if (MTLogEnabled()) { - MTLog(@"[MTContext#%x: address set updated for %d]", (int)self, datacenterId); + MTLog(@"[MTContext#%llx: address set updated for %d]", (intptr_t)self, datacenterId); } bool updateSchemes = forceUpdateSchemes; @@ -592,7 +592,7 @@ static int32_t fixedTimeDifferenceValue = 0; if (updated) { if (MTLogEnabled()) { - MTLog(@"[MTContext#%x: added address %@ for datacenter %d]", (int)self, address, datacenterId); + MTLog(@"[MTContext#%llx: added address %@ for datacenter %d]", (intptr_t)self, address, datacenterId); } _datacenterAddressSetById[@(datacenterId)] = addressSet; @@ -631,7 +631,7 @@ static int32_t fixedTimeDifferenceValue = 0; if (MTLogEnabled()) { MTDatacenterAuthInfo *persistentInfo = _datacenterAuthInfoById[authInfoMapIntegerKey((int32_t)datacenterId, MTDatacenterAuthInfoSelectorPersistent)]; - MTLog(@"[MTContext#%x: auth info updated for %d selector %d to %@ (persistent key id is %llu)]", (int)self, datacenterId, selector, authInfo, persistentInfo.authKeyId); + MTLog(@"[MTContext#%llx: auth info updated for %d selector %d to %@ (persistent key id is %llu)]", (intptr_t)self, datacenterId, selector, authInfo, persistentInfo.authKeyId); } [_keychain setObject:_datacenterAuthInfoById forKey:@"datacenterAuthInfoById" group:@"persistent"]; @@ -706,7 +706,7 @@ static int32_t fixedTimeDifferenceValue = 0; NSArray *currentListeners = [[NSArray alloc] initWithArray:_changeListeners]; if (MTLogEnabled()) { - MTLog(@"[MTContext#%x: %@ transport scheme updated for %d: %@]", (int)self, media ? @"media" : @"generic", datacenterId, transportScheme); + MTLog(@"[MTContext#%llx: %@ transport scheme updated for %d: %@]", (intptr_t)self, media ? @"media" : @"generic", datacenterId, transportScheme); } for (id listener in currentListeners) { diff --git a/submodules/MtProtoKit/Sources/MTDiscoverConnectionSignals.h b/submodules/MtProtoKit/Sources/MTDiscoverConnectionSignals.h index a71313c198..c704342db0 100644 --- a/submodules/MtProtoKit/Sources/MTDiscoverConnectionSignals.h +++ b/submodules/MtProtoKit/Sources/MTDiscoverConnectionSignals.h @@ -11,10 +11,10 @@ typedef struct { @interface MTDiscoverConnectionSignals : NSObject -+ (NSData *)payloadData:(MTPayloadData *)outPayloadData context:(MTContext *)context address:(MTDatacenterAddress *)address; ++ (NSData * _Nonnull)payloadData:(MTPayloadData * _Nonnull)outPayloadData context:(MTContext * _Nonnull)context address:(MTDatacenterAddress * _Nonnull)address; -+ (MTSignal *)discoverSchemeWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId addressList:(NSArray *)addressList media:(bool)media isProxy:(bool)isProxy; ++ (MTSignal * _Nonnull)discoverSchemeWithContext:(MTContext * _Nonnull)context datacenterId:(NSInteger)datacenterId addressList:(NSArray * _Nonnull)addressList media:(bool)media isProxy:(bool)isProxy; -+ (MTSignal * _Nonnull)checkIfAuthKeyRemovedWithContext:(MTContext * _Nonnull)context datacenterId:(NSInteger)datacenterId authKey:(MTDatacenterAuthKey *)authKey; ++ (MTSignal * _Nonnull)checkIfAuthKeyRemovedWithContext:(MTContext * _Nonnull)context datacenterId:(NSInteger)datacenterId authKey:(MTDatacenterAuthKey * _Nonnull)authKey; @end diff --git a/submodules/MtProtoKit/Sources/MTEncryption.m b/submodules/MtProtoKit/Sources/MTEncryption.m index 348e3065a3..9196eeb348 100644 --- a/submodules/MtProtoKit/Sources/MTEncryption.m +++ b/submodules/MtProtoKit/Sources/MTEncryption.m @@ -600,7 +600,7 @@ bool MTCheckIsSafePrime(id provider, NSData *numberBytes, id id bnNumberMinusOneDivByTwo = [context create]; [context rightShift1Bit:bnNumberMinusOneDivByTwo a:bnNumberMinusOne]; - int result = [context isPrime:bnNumberMinusOneDivByTwo numberOfChecks:30]; + result = [context isPrime:bnNumberMinusOneDivByTwo numberOfChecks:30]; } [keychain setObject:@(result == 1) forKey:primeKey group:@"primes"]; diff --git a/submodules/MtProtoKit/Sources/MTNetworkAvailability.m b/submodules/MtProtoKit/Sources/MTNetworkAvailability.m index 8e45f15cf3..da33990b8c 100644 --- a/submodules/MtProtoKit/Sources/MTNetworkAvailability.m +++ b/submodules/MtProtoKit/Sources/MTNetworkAvailability.m @@ -137,7 +137,6 @@ static void MTNetworkAvailabilityContextRelease(const void *info) [[MTNetworkAvailability networkAvailabilityQueue] dispatchOnQueue:^ { BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0); - BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0); BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)); BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && diff --git a/submodules/MtProtoKit/Sources/MTRequestMessageService.m b/submodules/MtProtoKit/Sources/MTRequestMessageService.m index ac402c9c84..ab20463fb5 100644 --- a/submodules/MtProtoKit/Sources/MTRequestMessageService.m +++ b/submodules/MtProtoKit/Sources/MTRequestMessageService.m @@ -117,7 +117,7 @@ if (request.requestContext.messageId != 0) { if (MTLogEnabled()) { - MTLog(@"[MTRequestMessageService#%x drop %" PRId64 "]", (int)self, request.requestContext.messageId); + MTLog(@"[MTRequestMessageService#%llx drop %" PRId64 "]", (intptr_t)self, request.requestContext.messageId); } } @@ -933,8 +933,8 @@ request.requestContext.responseMessageId = responseMessageId; return true; } else { - MTLog(@"[MTRequestMessageService#%x will not request message %" PRId64 " (transaction was not completed)]", (int)self, messageId); - MTLog(@"[MTRequestMessageService#%x but today it will]", (int)self); + MTLog(@"[MTRequestMessageService#%llx will not request message %" PRId64 " (transaction was not completed)]", (intptr_t)self, messageId); + MTLog(@"[MTRequestMessageService#%llx but today it will]", (intptr_t)self); return true; } } diff --git a/submodules/MtProtoKit/Sources/MTTcpConnection.m b/submodules/MtProtoKit/Sources/MTTcpConnection.m index e5c55842fd..b1c89c85b0 100644 --- a/submodules/MtProtoKit/Sources/MTTcpConnection.m +++ b/submodules/MtProtoKit/Sources/MTTcpConnection.m @@ -106,7 +106,7 @@ static void generate_public_key(unsigned char key[32], id pr } } -typedef enum { +/*typedef enum { HelloGenerationCommandInvalid = 0, HelloGenerationCommandString = 1, HelloGenerationCommandZero = 2, @@ -471,7 +471,7 @@ static NSData *executeGenerationCode(id provider, NSData *do ((uint8_t *)resultData.mutableBytes)[paddingLengthPosition + 1] = ((uint8_t *)&calculatedLength)[0]; return resultData; -} +}*/ @interface MTTcpConnectionData : NSObject @@ -861,14 +861,14 @@ struct ctr_state { if (MTLogEnabled()) { if (strongSelf->_socksIp != nil) { if (strongSelf->_socksUsername.length == 0) { - MTLog(@"[MTTcpConnection#%x connecting to %@:%d via %@:%d]", (int)strongSelf, strongSelf->_scheme.address.ip, (int)strongSelf->_scheme.address.port, strongSelf->_socksIp, (int)strongSelf->_socksPort); + MTLog(@"[MTTcpConnection#%llx connecting to %@:%d via %@:%d]", (intptr_t)strongSelf, strongSelf->_scheme.address.ip, (int)strongSelf->_scheme.address.port, strongSelf->_socksIp, (int)strongSelf->_socksPort); } else { - MTLog(@"[MTTcpConnection#%x connecting to %@:%d via %@:%d using %@:%@]", (int)strongSelf, strongSelf->_scheme.address.ip, (int)strongSelf->_scheme.address.port, strongSelf->_socksIp, (int)strongSelf->_socksPort, strongSelf->_socksUsername, strongSelf->_socksPassword); + MTLog(@"[MTTcpConnection#%llx connecting to %@:%d via %@:%d using %@:%@]", (intptr_t)strongSelf, strongSelf->_scheme.address.ip, (int)strongSelf->_scheme.address.port, strongSelf->_socksIp, (int)strongSelf->_socksPort, strongSelf->_socksUsername, strongSelf->_socksPassword); } } else if (strongSelf->_mtpIp != nil) { - MTLog(@"[MTTcpConnection#%x connecting to %@:%d via mtp://%@:%d:%@]", (int)strongSelf, strongSelf->_scheme.address.ip, (int)strongSelf->_scheme.address.port, strongSelf->_mtpIp, (int)strongSelf->_mtpPort, strongSelf->_mtpSecret); + MTLog(@"[MTTcpConnection#%llx connecting to %@:%d via mtp://%@:%d:%@]", (intptr_t)strongSelf, strongSelf->_scheme.address.ip, (int)strongSelf->_scheme.address.port, strongSelf->_mtpIp, (int)strongSelf->_mtpPort, strongSelf->_mtpSecret); } else { - MTLog(@"[MTTcpConnection#%x connecting to %@:%d]", (int)strongSelf, strongSelf->_scheme.address.ip, (int)strongSelf->_scheme.address.port); + MTLog(@"[MTTcpConnection#%llx connecting to %@:%d]", (intptr_t)strongSelf, strongSelf->_scheme.address.ip, (int)strongSelf->_scheme.address.port); } } @@ -1280,7 +1280,7 @@ struct ctr_state { _responseTimeoutTimer = nil; if (MTLogEnabled()) { - MTLog(@"[MTTcpConnection#%x response timeout]", (int)self); + MTLog(@"[MTTcpConnection#%llx response timeout]", (intptr_t)self); } [self closeAndNotifyWithError:true]; } @@ -1421,7 +1421,7 @@ struct ctr_state { if (resp.Reply != 0x00) { if (MTLogEnabled()) { - MTLog(@"***** %x %s: socks5 connect failed, error 0x%02x", (int)self, __PRETTY_FUNCTION__, resp.Reply); + MTLog(@"***** %llx %s: socks5 connect failed, error 0x%02x", (intptr_t)self, __PRETTY_FUNCTION__, resp.Reply); } [self closeAndNotifyWithError:true]; return; @@ -1772,7 +1772,7 @@ struct ctr_state { } else { if (length > 16 * 1024 * 1024) { if (MTLogEnabled()) { - MTLog(@"[MTTcpConnection#%x received invalid length %d]", (int)self, length); + MTLog(@"[MTTcpConnection#%llx received invalid length %d]", (intptr_t)self, length); } [self closeAndNotifyWithError:true]; } else { @@ -1848,7 +1848,7 @@ struct ctr_state { } } else if (header == 0 && packetData.length < 16) { if (MTLogEnabled()) { - MTLog(@"[MTTcpConnection#%x received nop packet]", (int)self); + MTLog(@"[MTTcpConnection#%llx received nop packet]", (intptr_t)self); } ignorePacket = true; } @@ -1907,12 +1907,12 @@ struct ctr_state { { if (error != nil) { if (MTLogEnabled()) { - MTLog(@"[MTTcpConnection#%x disconnected from %@ (%@)]", (int)self, _scheme.address.ip, error); + MTLog(@"[MTTcpConnection#%llx disconnected from %@ (%@)]", (intptr_t)self, _scheme.address.ip, error); } } else { if (MTLogEnabled()) { - MTLog(@"[MTTcpConnection#%x disconnected from %@]", (int)self, _scheme.address.ip); + MTLog(@"[MTTcpConnection#%llx disconnected from %@]", (intptr_t)self, _scheme.address.ip); } } diff --git a/submodules/MtProtoKit/Sources/MTTcpTransport.m b/submodules/MtProtoKit/Sources/MTTcpTransport.m index 3b0883b3aa..61c650e87f 100644 --- a/submodules/MtProtoKit/Sources/MTTcpTransport.m +++ b/submodules/MtProtoKit/Sources/MTTcpTransport.m @@ -573,7 +573,7 @@ static const NSTimeInterval MTTcpTransportSleepWatchdogTimeout = 60.0; if (!transportContext.didSendActualizationPingAfterConnection) { if (MTLogEnabled()) { - MTLog(@"[MTTcpTransport#%x unlocking transaction processing due to connection context update task]", (int)self); + MTLog(@"[MTTcpTransport#%llx unlocking transaction processing due to connection context update task]", (intptr_t)self); } transportContext.isWaitingForTransactionToBecomeReady = false; transportContext.transactionLockTime = 0.0; @@ -581,7 +581,7 @@ static const NSTimeInterval MTTcpTransportSleepWatchdogTimeout = 60.0; else if (CFAbsoluteTimeGetCurrent() > transportContext.transactionLockTime + 1.0) { if (MTLogEnabled()) { - MTLog(@"[MTTcpTransport#%x unlocking transaction processing due to timeout]", (int)self); + MTLog(@"[MTTcpTransport#%llx unlocking transaction processing due to timeout]", (intptr_t)self); } transportContext.isWaitingForTransactionToBecomeReady = false; transportContext.transactionLockTime = 0.0; @@ -589,7 +589,7 @@ static const NSTimeInterval MTTcpTransportSleepWatchdogTimeout = 60.0; else { if (MTLogEnabled()) { - MTLog(@"[MTTcpTransport#%x skipping transaction request]", (int)self); + MTLog(@"[MTTcpTransport#%llx skipping transaction request]", (intptr_t)self); } transportContext.requestAnotherTransactionWhenReady = true; diff --git a/submodules/MusicAlbumArtResources/BUILD b/submodules/MusicAlbumArtResources/BUILD index e3ea1ddb2b..a975f21fff 100644 --- a/submodules/MusicAlbumArtResources/BUILD +++ b/submodules/MusicAlbumArtResources/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramCore:TelegramCore", "//submodules/Postbox:Postbox", diff --git a/submodules/NotificationMuteSettingsUI/BUILD b/submodules/NotificationMuteSettingsUI/BUILD index df31e5c424..bb062cc530 100644 --- a/submodules/NotificationMuteSettingsUI/BUILD +++ b/submodules/NotificationMuteSettingsUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Display:Display", "//submodules/Postbox:Postbox", diff --git a/submodules/NotificationSoundSelectionUI/BUILD b/submodules/NotificationSoundSelectionUI/BUILD index 87a3bc9db2..8a0519ab3b 100644 --- a/submodules/NotificationSoundSelectionUI/BUILD +++ b/submodules/NotificationSoundSelectionUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/Display:Display", diff --git a/submodules/NotificationSoundSelectionUI/Sources/NotificationSoundSelection.swift b/submodules/NotificationSoundSelectionUI/Sources/NotificationSoundSelection.swift index 4793b33ce5..f58c1f93ee 100644 --- a/submodules/NotificationSoundSelectionUI/Sources/NotificationSoundSelection.swift +++ b/submodules/NotificationSoundSelectionUI/Sources/NotificationSoundSelection.swift @@ -127,19 +127,19 @@ private enum NotificationSoundSelectionEntry: ItemListNodeEntry { func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem { let arguments = arguments as! NotificationSoundSelectionArguments switch self { - case let.modernHeader(theme, text): + case let.modernHeader(_, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) - case let .classicHeader(theme, text): + case let .classicHeader(_, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) - case let .none(_, theme, text, selected): + case let .none(_, _, text, selected): return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: selected, zeroSeparatorInsets: true, sectionId: self.section, action: { arguments.selectSound(.none) }) - case let .default(_, theme, text, selected): + case let .default(_, _, text, selected): return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: { arguments.selectSound(.default) }) - case let .sound(_, _, theme, text, sound, selected): + case let .sound(_, _, _, text, sound, selected): return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: { arguments.selectSound(sound) }) @@ -233,7 +233,7 @@ public func playSound(context: AccountContext, sound: PeerMessageSound, defaultS currentPlayer?.play() } } - }, deactivate: { + }, deactivate: { _ in return Signal { subscriber in Queue.mainQueue().async { currentPlayer?.stop() diff --git a/submodules/NotificationsPresentationData/BUILD b/submodules/NotificationsPresentationData/BUILD index 7bc884bb37..275798a4c3 100644 --- a/submodules/NotificationsPresentationData/BUILD +++ b/submodules/NotificationsPresentationData/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], visibility = [ "//visibility:public", ], diff --git a/submodules/ObjCRuntimeUtils/Source/ObjCRuntimeUtils/RuntimeUtils.h b/submodules/ObjCRuntimeUtils/Source/ObjCRuntimeUtils/RuntimeUtils.h index 945d11ae6d..3a06e3c3bf 100644 --- a/submodules/ObjCRuntimeUtils/Source/ObjCRuntimeUtils/RuntimeUtils.h +++ b/submodules/ObjCRuntimeUtils/Source/ObjCRuntimeUtils/RuntimeUtils.h @@ -8,19 +8,21 @@ typedef enum { @interface RuntimeUtils : NSObject -+ (void)swizzleInstanceMethodOfClass:(Class)targetClass currentSelector:(SEL)currentSelector newSelector:(SEL)newSelector; -+ (void)swizzleInstanceMethodOfClass:(Class)targetClass currentSelector:(SEL)currentSelector withAnotherClass:(Class)anotherClass newSelector:(SEL)newSelector; -+ (void)swizzleClassMethodOfClass:(Class)targetClass currentSelector:(SEL)currentSelector newSelector:(SEL)newSelector; ++ (void)swizzleInstanceMethodOfClass:(Class _Nonnull)targetClass currentSelector:(SEL _Nonnull)currentSelector newSelector:(SEL _Nonnull)newSelector; ++ (void)swizzleInstanceMethodOfClass:(Class _Nonnull)targetClass currentSelector:(SEL _Nonnull)currentSelector withAnotherClass:(Class _Nonnull)anotherClass newSelector:(SEL _Nonnull)newSelector; ++ (void)swizzleClassMethodOfClass:(Class _Nonnull)targetClass currentSelector:(SEL _Nonnull)currentSelector newSelector:(SEL _Nonnull)newSelector; + (CALayer * _Nonnull)makeLayerHostCopy:(CALayer * _Nonnull)another; @end @interface NSObject (AssociatedObject) -- (void)setAssociatedObject:(id)object forKey:(void const *)key; -- (void)setAssociatedObject:(id)object forKey:(void const *)key associationPolicy:(NSObjectAssociationPolicy)associationPolicy; -- (id)associatedObjectForKey:(void const *)key; -- (bool)checkObjectIsKindOfClass:(Class)targetClass; -- (void)setClass:(Class)newClass; +- (void)setAssociatedObject:(id _Nullable)object forKey:(void const * _Nonnull)key; +- (void)setAssociatedObject:(id _Nullable)object forKey:(void const * _Nonnull)key associationPolicy:(NSObjectAssociationPolicy)associationPolicy; +- (id _Nullable)associatedObjectForKey:(void const * _Nonnull)key; +- (bool)checkObjectIsKindOfClass:(Class _Nonnull)targetClass; +- (void)setClass:(Class _Nonnull)newClass; @end + +SEL _Nonnull makeSelectorFromString(NSString * _Nonnull string); diff --git a/submodules/ObjCRuntimeUtils/Source/ObjCRuntimeUtils/RuntimeUtils.m b/submodules/ObjCRuntimeUtils/Source/ObjCRuntimeUtils/RuntimeUtils.m index 44d2c8830f..060faf64d0 100644 --- a/submodules/ObjCRuntimeUtils/Source/ObjCRuntimeUtils/RuntimeUtils.m +++ b/submodules/ObjCRuntimeUtils/Source/ObjCRuntimeUtils/RuntimeUtils.m @@ -122,3 +122,7 @@ static Class freedomMakeClass(Class superclass, Class subclass, SEL *copySelecto } @end + +SEL _Nonnull makeSelectorFromString(NSString * _Nonnull string) { + return NSSelectorFromString(string); +} diff --git a/submodules/OpenInExternalAppUI/BUILD b/submodules/OpenInExternalAppUI/BUILD index 9006bc6699..8eb226d04e 100644 --- a/submodules/OpenInExternalAppUI/BUILD +++ b/submodules/OpenInExternalAppUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/OpenInExternalAppUI/Sources/OpenInActionSheetController.swift b/submodules/OpenInExternalAppUI/Sources/OpenInActionSheetController.swift index 63d8118788..10b564aa8a 100644 --- a/submodules/OpenInExternalAppUI/Sources/OpenInActionSheetController.swift +++ b/submodules/OpenInExternalAppUI/Sources/OpenInActionSheetController.swift @@ -136,7 +136,6 @@ private final class OpenInActionSheetItemNode: ActionSheetItemNode { self.strings = strings let titleFont = Font.medium(floor(theme.baseFontSize * 20.0 / 17.0)) - let textFont = Font.regular(floor(theme.baseFontSize * 11.0 / 17.0)) self.titleNode = ASTextNode() self.titleNode.isUserInteractionEnabled = false diff --git a/submodules/OverlayStatusController/BUILD b/submodules/OverlayStatusController/BUILD index e28e643223..633601d439 100644 --- a/submodules/OverlayStatusController/BUILD +++ b/submodules/OverlayStatusController/BUILD @@ -14,6 +14,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Display:Display", "//submodules/OverlayStatusController/Impl:OverlayStatusControllerImpl" diff --git a/submodules/PasscodeInputFieldNode/BUILD b/submodules/PasscodeInputFieldNode/BUILD index e44fe77ad8..c7fbc40da1 100644 --- a/submodules/PasscodeInputFieldNode/BUILD +++ b/submodules/PasscodeInputFieldNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/PasscodeUI/BUILD b/submodules/PasscodeUI/BUILD index 46648d9edb..4230fc328a 100644 --- a/submodules/PasscodeUI/BUILD +++ b/submodules/PasscodeUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift b/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift index 92709398a2..93cfd75f59 100644 --- a/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift +++ b/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift @@ -234,8 +234,8 @@ final class PasscodeEntryControllerNode: ASDisplayNode { if let image = chatControllerBackgroundImage(theme: self.theme, wallpaper: self.wallpaper, mediaBox: self.accountManager.mediaBox, composed: false, knockoutMode: false) { self.background = ImageBasedPasscodeBackground(image: image, size: size) } else { - if case let .file(file) = self.wallpaper, !file.settings.colors.isEmpty { - self.background = CustomPasscodeBackground(size: size, colors: file.settings.colors.compactMap { UIColor(rgb: $0) }, inverted: (file.settings.intensity ?? 0) < 0) + if case let .file(_, _, _, _, _, _, _, _, settings) = self.wallpaper, !settings.colors.isEmpty { + self.background = CustomPasscodeBackground(size: size, colors: settings.colors.compactMap { UIColor(rgb: $0) }, inverted: (settings.intensity ?? 0) < 0) } else { self.background = GradientPasscodeBackground(size: size, backgroundColors: self.theme.passcode.backgroundColors.colors, buttonColor: self.theme.passcode.buttonColor) } diff --git a/submodules/PasscodeUI/Sources/PasscodeEntryKeyboardNode.swift b/submodules/PasscodeUI/Sources/PasscodeEntryKeyboardNode.swift index 83b322eb0e..7cd90fae47 100644 --- a/submodules/PasscodeUI/Sources/PasscodeEntryKeyboardNode.swift +++ b/submodules/PasscodeUI/Sources/PasscodeEntryKeyboardNode.swift @@ -124,14 +124,9 @@ final class PasscodeEntryButtonNode: HighlightTrackingButtonNode { self.subtitle = subtitle if let background = background as? CustomPasscodeBackground { - if false, background.inverted { - let gradientBackgroundNode = background.makeForegroundNode(backgroundNode: background.makeBackgroundNode()) - self.gradientBackgroundNode = gradientBackgroundNode as? GradientBackgroundNode.CloneNode - } else { - let blurredBackgroundColor = (background.inverted ? UIColor(rgb: 0xffffff, alpha: 0.1) : UIColor(rgb: 0x000000, alpha: 0.2), dateFillNeedsBlur(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper)) - let blurredBackgroundNode = NavigationBackgroundNode(color: blurredBackgroundColor.0, enableBlur: blurredBackgroundColor.1) - self.blurredBackgroundNode = blurredBackgroundNode - } + let blurredBackgroundColor = (background.inverted ? UIColor(rgb: 0xffffff, alpha: 0.1) : UIColor(rgb: 0x000000, alpha: 0.2), dateFillNeedsBlur(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper)) + let blurredBackgroundNode = NavigationBackgroundNode(color: blurredBackgroundColor.0, enableBlur: blurredBackgroundColor.1) + self.blurredBackgroundNode = blurredBackgroundNode } self.backgroundNode = ASImageNode() diff --git a/submodules/PassportUI/BUILD b/submodules/PassportUI/BUILD index bc03ee0145..76aba34269 100644 --- a/submodules/PassportUI/BUILD +++ b/submodules/PassportUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/PassportUI/Sources/SecureIdAuthController.swift b/submodules/PassportUI/Sources/SecureIdAuthController.swift index 429250b388..ec01a064a2 100644 --- a/submodules/PassportUI/Sources/SecureIdAuthController.swift +++ b/submodules/PassportUI/Sources/SecureIdAuthController.swift @@ -224,7 +224,7 @@ public final class SecureIdAuthController: ViewController, StandalonePresentable let primaryLanguageByCountry = configuration.nativeLanguageByCountry return .single(SecureIdEncryptedFormData(form: form, primaryLanguageByCountry: primaryLanguageByCountry, accountPeer: accountPeer, servicePeer: servicePeer)) } - |> mapError { _ in return RequestSecureIdFormError.generic } + |> castError(RequestSecureIdFormError.self) |> switchToLatest } |> deliverOnMainQueue).start(next: { [weak self] formData in @@ -252,7 +252,7 @@ public final class SecureIdAuthController: ViewController, StandalonePresentable return .single(accountPeer) } - |> mapError { _ in return GetAllSecureIdValuesError.generic } + |> castError(GetAllSecureIdValuesError.self) |> switchToLatest) |> deliverOnMainQueue).start(next: { [weak self] values, configuration, accountPeer in if let strongSelf = self { @@ -496,17 +496,17 @@ public final class SecureIdAuthController: ViewController, StandalonePresentable } private func openPasswordHelp() { - guard let verificationState = self.state.verificationState, case let .passwordChallenge(passwordChallenge) = verificationState else { + guard let verificationState = self.state.verificationState, case let .passwordChallenge(_, state, hasRecoveryEmail) = verificationState else { return } - switch passwordChallenge.state { + switch state { case .checking: return case .none, .invalid: break } - if passwordChallenge.hasRecoveryEmail { + if hasRecoveryEmail { self.present(textAlertController(context: self.context, title: self.presentationData.strings.Passport_ForgottenPassword, text: self.presentationData.strings.Passport_PasswordReset, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Login_ResetAccountProtected_Reset, action: { [weak self] in guard let strongSelf = self else { return diff --git a/submodules/PassportUI/Sources/SecureIdAuthHeaderNode.swift b/submodules/PassportUI/Sources/SecureIdAuthHeaderNode.swift index 471a914f72..9cd9360036 100644 --- a/submodules/PassportUI/Sources/SecureIdAuthHeaderNode.swift +++ b/submodules/PassportUI/Sources/SecureIdAuthHeaderNode.swift @@ -53,7 +53,7 @@ final class SecureIdAuthHeaderNode: ASDisplayNode { func updateState(formData: SecureIdEncryptedFormData?, verificationState: SecureIdAuthControllerVerificationState) { if let formData = formData { - self.serviceAvatarNode.setPeer(context: self.context, theme: self.theme, peer: formData.servicePeer) + self.serviceAvatarNode.setPeer(context: self.context, theme: self.theme, peer: EnginePeer(formData.servicePeer)) let titleData = self.strings.Passport_RequestHeader(formData.servicePeer.displayTitle(strings: self.strings, displayOrder: self.nameDisplayOrder)) let titleString = NSMutableAttributedString() diff --git a/submodules/PassportUI/Sources/SecureIdDocumentFormControllerNode.swift b/submodules/PassportUI/Sources/SecureIdDocumentFormControllerNode.swift index 0746379912..e3f85c953c 100644 --- a/submodules/PassportUI/Sources/SecureIdDocumentFormControllerNode.swift +++ b/submodules/PassportUI/Sources/SecureIdDocumentFormControllerNode.swift @@ -2683,7 +2683,7 @@ final class SecureIdDocumentFormControllerNode: FormControllerNode Bool { - guard var innerState = self.innerState else { + guard let innerState = self.innerState else { return false } guard let values = innerState.makeValues(), !values.isEmpty else { diff --git a/submodules/PassportUI/Sources/SecureIdDocumentTypeSelectionController.swift b/submodules/PassportUI/Sources/SecureIdDocumentTypeSelectionController.swift index 028eab0e19..1e9f8c5a56 100644 --- a/submodules/PassportUI/Sources/SecureIdDocumentTypeSelectionController.swift +++ b/submodules/PassportUI/Sources/SecureIdDocumentTypeSelectionController.swift @@ -83,7 +83,6 @@ final class SecureIdDocumentTypeSelectionController: ActionSheetController { init(context: AccountContext, field: SecureIdParsedRequestedFormField, currentValues: [SecureIdValueWithContext], completion: @escaping (SecureIdDocumentFormRequestedData) -> Void) { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let theme = presentationData.theme let strings = presentationData.strings self.completion = completion diff --git a/submodules/PassportUI/Sources/SecureIdLocalResource.swift b/submodules/PassportUI/Sources/SecureIdLocalResource.swift index dfb8a8ffce..66aaaa049b 100644 --- a/submodules/PassportUI/Sources/SecureIdLocalResource.swift +++ b/submodules/PassportUI/Sources/SecureIdLocalResource.swift @@ -110,7 +110,10 @@ public func fetchSecureIdLocalImageResource(postbox: Postbox, resource: SecureId if buffer.data.count < range.count { buffer.data.count = range.count } - buffer.data.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + buffer.data.withUnsafeMutableBytes { buffer -> Void in + guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } data.copyBytes(to: bytes, from: range) } } @@ -119,7 +122,10 @@ public func fetchSecureIdLocalImageResource(postbox: Postbox, resource: SecureId if buffer.data.count < resourceOffset + range.count { buffer.data.count = resourceOffset + range.count } - buffer.data.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + buffer.data.withUnsafeMutableBytes { buffer -> Void in + guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } data.copyBytes(to: bytes.advanced(by: resourceOffset), from: range) } } diff --git a/submodules/PassportUI/Sources/SecureIdVerificationDocumentsContext.swift b/submodules/PassportUI/Sources/SecureIdVerificationDocumentsContext.swift index 9e60e8f3ff..f72286c482 100644 --- a/submodules/PassportUI/Sources/SecureIdVerificationDocumentsContext.swift +++ b/submodules/PassportUI/Sources/SecureIdVerificationDocumentsContext.swift @@ -55,10 +55,7 @@ final class SecureIdVerificationDocumentsContext { } } } - }, error: { [weak self] _ in - if let strongSelf = self { - - } + }, error: { _ in })) } case .remote: diff --git a/submodules/PasswordSetupUI/BUILD b/submodules/PasswordSetupUI/BUILD index cbd779be7a..a0da0b6d88 100644 --- a/submodules/PasswordSetupUI/BUILD +++ b/submodules/PasswordSetupUI/BUILD @@ -20,6 +20,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/PasswordSetupUI/Sources/ManagedMonkeyAnimationNode.swift b/submodules/PasswordSetupUI/Sources/ManagedMonkeyAnimationNode.swift index b564786ab5..028761c0b7 100644 --- a/submodules/PasswordSetupUI/Sources/ManagedMonkeyAnimationNode.swift +++ b/submodules/PasswordSetupUI/Sources/ManagedMonkeyAnimationNode.swift @@ -111,7 +111,7 @@ final class ManagedMonkeyAnimationNode: ManagedAnimationNode { } switch previousState { - case let .idle(previousIdle): + case .idle: switch monkeyState { case let .idle(idle): self.enqueueIdle(idle) diff --git a/submodules/PasswordSetupUI/Sources/ResetPasswordController.swift b/submodules/PasswordSetupUI/Sources/ResetPasswordController.swift index 174a50aa75..4bb391d32d 100644 --- a/submodules/PasswordSetupUI/Sources/ResetPasswordController.swift +++ b/submodules/PasswordSetupUI/Sources/ResetPasswordController.swift @@ -146,7 +146,7 @@ public func resetPasswordController(context: AccountContext, emailPattern: Strin completion(false) case .declined: break - case let .error(reason): + case .error: break } }) diff --git a/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationControllerNode.swift b/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationControllerNode.swift index 2a9f9ee4b9..e3cda020c3 100644 --- a/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationControllerNode.swift +++ b/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationControllerNode.swift @@ -428,7 +428,7 @@ final class SetupTwoStepVerificationControllerNode: ViewControllerTracingNode { return } var inplicitelyActivateNextAction = false - if case let .confirmEmail(confirmEmail)? = strongSelf.innerState.data.state, let codeLength = confirmEmail.codeLength, confirmEmail.code.count != codeLength, text.count == codeLength { + if case let .confirmEmail(_, _, codeLength?, code)? = strongSelf.innerState.data.state, code.count != codeLength, text.count == codeLength { inplicitelyActivateNextAction = true } strongSelf.updateState({ state in diff --git a/submodules/Pdf/BUILD b/submodules/Pdf/BUILD index cf05f0ebeb..8b0dc1e616 100644 --- a/submodules/Pdf/BUILD +++ b/submodules/Pdf/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Display:Display", ], diff --git a/submodules/PeerAvatarGalleryUI/BUILD b/submodules/PeerAvatarGalleryUI/BUILD index 347f0abd24..40dbe21520 100644 --- a/submodules/PeerAvatarGalleryUI/BUILD +++ b/submodules/PeerAvatarGalleryUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift b/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift index 6c3b315f8e..4d8a4370af 100644 --- a/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift +++ b/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift @@ -227,8 +227,8 @@ public func fetchedAvatarGalleryEntries(engine: TelegramEngine, account: Account if [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(peer.id.namespace) { var initialMediaIds = Set() for entry in initialEntries { - if case let .image(image) = entry { - initialMediaIds.insert(image.0) + if case let .image(mediaId, _, _, _, _, _, _, _, _, _) = entry { + initialMediaIds.insert(mediaId) } } @@ -239,8 +239,8 @@ public func fetchedAvatarGalleryEntries(engine: TelegramEngine, account: Account photosCount += 1 for entry in initialEntries { let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photosCount)) - if case let .image(image) = entry { - result.append(.image(image.0, image.1, image.2, image.3, image.4, nil, indexData, nil, image.8, nil)) + if case let .image(mediaId, imageReference, representations, videoRepresentations, peer, _, _, _, thumbnailData, _) = entry { + result.append(.image(mediaId, imageReference, representations, videoRepresentations, peer, nil, indexData, nil, thumbnailData, nil)) index += 1 } } @@ -284,8 +284,8 @@ public func fetchedAvatarGalleryEntries(engine: TelegramEngine, account: Account if [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(peer.id.namespace) { var initialMediaIds = Set() for entry in initialEntries { - if case let .image(image) = entry { - initialMediaIds.insert(image.0) + if case let .image(mediaId, _, _, _, _, _, _, _, _, _) = entry { + initialMediaIds.insert(mediaId) } } @@ -298,8 +298,8 @@ public func fetchedAvatarGalleryEntries(engine: TelegramEngine, account: Account photosCount += 1 for entry in initialEntries { let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photosCount)) - if case let .image(image) = entry { - result.append(.image(image.0, image.1, image.2, image.3, image.4, nil, indexData, nil, image.8, nil)) + if case let .image(mediaId, imageReference, representations, videoRepresentations, peer, _, _, _, thumbnailData, _) = entry { + result.append(.image(mediaId, imageReference, representations, videoRepresentations, peer, nil, indexData, nil, thumbnailData, nil)) index += 1 } } @@ -429,8 +429,8 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr let isFirstTime = strongSelf.entries.isEmpty var entries = entries - if !isFirstTime, let updated = entries.first, case let .image(image) = updated, !image.3.isEmpty, let previous = strongSelf.entries.first, case let .topImage(topImage) = previous { - let firstEntry = AvatarGalleryEntry.image(image.0, image.1, topImage.0, image.3, image.4, image.5, image.6, image.7, image.8, image.9) + if !isFirstTime, let updated = entries.first, case let .image(mediaId, imageReference, _, videoRepresentations, peer, index, indexData, messageId, thumbnailData, caption) = updated, !videoRepresentations.isEmpty, let previous = strongSelf.entries.first, case let .topImage(representations, _, _, _, _, _) = previous { + let firstEntry = AvatarGalleryEntry.image(mediaId, imageReference, representations, videoRepresentations, peer, index, indexData, messageId, thumbnailData, caption) entries.remove(at: 0) entries.insert(firstEntry, at: 0) } @@ -749,8 +749,8 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr if let reference = reference { let _ = (self.context.engine.accountData.updatePeerPhotoExisting(reference: reference) |> deliverOnMainQueue).start(next: { [weak self] photo in - if let strongSelf = self, let photo = photo, let firstEntry = strongSelf.entries.first, case let .image(image) = firstEntry { - let updatedEntry = AvatarGalleryEntry.image(photo.imageId, photo.reference, photo.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), photo.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), strongSelf.peer, image.5, image.6, image.7, photo.immediateThumbnailData, image.9) + if let strongSelf = self, let photo = photo, let firstEntry = strongSelf.entries.first, case let .image(_, _, _, _, _, index, indexData, messageId, _, caption) = firstEntry { + let updatedEntry = AvatarGalleryEntry.image(photo.imageId, photo.reference, photo.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatar(peer: peerReference, resource: $0.resource)) }), photo.videoRepresentations.map({ VideoRepresentationWithReference(representation: $0, reference: MediaResourceReference.avatarList(peer: peerReference, resource: $0.resource)) }), strongSelf.peer, index, indexData, messageId, photo.immediateThumbnailData, caption) for (lhs, rhs) in zip(firstEntry.representations, updatedEntry.representations) { if lhs.representation.dimensions == rhs.representation.dimensions { diff --git a/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift b/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift index 4ff3e70633..7c878bf1f2 100644 --- a/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift +++ b/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift @@ -252,9 +252,9 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode { var id: Int64 var category: String? - if case let .image(image) = entry { - id = image.0.id - category = image.9 + if case let .image(mediaId, _, _, _, _, _, _, _, _, categoryValue) = entry { + id = mediaId.id + category = categoryValue } else { id = Int64(entry.peer?.id.id._internalGetInt64Value() ?? 0) if let resource = entry.videoRepresentations.first?.representation.resource as? CloudPhotoSizeMediaResource { @@ -462,7 +462,6 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode { var positionCompleted = false var boundsCompleted = false var copyCompleted = false - var surfaceCopyCompleted = false let (maybeCopyView, copyViewBackground) = node.2() copyViewBackground?.alpha = 1.0 @@ -508,11 +507,8 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode { surfaceCopyView.layer.animatePosition(from: CGPoint(x: transformedSurfaceCopyViewInitialFrame.midX, y: transformedSurfaceCopyViewInitialFrame.midY), to: CGPoint(x: transformedSurfaceFrame.midX, y: transformedSurfaceFrame.midY), duration: 0.25 * durationFactor, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) let scale = CGSize(width: transformedSurfaceCopyViewInitialFrame.size.width / transformedSurfaceFrame.size.width, height: transformedSurfaceCopyViewInitialFrame.size.height / transformedSurfaceFrame.size.height) surfaceCopyView.layer.animate(from: NSValue(caTransform3D: CATransform3DMakeScale(scale.width, scale.height, 1.0)), to: NSValue(caTransform3D: CATransform3DIdentity), keyPath: "transform", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.25 * durationFactor, removeOnCompletion: false, completion: { _ in - surfaceCopyCompleted = true intermediateCompletion() }) - } else { - surfaceCopyCompleted = true } copyView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1 * durationFactor, removeOnCompletion: false) diff --git a/submodules/PeerInfoAvatarListNode/BUILD b/submodules/PeerInfoAvatarListNode/BUILD index edff292ed4..a6cb6685e6 100644 --- a/submodules/PeerInfoAvatarListNode/BUILD +++ b/submodules/PeerInfoAvatarListNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift b/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift index 99c8b23d6d..a65e7a42bb 100644 --- a/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift +++ b/submodules/PeerInfoAvatarListNode/Sources/PeerInfoAvatarListNode.swift @@ -846,7 +846,6 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode { self.updateItems(size: size, transition: .animated(duration: 0.3, curve: .spring), stripTransition: .animated(duration: 0.3, curve: .spring)) } case .cancelled, .ended: - let translation = recognizer.translation(in: self.view) let velocity = recognizer.velocity(in: self.view) var directionIsToRight: Bool? if abs(velocity.x) > 10.0 { @@ -879,7 +878,7 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode { } func setMainItem(_ item: PeerInfoAvatarListItem) { - guard case let .image(image) = item else { + guard case let .image(imageReference, _, _, _) = item else { return } var items: [PeerInfoAvatarListItem] = [] @@ -893,7 +892,7 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode { if representations.isEmpty { continue } - if image.0 == reference { + if imageReference == reference { entries.insert(entry, at: 0) items.insert(.image(reference, representations, videoRepresentations, immediateThumbnailData), at: 0) } else { @@ -917,7 +916,7 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode { } public func deleteItem(_ item: PeerInfoAvatarListItem) -> Bool { - guard case let .image(image) = item else { + guard case let .image(imageReference, _, _, _) = item else { return false } @@ -936,7 +935,7 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode { if representations.isEmpty { continue } - if image.0 != reference { + if imageReference != reference { entries.append(entry) items.append(.image(reference, representations, videoRepresentations, immediateThumbnailData)) } else { @@ -1004,8 +1003,8 @@ public final class PeerInfoAvatarListContainerNode: ASDisplayNode { } var synchronous = false - if !strongSelf.galleryEntries.isEmpty, let updated = entries.first, case let .image(image) = updated, !image.3.isEmpty, let previous = strongSelf.galleryEntries.first, case let .topImage(topImage) = previous { - let firstEntry = AvatarGalleryEntry.image(image.0, image.1, topImage.0, image.3, image.4, image.5, image.6, image.7, image.8, image.9) + if !strongSelf.galleryEntries.isEmpty, let updated = entries.first, case let .image(mediaId, reference, _, videoRepresentations, peer, index, indexData, messageId, thumbnailData, caption) = updated, !videoRepresentations.isEmpty, let previous = strongSelf.galleryEntries.first, case let .topImage(representations, _, _, _, _, _) = previous { + let firstEntry = AvatarGalleryEntry.image(mediaId, reference, representations, videoRepresentations, peer, index, indexData, messageId, thumbnailData, caption) entries.remove(at: 0) entries.insert(firstEntry, at: 0) synchronous = true diff --git a/submodules/PeerInfoUI/BUILD b/submodules/PeerInfoUI/BUILD index 9695768934..87c4038f92 100644 --- a/submodules/PeerInfoUI/BUILD +++ b/submodules/PeerInfoUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/PeerInfoUI/Sources/ChannelAdminController.swift b/submodules/PeerInfoUI/Sources/ChannelAdminController.swift index 7ad49ac1e4..05c6b82ead 100644 --- a/submodules/PeerInfoUI/Sources/ChannelAdminController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelAdminController.swift @@ -70,88 +70,6 @@ private enum ChannelAdminEntryStableId: Hashable { case addAdminsInfo case transfer case dismiss - - var hashValue: Int { - switch self { - case .info: - return 0 - case .rankTitle: - return 1 - case .rank: - return 2 - case .rankInfo: - return 3 - case .rightsTitle: - return 4 - case .addAdminsInfo: - return 5 - case .dismiss: - return 6 - case .transfer: - return 7 - case let .right(flags): - return flags.rawValue.hashValue - } - } - - static func ==(lhs: ChannelAdminEntryStableId, rhs: ChannelAdminEntryStableId) -> Bool { - switch lhs { - case .info: - if case .info = rhs { - return true - } else { - return false - } - case .rankTitle: - if case .rankTitle = rhs { - return true - } else { - return false - } - case .rank: - if case .rank = rhs { - return true - } else { - return false - } - case .rankInfo: - if case .rankInfo = rhs { - return true - } else { - return false - } - case .rightsTitle: - if case .rightsTitle = rhs { - return true - } else { - return false - } - case let right(flags): - if case .right(flags) = rhs { - return true - } else { - return false - } - case .addAdminsInfo: - if case .addAdminsInfo = rhs { - return true - } else { - return false - } - case .transfer: - if case .transfer = rhs { - return true - } else { - return false - } - case .dismiss: - if case .dismiss = rhs { - return true - } else { - return false - } - } - } } private enum ChannelAdminEntry: ItemListNodeEntry { @@ -691,7 +609,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s canTransfer = true } - if let initialParticipant = initialParticipant, case let .member(_, _, adminInfoValue, _, _) = initialParticipant, let adminInfo = adminInfoValue, admin.id != accountPeerId { + if let initialParticipant = initialParticipant, case .member = initialParticipant, admin.id != accountPeerId { if channel.flags.contains(.isCreator) { canDismiss = true } else { @@ -812,7 +730,7 @@ private func channelAdminControllerEntries(presentationData: PresentationData, s entries.append(.rankTitle(presentationData.theme, presentationData.strings.Group_EditAdmin_RankTitle.uppercased(), rankEnabled && state.focusedOnRank ? Int32(currentRank?.count ?? 0) : nil, rankMaxLength)) entries.append(.rank(presentationData.theme, presentationData.strings, isCreator ? presentationData.strings.Group_EditAdmin_RankOwnerPlaceholder : presentationData.strings.Group_EditAdmin_RankAdminPlaceholder, currentRank ?? "", rankEnabled)) - if let initialParticipant = initialParticipant, case let .member(participant) = initialParticipant, let adminInfo = participant.adminInfo, admin.id != accountPeerId { + if let initialParticipant = initialParticipant, case .member = initialParticipant, admin.id != accountPeerId { entries.append(.dismiss(presentationData.theme, presentationData.strings.Channel_Moderator_AccessLevelRevoke)) } } diff --git a/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift b/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift index 2ce7af4502..89c10d7ade 100644 --- a/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift @@ -43,32 +43,6 @@ private enum ChannelAdminsSection: Int32 { private enum ChannelAdminsEntryStableId: Hashable { case index(Int32) case peer(PeerId) - - var hashValue: Int { - switch self { - case let .index(index): - return index.hashValue - case let .peer(peerId): - return peerId.hashValue - } - } - - static func ==(lhs: ChannelAdminsEntryStableId, rhs: ChannelAdminsEntryStableId) -> Bool { - switch lhs { - case let .index(index): - if case .index(index) = rhs { - return true - } else { - return false - } - case let .peer(peerId): - if case .peer(peerId) = rhs { - return true - } else { - return false - } - } - } } private enum ChannelAdminsEntry: ItemListNodeEntry { @@ -203,13 +177,13 @@ private enum ChannelAdminsEntry: ItemListNodeEntry { func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem { let arguments = arguments as! ChannelAdminsControllerArguments switch self { - case let .recentActions(theme, text): + case let .recentActions(_, text): return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .blocks, action: { arguments.openRecentActions() }) - case let .adminsHeader(theme, title): + case let .adminsHeader(_, title): return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section) - case let .adminPeerItem(theme, strings, dateTimeFormat, nameDisplayOrder, _, _, participant, editing, enabled, hasAction): + case let .adminPeerItem(_, strings, dateTimeFormat, nameDisplayOrder, _, _, participant, editing, enabled, hasAction): let peerText: String var action: (() -> Void)? switch participant.participant { @@ -244,7 +218,7 @@ private enum ChannelAdminsEntry: ItemListNodeEntry { return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.addPersonIcon(theme), title: text, sectionId: self.section, editing: editing, action: { arguments.addAdmin() }) - case let .adminsInfo(theme, text): + case let .adminsInfo(_, text): return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) } } @@ -826,11 +800,9 @@ public func channelAdminsController(context: AccountContext, peerId initialPeerI guard let controller = controller, let navigationController = controller.navigationController as? NavigationController else { return } - - var replacedSelf = false + rebuildControllerStackAfterSupergroupUpgrade(controller: controller, navigationController: navigationController, replace: { c in if c === controller { - replacedSelf = true return channelAdminsController(context: context, peerId: upgradedPeerId, loadCompleted: { }) } else { diff --git a/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift b/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift index b5ef77db87..116be51446 100644 --- a/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift @@ -44,23 +44,6 @@ private enum ChannelBannedMemberEntryStableId: Hashable { case timeout case exceptionInfo case delete - - var hashValue: Int { - switch self { - case .info: - return 0 - case .rightsHeader: - return 1 - case .timeout: - return 2 - case .exceptionInfo: - return 3 - case .delete: - return 4 - case let .right(flags): - return flags.rawValue.hashValue - } - } } private enum ChannelBannedMemberEntry: ItemListNodeEntry { @@ -223,25 +206,25 @@ private enum ChannelBannedMemberEntry: ItemListNodeEntry { func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem { let arguments = arguments as! ChannelBannedMemberControllerArguments switch self { - case let .info(theme, strings, dateTimeFormat, peer, presence): + case let .info(_, _, dateTimeFormat, peer, presence): return ItemListAvatarAndNameInfoItem(accountContext: arguments.context, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .generic, peer: peer, presence: presence, cachedData: nil, state: ItemListAvatarAndNameInfoItemState(), sectionId: self.section, style: .blocks(withTopInset: true, withExtendedBottomInset: false), editingNameUpdated: { _ in }, avatarTapped: { }) - case let .rightsHeader(theme, text): + case let .rightsHeader(_, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) - case let .rightItem(theme, _, text, right, value, enabled): + case let .rightItem(_, _, text, right, value, enabled): return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, type: .icon, enableInteractiveChanges: enabled, enabled: enabled, sectionId: self.section, style: .blocks, updated: { value in arguments.toggleRight(right, value) }, activatedWhileDisabled: { arguments.toggleRightWhileDisabled(right) }) - case let .timeout(theme, text, value): + case let .timeout(_, text, value): return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, action: { arguments.openTimeout() }) - case let .exceptionInfo(theme, text): + case let .exceptionInfo(_, text): return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) - case let .delete(theme, text): + case let .delete(_, text): return ItemListActionItem(presentationData: presentationData, title: text, kind: .destructive, alignment: .center, sectionId: self.section, style: .blocks, action: { arguments.delete() }) @@ -316,7 +299,7 @@ private func channelBannedMemberControllerEntries(presentationData: Presentation entries.append(.timeout(presentationData.theme, presentationData.strings.GroupPermission_Duration, currentTimeoutString)) - if let initialParticipant = initialParticipant, case let .member(member) = initialParticipant, let banInfo = member.banInfo, let initialBannedBy = initialBannedBy { + if let initialParticipant = initialParticipant, case let .member(_, _, _, banInfo?, _) = initialParticipant, let initialBannedBy = initialBannedBy { entries.append(.exceptionInfo(presentationData.theme, presentationData.strings.GroupPermission_AddedInfo(initialBannedBy.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), stringForRelativeSymbolicTimestamp(strings: presentationData.strings, relativeTimestamp: banInfo.timestamp, relativeTo: state.referenceTimestamp, dateTimeFormat: presentationData.dateTimeFormat)).string)) entries.append(.delete(presentationData.theme, presentationData.strings.GroupPermission_Delete)) } @@ -362,7 +345,7 @@ private func channelBannedMemberControllerEntries(presentationData: Presentation entries.append(.timeout(presentationData.theme, presentationData.strings.GroupPermission_Duration, currentTimeoutString)) - if let initialParticipant = initialParticipant, case let .member(member) = initialParticipant, let banInfo = member.banInfo, let initialBannedBy = initialBannedBy { + if let initialParticipant = initialParticipant, case let .member(_, _, _, banInfo?, _) = initialParticipant, let initialBannedBy = initialBannedBy { entries.append(.exceptionInfo(presentationData.theme, presentationData.strings.GroupPermission_AddedInfo(initialBannedBy.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), stringForRelativeSymbolicTimestamp(strings: presentationData.strings, relativeTimestamp: banInfo.timestamp, relativeTo: state.referenceTimestamp, dateTimeFormat: presentationData.dateTimeFormat)).string)) entries.append(.delete(presentationData.theme, presentationData.strings.GroupPermission_Delete)) } @@ -413,7 +396,7 @@ public func channelBannedMemberController(context: AccountContext, peerId: PeerI var effectiveRightsFlags: TelegramChatBannedRightsFlags if let updatedFlags = state.updatedFlags { effectiveRightsFlags = updatedFlags - } else if let initialParticipant = initialParticipant, case let .member(member) = initialParticipant, let banInfo = member.banInfo { + } else if let initialParticipant = initialParticipant, case let .member(_, _, _, banInfo?, _) = initialParticipant { effectiveRightsFlags = banInfo.rights.flags } else { effectiveRightsFlags = defaultBannedRightsFlags @@ -630,8 +613,8 @@ public func channelBannedMemberController(context: AccountContext, peerId: PeerI } var previousRights: TelegramChatBannedRights? - if let initialParticipant = initialParticipant, case let .member(member) = initialParticipant, member.banInfo != nil { - previousRights = member.banInfo?.rights + if let initialParticipant = initialParticipant, case let .member(_, _, _, banInfo, _) = initialParticipant, banInfo != nil { + previousRights = banInfo?.rights } if let resolvedRights = resolvedRights, previousRights != resolvedRights { @@ -692,11 +675,6 @@ public func channelBannedMemberController(context: AccountContext, peerId: PeerI } } }, error: { _ in - updateState { current in - var current = current - current.updating = false - return current - } })) } else { updateRightsDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: context.engine, peerId: peerId, memberId: memberId, bannedRights: cleanResolvedRights) @@ -736,7 +714,7 @@ public func channelBannedMemberController(context: AccountContext, peerId: PeerI } let title: String - if let initialParticipant = initialParticipant, case let .member(member) = initialParticipant, member.banInfo != nil { + if let initialParticipant = initialParticipant, case let .member(_, _, _, banInfo, _) = initialParticipant, banInfo != nil { title = presentationData.strings.GroupPermission_Title } else { title = presentationData.strings.GroupPermission_NewTitle diff --git a/submodules/PeerInfoUI/Sources/ChannelBlacklistController.swift b/submodules/PeerInfoUI/Sources/ChannelBlacklistController.swift index 16c1ce0581..190afe015c 100644 --- a/submodules/PeerInfoUI/Sources/ChannelBlacklistController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelBlacklistController.swift @@ -324,9 +324,7 @@ public func channelBlacklistController(context: AccountContext, peerId: PeerId) let progress = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil)) presentControllerImpl?(progress, nil) removePeerDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: context.engine, peerId: peerId, memberId: peer.id, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: Int32.max)) - |> deliverOnMainQueue).start(error: { [weak progress] _ in - progress?.dismiss() - dismissController?() + |> deliverOnMainQueue).start(error: { _ in }, completed: { [weak progress] in progress?.dismiss() dismissController?() @@ -343,9 +341,6 @@ public func channelBlacklistController(context: AccountContext, peerId: PeerId) } removePeerDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: context.engine, peerId: peerId, memberId: memberId, bannedRights: nil) |> deliverOnMainQueue).start(error: { _ in - updateState { - return $0.withUpdatedRemovingPeerId(nil) - } }, completed: { updateState { return $0.withUpdatedRemovingPeerId(nil) @@ -392,7 +387,6 @@ public func channelBlacklistController(context: AccountContext, peerId: PeerId) |> then( context.peerChannelMemberCategoriesContextsManager.addMember(engine: context.engine, peerId: peerId, memberId: memberId) |> map { _ -> Void in - return Void() } |> `catch` { _ -> Signal in return .complete() @@ -400,9 +394,6 @@ public func channelBlacklistController(context: AccountContext, peerId: PeerId) |> ignoreValues ) removePeerDisposable.set((signal |> deliverOnMainQueue).start(error: { _ in - updateState { - return $0.withUpdatedRemovingPeerId(nil) - } }, completed: { updateState { return $0.withUpdatedRemovingPeerId(nil) @@ -418,9 +409,6 @@ public func channelBlacklistController(context: AccountContext, peerId: PeerId) } removePeerDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: context.engine, peerId: peerId, memberId: memberId, bannedRights: nil) |> deliverOnMainQueue).start(error: { _ in - updateState { - return $0.withUpdatedRemovingPeerId(nil) - } }, completed: { updateState { return $0.withUpdatedRemovingPeerId(nil) diff --git a/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupActionSheetItem.swift b/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupActionSheetItem.swift index a485d192bd..10eb8591c4 100644 --- a/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupActionSheetItem.swift +++ b/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupActionSheetItem.swift @@ -65,8 +65,8 @@ private final class ChannelDiscussionGroupActionSheetItemNode: ActionSheetItemNo self.addSubnode(self.channelAvatarNode) self.addSubnode(self.textNode) - self.channelAvatarNode.setPeer(context: context, theme: (context.sharedContext.currentPresentationData.with { $0 }).theme, peer: channelPeer) - self.groupAvatarNode.setPeer(context: context, theme: (context.sharedContext.currentPresentationData.with { $0 }).theme, peer: groupPeer) + self.channelAvatarNode.setPeer(context: context, theme: (context.sharedContext.currentPresentationData.with { $0 }).theme, peer: EnginePeer(channelPeer)) + self.groupAvatarNode.setPeer(context: context, theme: (context.sharedContext.currentPresentationData.with { $0 }).theme, peer: EnginePeer(groupPeer)) let text: PresentationStrings.FormattedString if let channelPeer = channelPeer as? TelegramChannel, let addressName = channelPeer.addressName, !addressName.isEmpty { diff --git a/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSetupController.swift b/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSetupController.swift index ba557ede27..99fbab76ff 100644 --- a/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSetupController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSetupController.swift @@ -406,9 +406,6 @@ public func channelDiscussionGroupSetupController(context: AccountContext, peerI state.searching = false return state } - case .hasNotPermissions: - //TODO process error - break case .groupHistoryIsCurrentlyPrivate: let presentationData = context.sharedContext.currentPresentationData.with { $0 } presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Channel_DiscussionGroup_MakeHistoryPublic, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Channel_DiscussionGroup_MakeHistoryPublicProceed, action: { diff --git a/submodules/PeerInfoUI/Sources/ChannelMembersController.swift b/submodules/PeerInfoUI/Sources/ChannelMembersController.swift index aed5d89d34..d6f7991480 100644 --- a/submodules/PeerInfoUI/Sources/ChannelMembersController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelMembersController.swift @@ -40,32 +40,6 @@ private enum ChannelMembersSection: Int32 { private enum ChannelMembersEntryStableId: Hashable { case index(Int32) case peer(PeerId) - - var hashValue: Int { - switch self { - case let .index(index): - return index.hashValue - case let .peer(peerId): - return peerId.hashValue - } - } - - static func ==(lhs: ChannelMembersEntryStableId, rhs: ChannelMembersEntryStableId) -> Bool { - switch lhs { - case let .index(index): - if case .index(index) = rhs { - return true - } else { - return false - } - case let .peer(peerId): - if case .peer(peerId) = rhs { - return true - } else { - return false - } - } - } } private enum ChannelMembersEntry: ItemListNodeEntry { diff --git a/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift b/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift index 8035635042..412bd2d7df 100644 --- a/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift +++ b/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift @@ -779,8 +779,8 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon switch participant.participant { case .creator: label = presentationData.strings.Channel_Management_LabelOwner - case let .member(member): - if member.adminInfo != nil { + case let .member(_, _, adminInfo, _, _): + if adminInfo != nil { label = presentationData.strings.Channel_Management_LabelEditor } } @@ -930,7 +930,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon return entries } - } else if let group = peerView.peers[peerId] as? TelegramGroup, let cachedData = peerView.cachedData as? CachedGroupData { + } else if let _ = peerView.peers[peerId] as? TelegramGroup, let cachedData = peerView.cachedData as? CachedGroupData { updateActivity(true) let foundGroupMembers: Signal<[RenderedChannelParticipant], NoError> let foundMembers: Signal<[RenderedChannelParticipant], NoError> @@ -1043,39 +1043,6 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon section = .none } - var canPromote: Bool = false - var canRestrict: Bool = false - /*switch participant.participant { - case .creator: - canPromote = false - canRestrict = false - case let .member(_, _, adminRights, bannedRights): - if channel.hasPermission(.addAdmins) { - canPromote = true - } else { - canPromote = false - } - if channel.hasPermission(.banMembers) { - canRestrict = true - } else { - canRestrict = false - } - if canPromote { - if let bannedRights = bannedRights { - if bannedRights.restrictedBy != account.peerId && !channel.flags.contains(.isCreator) { - canPromote = false - } - } - } - if canRestrict { - if let adminRights = adminRights { - if adminRights.promotedBy != account.peerId && !channel.flags.contains(.isCreator) { - canRestrict = false - } - } - } - }*/ - var label: String? var enabled = true if case .banAndPromoteActions = mode { @@ -1087,8 +1054,8 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon switch participant.participant { case .creator: label = presentationData.strings.Channel_Management_LabelOwner - case let .member(member): - if member.adminInfo != nil { + case let .member(_, _, adminInfo, _, _): + if adminInfo != nil { label = presentationData.strings.Channel_Management_LabelEditor } } @@ -1098,17 +1065,6 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon enabled = false } - var peerActions: [ParticipantRevealAction] = [] - /*if case .searchMembers = mode { - if canPromote { - peerActions.append(ParticipantRevealAction(type: .neutral, title: presentationData.strings.GroupInfo_ActionPromote, action: .promote)) - } - if canRestrict { - peerActions.append(ParticipantRevealAction(type: .warning, title: presentationData.strings.GroupInfo_ActionRestrict, action: .restrict)) - peerActions.append(ParticipantRevealAction(type: .destructive, title: presentationData.strings.Common_Delete, action: .remove)) - } - }*/ - switch mode { case .searchAdmins: switch participant.participant { @@ -1155,7 +1111,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon default: break } - entries.append(ChannelMembersSearchEntry(index: index, content: .participant(participant: participant, label: label, revealActions: peerActions, revealed: state.revealedPeerId == RevealedPeerId(peerId: participant.peer.id, section: section), enabled: enabled), section: section, dateTimeFormat: presentationData.dateTimeFormat)) + entries.append(ChannelMembersSearchEntry(index: index, content: .participant(participant: participant, label: label, revealActions: [], revealed: state.revealedPeerId == RevealedPeerId(peerId: participant.peer.id, section: section), enabled: enabled), section: section, dateTimeFormat: presentationData.dateTimeFormat)) index += 1 } } diff --git a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift index 4af5f039cf..5d9f4a6433 100644 --- a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift @@ -680,11 +680,6 @@ public func channelPermissionsController(context: AccountContext, peerId origina removePeerDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: context.engine, peerId: peerId, memberId: memberId, bannedRights: nil) |> deliverOnMainQueue).start(error: { _ in - updateState { state in - var state = state - state.removingPeerId = nil - return state - } }, completed: { updateState { state in var state = state diff --git a/submodules/PeerInfoUI/Sources/ChatSlowmodeItem.swift b/submodules/PeerInfoUI/Sources/ChatSlowmodeItem.swift index 05a4025bdc..10bbb62297 100644 --- a/submodules/PeerInfoUI/Sources/ChatSlowmodeItem.swift +++ b/submodules/PeerInfoUI/Sources/ChatSlowmodeItem.swift @@ -251,7 +251,7 @@ class ChatSlowmodeItemNode: ListViewItemNode { let _ = apply() } - var textNodes: [(TextNode, CGSize)] = textLayoutAndApply.map { layout, apply -> (TextNode, CGSize) in + let textNodes: [(TextNode, CGSize)] = textLayoutAndApply.map { layout, apply -> (TextNode, CGSize) in let node = apply() return (node, layout.size) } diff --git a/submodules/PeerInfoUI/Sources/ConvertToSupergroupController.swift b/submodules/PeerInfoUI/Sources/ConvertToSupergroupController.swift index 5b4bcfe908..18fdb87649 100644 --- a/submodules/PeerInfoUI/Sources/ConvertToSupergroupController.swift +++ b/submodules/PeerInfoUI/Sources/ConvertToSupergroupController.swift @@ -79,13 +79,13 @@ private enum ConvertToSupergroupEntry: ItemListNodeEntry { func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem { let arguments = arguments as! ConvertToSupergroupArguments switch self { - case let .info(theme, text): + case let .info(_, text): return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section) - case let .action(theme, title): + case let .action(_, title): return ItemListActionItem(presentationData: presentationData, title: title, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { arguments.convert() }) - case let .actionInfo(theme, text): + case let .actionInfo(_, text): return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section) } } diff --git a/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift b/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift index d2c519b3b0..1ea61bf2a2 100644 --- a/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift +++ b/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift @@ -312,8 +312,8 @@ private enum DeviceContactInfoEntry: ItemListNodeEntry { } else { return false } - case let .address(lhsIndex, lhsCatIndex, lhsTheme, lhsTitle, lhsValue, lhsImageSignal, lhsSelected): - if case let .address(rhsIndex, rhsCatIndex, rhsTheme, rhsTitle, rhsValue, rhsImageSignal, rhsSelected) = rhs, lhsIndex == rhsIndex, lhsCatIndex == rhsCatIndex, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsValue == rhsValue, lhsSelected == rhsSelected { + case let .address(lhsIndex, lhsCatIndex, lhsTheme, lhsTitle, lhsValue, _, lhsSelected): + if case let .address(rhsIndex, rhsCatIndex, rhsTheme, rhsTitle, rhsValue, _, rhsSelected) = rhs, lhsIndex == rhsIndex, lhsCatIndex == rhsCatIndex, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsValue == rhsValue, lhsSelected == rhsSelected { return true } else { return false @@ -439,7 +439,7 @@ private enum DeviceContactInfoEntry: ItemListNodeEntry { }) case let .phoneNumberShareViaExceptionInfo(_, _, text): return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section) - case let .editingPhoneNumber(_, _, strings, id, title, label, value, hasActiveRevealControls): + case let .editingPhoneNumber(_, _, _, id, title, label, value, hasActiveRevealControls): return UserInfoEditingPhoneItem(presentationData: presentationData, id: id, label: title, value: value, editing: UserInfoEditingPhoneItemEditing(editable: true, hasActiveRevealControls: hasActiveRevealControls), sectionId: self.section, setPhoneIdWithRevealedOptions: { lhs, rhs in arguments.setPhoneIdWithRevealedOptions(lhs, rhs) }, updated: { value in @@ -1002,11 +1002,6 @@ public func deviceContactInfoController(context: AccountContext, subject: Device case .createContact: pushControllerImpl?(deviceContactInfoController(context: context, subject: .create(peer: subject.peer, contactData: subject.contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in dismissImpl?(false) - if let peer = peer { - - } else { - - } }), completed: nil, cancelled: nil)) case .addToExisting: addToExistingImpl?() @@ -1139,7 +1134,6 @@ public func deviceContactInfoController(context: AccountContext, subject: Device if share, let peer = peer { return context.engine.contacts.addContactInteractively(peerId: peer.id, firstName: composedContactData.basicData.firstName, lastName: composedContactData.basicData.lastName, phoneNumber: filteredPhoneNumbers.first?.value ?? "", addToPrivacyExceptions: shareViaException && addToPrivacyExceptions) |> mapToSignal { _ -> Signal<(DeviceContactStableId, DeviceContactExtendedData, Peer?)?, AddContactError> in - return .complete() } |> then( context.account.postbox.transaction { transaction -> (DeviceContactStableId, DeviceContactExtendedData, Peer?)? in @@ -1401,12 +1395,7 @@ func addContactOptionsController(context: AccountContext, peer: Peer?, contactDa controller.setItemGroups([ ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: presentationData.strings.Profile_CreateNewContact, action: { [weak controller] in - controller?.present(context.sharedContext.makeDeviceContactInfoController(context: context, subject: .create(peer: peer, contactData: contactData, isSharing: peer != nil, shareViaException: false, completion: { peer, stableId, contactData in - if let peer = peer { - - } else { - - } + controller?.present(context.sharedContext.makeDeviceContactInfoController(context: context, subject: .create(peer: peer, contactData: contactData, isSharing: peer != nil, shareViaException: false, completion: { _, _, _ in }), completed: nil, cancelled: nil), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) dismissAction() }), diff --git a/submodules/PeerInfoUI/Sources/GroupPreHistorySetupController.swift b/submodules/PeerInfoUI/Sources/GroupPreHistorySetupController.swift index 05aedd3fac..2efe6d5472 100644 --- a/submodules/PeerInfoUI/Sources/GroupPreHistorySetupController.swift +++ b/submodules/PeerInfoUI/Sources/GroupPreHistorySetupController.swift @@ -80,17 +80,17 @@ private enum GroupPreHistorySetupEntry: ItemListNodeEntry { func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem { let arguments = arguments as! GroupPreHistorySetupArguments switch self { - case let .header(theme, text): + case let .header(_, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) - case let .visible(theme, text, value): + case let .visible(_, text, value): return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: { arguments.toggle(true) }) - case let .hidden(theme, text, value): + case let .hidden(_, text, value): return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: { arguments.toggle(false) }) - case let .info(theme, text): + case let .info(_, text): return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section) } } diff --git a/submodules/PeerInfoUI/Sources/GroupStickerPackCurrentItem.swift b/submodules/PeerInfoUI/Sources/GroupStickerPackCurrentItem.swift index 886357fe9e..a793692dde 100644 --- a/submodules/PeerInfoUI/Sources/GroupStickerPackCurrentItem.swift +++ b/submodules/PeerInfoUI/Sources/GroupStickerPackCurrentItem.swift @@ -182,8 +182,8 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode { var file: TelegramMediaFile? var previousFile: TelegramMediaFile? - if let currentItem = currentItem, case let .found(found) = currentItem.content { - previousFile = found.topItem?.file + if let currentItem = currentItem, case let .found(_, topItem, _) = currentItem.content { + previousFile = topItem?.file } switch item.content { diff --git a/submodules/PeerInfoUI/Sources/GroupStickerPackSetupController.swift b/submodules/PeerInfoUI/Sources/GroupStickerPackSetupController.swift index b10071abf6..7dc0927e21 100644 --- a/submodules/PeerInfoUI/Sources/GroupStickerPackSetupController.swift +++ b/submodules/PeerInfoUI/Sources/GroupStickerPackSetupController.swift @@ -38,15 +38,6 @@ private enum GroupStickerPackEntryId: Hashable { case index(Int32) case pack(ItemCollectionId) - var hashValue: Int { - switch self { - case let .index(index): - return index.hashValue - case let .pack(id): - return id.hashValue - } - } - static func ==(lhs: GroupStickerPackEntryId, rhs: GroupStickerPackEntryId) -> Bool { switch lhs { case let .index(index): @@ -240,8 +231,8 @@ private enum GroupStickerPackEntry: ItemListNodeEntry { }) case let .currentPack(_, theme, strings, content): return GroupStickerPackCurrentItem(theme: theme, strings: strings, account: arguments.account, content: content, sectionId: self.section, action: { - if case let .found(found) = content { - arguments.openStickerPack(found.packInfo) + if case let .found(packInfo, _, _) = content { + arguments.openStickerPack(packInfo) } }) } diff --git a/submodules/PeerInfoUI/Sources/OldChannelsController.swift b/submodules/PeerInfoUI/Sources/OldChannelsController.swift index 4ccdc1b042..d77fd70ac2 100644 --- a/submodules/PeerInfoUI/Sources/OldChannelsController.swift +++ b/submodules/PeerInfoUI/Sources/OldChannelsController.swift @@ -244,7 +244,7 @@ private final class OldChannelsActionPanelNode: ASDisplayNode { transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: UIScreenPixel))) - self.buttonNode.updateLayout(width: layout.size.width - sideInset * 2.0, transition: transition) + let _ = self.buttonNode.updateLayout(width: layout.size.width - sideInset * 2.0, transition: transition) transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: sideInset, y: verticalInset), size: CGSize(width: layout.size.width, height: buttonHeight))) return buttonHeight + verticalInset * 2.0 + insets.bottom diff --git a/submodules/PeerInfoUI/Sources/PeerAutoremoveSetupScreen.swift b/submodules/PeerInfoUI/Sources/PeerAutoremoveSetupScreen.swift index 67ab548d2f..7f08935a61 100644 --- a/submodules/PeerInfoUI/Sources/PeerAutoremoveSetupScreen.swift +++ b/submodules/PeerInfoUI/Sources/PeerAutoremoveSetupScreen.swift @@ -117,7 +117,8 @@ private func peerAutoremoveSetupEntries(peer: Peer?, presentationData: Presentat var availableValues: [Int32] = [ Int32.max, 24 * 60 * 60, - 24 * 60 * 60 * 7 + 24 * 60 * 60 * 7, + 24 * 60 * 60 * 31, ] if isDebug { availableValues[1] = 5 diff --git a/submodules/PeerInfoUI/Sources/PeerAutoremoveTimeoutItem.swift b/submodules/PeerInfoUI/Sources/PeerAutoremoveTimeoutItem.swift index 1e47daee98..412b8bb725 100644 --- a/submodules/PeerInfoUI/Sources/PeerAutoremoveTimeoutItem.swift +++ b/submodules/PeerInfoUI/Sources/PeerAutoremoveTimeoutItem.swift @@ -130,7 +130,7 @@ class PeerRemoveTimeoutItemNode: ListViewItemNode, ItemListItemNode { self.disabledOverlayNode = ASDisplayNode() - self.titleNodes = (0 ..< 3).map { _ in + self.titleNodes = (0 ..< 4).map { _ in return TextNode() } @@ -150,9 +150,9 @@ class PeerRemoveTimeoutItemNode: ListViewItemNode, ItemListItemNode { sliderView.lineSize = 2.0 sliderView.dotSize = 5.0 sliderView.minimumValue = 0.0 - sliderView.maximumValue = 2.0 + sliderView.maximumValue = CGFloat(self.titleNodes.count - 1) sliderView.startValue = 0.0 - sliderView.positionsCount = 3 + sliderView.positionsCount = self.titleNodes.count sliderView.useLinesForPositions = true sliderView.minimumUndottedValue = 0 sliderView.disablesInteractiveTransitionGestureRecognizer = true @@ -195,7 +195,7 @@ class PeerRemoveTimeoutItemNode: ListViewItemNode, ItemListItemNode { if item.availableValues[index] == Int32.max { text = item.presentationData.strings.AutoremoveSetup_TimerValueNever } else { - text = item.presentationData.strings.AutoremoveSetup_TimerValueAfter(timeIntervalString(strings: item.presentationData.strings, value: item.availableValues[index])).string + text = timeIntervalString(strings: item.presentationData.strings, value: item.availableValues[index]) } return makeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: text, font: Font.regular(13.0), textColor: item.presentationData.theme.list.itemSecondaryTextColor), maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: 100.0))) } @@ -264,19 +264,24 @@ class PeerRemoveTimeoutItemNode: ListViewItemNode, ItemListItemNode { strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) - - zip(0 ..< titleLayouts.count, titleLayouts).forEach { index, layoutAndApply in - let textNode = layoutAndApply.1() - - let size = layoutAndApply.0.size - switch index { - case 0: - textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 13.0), size: size) - case 1: - textNode.frame = CGRect(origin: CGPoint(x: floor((params.width - size.width) / 2.0), y: 13.0), size: size) - default: - textNode.frame = CGRect(origin: CGPoint(x: params.width - leftInset - size.width, y: 13.0), size: size) + + let usableWidth = params.width - (leftInset + 7.0) * 2.0 + + for i in 0 ..< titleLayouts.count { + let textNode = titleLayouts[i].1() + + let size = titleLayouts[i].0.size + + let nextX: CGFloat + if i == 0 { + nextX = leftInset + } else if i == titleLayouts.count - 1 { + nextX = params.width - leftInset - size.width + } else { + nextX = floor(leftInset + 7.0 + CGFloat(i) * usableWidth / CGFloat(titleLayouts.count - 1) - size.width / 2.0) } + + textNode.frame = CGRect(origin: CGPoint(x: nextX, y: 13.0), size: size) } if let sliderView = strongSelf.sliderView { @@ -301,9 +306,8 @@ class PeerRemoveTimeoutItemNode: ListViewItemNode, ItemListItemNode { if firstTime { sliderView.value = value } - - let sliderInset: CGFloat = leftInset - sliderView.frame = CGRect(origin: CGPoint(x: sliderInset, y: 38.0), size: CGSize(width: params.width - sliderInset * 2.0, height: 44.0)) + + sliderView.frame = CGRect(origin: CGPoint(x: leftInset, y: 38.0), size: CGSize(width: params.width - leftInset * 2.0, height: 44.0)) } } }) diff --git a/submodules/PeerInfoUI/Sources/PeerBanTimeoutController.swift b/submodules/PeerInfoUI/Sources/PeerBanTimeoutController.swift index 9b2c85082d..c5f21ceb24 100644 --- a/submodules/PeerInfoUI/Sources/PeerBanTimeoutController.swift +++ b/submodules/PeerInfoUI/Sources/PeerBanTimeoutController.swift @@ -20,7 +20,6 @@ final class PeerBanTimeoutController: ActionSheetController { init(context: AccountContext, currentValue: Int32, applyValue: @escaping (Int32?) -> Void) { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let theme = presentationData.theme let strings = presentationData.strings super.init(theme: ActionSheetControllerTheme(presentationData: presentationData)) diff --git a/submodules/PeerInfoUI/Sources/PeerReportController.swift b/submodules/PeerInfoUI/Sources/PeerReportController.swift index 41d3965468..f61ca0c2d9 100644 --- a/submodules/PeerInfoUI/Sources/PeerReportController.swift +++ b/submodules/PeerInfoUI/Sources/PeerReportController.swift @@ -117,7 +117,7 @@ public func presentPeerReportOptions(context: AccountContext, parent: ViewContro displaySuccess() completion(nil, false) }) - case let .profilePhoto(peerId, photoId): + case let .profilePhoto(peerId, _): let _ = (context.engine.peers.reportPeerPhoto(peerId: peerId, reason: reportReason, message: "") |> deliverOnMainQueue).start(completed: { displaySuccess() @@ -243,7 +243,7 @@ public func peerReportOptionsController(context: AccountContext, subject: PeerRe displaySuccess() completion(nil, true) }) - case let .profilePhoto(peerId, photoId): + case let .profilePhoto(peerId, _): let _ = (context.engine.peers.reportPeerPhoto(peerId: peerId, reason: reportReason, message: message) |> deliverOnMainQueue).start(completed: { displaySuccess() diff --git a/submodules/PeerInfoUI/Sources/PhoneLabelController.swift b/submodules/PeerInfoUI/Sources/PhoneLabelController.swift index 1b7765e3b2..22bafbf8c8 100644 --- a/submodules/PeerInfoUI/Sources/PhoneLabelController.swift +++ b/submodules/PeerInfoUI/Sources/PhoneLabelController.swift @@ -59,7 +59,7 @@ private enum PhoneLabelEntry: ItemListNodeEntry { func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem { let arguments = arguments as! PhoneLabelArguments switch self { - case let .label(_, theme, value, text, selected): + case let .label(_, _, value, text, selected): return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: { arguments.selectLabel(value) }) diff --git a/submodules/PeerOnlineMarkerNode/BUILD b/submodules/PeerOnlineMarkerNode/BUILD index d00d4583a4..609546dfde 100644 --- a/submodules/PeerOnlineMarkerNode/BUILD +++ b/submodules/PeerOnlineMarkerNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/PeerPresenceStatusManager/BUILD b/submodules/PeerPresenceStatusManager/BUILD index afd5f24931..f82042b8dd 100644 --- a/submodules/PeerPresenceStatusManager/BUILD +++ b/submodules/PeerPresenceStatusManager/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/Postbox:Postbox", diff --git a/submodules/PeersNearbyIconNode/BUILD b/submodules/PeersNearbyIconNode/BUILD index 3c1dc898a9..bcb7f6d667 100644 --- a/submodules/PeersNearbyIconNode/BUILD +++ b/submodules/PeersNearbyIconNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/PeersNearbyUI/BUILD b/submodules/PeersNearbyUI/BUILD index aae93d7ed4..2350ad063c 100644 --- a/submodules/PeersNearbyUI/BUILD +++ b/submodules/PeersNearbyUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift b/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift index f7dbecbfad..0a9d4c1e5d 100644 --- a/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift +++ b/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift @@ -387,52 +387,8 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent } private func peerNearbyContextMenuItems(context: AccountContext, peerId: PeerId, present: @escaping (ViewController) -> Void) -> Signal<[ContextMenuItem], NoError> { - let presentationData = context.sharedContext.currentPresentationData.with({ $0 }) - return context.account.postbox.transaction { transaction -> [ContextMenuItem] in - var items: [ContextMenuItem] = [] -// -// let peer = transaction.getPeer(peerId) -// -// if let peer = peer as? TelegramUser { -// items.append(.action(ContextMenuActionItem(text: presentationData.strings.ChatList_Context_AddToContacts, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor) }, action: { _, f in -// f(.default) -// }))) -// } else { -// items.append(.action(ContextMenuActionItem(text: presentationData.strings.PeopleNearby_Context_JoinGroup, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor) }, action: { _, f in -// let _ = (joinChannel(account: context.account, peerId: peerId) |> deliverOnMainQueue).start(next: { participant in -// f(.default) -// }, error: { error in -//// if let strongSelf = self { -//// if case .tooMuchJoined = error { -//// if let parentNavigationController = strongSelf.parentNavigationController { -//// let context = strongSelf.context -//// let link = strongSelf.link -//// let navigateToPeer = strongSelf.navigateToPeer -//// let resolvedState = strongSelf.resolvedState -//// parentNavigationController.pushViewController(oldChannelsController(context: strongSelf.context, intent: .join, completed: { [weak parentNavigationController] value in -//// if value { -//// (parentNavigationController?.viewControllers.last as? ViewController)?.present(JoinLinkPreviewController(context: context, link: link, navigateToPeer: navigateToPeer, parentNavigationController: parentNavigationController, resolvedState: resolvedState), in: .window(.root)) -//// } -//// })) -//// } else { -//// strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Join_ChannelsTooMuch, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) -//// } -//// strongSelf.dismiss() -//// } -//// } -// }) -// }))) -// -// items.append(.action(ContextMenuActionItem(text: presentationData.strings.PeopleNearby_Context_UnrelatedLocation, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Report"), color: theme.contextMenu.primaryColor) }, action: { _, f in -// let _ = (TelegramCore.reportPeer(account: context.account, peerId: peerId, reason: .irrelevantLocation) -// |> deliverOnMainQueue).start(completed: { -// let _ = ApplicationSpecificNotice.setIrrelevantPeerGeoReport(postbox: context.account.postbox, peerId: peerId).start() -// -// present(textAlertController(context: context, title: nil, text: presentationData.strings.ReportPeer_AlertSuccess, actions: [TextAlertAction(type: TextAlertActionType.defaultAction, title: presentationData.strings.Common_OK, action: {})])) -// }) -// f(.default) -// }))) -// } + return context.account.postbox.transaction { _ -> [ContextMenuItem] in + let items: [ContextMenuItem] = [] return items } diff --git a/submodules/PersistentStringHash/BUILD b/submodules/PersistentStringHash/BUILD index 6ff7160fb5..e33565769f 100644 --- a/submodules/PersistentStringHash/BUILD +++ b/submodules/PersistentStringHash/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], visibility = [ "//visibility:public", ], diff --git a/submodules/PhoneInputNode/BUILD b/submodules/PhoneInputNode/BUILD index f16f32df24..3b4251f22e 100644 --- a/submodules/PhoneInputNode/BUILD +++ b/submodules/PhoneInputNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/PhoneInputNode/Sources/PhoneInputNode.swift b/submodules/PhoneInputNode/Sources/PhoneInputNode.swift index 68202bbe0a..6d9662852b 100644 --- a/submodules/PhoneInputNode/Sources/PhoneInputNode.swift +++ b/submodules/PhoneInputNode/Sources/PhoneInputNode.swift @@ -82,7 +82,7 @@ extension String { var pureNumber = self.replacingOccurrences( of: "[^0-9]", with: "", options: .regularExpression) for index in 0 ..< pattern.count { guard index < pureNumber.count else { return pureNumber } - let stringIndex = String.Index(encodedOffset: index) + let stringIndex = pattern.index(pattern.startIndex, offsetBy: index) let patternCharacter = pattern[stringIndex] guard patternCharacter != replacementCharacter else { continue } pureNumber.insert(patternCharacter, at: stringIndex) diff --git a/submodules/PhoneNumberFormat/BUILD b/submodules/PhoneNumberFormat/BUILD index 003cc11da5..678a034679 100644 --- a/submodules/PhoneNumberFormat/BUILD +++ b/submodules/PhoneNumberFormat/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/libphonenumber:libphonenumber", ], diff --git a/submodules/PhotoResources/BUILD b/submodules/PhotoResources/BUILD index 8fd840bad4..2a02b1e577 100644 --- a/submodules/PhotoResources/BUILD +++ b/submodules/PhotoResources/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramCore:TelegramCore", "//submodules/Postbox:Postbox", diff --git a/submodules/PhotoResources/Sources/PhotoResources.swift b/submodules/PhotoResources/Sources/PhotoResources.swift index 9c719cb833..0ea1c6c498 100644 --- a/submodules/PhotoResources/Sources/PhotoResources.swift +++ b/submodules/PhotoResources/Sources/PhotoResources.swift @@ -1745,8 +1745,7 @@ public func chatWebpageSnippetFileData(account: Account, fileReference: FileMedi let disposable = DisposableSet() disposable.add(resourceData.start(next: { data in subscriber.putNext(data) - }, error: { error in - subscriber.putError(error) + }, error: { _ in }, completed: { subscriber.putCompletion() })) @@ -1766,8 +1765,7 @@ public func chatWebpageSnippetPhotoData(account: Account, photoReference: ImageM let disposable = DisposableSet() disposable.add(resourceData.start(next: { data in subscriber.putNext(data) - }, error: { error in - subscriber.putError(error) + }, error: { _ in }, completed: { subscriber.putCompletion() })) @@ -2186,7 +2184,6 @@ public func chatMessageImageFile(account: Account, fileReference: FileMediaRefer public func instantPageImageFile(account: Account, fileReference: FileMediaReference, fetched: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { return chatMessageFileDatas(account: account, fileReference: fileReference, progressive: false, fetched: fetched) |> map { value in - let thumbnailData = value._0 let fullSizePath = value._1 let fullSizeComplete = value._2 return { arguments in diff --git a/submodules/PlatformRestrictionMatching/BUILD b/submodules/PlatformRestrictionMatching/BUILD index 38a989db14..aac02946cc 100644 --- a/submodules/PlatformRestrictionMatching/BUILD +++ b/submodules/PlatformRestrictionMatching/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramCore:TelegramCore", ], diff --git a/submodules/Postbox/Sources/ChatListTable.swift b/submodules/Postbox/Sources/ChatListTable.swift index 92170fe4c2..107c7860d5 100644 --- a/submodules/Postbox/Sources/ChatListTable.swift +++ b/submodules/Postbox/Sources/ChatListTable.swift @@ -110,7 +110,7 @@ private func addOperation(_ operation: ChatListOperation, groupId: PeerGroupId, } public enum ChatListNamespaceEntry { - case peer(index: ChatListIndex, readState: PeerReadState?, topMessageAttributes: [MessageAttribute], tagSummary: MessageHistoryTagNamespaceSummary?, interfaceState: PeerChatInterfaceState?) + case peer(index: ChatListIndex, readState: PeerReadState?, topMessageAttributes: [MessageAttribute], tagSummary: MessageHistoryTagNamespaceSummary?, interfaceState: StoredPeerChatInterfaceState?) case hole(MessageIndex) public var index: ChatListIndex { @@ -294,12 +294,12 @@ final class ChatListTable: Table { return result } - func replay(historyOperationsByPeerId: [PeerId: [MessageHistoryOperation]], updatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?], updatedChatListInclusions: [PeerId: PeerChatListInclusion], messageHistoryTable: MessageHistoryTable, peerChatInterfaceStateTable: PeerChatInterfaceStateTable, operations: inout [PeerGroupId: [ChatListOperation]]) { + func replay(historyOperationsByPeerId: [PeerId: [MessageHistoryOperation]], updatedPeerChatListEmbeddedStates: Set, updatedChatListInclusions: [PeerId: PeerChatListInclusion], messageHistoryTable: MessageHistoryTable, peerChatInterfaceStateTable: PeerChatInterfaceStateTable, operations: inout [PeerGroupId: [ChatListOperation]]) { var changedPeerIds = Set() for peerId in historyOperationsByPeerId.keys { changedPeerIds.insert(peerId) } - for peerId in updatedPeerChatListEmbeddedStates.keys { + for peerId in updatedPeerChatListEmbeddedStates { changedPeerIds.insert(peerId) } for peerId in updatedChatListInclusions.keys { @@ -315,19 +315,19 @@ final class ChatListTable: Table { } let topMessage = messageHistoryTable.topIndex(peerId: peerId) - let embeddedChatState = peerChatInterfaceStateTable.get(peerId)?.chatListEmbeddedState + let embeddedChatStateOverrideTimestamp = peerChatInterfaceStateTable.get(peerId)?.overrideChatTimestamp let rawTopMessageIndex: MessageIndex? let topMessageIndex: MessageIndex? if let topMessage = topMessage { var updatedTimestamp = topMessage.timestamp rawTopMessageIndex = MessageIndex(id: topMessage.id, timestamp: topMessage.timestamp) - if let embeddedChatState = embeddedChatState { - updatedTimestamp = max(updatedTimestamp, embeddedChatState.timestamp) + if let embeddedChatStateOverrideTimestamp = embeddedChatStateOverrideTimestamp { + updatedTimestamp = max(updatedTimestamp, embeddedChatStateOverrideTimestamp) } topMessageIndex = MessageIndex(id: topMessage.id, timestamp: updatedTimestamp) - } else if let embeddedChatState = embeddedChatState, embeddedChatState.timestamp != 0 { - topMessageIndex = MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 1), timestamp: embeddedChatState.timestamp) + } else if let embeddedChatStateOverrideTimestamp = embeddedChatStateOverrideTimestamp, embeddedChatStateOverrideTimestamp != 0 { + topMessageIndex = MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 1), timestamp: embeddedChatStateOverrideTimestamp) rawTopMessageIndex = nil } else { topMessageIndex = nil diff --git a/submodules/Postbox/Sources/ChatListView.swift b/submodules/Postbox/Sources/ChatListView.swift index 772deea1f9..4c32c8dce4 100644 --- a/submodules/Postbox/Sources/ChatListView.swift +++ b/submodules/Postbox/Sources/ChatListView.swift @@ -88,7 +88,7 @@ public struct ChatListGroupReferenceEntry: Equatable { } public enum ChatListEntry: Comparable { - case MessageEntry(index: ChatListIndex, messages: [Message], readState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, hasFailed: Bool, isContact: Bool) + case MessageEntry(index: ChatListIndex, messages: [Message], readState: CombinedPeerReadState?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: StoredPeerChatInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, hasFailed: Bool, isContact: Bool) case HoleEntry(ChatListHole) public var index: ChatListIndex { @@ -123,7 +123,7 @@ public enum ChatListEntry: Comparable { return false } if let lhsEmbeddedState = lhsEmbeddedState, let rhsEmbeddedState = rhsEmbeddedState { - if !lhsEmbeddedState.isEqual(to: rhsEmbeddedState) { + if lhsEmbeddedState != rhsEmbeddedState { return false } } else if (lhsEmbeddedState != nil) != (rhsEmbeddedState != nil) { @@ -185,7 +185,7 @@ public enum ChatListEntry: Comparable { enum MutableChatListEntry: Equatable { case IntermediateMessageEntry(index: ChatListIndex, messageIndex: MessageIndex?) - case MessageEntry(index: ChatListIndex, messages: [Message], readState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, tagSummaryInfo: ChatListMessageTagSummaryInfo, hasFailedMessages: Bool, isContact: Bool) + case MessageEntry(index: ChatListIndex, messages: [Message], readState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, isRemovedFromTotalUnreadCount: Bool, embeddedInterfaceState: StoredPeerChatInterfaceState?, renderedPeer: RenderedPeer, presence: PeerPresence?, tagSummaryInfo: ChatListMessageTagSummaryInfo, hasFailedMessages: Bool, isContact: Bool) case HoleEntry(ChatListHole) init(_ intermediateEntry: ChatListIntermediateEntry, cachedDataTable: CachedPeerDataTable, readStateTable: MessageHistoryReadStateTable, messageHistoryTable: MessageHistoryTable) { @@ -591,7 +591,7 @@ final class MutableChatListView { let tagSummaryCount: Int32? = nil let actionsSummaryCount: Int32? = nil - return .MessageEntry(index: index, messages: renderedMessages, readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: false, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId)?.chatListEmbeddedState, renderedPeer: RenderedPeer(peerId: index.messageIndex.id.peerId, peers: peers), presence: presence, tagSummaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: tagSummaryCount, actionsSummaryCount: actionsSummaryCount), hasFailedMessages: postbox.messageHistoryFailedTable.contains(peerId: index.messageIndex.id.peerId), isContact: isContact) + return .MessageEntry(index: index, messages: renderedMessages, readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: false, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId), renderedPeer: RenderedPeer(peerId: index.messageIndex.id.peerId, peers: peers), presence: presence, tagSummaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: tagSummaryCount, actionsSummaryCount: actionsSummaryCount), hasFailedMessages: postbox.messageHistoryFailedTable.contains(peerId: index.messageIndex.id.peerId), isContact: isContact) default: return nil } diff --git a/submodules/Postbox/Sources/ChatListViewState.swift b/submodules/Postbox/Sources/ChatListViewState.swift index 7e8d365f44..02b160bae6 100644 --- a/submodules/Postbox/Sources/ChatListViewState.swift +++ b/submodules/Postbox/Sources/ChatListViewState.swift @@ -1394,7 +1394,7 @@ struct ChatListViewState { } } - let updatedEntry: MutableChatListEntry = .MessageEntry(index: index, messages: renderedMessages, readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId)?.chatListEmbeddedState, renderedPeer: renderedPeer, presence: presence, tagSummaryInfo: tagSummaryInfo, hasFailedMessages: false, isContact: postbox.contactsTable.isContact(peerId: index.messageIndex.id.peerId)) + let updatedEntry: MutableChatListEntry = .MessageEntry(index: index, messages: renderedMessages, readState: postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId), notificationSettings: notificationSettings, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, embeddedInterfaceState: postbox.peerChatInterfaceStateTable.get(index.messageIndex.id.peerId), renderedPeer: renderedPeer, presence: presence, tagSummaryInfo: tagSummaryInfo, hasFailedMessages: false, isContact: postbox.contactsTable.isContact(peerId: index.messageIndex.id.peerId)) if directionIndex == 0 { self.stateBySpace[space]!.orderedEntries.setLowerOrAtAnchorAtArrayIndex(listIndex, to: updatedEntry) } else { diff --git a/submodules/Postbox/Sources/Coding.swift b/submodules/Postbox/Sources/Coding.swift index 26de86c051..33edb55996 100644 --- a/submodules/Postbox/Sources/Coding.swift +++ b/submodules/Postbox/Sources/Coding.swift @@ -215,7 +215,7 @@ public final class ReadBuffer: MemoryBuffer { } } -private enum ValueType: Int8 { +enum ValueType: Int8 { case Int32 = 0 case Int64 = 1 case Bool = 2 @@ -258,35 +258,21 @@ public final class PostboxEncoder { self.buffer.reset() } - public func encodeKey(_ key: StaticString) { - var length: Int8 = Int8(key.utf8CodeUnitCount) - self.buffer.write(&length, offset: 0, length: 1) - self.buffer.write(key.utf8Start, offset: 0, length: Int(length)) - } - public func encodeKey(_ key: String) { let data = key.data(using: .utf8)! - data.withUnsafeBytes { (keyBytes: UnsafePointer) -> Void in - var length: Int8 = Int8(data.count) - self.buffer.write(&length, offset: 0, length: 1) - self.buffer.write(keyBytes, offset: 0, length: Int(length)) + var length: Int8 = Int8(data.count) + self.buffer.write(&length, offset: 0, length: 1) + data.withUnsafeBytes { bytes in + self.buffer.write(bytes.baseAddress!, offset: 0, length: Int(length)) } } - public func encodeNil(forKey key: StaticString) { + public func encodeNil(forKey key: String) { self.encodeKey(key) var type: Int8 = ValueType.Nil.rawValue self.buffer.write(&type, offset: 0, length: 1) } - public func encodeInt32(_ value: Int32, forKey key: StaticString) { - self.encodeKey(key) - var type: Int8 = ValueType.Int32.rawValue - self.buffer.write(&type, offset: 0, length: 1) - var v = value - self.buffer.write(&v, offset: 0, length: 4) - } - public func encodeInt32(_ value: Int32, forKey key: String) { self.encodeKey(key) var type: Int8 = ValueType.Int32.rawValue @@ -295,7 +281,7 @@ public final class PostboxEncoder { self.buffer.write(&v, offset: 0, length: 4) } - public func encodeInt64(_ value: Int64, forKey key: StaticString) { + public func encodeInt64(_ value: Int64, forKey key: String) { self.encodeKey(key) var type: Int8 = ValueType.Int64.rawValue self.buffer.write(&type, offset: 0, length: 1) @@ -303,7 +289,7 @@ public final class PostboxEncoder { self.buffer.write(&v, offset: 0, length: 8) } - public func encodeBool(_ value: Bool, forKey key: StaticString) { + public func encodeBool(_ value: Bool, forKey key: String) { self.encodeKey(key) var type: Int8 = ValueType.Bool.rawValue self.buffer.write(&type, offset: 0, length: 1) @@ -311,7 +297,7 @@ public final class PostboxEncoder { self.buffer.write(&v, offset: 0, length: 1) } - public func encodeDouble(_ value: Double, forKey key: StaticString) { + public func encodeDouble(_ value: Double, forKey key: String) { self.encodeKey(key) var type: Int8 = ValueType.Double.rawValue self.buffer.write(&type, offset: 0, length: 1) @@ -319,7 +305,7 @@ public final class PostboxEncoder { self.buffer.write(&v, offset: 0, length: 8) } - public func encodeString(_ value: String, forKey key: StaticString) { + public func encodeString(_ value: String, forKey key: String) { self.encodeKey(key) var type: Int8 = ValueType.String.rawValue self.buffer.write(&type, offset: 0, length: 1) @@ -337,13 +323,13 @@ public final class PostboxEncoder { self.encodeObject(value, forKey: "_") } - public func encodeCodable(_ value: T, forKey key: StaticString) { + public func encodeCodable(_ value: T, forKey key: String) { if let data = try? JSONEncoder().encode(value) { self.encodeData(data, forKey: key) } } - public func encodeObject(_ value: PostboxCoding, forKey key: StaticString) { + public func encodeObject(_ value: PostboxCoding, forKey key: String) { self.encodeKey(key) var t: Int8 = ValueType.Object.rawValue self.buffer.write(&t, offset: 0, length: 1) @@ -377,7 +363,7 @@ public final class PostboxEncoder { self.buffer.write(innerEncoder.buffer.memory, offset: 0, length: Int(length)) } - public func encodeInt32Array(_ value: [Int32], forKey key: StaticString) { + public func encodeInt32Array(_ value: [Int32], forKey key: String) { self.encodeKey(key) var type: Int8 = ValueType.Int32Array.rawValue self.buffer.write(&type, offset: 0, length: 1) @@ -389,7 +375,7 @@ public final class PostboxEncoder { } } - public func encodeInt64Array(_ value: [Int64], forKey key: StaticString) { + public func encodeInt64Array(_ value: [Int64], forKey key: String) { self.encodeKey(key) var type: Int8 = ValueType.Int64Array.rawValue self.buffer.write(&type, offset: 0, length: 1) @@ -401,7 +387,7 @@ public final class PostboxEncoder { } } - public func encodeObjectArray(_ value: [T], forKey key: StaticString) { + public func encodeObjectArray(_ value: [T], forKey key: String) { self.encodeKey(key) var t: Int8 = ValueType.ObjectArray.rawValue self.buffer.write(&t, offset: 0, length: 1) @@ -421,7 +407,7 @@ public final class PostboxEncoder { } } - public func encodeObjectArrayWithEncoder(_ value: [T], forKey key: StaticString, encoder: (T, PostboxEncoder) -> Void) { + public func encodeObjectArrayWithEncoder(_ value: [T], forKey key: String, encoder: (T, PostboxEncoder) -> Void) { self.encodeKey(key) var t: Int8 = ValueType.ObjectArray.rawValue self.buffer.write(&t, offset: 0, length: 1) @@ -441,7 +427,7 @@ public final class PostboxEncoder { } } - public func encodeGenericObjectArray(_ value: [PostboxCoding], forKey key: StaticString) { + public func encodeGenericObjectArray(_ value: [PostboxCoding], forKey key: String) { self.encodeKey(key) var t: Int8 = ValueType.ObjectArray.rawValue self.buffer.write(&t, offset: 0, length: 1) @@ -461,7 +447,7 @@ public final class PostboxEncoder { } } - public func encodeStringArray(_ value: [String], forKey key: StaticString) { + public func encodeStringArray(_ value: [String], forKey key: String) { self.encodeKey(key) var type: Int8 = ValueType.StringArray.rawValue self.buffer.write(&type, offset: 0, length: 1) @@ -476,7 +462,7 @@ public final class PostboxEncoder { } } - public func encodeBytesArray(_ value: [MemoryBuffer], forKey key: StaticString) { + public func encodeBytesArray(_ value: [MemoryBuffer], forKey key: String) { self.encodeKey(key) var type: Int8 = ValueType.BytesArray.rawValue self.buffer.write(&type, offset: 0, length: 1) @@ -490,7 +476,7 @@ public final class PostboxEncoder { } } - public func encodeDataArray(_ value: [Data], forKey key: StaticString) { + public func encodeDataArray(_ value: [Data], forKey key: String) { self.encodeKey(key) var type: Int8 = ValueType.BytesArray.rawValue self.buffer.write(&type, offset: 0, length: 1) @@ -506,7 +492,7 @@ public final class PostboxEncoder { } } - public func encodeObjectDictionary(_ value: [K : V], forKey key: StaticString) where K: PostboxCoding { + public func encodeObjectDictionary(_ value: [K : V], forKey key: String) where K: PostboxCoding { self.encodeKey(key) var t: Int8 = ValueType.ObjectDictionary.rawValue self.buffer.write(&t, offset: 0, length: 1) @@ -533,7 +519,7 @@ public final class PostboxEncoder { } } - public func encodeObjectDictionary(_ value: [K : V], forKey key: StaticString, keyEncoder: (K, PostboxEncoder) -> Void) { + public func encodeObjectDictionary(_ value: [K : V], forKey key: String, keyEncoder: (K, PostboxEncoder) -> Void) { self.encodeKey(key) var t: Int8 = ValueType.ObjectDictionary.rawValue self.buffer.write(&t, offset: 0, length: 1) @@ -560,7 +546,7 @@ public final class PostboxEncoder { } } - public func encodeBytes(_ bytes: WriteBuffer, forKey key: StaticString) { + public func encodeBytes(_ bytes: WriteBuffer, forKey key: String) { self.encodeKey(key) var type: Int8 = ValueType.Bytes.rawValue self.buffer.write(&type, offset: 0, length: 1) @@ -569,7 +555,7 @@ public final class PostboxEncoder { self.buffer.write(bytes.memory, offset: 0, length: bytes.offset) } - public func encodeBytes(_ bytes: ReadBuffer, forKey key: StaticString) { + public func encodeBytes(_ bytes: ReadBuffer, forKey key: String) { self.encodeKey(key) var type: Int8 = ValueType.Bytes.rawValue self.buffer.write(&type, offset: 0, length: 1) @@ -578,7 +564,7 @@ public final class PostboxEncoder { self.buffer.write(bytes.memory, offset: 0, length: bytes.offset) } - public func encodeBytes(_ bytes: MemoryBuffer, forKey key: StaticString) { + public func encodeBytes(_ bytes: MemoryBuffer, forKey key: String) { self.encodeKey(key) var type: Int8 = ValueType.Bytes.rawValue self.buffer.write(&type, offset: 0, length: 1) @@ -587,7 +573,7 @@ public final class PostboxEncoder { self.buffer.write(bytes.memory, offset: 0, length: bytes.length) } - public func encodeData(_ data: Data, forKey key: StaticString) { + public func encodeData(_ data: Data, forKey key: String) { self.encodeKey(key) var type: Int8 = ValueType.Bytes.rawValue self.buffer.write(&type, offset: 0, length: 1) @@ -598,6 +584,24 @@ public final class PostboxEncoder { } } + public func encode(_ value: T, forKey key: String) { + let typeHash: Int32 = murMurHashString32("\(type(of: value))") + let innerEncoder = _AdaptedPostboxEncoder(typeHash: typeHash) + try! value.encode(to: innerEncoder) + + let (data, valueType) = innerEncoder.makeData(addHeader: true) + self.encodeInnerObjectData(data, valueType: valueType, forKey: key) + } + + func encodeInnerObjectData(_ value: Data, valueType: ValueType, forKey key: String) { + self.encodeKey(key) + + var t: Int8 = valueType.rawValue + self.buffer.write(&t, offset: 0, length: 1) + + self.buffer.write(value) + } + public let sharedWriteBuffer = WriteBuffer() } @@ -680,41 +684,65 @@ public final class PostboxDecoder { } } } - - private class func positionOnKey(_ rawBytes: UnsafeRawPointer, offset: inout Int, maxOffset: Int, length: Int, key: StaticString, valueType: ValueType) -> Bool + + private class func positionOnKey(_ rawBytes: UnsafeRawPointer, offset: inout Int, maxOffset: Int, length: Int, key: String, valueType: ValueType) -> Bool { + var actualValueType: ValueType = .Nil + return positionOnKey(rawBytes, offset: &offset, maxOffset: maxOffset, length: length, key: key, valueType: valueType, actualValueType: &actualValueType, consumeKey: true) + } + + private class func positionOnKey(_ rawBytes: UnsafeRawPointer, offset: inout Int, maxOffset: Int, length: Int, key: String, valueType: ValueType?, actualValueType: inout ValueType, consumeKey: Bool) -> Bool { let bytes = rawBytes.assumingMemoryBound(to: Int8.self) - + let startOffset = offset - - let keyLength: Int = key.utf8CodeUnitCount + + let keyData = key.data(using: .utf8)! + + let keyLength: Int = keyData.count while (offset < maxOffset) { + let keyOffset = offset let readKeyLength = bytes[offset] assert(readKeyLength >= 0) offset += 1 offset += Int(readKeyLength) - + let readValueType = bytes[offset] offset += 1 - - if keyLength == Int(readKeyLength) && memcmp(bytes + (offset - Int(readKeyLength) - 1), key.utf8Start, keyLength) == 0 { - if readValueType == valueType.rawValue { - return true - } else if readValueType == ValueType.Nil.rawValue { - return false + + if keyLength != Int(readKeyLength) { + skipValue(bytes, offset: &offset, length: length, valueType: ValueType(rawValue: readValueType)!) + continue + } + + if keyData.withUnsafeBytes({ keyBytes -> Bool in + return memcmp(bytes + (offset - Int(readKeyLength) - 1), keyBytes.baseAddress!, keyLength) == 0 + }) { + if let valueType = valueType { + if readValueType == valueType.rawValue { + actualValueType = valueType + return true + } else if readValueType == ValueType.Nil.rawValue { + return false + } else { + skipValue(bytes, offset: &offset, length: length, valueType: ValueType(rawValue: readValueType)!) + } } else { - skipValue(bytes, offset: &offset, length: length, valueType: ValueType(rawValue: readValueType)!) + if !consumeKey { + offset = keyOffset + } + actualValueType = ValueType(rawValue: readValueType)! + return true } } else { skipValue(bytes, offset: &offset, length: length, valueType: ValueType(rawValue: readValueType)!) } } - + if (startOffset != 0) { offset = 0 - return positionOnKey(bytes, offset: &offset, maxOffset: startOffset, length: length, key: key, valueType: valueType) + return positionOnKey(bytes, offset: &offset, maxOffset: startOffset, length: length, key: key, valueType: valueType, actualValueType: &actualValueType, consumeKey: consumeKey) } - + return false } @@ -789,20 +817,26 @@ public final class PostboxDecoder { return false } - - public func decodeInt32ForKey(_ key: StaticString, orElse: Int32) -> Int32 { - if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32) { - var value: Int32 = 0 - memcpy(&value, self.buffer.memory + self.offset, 4) - self.offset += 4 - return value + + public func containsKey(_ key: String) -> Bool { + var actualValueType: ValueType = .Nil + if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: nil, actualValueType: &actualValueType, consumeKey: false) { + return true } else { - return orElse + return false + } + } + + public func decodeNilForKey(_ key: String) -> Bool { + if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Nil) { + return true + } else { + return false } } public func decodeInt32ForKey(_ key: String, orElse: Int32) -> Int32 { - if PostboxDecoder.positionOnStringKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32) { + if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32) { var value: Int32 = 0 memcpy(&value, self.buffer.memory + self.offset, 4) self.offset += 4 @@ -812,7 +846,7 @@ public final class PostboxDecoder { } } - public func decodeOptionalInt32ForKey(_ key: StaticString) -> Int32? { + public func decodeOptionalInt32ForKey(_ key: String) -> Int32? { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32) { var value: Int32 = 0 memcpy(&value, self.buffer.memory + self.offset, 4) @@ -823,18 +857,7 @@ public final class PostboxDecoder { } } - public func decodeOptionalInt32ForKey(_ key: String) -> Int32? { - if PostboxDecoder.positionOnStringKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32) { - var value: Int32 = 0 - memcpy(&value, self.buffer.memory + self.offset, 4) - self.offset += 4 - return value - } else { - return nil - } - } - - public func decodeInt64ForKey(_ key: StaticString, orElse: Int64) -> Int64 { + public func decodeInt64ForKey(_ key: String, orElse: Int64) -> Int64 { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int64) { var value: Int64 = 0 memcpy(&value, self.buffer.memory + self.offset, 8) @@ -845,7 +868,7 @@ public final class PostboxDecoder { } } - public func decodeOptionalInt64ForKey(_ key: StaticString) -> Int64? { + public func decodeOptionalInt64ForKey(_ key: String) -> Int64? { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int64) { var value: Int64 = 0 memcpy(&value, self.buffer.memory + self.offset, 8) @@ -856,7 +879,7 @@ public final class PostboxDecoder { } } - public func decodeBoolForKey(_ key: StaticString, orElse: Bool) -> Bool { + public func decodeBoolForKey(_ key: String, orElse: Bool) -> Bool { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Bool) { var value: Int8 = 0 memcpy(&value, self.buffer.memory + self.offset, 1) @@ -867,7 +890,7 @@ public final class PostboxDecoder { } } - public func decodeOptionalBoolForKey(_ key: StaticString) -> Bool? { + public func decodeOptionalBoolForKey(_ key: String) -> Bool? { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Bool) { var value: Int8 = 0 memcpy(&value, self.buffer.memory + self.offset, 1) @@ -878,7 +901,7 @@ public final class PostboxDecoder { } } - public func decodeDoubleForKey(_ key: StaticString, orElse: Double) -> Double { + public func decodeDoubleForKey(_ key: String, orElse: Double) -> Double { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Double) { var value: Double = 0 memcpy(&value, self.buffer.memory + self.offset, 8) @@ -889,7 +912,7 @@ public final class PostboxDecoder { } } - public func decodeOptionalDoubleForKey(_ key: StaticString) -> Double? { + public func decodeOptionalDoubleForKey(_ key: String) -> Double? { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Double) { var value: Double = 0 memcpy(&value, self.buffer.memory + self.offset, 8) @@ -900,7 +923,7 @@ public final class PostboxDecoder { } } - public func decodeStringForKey(_ key: StaticString, orElse: String) -> String { + public func decodeStringForKey(_ key: String, orElse: String) -> String { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .String) { var length: Int32 = 0 memcpy(&length, self.buffer.memory + self.offset, 4) @@ -912,7 +935,7 @@ public final class PostboxDecoder { } } - public func decodeOptionalStringForKey(_ key: StaticString) -> String? { + public func decodeOptionalStringForKey(_ key: String) -> String? { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .String) { var length: Int32 = 0 memcpy(&length, self.buffer.memory + self.offset, 4) @@ -928,7 +951,7 @@ public final class PostboxDecoder { return self.decodeObjectForKey("_") } - public func decodeCodable(_ type: T.Type, forKey key: StaticString) -> T? { + public func decodeCodable(_ type: T.Type, forKey key: String) -> T? { if let data = self.decodeDataForKey(key) { return try? JSONDecoder().decode(T.self, from: data) } else { @@ -936,7 +959,7 @@ public final class PostboxDecoder { } } - public func decodeObjectForKey(_ key: StaticString) -> PostboxCoding? { + public func decodeObjectForKey(_ key: String) -> PostboxCoding? { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Object) { var typeHash: Int32 = 0 memcpy(&typeHash, self.buffer.memory + self.offset, 4) @@ -953,8 +976,35 @@ public final class PostboxDecoder { return nil } } + + func decodeObjectDataForKey(_ key: String) -> (Data, ValueType)? { + var actualValueType: ValueType = .Nil + if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: nil, actualValueType: &actualValueType, consumeKey: true) { + if case .Object = actualValueType { + self.offset += 4 + + var length: Int32 = 0 + memcpy(&length, self.buffer.memory + self.offset, 4) + self.offset += 4 + + let innerData = ReadBuffer(memory: self.buffer.memory + self.offset, length: Int(length), freeWhenDone: false).makeData() + self.offset += Int(length) + + return (innerData, actualValueType) + } else { + let initialOffset = self.offset + PostboxDecoder.skipValue(self.buffer.memory.assumingMemoryBound(to: Int8.self), offset: &self.offset, length: self.buffer.length, valueType: actualValueType) + + let data = ReadBuffer(memory: UnsafeMutableRawPointer(mutating: self.buffer.memory.advanced(by: initialOffset)), length: self.offset - initialOffset, freeWhenDone: false).makeData() + + return (data, actualValueType) + } + } else { + return nil + } + } - public func decodeObjectForKey(_ key: StaticString, decoder: (PostboxDecoder) -> PostboxCoding) -> PostboxCoding? { + public func decodeObjectForKey(_ key: String, decoder: (PostboxDecoder) -> PostboxCoding) -> PostboxCoding? { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Object) { var typeHash: Int32 = 0 memcpy(&typeHash, self.buffer.memory + self.offset, 4) @@ -972,7 +1022,7 @@ public final class PostboxDecoder { } } - public func decodeAnyObjectForKey(_ key: StaticString, decoder: (PostboxDecoder) -> Any?) -> Any? { + public func decodeAnyObjectForKey(_ key: String, decoder: (PostboxDecoder) -> Any?) -> Any? { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Object) { var typeHash: Int32 = 0 memcpy(&typeHash, self.buffer.memory + self.offset, 4) @@ -990,7 +1040,7 @@ public final class PostboxDecoder { } } - public func decodeObjectForKeyThrowing(_ key: StaticString, decoder: (PostboxDecoder) throws -> Any) throws -> Any? { + public func decodeObjectForKeyThrowing(_ key: String, decoder: (PostboxDecoder) throws -> Any) throws -> Any? { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Object) { var typeHash: Int32 = 0 memcpy(&typeHash, self.buffer.memory + self.offset, 4) @@ -1008,47 +1058,55 @@ public final class PostboxDecoder { } } - public func decodeInt32ArrayForKey(_ key: StaticString) -> [Int32] { + public func decodeInt32ArrayForKey(_ key: String) -> [Int32] { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32Array) { - var length: Int32 = 0 - memcpy(&length, self.buffer.memory + self.offset, 4) - var array: [Int32] = [] - array.reserveCapacity(Int(length)) - var i: Int32 = 0 - while i < length { - var element: Int32 = 0 - memcpy(&element, self.buffer.memory + (self.offset + 4 + 4 * Int(i)), 4) - array.append(element) - i += 1 - } - self.offset += 4 + Int(length) * 4 - return array + return decodeInt32ArrayRaw() } else { return [] } } + + func decodeInt32ArrayRaw() -> [Int32] { + var length: Int32 = 0 + memcpy(&length, self.buffer.memory + self.offset, 4) + var array: [Int32] = [] + array.reserveCapacity(Int(length)) + var i: Int32 = 0 + while i < length { + var element: Int32 = 0 + memcpy(&element, self.buffer.memory + (self.offset + 4 + 4 * Int(i)), 4) + array.append(element) + i += 1 + } + self.offset += 4 + Int(length) * 4 + return array + } - public func decodeInt64ArrayForKey(_ key: StaticString) -> [Int64] { + public func decodeInt64ArrayForKey(_ key: String) -> [Int64] { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int64Array) { - var length: Int32 = 0 - memcpy(&length, self.buffer.memory + self.offset, 4) - var array: [Int64] = [] - array.reserveCapacity(Int(length)) - var i: Int32 = 0 - while i < length { - var element: Int64 = 0 - memcpy(&element, self.buffer.memory + (self.offset + 4 + 8 * Int(i)), 8) - array.append(element) - i += 1 - } - self.offset += 4 + Int(length) * 8 - return array + return decodeInt64ArrayRaw() } else { return [] } } + + func decodeInt64ArrayRaw() -> [Int64] { + var length: Int32 = 0 + memcpy(&length, self.buffer.memory + self.offset, 4) + var array: [Int64] = [] + array.reserveCapacity(Int(length)) + var i: Int32 = 0 + while i < length { + var element: Int64 = 0 + memcpy(&element, self.buffer.memory + (self.offset + 4 + 8 * Int(i)), 8) + array.append(element) + i += 1 + } + self.offset += 4 + Int(length) * 8 + return array + } - public func decodeObjectArrayWithDecoderForKey(_ key: StaticString) -> [T] where T: PostboxCoding { + public func decodeObjectArrayWithDecoderForKey(_ key: String) -> [T] where T: PostboxCoding { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .ObjectArray) { var length: Int32 = 0 memcpy(&length, self.buffer.memory + self.offset, 4) @@ -1080,7 +1138,7 @@ public final class PostboxDecoder { } } - public func decodeOptionalObjectArrayWithDecoderForKey(_ key: StaticString) -> [T]? where T: PostboxCoding { + public func decodeOptionalObjectArrayWithDecoderForKey(_ key: String) -> [T]? where T: PostboxCoding { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .ObjectArray) { var length: Int32 = 0 memcpy(&length, self.buffer.memory + self.offset, 4) @@ -1108,11 +1166,20 @@ public final class PostboxDecoder { return array } else { - return nil + if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32Array) { + let array = decodeInt32ArrayRaw() + if array.isEmpty { + return [] + } else { + return nil + } + } else { + return nil + } } } - public func decodeObjectArrayWithCustomDecoderForKey(_ key: StaticString, decoder: (PostboxDecoder) throws -> T) throws -> [T] { + public func decodeObjectArrayWithCustomDecoderForKey(_ key: String, decoder: (PostboxDecoder) throws -> T) throws -> [T] { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .ObjectArray) { var length: Int32 = 0 memcpy(&length, self.buffer.memory + self.offset, 4) @@ -1145,65 +1212,105 @@ public final class PostboxDecoder { } } - public func decodeStringArrayForKey(_ key: StaticString) -> [String] { + public func decodeStringArrayForKey(_ key: String) -> [String] { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .StringArray) { - var length: Int32 = 0 - memcpy(&length, self.buffer.memory + self.offset, 4) - self.offset += 4 - - var array: [String] = [] - array.reserveCapacity(Int(length)) - - var i: Int32 = 0 - while i < length { - var length: Int32 = 0 - memcpy(&length, self.buffer.memory + self.offset, 4) - let data = Data(bytes: self.buffer.memory.assumingMemoryBound(to: UInt8.self).advanced(by: self.offset + 4), count: Int(length)) - self.offset += 4 + Int(length) - if let string = String(data: data, encoding: .utf8) { - array.append(string) - } else { - assertionFailure() - array.append("") - } - - i += 1 - } - - return array + return decodeStringArrayRaw() } else { return [] } } + + public func decodeStringArrayRaw() -> [String] { + var length: Int32 = 0 + memcpy(&length, self.buffer.memory + self.offset, 4) + self.offset += 4 + + var array: [String] = [] + array.reserveCapacity(Int(length)) + + var i: Int32 = 0 + while i < length { + var length: Int32 = 0 + memcpy(&length, self.buffer.memory + self.offset, 4) + let data = Data(bytes: self.buffer.memory.assumingMemoryBound(to: UInt8.self).advanced(by: self.offset + 4), count: Int(length)) + self.offset += 4 + Int(length) + if let string = String(data: data, encoding: .utf8) { + array.append(string) + } else { + assertionFailure() + array.append("") + } + + i += 1 + } + + return array + } - public func decodeBytesArrayForKey(_ key: StaticString) -> [MemoryBuffer] { + public func decodeBytesArrayForKey(_ key: String) -> [MemoryBuffer] { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .BytesArray) { - var length: Int32 = 0 - memcpy(&length, self.buffer.memory + self.offset, 4) - self.offset += 4 - - var array: [MemoryBuffer] = [] - array.reserveCapacity(Int(length)) - - var i: Int32 = 0 - while i < length { - var length: Int32 = 0 - memcpy(&length, self.buffer.memory + self.offset, 4) - let bytes = malloc(Int(length))! - memcpy(bytes, self.buffer.memory.advanced(by: self.offset + 4), Int(length)) - array.append(MemoryBuffer(memory: bytes, capacity: Int(length), length: Int(length), freeWhenDone: true)) - self.offset += 4 + Int(length) - - i += 1 - } - - return array + return decodeBytesArrayRaw() } else { return [] } } + + func decodeBytesArrayRaw() -> [MemoryBuffer] { + var length: Int32 = 0 + memcpy(&length, self.buffer.memory + self.offset, 4) + self.offset += 4 + + var array: [MemoryBuffer] = [] + array.reserveCapacity(Int(length)) + + var i: Int32 = 0 + while i < length { + var length: Int32 = 0 + memcpy(&length, self.buffer.memory + self.offset, 4) + let bytes = malloc(Int(length))! + memcpy(bytes, self.buffer.memory.advanced(by: self.offset + 4), Int(length)) + array.append(MemoryBuffer(memory: bytes, capacity: Int(length), length: Int(length), freeWhenDone: true)) + self.offset += 4 + Int(length) + + i += 1 + } + + return array + } + + func decodeObjectDataArrayRaw() -> [Data] { + var length: Int32 = 0 + memcpy(&length, self.buffer.memory + self.offset, 4) + self.offset += 4 + + var array: [Data] = [] + array.reserveCapacity(Int(length)) + + var i: Int32 = 0 + while i < length { + var typeHash: Int32 = 0 + memcpy(&typeHash, self.buffer.memory + self.offset, 4) + self.offset += 4 + + var objectLength: Int32 = 0 + memcpy(&objectLength, self.buffer.memory + self.offset, 4) + if objectLength < 0 || objectLength > 2 * 1024 * 1024 { + preconditionFailure() + } + + let innerBuffer = ReadBuffer(memory: self.buffer.memory + (self.offset + 4), length: Int(objectLength), freeWhenDone: false) + let innerData = innerBuffer.makeData() + self.offset += 4 + Int(objectLength) + + array.append(innerData) + + i += 1 + } + + return array + } - public func decodeOptionalDataArrayForKey(_ key: StaticString) -> [Data]? { + public func decodeOptionalDataArrayForKey(_ key: String) -> [Data]? { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .BytesArray) { var length: Int32 = 0 memcpy(&length, self.buffer.memory + self.offset, 4) @@ -1228,7 +1335,7 @@ public final class PostboxDecoder { } } - public func decodeObjectArrayForKey(_ key: StaticString) -> [T] where T: PostboxCoding { + public func decodeObjectArrayForKey(_ key: String) -> [T] where T: PostboxCoding { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .ObjectArray) { var length: Int32 = 0 memcpy(&length, self.buffer.memory + self.offset, 4) @@ -1271,7 +1378,7 @@ public final class PostboxDecoder { } } - public func decodeObjectArrayForKey(_ key: StaticString) -> [PostboxCoding] { + public func decodeObjectArrayForKey(_ key: String) -> [PostboxCoding] { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .ObjectArray) { var length: Int32 = 0 memcpy(&length, self.buffer.memory + self.offset, 4) @@ -1314,7 +1421,7 @@ public final class PostboxDecoder { } } - public func decodeObjectDictionaryForKey(_ key: StaticString) -> [K : V] where K: PostboxCoding, K: Hashable { + public func decodeObjectDictionaryForKey(_ key: String) -> [K : V] where K: PostboxCoding, K: Hashable { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .ObjectDictionary) { var length: Int32 = 0 memcpy(&length, self.buffer.memory + self.offset, 4) @@ -1368,7 +1475,7 @@ public final class PostboxDecoder { } } - public func decodeObjectDictionaryForKey(_ key: StaticString, keyDecoder: (PostboxDecoder) -> K) -> [K : V] where K: Hashable { + public func decodeObjectDictionaryForKey(_ key: String, keyDecoder: (PostboxDecoder) -> K) -> [K : V] where K: Hashable { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .ObjectDictionary) { var length: Int32 = 0 memcpy(&length, self.buffer.memory + self.offset, 4) @@ -1425,7 +1532,7 @@ public final class PostboxDecoder { } } - public func decodeObjectDictionaryForKey(_ key: StaticString, keyDecoder: (PostboxDecoder) -> K, valueDecoder: (PostboxDecoder) -> V) -> [K : V] where K: Hashable { + public func decodeObjectDictionaryForKey(_ key: String, keyDecoder: (PostboxDecoder) -> K, valueDecoder: (PostboxDecoder) -> V) -> [K : V] where K: Hashable { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .ObjectDictionary) { var length: Int32 = 0 memcpy(&length, self.buffer.memory + self.offset, 4) @@ -1482,7 +1589,7 @@ public final class PostboxDecoder { } } - public func decodeBytesForKeyNoCopy(_ key: StaticString) -> ReadBuffer? { + public func decodeBytesForKeyNoCopy(_ key: String) -> ReadBuffer? { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Bytes) { var length: Int32 = 0 memcpy(&length, self.buffer.memory + self.offset, 4) @@ -1493,7 +1600,7 @@ public final class PostboxDecoder { } } - public func decodeBytesForKey(_ key: StaticString) -> ReadBuffer? { + public func decodeBytesForKey(_ key: String) -> ReadBuffer? { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Bytes) { var length: Int32 = 0 memcpy(&length, self.buffer.memory + self.offset, 4) @@ -1505,8 +1612,31 @@ public final class PostboxDecoder { return nil } } + + static func parseDataRaw(data: Data) -> Data? { + return data.withUnsafeBytes { bytes -> Data? in + guard let baseAddress = bytes.baseAddress else { + return nil + } + if bytes.count < 4 { + return nil + } + + var length: Int32 = 0 + memcpy(&length, baseAddress, 4) + + if length < 0 || length != (bytes.count - 4) { + return nil + } + if length == 0 { + return Data() + } + + return Data(bytes: baseAddress.advanced(by: 4), count: Int(length)) + } + } - public func decodeDataForKey(_ key: StaticString) -> Data? { + public func decodeDataForKey(_ key: String) -> Data? { if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Bytes) { var length: Int32 = 0 memcpy(&length, self.buffer.memory + self.offset, 4) @@ -1520,4 +1650,23 @@ public final class PostboxDecoder { return nil } } + + public func decode(_ type: T.Type, forKey key: String) -> T? { + if PostboxDecoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Object) { + var typeHash: Int32 = 0 + memcpy(&typeHash, self.buffer.memory + self.offset, 4) + self.offset += 4 + + var length: Int32 = 0 + memcpy(&length, self.buffer.memory + self.offset, 4) + + let innerBuffer = ReadBuffer(memory: self.buffer.memory + (self.offset + 4), length: Int(length), freeWhenDone: false) + let innerData = innerBuffer.makeData() + self.offset += 4 + Int(length) + + return try? AdaptedPostboxDecoder().decode(T.self, from: innerData) + } else { + return nil + } + } } diff --git a/submodules/Postbox/Sources/InitialMessageHistoryData.swift b/submodules/Postbox/Sources/InitialMessageHistoryData.swift index 4c3c7d6e83..463e4750c3 100644 --- a/submodules/Postbox/Sources/InitialMessageHistoryData.swift +++ b/submodules/Postbox/Sources/InitialMessageHistoryData.swift @@ -2,6 +2,6 @@ import Foundation public struct InitialMessageHistoryData { public let peer: Peer? - public let chatInterfaceState: PeerChatInterfaceState? + public let storedInterfaceState: StoredPeerChatInterfaceState? public let associatedMessages: [MessageId: Message] } diff --git a/submodules/Postbox/Sources/Media.swift b/submodules/Postbox/Sources/Media.swift index 021b5038c4..66dfc45bfd 100644 --- a/submodules/Postbox/Sources/Media.swift +++ b/submodules/Postbox/Sources/Media.swift @@ -79,6 +79,8 @@ public protocol Media: class, PostboxCoding { var indexableText: String? { get } func isLikelyToBeUpdated() -> Bool + + func preventsAutomaticMessageSendingFailure() -> Bool func isEqual(to other: Media) -> Bool func isSemanticallyEqual(to other: Media) -> Bool @@ -89,6 +91,10 @@ public extension Media { return false } + func preventsAutomaticMessageSendingFailure() -> Bool { + return false + } + var indexableText: String? { return nil } diff --git a/submodules/Postbox/Sources/Message.swift b/submodules/Postbox/Sources/Message.swift index 85b35fefca..ae09e04b3f 100644 --- a/submodules/Postbox/Sources/Message.swift +++ b/submodules/Postbox/Sources/Message.swift @@ -1,6 +1,6 @@ import Foundation -public struct MessageId: Hashable, Comparable, CustomStringConvertible, PostboxCoding { +public struct MessageId: Hashable, Comparable, CustomStringConvertible, PostboxCoding, Codable { public typealias Namespace = Int32 public typealias Id = Int32 @@ -38,6 +38,22 @@ public struct MessageId: Hashable, Comparable, CustomStringConvertible, PostboxC buffer.offset += 16 } + + private init(bytes: UnsafePointer, offset: inout Int) { + var peerIdInt64Value: Int64 = 0 + memcpy(&peerIdInt64Value, bytes.advanced(by: offset), 8) + + self.peerId = PeerId(peerIdInt64Value) + + var namespaceValue: Int32 = 0 + memcpy(&namespaceValue, bytes.advanced(by: offset + 8), 4) + self.namespace = namespaceValue + var idValue: Int32 = 0 + memcpy(&idValue, bytes.advanced(by: offset + 12), 4) + self.id = idValue + + offset += 16 + } public init(decoder: PostboxDecoder) { self.peerId = PeerId(decoder.decodeInt64ForKey("p", orElse: 0)) @@ -59,6 +75,15 @@ public struct MessageId: Hashable, Comparable, CustomStringConvertible, PostboxC buffer.write(&namespace, offset: 0, length: 4) buffer.write(&id, offset: 0, length: 4) } + + public func encodeToData(_ data: inout Data) { + var peerIdValue = self.peerId.toInt64() + var namespace = self.namespace + var id = self.id + data.append(Data(bytesNoCopy: &peerIdValue, count: 8, deallocator: .none)) + data.append(Data(bytesNoCopy: &namespace, count: 4, deallocator: .none)) + data.append(Data(bytesNoCopy: &id, count: 4, deallocator: .none)) + } public static func encodeArrayToBuffer(_ array: [MessageId], buffer: WriteBuffer) { var length: Int32 = Int32(array.count) @@ -67,6 +92,16 @@ public struct MessageId: Hashable, Comparable, CustomStringConvertible, PostboxC id.encodeToBuffer(buffer) } } + + public static func encodeArrayToData(_ array: [MessageId]) -> Data { + var result = Data() + var length: Int32 = Int32(array.count) + result.append(Data(bytesNoCopy: &length, count: 4, deallocator: .none)) + for id in array { + id.encodeToData(&result) + } + return result + } public static func decodeArrayFromBuffer(_ buffer: ReadBuffer) -> [MessageId] { var length: Int32 = 0 @@ -81,6 +116,25 @@ public struct MessageId: Hashable, Comparable, CustomStringConvertible, PostboxC return array } + public static func decodeArrayFromData(_ data: Data) -> [MessageId] { + return data.withUnsafeBytes { bytes -> [MessageId] in + guard let baseAddress = bytes.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return [] + } + var offset = 0 + var length: Int32 = 0 + memcpy(&length, baseAddress, 4) + offset += 4 + var i = 0 + var array: [MessageId] = [] + while i < Int(length) { + array.append(MessageId(bytes: baseAddress, offset: &offset)) + i += 1 + } + return array + } + } + public static func <(lhs: MessageId, rhs: MessageId) -> Bool { if lhs.namespace == rhs.namespace { if lhs.id == rhs.id { @@ -94,7 +148,7 @@ public struct MessageId: Hashable, Comparable, CustomStringConvertible, PostboxC } } -public struct MessageIndex: Comparable, Hashable { +public struct MessageIndex: Codable, Comparable, Hashable { public let id: MessageId public let timestamp: Int32 @@ -514,7 +568,7 @@ public struct MessageForwardInfo: Equatable { } } -public protocol MessageAttribute: class, PostboxCoding { +public protocol MessageAttribute: AnyObject, PostboxCoding { var associatedPeerIds: [PeerId] { get } var associatedMessageIds: [MessageId] { get } var automaticTimestampBasedAttribute: (UInt16, Int32)? { get } diff --git a/submodules/Postbox/Sources/Peer.swift b/submodules/Postbox/Sources/Peer.swift index 24ed7cce16..a75405738c 100644 --- a/submodules/Postbox/Sources/Peer.swift +++ b/submodules/Postbox/Sources/Peer.swift @@ -1,6 +1,10 @@ import Foundation public struct PeerId: Hashable, CustomStringConvertible, Comparable, Codable { + enum CodingKeys: String, CodingKey { + case internalValue = "iv" + } + public struct Namespace: Comparable, Hashable, Codable, CustomStringConvertible { public static var max: Namespace { return Namespace(rawValue: 0x7) @@ -164,6 +168,17 @@ public struct PeerId: Hashable, CustomStringConvertible, Comparable, Codable { assert(self._toInt64() == n) } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let value = try container.decode(Int64.self, forKey: .internalValue) + self.init(value) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.toInt64(), forKey: .internalValue) + } public func toInt64() -> Int64 { let result = self._toInt64() diff --git a/submodules/Postbox/Sources/PeerChatInterfaceState.swift b/submodules/Postbox/Sources/PeerChatInterfaceState.swift index eb395081d0..3e491225f0 100644 --- a/submodules/Postbox/Sources/PeerChatInterfaceState.swift +++ b/submodules/Postbox/Sources/PeerChatInterfaceState.swift @@ -1,14 +1,75 @@ +import Foundation -public protocol PeerChatListEmbeddedInterfaceState: PostboxCoding { +/*public protocol PeerChatListEmbeddedInterfaceState: Codable { var timestamp: Int32 { get } func isEqual(to: PeerChatListEmbeddedInterfaceState) -> Bool +}*/ + +public final class StoredPeerChatInterfaceState: Codable, Equatable { + private enum CodingKeys: CodingKey { + case overrideChatTimestamp + case historyScrollMessageIndex + case associatedMessageIds + case data + } + + public let overrideChatTimestamp: Int32? + public let historyScrollMessageIndex: MessageIndex? + public let associatedMessageIds: [MessageId] + public let data: Data? + + public init( + overrideChatTimestamp: Int32?, + historyScrollMessageIndex: MessageIndex?, + associatedMessageIds: [MessageId], + data: Data? + ) { + self.overrideChatTimestamp = overrideChatTimestamp + self.historyScrollMessageIndex = historyScrollMessageIndex + self.associatedMessageIds = associatedMessageIds + self.data = data + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.overrideChatTimestamp = try? container.decodeIfPresent(Int32.self, forKey: .overrideChatTimestamp) + self.historyScrollMessageIndex = try? container.decodeIfPresent(MessageIndex.self, forKey: .historyScrollMessageIndex) + self.associatedMessageIds = try container.decode([MessageId].self, forKey: .associatedMessageIds) + self.data = try? container.decodeIfPresent(Data.self, forKey: .data) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encodeIfPresent(self.overrideChatTimestamp, forKey: .overrideChatTimestamp) + try container.encodeIfPresent(self.historyScrollMessageIndex, forKey: .historyScrollMessageIndex) + try container.encodeIfPresent(self.associatedMessageIds, forKey: .associatedMessageIds) + try container.encodeIfPresent(self.data, forKey: .data) + } + + public static func ==(lhs: StoredPeerChatInterfaceState, rhs: StoredPeerChatInterfaceState) -> Bool { + if lhs.overrideChatTimestamp != rhs.overrideChatTimestamp { + return false + } + if lhs.historyScrollMessageIndex != rhs.historyScrollMessageIndex { + return false + } + if lhs.associatedMessageIds != rhs.associatedMessageIds { + return false + } + if lhs.data != rhs.data { + return false + } + return true + } } -public protocol PeerChatInterfaceState: PostboxCoding { +/*public protocol PeerChatInterfaceState: PostboxCoding { var chatListEmbeddedState: PeerChatListEmbeddedInterfaceState? { get } var historyScrollMessageIndex: MessageIndex? { get } var associatedMessageIds: [MessageId] { get } func isEqual(to: PeerChatInterfaceState) -> Bool -} +}*/ diff --git a/submodules/Postbox/Sources/PeerChatInterfaceStateTable.swift b/submodules/Postbox/Sources/PeerChatInterfaceStateTable.swift index 5a39de8b2b..2467e38467 100644 --- a/submodules/Postbox/Sources/PeerChatInterfaceStateTable.swift +++ b/submodules/Postbox/Sources/PeerChatInterfaceStateTable.swift @@ -5,7 +5,7 @@ final class PeerChatInterfaceStateTable: Table { return ValueBoxTable(id: id, keyType: .int64, compactValuesOnCreation: false) } - private var states: [PeerId: PeerChatInterfaceState?] = [:] + private var states: [PeerId: StoredPeerChatInterfaceState?] = [:] private var peerIdsWithUpdatedStates = Set() private let sharedKey = ValueBoxKey(length: 8) @@ -15,10 +15,10 @@ final class PeerChatInterfaceStateTable: Table { return sharedKey } - func get(_ peerId: PeerId) -> PeerChatInterfaceState? { + func get(_ peerId: PeerId) -> StoredPeerChatInterfaceState? { if let cachedValue = self.states[peerId] { return cachedValue - } else if let value = self.valueBox.get(self.table, key: self.key(peerId, sharedKey: self.sharedKey)), let state = PostboxDecoder(buffer: value).decodeRootObject() as? PeerChatInterfaceState { + } else if let value = self.valueBox.get(self.table, key: self.key(peerId, sharedKey: self.sharedKey)), let state = try? AdaptedPostboxDecoder().decode(StoredPeerChatInterfaceState.self, from: value.makeData()) { self.states[peerId] = state return state } else { @@ -27,18 +27,14 @@ final class PeerChatInterfaceStateTable: Table { } } - func set(_ peerId: PeerId, state: PeerChatInterfaceState?) -> (updated: Bool, updatedEmbeddedState: Bool) { + func set(_ peerId: PeerId, state: StoredPeerChatInterfaceState?) -> (updated: Bool, updatedEmbeddedState: Bool) { let currentState = self.get(peerId) var updated = false var updatedEmbeddedState = false if let currentState = currentState, let state = state { - if !currentState.isEqual(to: state) { + if currentState != state { updated = true - if let currentEmbeddedState = currentState.chatListEmbeddedState, let embeddedState = state.chatListEmbeddedState { - if !currentEmbeddedState.isEqual(to: embeddedState) { - updatedEmbeddedState = true - } - } else if (currentState.chatListEmbeddedState != nil) != (state.chatListEmbeddedState != nil) { + if currentState.overrideChatTimestamp != state.overrideChatTimestamp { updatedEmbeddedState = true } } @@ -60,13 +56,10 @@ final class PeerChatInterfaceStateTable: Table { override func beforeCommit() { if !self.peerIdsWithUpdatedStates.isEmpty { - let sharedEncoder = PostboxEncoder() for peerId in self.peerIdsWithUpdatedStates { if let state = self.states[peerId] { - if let state = state { - sharedEncoder.reset() - sharedEncoder.encodeRootObject(state) - self.valueBox.set(self.table, key: self.key(peerId, sharedKey: self.sharedKey), value: sharedEncoder.readBufferNoCopy()) + if let state = state, let data = try? AdaptedPostboxEncoder().encode(state) { + self.valueBox.set(self.table, key: self.key(peerId, sharedKey: self.sharedKey), value: ReadBuffer(data: data)) } else { self.valueBox.remove(self.table, key: self.key(peerId, sharedKey: self.sharedKey), secure: false) } diff --git a/submodules/Postbox/Sources/PeerChatThreadInterfaceStateTable.swift b/submodules/Postbox/Sources/PeerChatThreadInterfaceStateTable.swift index fb75665a23..12f7818461 100644 --- a/submodules/Postbox/Sources/PeerChatThreadInterfaceStateTable.swift +++ b/submodules/Postbox/Sources/PeerChatThreadInterfaceStateTable.swift @@ -15,7 +15,7 @@ final class PeerChatThreadInterfaceStateTable: Table { return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: false) } - private var states: [PeerChatThreadId: PeerChatInterfaceState?] = [:] + private var states: [PeerChatThreadId: StoredPeerChatInterfaceState?] = [:] private var peerIdsWithUpdatedStates = Set() private let sharedKey = ValueBoxKey(length: 8 + 8) @@ -26,10 +26,10 @@ final class PeerChatThreadInterfaceStateTable: Table { return sharedKey } - func get(_ peerId: PeerChatThreadId) -> PeerChatInterfaceState? { + func get(_ peerId: PeerChatThreadId) -> StoredPeerChatInterfaceState? { if let cachedValue = self.states[peerId] { return cachedValue - } else if let value = self.valueBox.get(self.table, key: self.key(peerId, sharedKey: self.sharedKey)), let state = PostboxDecoder(buffer: value).decodeRootObject() as? PeerChatInterfaceState { + } else if let value = self.valueBox.get(self.table, key: self.key(peerId, sharedKey: self.sharedKey)), let state = try? AdaptedPostboxDecoder().decode(StoredPeerChatInterfaceState.self, from: value.makeData()) { self.states[peerId] = state return state } else { @@ -38,11 +38,11 @@ final class PeerChatThreadInterfaceStateTable: Table { } } - func set(_ peerId: PeerChatThreadId, state: PeerChatInterfaceState?) -> Bool { + func set(_ peerId: PeerChatThreadId, state: StoredPeerChatInterfaceState?) -> Bool { let currentState = self.get(peerId) var updated = false if let currentState = currentState, let state = state { - if !currentState.isEqual(to: state) { + if currentState != state { updated = true } } else if (currentState != nil) != (state != nil) { @@ -62,13 +62,10 @@ final class PeerChatThreadInterfaceStateTable: Table { override func beforeCommit() { if !self.peerIdsWithUpdatedStates.isEmpty { - let sharedEncoder = PostboxEncoder() for peerId in self.peerIdsWithUpdatedStates { if let state = self.states[peerId] { - if let state = state { - sharedEncoder.reset() - sharedEncoder.encodeRootObject(state) - self.valueBox.set(self.table, key: self.key(peerId, sharedKey: self.sharedKey), value: sharedEncoder.readBufferNoCopy()) + if let state = state, let data = try? AdaptedPostboxEncoder().encode(state) { + self.valueBox.set(self.table, key: self.key(peerId, sharedKey: self.sharedKey), value: ReadBuffer(data: data)) } else { self.valueBox.remove(self.table, key: self.key(peerId, sharedKey: self.sharedKey), secure: false) } diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 1a93b8aee3..dbd3d51a78 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -259,29 +259,24 @@ public final class Transaction { } } - /*public func getPeerGroupState(_ id: PeerGroupId) -> PeerGroupState? { - assert(!self.disposed) - return self.postbox?.peerGroupStateTable.get(id) - } - - public func setPeerGroupState(_ id: PeerGroupId, state: PeerGroupState) { - assert(!self.disposed) - self.postbox?.setPeerGroupState(id, state: state) - }*/ - - public func getPeerChatInterfaceState(_ id: PeerId) -> PeerChatInterfaceState? { + public func getPeerChatInterfaceState(_ id: PeerId) -> StoredPeerChatInterfaceState? { assert(!self.disposed) return self.postbox?.peerChatInterfaceStateTable.get(id) } - - public func updatePeerChatInterfaceState(_ id: PeerId, update: (PeerChatInterfaceState?) -> (PeerChatInterfaceState?)) { + + public func getPeerChatThreadInterfaceState(_ id: PeerId, threadId: Int64) -> StoredPeerChatInterfaceState? { assert(!self.disposed) - self.postbox?.updatePeerChatInterfaceState(id, update: update) + return self.postbox?.peerChatThreadInterfaceStateTable.get(PeerChatThreadId(peerId: id, threadId: threadId)) } - public func updatePeerChatThreadInterfaceState(_ id: PeerId, threadId: Int64, update: (PeerChatInterfaceState?) -> (PeerChatInterfaceState?)) { + public func setPeerChatInterfaceState(_ id: PeerId, state: StoredPeerChatInterfaceState?) { assert(!self.disposed) - self.postbox?.updatePeerChatThreadInterfaceState(id, threadId: threadId, update: update) + self.postbox?.setPeerChatInterfaceState(id, state: state) + } + + public func setPeerChatThreadInterfaceState(_ id: PeerId, threadId: Int64, state: StoredPeerChatInterfaceState?) { + assert(!self.disposed) + self.postbox?.setPeerChatThreadInterfaceState(id, threadId: threadId, state: state) } public func getPeer(_ id: PeerId) -> Peer? { @@ -1306,7 +1301,7 @@ public final class Postbox { private var currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp] = [:] private var currentUpdatedCachedPeerData: [PeerId: CachedPeerData] = [:] private var currentUpdatedPeerPresences: [PeerId: PeerPresence] = [:] - private var currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?] = [:] + private var currentUpdatedPeerChatListEmbeddedStates = Set() private var currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState] = [:] private var currentUpdatedGroupTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary] = [:] private var currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation] = [] @@ -1479,8 +1474,8 @@ public final class Postbox { self.itemCollectionInfoTable = ItemCollectionInfoTable(valueBox: self.valueBox, table: ItemCollectionInfoTable.tableSpec(21)) self.itemCollectionReverseIndexTable = ReverseIndexReferenceTable(valueBox: self.valueBox, table: ReverseIndexReferenceTable.tableSpec(36)) self.itemCollectionItemTable = ItemCollectionItemTable(valueBox: self.valueBox, table: ItemCollectionItemTable.tableSpec(22), reverseIndexTable: self.itemCollectionReverseIndexTable) - self.peerChatInterfaceStateTable = PeerChatInterfaceStateTable(valueBox: self.valueBox, table: PeerChatInterfaceStateTable.tableSpec(23)) - self.peerChatThreadInterfaceStateTable = PeerChatThreadInterfaceStateTable(valueBox: self.valueBox, table: PeerChatThreadInterfaceStateTable.tableSpec(64)) + self.peerChatInterfaceStateTable = PeerChatInterfaceStateTable(valueBox: self.valueBox, table: PeerChatInterfaceStateTable.tableSpec(67)) + self.peerChatThreadInterfaceStateTable = PeerChatThreadInterfaceStateTable(valueBox: self.valueBox, table: PeerChatThreadInterfaceStateTable.tableSpec(68)) self.itemCacheMetaTable = ItemCacheMetaTable(valueBox: self.valueBox, table: ItemCacheMetaTable.tableSpec(24)) self.itemCacheTable = ItemCacheTable(valueBox: self.valueBox, table: ItemCacheTable.tableSpec(25)) self.chatListIndexTable = ChatListIndexTable(valueBox: self.valueBox, table: ChatListIndexTable.tableSpec(8), peerNameIndexTable: self.peerNameIndexTable, metadataTable: self.messageHistoryMetadataTable, readStateTable: self.readStateTable, notificationSettingsTable: self.peerNotificationSettingsTable) @@ -1603,6 +1598,12 @@ public final class Postbox { for id in self.messageHistoryUnsentTable.get() { transaction.updateMessage(id, update: { message in if !message.flags.contains(.Failed) { + for media in message.media { + if media.preventsAutomaticMessageSendingFailure() { + return .skip + } + } + if message.timestamp + 60 * 10 > timestampForAbsoluteTimeBasedOperations { return .skip } @@ -2198,16 +2199,16 @@ public final class Postbox { self.currentUpdatedPeerChatStates.insert(id) } - fileprivate func updatePeerChatInterfaceState(_ id: PeerId, update: (PeerChatInterfaceState?) -> (PeerChatInterfaceState?)) { - let updatedState = update(self.peerChatInterfaceStateTable.get(id)) + fileprivate func setPeerChatInterfaceState(_ id: PeerId, state: StoredPeerChatInterfaceState?) { + let updatedState = state let (_, updatedEmbeddedState) = self.peerChatInterfaceStateTable.set(id, state: updatedState) if updatedEmbeddedState { - self.currentUpdatedPeerChatListEmbeddedStates[id] = updatedState?.chatListEmbeddedState + self.currentUpdatedPeerChatListEmbeddedStates.insert(id) } } - fileprivate func updatePeerChatThreadInterfaceState(_ id: PeerId, threadId: Int64, update: (PeerChatInterfaceState?) -> (PeerChatInterfaceState?)) { - let updatedState = update(self.peerChatThreadInterfaceStateTable.get(PeerChatThreadId(peerId: id, threadId: threadId))) + fileprivate func setPeerChatThreadInterfaceState(_ id: PeerId, threadId: Int64, state: StoredPeerChatInterfaceState?) { + let updatedState = state let _ = self.peerChatThreadInterfaceStateTable.set(PeerChatThreadId(peerId: id, threadId: threadId), state: updatedState) } @@ -2791,7 +2792,7 @@ public final class Postbox { } } } - return InitialMessageHistoryData(peer: self.peerTable.get(peerId), chatInterfaceState: chatInterfaceState, associatedMessages: associatedMessages) + return InitialMessageHistoryData(peer: self.peerTable.get(peerId), storedInterfaceState: chatInterfaceState, associatedMessages: associatedMessages) } else { let chatInterfaceState = self.peerChatInterfaceStateTable.get(peerId) var associatedMessages: [MessageId: Message] = [:] @@ -2802,7 +2803,7 @@ public final class Postbox { } } } - return InitialMessageHistoryData(peer: self.peerTable.get(peerId), chatInterfaceState: chatInterfaceState, associatedMessages: associatedMessages) + return InitialMessageHistoryData(peer: self.peerTable.get(peerId), storedInterfaceState: chatInterfaceState, associatedMessages: associatedMessages) } } diff --git a/submodules/Postbox/Sources/PostboxTransaction.swift b/submodules/Postbox/Sources/PostboxTransaction.swift index 22f57a7c98..03113f3a8f 100644 --- a/submodules/Postbox/Sources/PostboxTransaction.swift +++ b/submodules/Postbox/Sources/PostboxTransaction.swift @@ -11,7 +11,7 @@ final class PostboxTransaction { let currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp] let currentUpdatedCachedPeerData: [PeerId: CachedPeerData] let currentUpdatedPeerPresences: [PeerId: PeerPresence] - let currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?] + let currentUpdatedPeerChatListEmbeddedStates: Set let currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState] let currentUpdatedTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary] let alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState] @@ -174,7 +174,7 @@ final class PostboxTransaction { return true } - init(currentUpdatedState: PostboxCoding?, currentPeerHoleOperations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:], currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], chatListOperations: [PeerGroupId: [ChatListOperation]], currentUpdatedChatListInclusions: [PeerId: PeerChatListInclusion], currentUpdatedPeers: [PeerId: Peer], currentUpdatedPeerNotificationSettings: [PeerId: (PeerNotificationSettings?, PeerNotificationSettings)], currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp], currentUpdatedCachedPeerData: [PeerId: CachedPeerData], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?], currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState], currentUpdatedTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary], alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], currentUpdatedGroupSummarySynchronizeOperations: [PeerGroupAndNamespace: Bool], currentPreferencesOperations: [PreferencesOperation], currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], currentUpdatedPeerChatStates: Set, currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: [MediaId: Media?], replaceRemoteContactCount: Int32?, replaceContactPeerIds: Set?, currentPendingMessageActionsOperations: [PendingMessageActionsOperation], currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], currentUpdatedPendingPeerNotificationSettings: Set, replacedAdditionalChatListItems: [AdditionalChatListItem]?, updatedNoticeEntryKeys: Set, updatedCacheEntryKeys: Set, currentUpdatedMasterClientId: Int64?, updatedFailedMessagePeerIds: Set, updatedFailedMessageIds: Set, updatedGlobalNotificationSettings: Bool) { + init(currentUpdatedState: PostboxCoding?, currentPeerHoleOperations: [MessageHistoryIndexHoleOperationKey: [MessageHistoryIndexHoleOperation]] = [:], currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], chatListOperations: [PeerGroupId: [ChatListOperation]], currentUpdatedChatListInclusions: [PeerId: PeerChatListInclusion], currentUpdatedPeers: [PeerId: Peer], currentUpdatedPeerNotificationSettings: [PeerId: (PeerNotificationSettings?, PeerNotificationSettings)], currentUpdatedPeerNotificationBehaviorTimestamps: [PeerId: PeerNotificationSettingsBehaviorTimestamp], currentUpdatedCachedPeerData: [PeerId: CachedPeerData], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: Set, currentUpdatedTotalUnreadStates: [PeerGroupId: ChatListTotalUnreadState], currentUpdatedTotalUnreadSummaries: [PeerGroupId: PeerGroupUnreadCountersCombinedSummary], alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], currentUpdatedGroupSummarySynchronizeOperations: [PeerGroupAndNamespace: Bool], currentPreferencesOperations: [PreferencesOperation], currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], currentUpdatedPeerChatStates: Set, currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: [MediaId: Media?], replaceRemoteContactCount: Int32?, replaceContactPeerIds: Set?, currentPendingMessageActionsOperations: [PendingMessageActionsOperation], currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], currentUpdatedPendingPeerNotificationSettings: Set, replacedAdditionalChatListItems: [AdditionalChatListItem]?, updatedNoticeEntryKeys: Set, updatedCacheEntryKeys: Set, currentUpdatedMasterClientId: Int64?, updatedFailedMessagePeerIds: Set, updatedFailedMessageIds: Set, updatedGlobalNotificationSettings: Bool) { self.currentUpdatedState = currentUpdatedState self.currentPeerHoleOperations = currentPeerHoleOperations self.currentOperationsByPeerId = currentOperationsByPeerId diff --git a/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxDecoder.swift b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxDecoder.swift new file mode 100644 index 0000000000..6b83b7b0d5 --- /dev/null +++ b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxDecoder.swift @@ -0,0 +1,128 @@ +import Foundation + +final public class AdaptedPostboxDecoder { + enum ContentType { + case object + case int32Array + case int64Array + case objectArray + case stringArray + case dataArray + } + + public init() { + } + + public func decode(_ type: T.Type, from data: Data) throws -> T where T : Decodable { + return try self.decode(type, from: data, contentType: .object) + } + + func decode(_ type: T.Type, from data: Data, contentType: ContentType) throws -> T where T : Decodable { + let decoder = _AdaptedPostboxDecoder(data: data, contentType: contentType) + return try T(from: decoder) + } +} + +extension AdaptedPostboxDecoder.ContentType { + init?(valueType: ValueType) { + switch valueType { + case .Int32: + return nil + case .Int64: + return nil + case .Bool: + return nil + case .Double: + return nil + case .String: + return nil + case .Object: + self = .object + case .Int32Array: + self = .int32Array + case .Int64Array: + self = .int64Array + case .ObjectArray: + self = .objectArray + case .ObjectDictionary: + return nil + case .Bytes: + return nil + case .Nil: + return nil + case .StringArray: + self = .stringArray + case .BytesArray: + self = .dataArray + } + } +} + +final class _AdaptedPostboxDecoder { + var codingPath: [CodingKey] = [] + + var userInfo: [CodingUserInfoKey : Any] = [:] + + var container: AdaptedPostboxDecodingContainer? + + fileprivate let data: Data + fileprivate let contentType: AdaptedPostboxDecoder.ContentType + + init(data: Data, contentType: AdaptedPostboxDecoder.ContentType) { + self.data = data + self.contentType = contentType + } +} + +extension _AdaptedPostboxDecoder: Decoder { + fileprivate func assertCanCreateContainer() { + precondition(self.container == nil) + } + + func container(keyedBy type: Key.Type) -> KeyedDecodingContainer where Key : CodingKey { + assertCanCreateContainer() + + let container = KeyedContainer(data: self.data, codingPath: self.codingPath, userInfo: self.userInfo) + self.container = container + + return KeyedDecodingContainer(container) + } + + func unkeyedContainer() -> UnkeyedDecodingContainer { + assertCanCreateContainer() + + let decoder = PostboxDecoder(buffer: MemoryBuffer(data: self.data)) + + var content: UnkeyedContainer.Content? + switch self.contentType { + case .object: + preconditionFailure() + case .int32Array: + content = .int32Array(decoder.decodeInt32ArrayRaw()) + case .int64Array: + content = .int64Array(decoder.decodeInt64ArrayRaw()) + case .objectArray: + content = .objectArray(decoder.decodeObjectDataArrayRaw()) + case .stringArray: + content = .stringArray(decoder.decodeStringArrayRaw()) + case .dataArray: + content = .dataArray(decoder.decodeBytesArrayRaw().map { $0.makeData() }) + } + + if let content = content { + let container = UnkeyedContainer(data: self.data, codingPath: self.codingPath, userInfo: self.userInfo, content: content) + self.container = container + + return container + } else { + preconditionFailure() + } + } + + func singleValueContainer() -> SingleValueDecodingContainer { + preconditionFailure() + } +} + +protocol AdaptedPostboxDecodingContainer: AnyObject { +} diff --git a/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxKeyedDecodingContainer.swift b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxKeyedDecodingContainer.swift new file mode 100644 index 0000000000..22e5e62c32 --- /dev/null +++ b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxKeyedDecodingContainer.swift @@ -0,0 +1,126 @@ +import Foundation + +extension _AdaptedPostboxDecoder { + final class KeyedContainer where Key: CodingKey { + var codingPath: [CodingKey] + var userInfo: [CodingUserInfoKey: Any] + let decoder: PostboxDecoder + + init(data: Data, codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) { + self.codingPath = codingPath + self.userInfo = userInfo + self.decoder = PostboxDecoder(buffer: MemoryBuffer(data: data)) + } + } +} + +private func decodingErrorBreakpoint() { + #if DEBUG + print("Decoding error. Install a breakpoint at decodingErrorBreakpoint to debug.") + #endif +} + +extension _AdaptedPostboxDecoder.KeyedContainer: KeyedDecodingContainerProtocol { + var allKeys: [Key] { + preconditionFailure() + } + + func contains(_ key: Key) -> Bool { + return self.decoder.containsKey(key.stringValue) + } + + func decodeNil(forKey key: Key) throws -> Bool { + return self.decoder.decodeNilForKey(key.stringValue) + } + + func decode(_ type: T.Type, forKey key: Key) throws -> T where T : Decodable { + if let (data, valueType) = self.decoder.decodeObjectDataForKey(key.stringValue) { + if let mappedType = AdaptedPostboxDecoder.ContentType(valueType: valueType) { + return try AdaptedPostboxDecoder().decode(T.self, from: data, contentType: mappedType) + } else { + switch valueType { + case .Bytes: + guard let resultData = PostboxDecoder.parseDataRaw(data: data) else { + decodingErrorBreakpoint() + throw DecodingError.typeMismatch(T.self, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) + } + if let resultData = resultData as? T { + return resultData + } else { + decodingErrorBreakpoint() + throw DecodingError.typeMismatch(T.self, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) + } + default: + decodingErrorBreakpoint() + throw DecodingError.typeMismatch(T.self, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) + } + } + } else { + decodingErrorBreakpoint() + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) + } + } + + func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 { + if let value = self.decoder.decodeOptionalInt32ForKey(key.stringValue) { + return value + } else { + decodingErrorBreakpoint() + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) + } + } + + func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 { + if let value = self.decoder.decodeOptionalInt64ForKey(key.stringValue) { + return value + } else { + decodingErrorBreakpoint() + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) + } + } + + func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool { + if let value = self.decoder.decodeOptionalBoolForKey(key.stringValue) { + return value + } else { + decodingErrorBreakpoint() + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) + } + } + + func decode(_ type: String.Type, forKey key: Key) throws -> String { + if let value = self.decoder.decodeOptionalStringForKey(key.stringValue) { + return value + } else { + decodingErrorBreakpoint() + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) + } + } + + func decode(_ type: Double.Type, forKey key: Key) throws -> Double { + if let value = self.decoder.decodeOptionalDoubleForKey(key.stringValue) { + return value + } else { + decodingErrorBreakpoint() + throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: self.codingPath + [key], debugDescription: "")) + } + } + + func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer { + preconditionFailure() + } + + func nestedContainer(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer where NestedKey : CodingKey { + preconditionFailure() + } + + func superDecoder() throws -> Decoder { + preconditionFailure() + } + + func superDecoder(forKey key: Key) throws -> Decoder { + preconditionFailure() + } +} + +extension _AdaptedPostboxDecoder.KeyedContainer: AdaptedPostboxDecodingContainer {} diff --git a/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxSingleValueDecodingContainer.swift b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxSingleValueDecodingContainer.swift new file mode 100644 index 0000000000..5f21bc88f7 --- /dev/null +++ b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxSingleValueDecodingContainer.swift @@ -0,0 +1,82 @@ +import Foundation + +extension _AdaptedPostboxDecoder { + final class SingleValueContainer { + var codingPath: [CodingKey] + var userInfo: [CodingUserInfoKey: Any] + + + init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) { + self.codingPath = codingPath + self.userInfo = userInfo + } + } +} + +extension _AdaptedPostboxDecoder.SingleValueContainer: SingleValueDecodingContainer { + func decodeNil() -> Bool { + preconditionFailure() + } + + func decode(_ type: Bool.Type) throws -> Bool { + preconditionFailure() + } + + func decode(_ type: String.Type) throws -> String { + preconditionFailure() + } + + func decode(_ type: Double.Type) throws -> Double { + preconditionFailure() + } + + func decode(_ type: Float.Type) throws -> Float { + preconditionFailure() + } + + func decode(_ type: Int.Type) throws -> Int { + preconditionFailure() + } + + func decode(_ type: Int8.Type) throws -> Int8 { + preconditionFailure() + } + + func decode(_ type: Int16.Type) throws -> Int16 { + preconditionFailure() + } + + func decode(_ type: Int32.Type) throws -> Int32 { + preconditionFailure() + } + + func decode(_ type: Int64.Type) throws -> Int64 { + preconditionFailure() + } + + func decode(_ type: UInt.Type) throws -> UInt { + preconditionFailure() + } + + func decode(_ type: UInt8.Type) throws -> UInt8 { + preconditionFailure() + } + + func decode(_ type: UInt16.Type) throws -> UInt16 { + preconditionFailure() + } + + func decode(_ type: UInt32.Type) throws -> UInt32 { + preconditionFailure() + } + + func decode(_ type: UInt64.Type) throws -> UInt64 { + preconditionFailure() + } + + func decode(_ type: T.Type) throws -> T where T : Decodable { + preconditionFailure() + } +} + +extension _AdaptedPostboxDecoder.SingleValueContainer: AdaptedPostboxDecodingContainer {} diff --git a/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxUnkeyedDecodingContainer.swift b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxUnkeyedDecodingContainer.swift new file mode 100644 index 0000000000..27bdeddcfd --- /dev/null +++ b/submodules/Postbox/Sources/Utils/Decoder/AdaptedPostboxUnkeyedDecodingContainer.swift @@ -0,0 +1,129 @@ +import Foundation + +extension _AdaptedPostboxDecoder { + final class UnkeyedContainer { + enum Content { + case int32Array([Int32]) + case int64Array([Int64]) + case objectArray([Data]) + case stringArray([String]) + case dataArray([Data]) + + var count: Int { + switch self { + case let .int32Array(array): + return array.count + case let .int64Array(array): + return array.count + case let .objectArray(array): + return array.count + case let .stringArray(array): + return array.count + case let .dataArray(array): + return array.count + } + } + } + + let codingPath: [CodingKey] + let userInfo: [CodingUserInfoKey: Any] + let content: Content + + var count: Int? { + return self.content.count + } + + var isAtEnd: Bool { + return self.currentIndex >= self.content.count + } + + fileprivate var _currentIndex: Int = 0 + + var currentIndex: Int { + return self._currentIndex + } + + init(data: Data, codingPath: [CodingKey], userInfo: [CodingUserInfoKey: Any], content: Content) { + self.codingPath = codingPath + self.userInfo = userInfo + self.content = content + } + } +} + +extension _AdaptedPostboxDecoder.UnkeyedContainer: UnkeyedDecodingContainer { + func decodeNil() throws -> Bool { + preconditionFailure() + } + + func decode(_ type: T.Type) throws -> T where T : Decodable { + if type == Data.self { + switch self.content { + case let .dataArray(array): + let index = self._currentIndex + self._currentIndex += 1 + return array[index] as! T + default: + throw DecodingError.typeMismatch(Data.self, DecodingError.Context(codingPath: self.codingPath, debugDescription: "")) + } + } else { + switch self.content { + case let .objectArray(array): + let index = self._currentIndex + self._currentIndex += 1 + + let data = array[index] + return try AdaptedPostboxDecoder().decode(T.self, from: data) + default: + throw DecodingError.typeMismatch(T.self, DecodingError.Context(codingPath: self.codingPath, debugDescription: "")) + } + } + } + + func decode(_ type: Int32.Type) throws -> Int32 { + switch self.content { + case let .int32Array(array): + let index = self._currentIndex + self._currentIndex += 1 + return array[index] + default: + throw DecodingError.typeMismatch(Int32.self, DecodingError.Context(codingPath: self.codingPath, debugDescription: "")) + } + } + + func decode(_ type: Int64.Type) throws -> Int64 { + switch self.content { + case let .int64Array(array): + let index = self._currentIndex + self._currentIndex += 1 + return array[index] + default: + throw DecodingError.typeMismatch(Int64.self, DecodingError.Context(codingPath: self.codingPath, debugDescription: "")) + } + } + + func decode(_ type: String.Type) throws -> String { + switch self.content { + case let .stringArray(array): + let index = self._currentIndex + self._currentIndex += 1 + return array[index] + default: + throw DecodingError.typeMismatch(String.self, DecodingError.Context(codingPath: self.codingPath, debugDescription: "")) + } + } + + func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer { + preconditionFailure() + } + + func nestedContainer(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer where NestedKey : CodingKey { + preconditionFailure() + } + + func superDecoder() throws -> Decoder { + preconditionFailure() + } +} + +extension _AdaptedPostboxDecoder.UnkeyedContainer: AdaptedPostboxDecodingContainer {} diff --git a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxEncoder.swift b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxEncoder.swift new file mode 100644 index 0000000000..5c46082034 --- /dev/null +++ b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxEncoder.swift @@ -0,0 +1,65 @@ +import Foundation +import MurMurHash32 + +public class AdaptedPostboxEncoder { + public init() { + } + + public func encode(_ value: Encodable) throws -> Data { + let typeHash: Int32 = murMurHashString32("\(type(of: value))") + + let encoder = _AdaptedPostboxEncoder(typeHash: typeHash) + try value.encode(to: encoder) + return encoder.makeData(addHeader: false).0 + } +} + +final class _AdaptedPostboxEncoder { + var codingPath: [CodingKey] = [] + + var userInfo: [CodingUserInfoKey : Any] = [:] + + let typeHash: Int32 + + fileprivate var container: AdaptedPostboxEncodingContainer? + + init(typeHash: Int32) { + self.typeHash = typeHash + } + + func makeData(addHeader: Bool) -> (Data, ValueType) { + return self.container!.makeData(addHeader: addHeader) + } +} + +extension _AdaptedPostboxEncoder: Encoder { + fileprivate func assertCanCreateContainer() { + precondition(self.container == nil) + } + + func container(keyedBy type: Key.Type) -> KeyedEncodingContainer where Key : CodingKey { + assertCanCreateContainer() + + let container = KeyedContainer(codingPath: self.codingPath, userInfo: self.userInfo, typeHash: self.typeHash) + self.container = container + + return KeyedEncodingContainer(container) + } + + func unkeyedContainer() -> UnkeyedEncodingContainer { + assertCanCreateContainer() + + let container = UnkeyedContainer(codingPath: self.codingPath, userInfo: self.userInfo) + self.container = container + + return container + } + + func singleValueContainer() -> SingleValueEncodingContainer { + preconditionFailure() + } +} + +protocol AdaptedPostboxEncodingContainer: AnyObject { + func makeData(addHeader: Bool) -> (Data, ValueType) +} diff --git a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxKeyedEncodingContainer.swift b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxKeyedEncodingContainer.swift new file mode 100644 index 0000000000..311d3edcf2 --- /dev/null +++ b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxKeyedEncodingContainer.swift @@ -0,0 +1,97 @@ +import Foundation +import MurMurHash32 + +extension _AdaptedPostboxEncoder { + final class KeyedContainer where Key: CodingKey { + var codingPath: [CodingKey] + var userInfo: [CodingUserInfoKey: Any] + let typeHash: Int32 + + let encoder: PostboxEncoder + + init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any], typeHash: Int32) { + self.codingPath = codingPath + self.userInfo = userInfo + self.typeHash = typeHash + + self.encoder = PostboxEncoder() + } + + func makeData(addHeader: Bool) -> (Data, ValueType) { + let buffer = WriteBuffer() + + if addHeader { + var typeHash: Int32 = self.typeHash + buffer.write(&typeHash, offset: 0, length: 4) + } + + let data = self.encoder.makeData() + + if addHeader { + var length: Int32 = Int32(data.count) + buffer.write(&length, offset: 0, length: 4) + } + + buffer.write(data) + + return (buffer.makeData(), .Object) + } + } +} + +extension _AdaptedPostboxEncoder.KeyedContainer: KeyedEncodingContainerProtocol { + func encode(_ value: T, forKey key: Key) throws where T : Encodable { + if let value = value as? Data { + self.encoder.encodeData(value, forKey: key.stringValue) + } else { + let typeHash: Int32 = murMurHashString32("\(type(of: value))") + let innerEncoder = _AdaptedPostboxEncoder(typeHash: typeHash) + try! value.encode(to: innerEncoder) + + let (data, valueType) = innerEncoder.makeData(addHeader: true) + self.encoder.encodeInnerObjectData(data, valueType: valueType, forKey: key.stringValue) + } + } + + func encodeNil(forKey key: Key) throws { + self.encoder.encodeNil(forKey: key.stringValue) + } + + func encode(_ value: Int32, forKey key: Key) throws { + self.encoder.encodeInt32(value, forKey: key.stringValue) + } + + func encode(_ value: Int64, forKey key: Key) throws { + self.encoder.encodeInt64(value, forKey: key.stringValue) + } + + func encode(_ value: Bool, forKey key: Key) throws { + self.encoder.encodeBool(value, forKey: key.stringValue) + } + + func encode(_ value: Double, forKey key: Key) throws { + self.encoder.encodeDouble(value, forKey: key.stringValue) + } + + func encode(_ value: String, forKey key: Key) throws { + self.encoder.encodeString(value, forKey: key.stringValue) + } + + func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { + preconditionFailure() + } + + func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer where NestedKey : CodingKey { + preconditionFailure() + } + + func superEncoder() -> Encoder { + preconditionFailure() + } + + func superEncoder(forKey key: Key) -> Encoder { + preconditionFailure() + } +} + +extension _AdaptedPostboxEncoder.KeyedContainer: AdaptedPostboxEncodingContainer {} diff --git a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxSingleValueEncodingContainer.swift b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxSingleValueEncodingContainer.swift new file mode 100644 index 0000000000..5ae52729e9 --- /dev/null +++ b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxSingleValueEncodingContainer.swift @@ -0,0 +1,85 @@ +import Foundation + +extension _AdaptedPostboxEncoder { + final class SingleValueContainer { + var codingPath: [CodingKey] + var userInfo: [CodingUserInfoKey: Any] + + init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) { + self.codingPath = codingPath + self.userInfo = userInfo + } + } +} + +extension _AdaptedPostboxEncoder.SingleValueContainer: SingleValueEncodingContainer { + func encodeNil() throws { + preconditionFailure() + } + + func encode(_ value: Bool) throws { + preconditionFailure() + } + + func encode(_ value: String) throws { + preconditionFailure() + } + + func encode(_ value: Double) throws { + preconditionFailure() + } + + func encode(_ value: Float) throws { + preconditionFailure() + } + + func encode(_ value: Int) throws { + preconditionFailure() + } + + func encode(_ value: Int8) throws { + preconditionFailure() + } + + func encode(_ value: Int16) throws { + preconditionFailure() + } + + func encode(_ value: Int32) throws { + preconditionFailure() + } + + func encode(_ value: Int64) throws { + preconditionFailure() + } + + func encode(_ value: UInt) throws { + preconditionFailure() + } + + func encode(_ value: UInt8) throws { + preconditionFailure() + } + + func encode(_ value: UInt16) throws { + preconditionFailure() + } + + func encode(_ value: UInt32) throws { + preconditionFailure() + } + + func encode(_ value: UInt64) throws { + preconditionFailure() + } + + func encode(_ value: T) throws where T : Encodable { + preconditionFailure() + } +} + +extension _AdaptedPostboxEncoder.SingleValueContainer: AdaptedPostboxEncodingContainer { + func makeData(addHeader: Bool) -> (Data, ValueType) { + preconditionFailure() + } +} diff --git a/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxUnkeyedEncodingContainer.swift b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxUnkeyedEncodingContainer.swift new file mode 100644 index 0000000000..568db0689a --- /dev/null +++ b/submodules/Postbox/Sources/Utils/Encoder/AdaptedPostboxUnkeyedEncodingContainer.swift @@ -0,0 +1,158 @@ +import Foundation +import MurMurHash32 + +extension _AdaptedPostboxEncoder { + final class UnkeyedContainer { + fileprivate enum Item { + case int32(Int32) + case int64(Int64) + case string(String) + case object(Data) + case data(Data) + } + + let codingPath: [CodingKey] + let userInfo: [CodingUserInfoKey: Any] + + fileprivate var items: [Item] = [] + + var count: Int { + return self.items.count + } + + init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey : Any]) { + self.codingPath = codingPath + self.userInfo = userInfo + } + + func makeData(addHeader: Bool) -> (Data, ValueType) { + precondition(addHeader) + + if self.items.isEmpty { + let buffer = WriteBuffer() + + var length: Int32 = Int32(self.items.count) + buffer.write(&length, offset: 0, length: 4) + + return (buffer.makeData(), .Int32Array) + } else if self.items.allSatisfy({ if case .int32 = $0 { return true } else { return false } }) { + let buffer = WriteBuffer() + + var length: Int32 = Int32(self.items.count) + buffer.write(&length, offset: 0, length: 4) + + for case .int32(var value) in self.items { + buffer.write(&value, offset: 0, length: 4) + } + + return (buffer.makeData(), .Int32Array) + } else if self.items.allSatisfy({ if case .int64 = $0 { return true } else { return false } }) { + let buffer = WriteBuffer() + + var length: Int32 = Int32(self.items.count) + buffer.write(&length, offset: 0, length: 4) + + for case .int64(var value) in self.items { + buffer.write(&value, offset: 0, length: 4) + } + + return (buffer.makeData(), .Int64Array) + } else if self.items.allSatisfy({ if case .string = $0 { return true } else { return false } }) { + let buffer = WriteBuffer() + + var length: Int32 = Int32(self.items.count) + buffer.write(&length, offset: 0, length: 4) + + for case .string(let value) in self.items { + let data = value.data(using: .utf8, allowLossyConversion: true) ?? (String("").data(using: .utf8)!) + var valueLength: Int32 = Int32(data.count) + buffer.write(&valueLength, offset: 0, length: 4) + buffer.write(data) + } + + return (buffer.makeData(), .StringArray) + } else if self.items.allSatisfy({ if case .object = $0 { return true } else { return false } }) { + let buffer = WriteBuffer() + + var length: Int32 = Int32(self.items.count) + buffer.write(&length, offset: 0, length: 4) + + for case .object(let data) in self.items { + buffer.write(data) + } + + return (buffer.makeData(), .ObjectArray) + } else if self.items.allSatisfy({ if case .data = $0 { return true } else { return false } }) { + let buffer = WriteBuffer() + + var length: Int32 = Int32(self.items.count) + buffer.write(&length, offset: 0, length: 4) + + for case .data(let data) in self.items { + var valueLength: Int32 = Int32(data.count) + buffer.write(&valueLength, offset: 0, length: 4) + buffer.write(data) + } + + return (buffer.makeData(), .BytesArray) + } else { + preconditionFailure() + } + } + } +} + +extension _AdaptedPostboxEncoder.UnkeyedContainer: UnkeyedEncodingContainer { + func encodeNil() throws { + preconditionFailure() + } + + func encode(_ value: T) throws where T : Encodable { + let typeHash: Int32 = murMurHashString32("\(type(of: value))") + + let innerEncoder = _AdaptedPostboxEncoder(typeHash: typeHash) + try! value.encode(to: innerEncoder) + + let (data, _) = innerEncoder.makeData(addHeader: true) + + let buffer = WriteBuffer() + + buffer.write(data) + + self.items.append(.object(buffer.makeData())) + } + + func encode(_ value: Int32) throws { + self.items.append(.int32(value)) + } + + func encode(_ value: Int64) throws { + self.items.append(.int64(value)) + } + + func encode(_ value: String) throws { + self.items.append(.string(value)) + } + + func encode(_ value: Data) throws { + self.items.append(.data(value)) + } + + func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer where NestedKey : CodingKey { + preconditionFailure() + } + + func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { + preconditionFailure() + } + + func superEncoder() -> Encoder { + preconditionFailure() + } +} + +extension _AdaptedPostboxEncoder.UnkeyedContainer: AdaptedPostboxEncodingContainer { + func makeData() -> Data { + preconditionFailure() + } +} diff --git a/submodules/Postbox/Sources/Utils/PostboxCodingAdapter.swift b/submodules/Postbox/Sources/Utils/PostboxCodingAdapter.swift new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/submodules/Postbox/Sources/Utils/PostboxCodingAdapter.swift @@ -0,0 +1 @@ + diff --git a/submodules/PresentationDataUtils/BUILD b/submodules/PresentationDataUtils/BUILD index 18415391b2..66b1049608 100644 --- a/submodules/PresentationDataUtils/BUILD +++ b/submodules/PresentationDataUtils/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Display:Display", "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", diff --git a/submodules/PresentationDataUtils/Sources/ItemListController.swift b/submodules/PresentationDataUtils/Sources/ItemListController.swift index e563d060a2..0da76bfc1c 100644 --- a/submodules/PresentationDataUtils/Sources/ItemListController.swift +++ b/submodules/PresentationDataUtils/Sources/ItemListController.swift @@ -4,7 +4,6 @@ import AlertUI import AccountContext import SwiftSignalKit import ItemListUI -import PresentationDataUtils public extension ItemListController { convenience init(context: AccountContext, state: Signal<(ItemListControllerState, (ItemListNodeState, ItemGenerationArguments)), NoError>, tabBarItem: Signal? = nil) { diff --git a/submodules/ProgressNavigationButtonNode/BUILD b/submodules/ProgressNavigationButtonNode/BUILD index ebf9a7e417..f6c0c21aa2 100644 --- a/submodules/ProgressNavigationButtonNode/BUILD +++ b/submodules/ProgressNavigationButtonNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Display:Display", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/QrCode/BUILD b/submodules/QrCode/BUILD index 4cba639671..7ece4c25ae 100644 --- a/submodules/QrCode/BUILD +++ b/submodules/QrCode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/Display:Display", diff --git a/submodules/RadialStatusNode/BUILD b/submodules/RadialStatusNode/BUILD index e6c85db366..c759d1bc5a 100644 --- a/submodules/RadialStatusNode/BUILD +++ b/submodules/RadialStatusNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Display:Display", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/Reachability/BUILD b/submodules/Reachability/BUILD index 5e08b22c98..c6191f98c0 100644 --- a/submodules/Reachability/BUILD +++ b/submodules/Reachability/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Reachability/LegacyReachability:LegacyReachability", "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", diff --git a/submodules/ReactionSelectionNode/BUILD b/submodules/ReactionSelectionNode/BUILD index c3d3459fc4..9e1c9e099d 100644 --- a/submodules/ReactionSelectionNode/BUILD +++ b/submodules/ReactionSelectionNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Postbox:Postbox", "//submodules/TelegramCore:TelegramCore", diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index e0270afa8f..1018936ef4 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -261,15 +261,8 @@ public final class ReactionContextNode: ASDisplayNode { let row = CGFloat(rowIndex) let column = CGFloat(columnIndex) - let isHighlighted = false - - var itemSize: CGFloat = minimizedItemSize - var itemOffset: CGFloat = 0.0 - if isHighlighted { - let updatedSize = itemSize * 1.15 - itemOffset = (updatedSize - itemSize) / 2.0 - itemSize = updatedSize - } + let itemSize: CGFloat = minimizedItemSize + let itemOffset: CGFloat = 0.0 let itemFrame = CGRect(origin: CGPoint(x: sideInset + column * (minimizedItemSize + itemSpacing) - itemOffset, y: verticalInset + row * (rowHeight + rowSpacing) + floor((rowHeight - minimizedItemSize) / 2.0) - itemOffset), size: CGSize(width: itemSize, height: itemSize)) transition.updateFrame(node: self.itemNodes[i], frame: itemFrame, beginWithCurrentState: true) diff --git a/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift index b6e96b6f7f..b6f695e21c 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift @@ -215,7 +215,7 @@ final class ReactionSelectionNode: ASDisplayNode { } let reactionSideInset: CGFloat = 10.0 - var reactionSpacing: CGFloat = 6.0 + let reactionSpacing: CGFloat = 6.0 let minReactionSpacing: CGFloat = 2.0 let minimizedReactionSize = self.minimizedReactionSize let contentWidth: CGFloat = CGFloat(self.reactions.count) * (minimizedReactionSize) + CGFloat(self.reactions.count - 1) * reactionSpacing + reactionSideInset * 2.0 @@ -252,9 +252,7 @@ final class ReactionSelectionNode: ASDisplayNode { interReactionSpacing = reactionSpacing } - if isInitial && self.reactionNodes.isEmpty { - let availableContentWidth = constrainedSize.width //max(100.0, initialAnchorX) - + if isInitial && self.reactionNodes.isEmpty { self.shadowBlur = floor(minimizedReactionSize * 0.26) self.smallCircleSize = 14.0 diff --git a/submodules/ReactionSelectionNode/Sources/ReactionSwipeGestureRecognizer.swift b/submodules/ReactionSelectionNode/Sources/ReactionSwipeGestureRecognizer.swift index 1226d74b03..44cc887651 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionSwipeGestureRecognizer.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionSwipeGestureRecognizer.swift @@ -110,7 +110,7 @@ public final class ReactionSwipeGestureRecognizer: UIPanGestureRecognizer { strongSelf.activationTimer = nil if strongSelf.validatedGesture { let location = strongSelf.currentLocation - if !strongSelf.currentReactions.isEmpty, let reactionContainer = strongSelf.getReactionContainer?(), let localAnchorPoint = strongSelf.getAnchorPoint?() { + if !strongSelf.currentReactions.isEmpty, let reactionContainer = strongSelf.getReactionContainer?(), let _ = strongSelf.getAnchorPoint?() { strongSelf.currentContainer = reactionContainer //let reactionContainerLocation = reactionContainer.view.convert(localAnchorPoint, from: strongSelf.view) let elevate = strongSelf.shouldElevateAnchorPoint?() ?? false diff --git a/submodules/SSignalKit/SSignalKit/BUILD b/submodules/SSignalKit/SSignalKit/BUILD index 5a980e8242..e27693a320 100644 --- a/submodules/SSignalKit/SSignalKit/BUILD +++ b/submodules/SSignalKit/SSignalKit/BUILD @@ -6,6 +6,9 @@ objc_library( srcs = glob([ "Source/SSignalKit/*.m", ]), + copts = [ + "-Werror", + ], hdrs = glob([ "Source/SSignalKit/*.h", ]), diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SAtomic.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SAtomic.h index 6d3e35b4dc..3e29f89bdf 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SAtomic.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SAtomic.h @@ -2,11 +2,11 @@ @interface SAtomic : NSObject -- (instancetype)initWithValue:(id)value; -- (instancetype)initWithValue:(id)value recursive:(bool)recursive; -- (id)swap:(id)newValue; -- (id)value; -- (id)modify:(id (^)(id))f; -- (id)with:(id (^)(id))f; +- (instancetype _Nonnull)initWithValue:(id _Nullable)value; +- (instancetype _Nonnull)initWithValue:(id _Nullable)value recursive:(bool)recursive; +- (id _Nullable)swap:(id _Nullable)newValue; +- (id _Nullable)value; +- (id _Nullable)modify:(id _Nullable (^ _Nonnull)(id _Nullable))f; +- (id _Nullable)with:(id _Nullable (^ _Nonnull)(id _Nullable))f; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SBag.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SBag.h index 7b79ead30f..6d9ca7a9bd 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SBag.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SBag.h @@ -2,10 +2,10 @@ @interface SBag : NSObject -- (NSInteger)addItem:(id)item; -- (void)enumerateItems:(void (^)(id))block; +- (NSInteger)addItem:(id _Nonnull)item; +- (void)enumerateItems:(void (^ _Nonnull)(id _Nonnull))block; - (void)removeItem:(NSInteger)key; - (bool)isEmpty; -- (NSArray *)copyItems; +- (NSArray * _Nonnull)copyItems; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SBlockDisposable.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SBlockDisposable.h index 2914604cba..9bf7f5a06d 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SBlockDisposable.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SBlockDisposable.h @@ -2,6 +2,6 @@ @interface SBlockDisposable : NSObject -- (instancetype)initWithBlock:(void (^)())block; +- (instancetype _Nonnull)initWithBlock:(void (^ _Nullable)())block; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SDisposableSet.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SDisposableSet.h index 7d7515c968..e7ec5b4c4c 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SDisposableSet.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SDisposableSet.h @@ -4,7 +4,7 @@ @interface SDisposableSet : NSObject -- (void)add:(id)disposable; -- (void)remove:(id)disposable; +- (void)add:(id _Nonnull)disposable; +- (void)remove:(id _Nonnull)disposable; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SMetaDisposable.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SMetaDisposable.h index 8938f9eacb..e22e797539 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SMetaDisposable.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SMetaDisposable.h @@ -2,6 +2,6 @@ @interface SMetaDisposable : NSObject -- (void)setDisposable:(id)disposable; +- (void)setDisposable:(id _Nullable)disposable; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SMulticastSignalManager.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SMulticastSignalManager.h index 243f015a52..af2bb7ad21 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SMulticastSignalManager.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SMulticastSignalManager.h @@ -2,10 +2,10 @@ @interface SMulticastSignalManager : NSObject -- (SSignal *)multicastedSignalForKey:(NSString *)key producer:(SSignal *(^)())producer; -- (void)startStandaloneSignalIfNotRunningForKey:(NSString *)key producer:(SSignal *(^)())producer; +- (SSignal * _Nonnull)multicastedSignalForKey:(NSString * _Nonnull)key producer:(SSignal * _Nonnull (^ _Nonnull)())producer; +- (void)startStandaloneSignalIfNotRunningForKey:(NSString * _Nonnull)key producer:(SSignal * _Nonnull (^ _Nonnull)())producer; -- (SSignal *)multicastedPipeForKey:(NSString *)key; -- (void)putNext:(id)next toMulticastedPipeForKey:(NSString *)key; +- (SSignal * _Nonnull)multicastedPipeForKey:(NSString * _Nonnull)key; +- (void)putNext:(id _Nullable)next toMulticastedPipeForKey:(NSString * _Nonnull)key; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SQueue.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SQueue.h index 228334c888..9f45afd273 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SQueue.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SQueue.h @@ -2,17 +2,17 @@ @interface SQueue : NSObject -+ (SQueue *)mainQueue; -+ (SQueue *)concurrentDefaultQueue; -+ (SQueue *)concurrentBackgroundQueue; ++ (SQueue * _Nonnull)mainQueue; ++ (SQueue * _Nonnull)concurrentDefaultQueue; ++ (SQueue * _Nonnull)concurrentBackgroundQueue; -+ (SQueue *)wrapConcurrentNativeQueue:(dispatch_queue_t)nativeQueue; ++ (SQueue * _Nonnull)wrapConcurrentNativeQueue:(dispatch_queue_t _Nonnull)nativeQueue; -- (void)dispatch:(dispatch_block_t)block; -- (void)dispatchSync:(dispatch_block_t)block; -- (void)dispatch:(dispatch_block_t)block synchronous:(bool)synchronous; +- (void)dispatch:(dispatch_block_t _Nonnull)block; +- (void)dispatchSync:(dispatch_block_t _Nonnull)block; +- (void)dispatch:(dispatch_block_t _Nonnull)block synchronous:(bool)synchronous; -- (dispatch_queue_t)_dispatch_queue; +- (dispatch_queue_t _Nonnull)_dispatch_queue; - (bool)isCurrentQueue; diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SQueueLocalObject.m b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SQueueLocalObject.m index 019a50a695..0b12201362 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SQueueLocalObject.m +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SQueueLocalObject.m @@ -9,12 +9,12 @@ #import "SQueueLocalObject.h" @implementation SQueueLocalObject { - SQueue *queue; + SQueue *_queue; id valueRef; } -(id)initWithQueue:(SQueue *)queue generate:(id _Nonnull (^)(void))next { if (self = [super init]) { - self->queue = queue; + self->_queue = queue; [queue dispatch:^{ self->valueRef = next(); }]; @@ -23,7 +23,7 @@ } -(void)with:(void (^)(id object))f { - [self->queue dispatch:^{ + [self->_queue dispatch:^{ f(self->valueRef); }]; } @@ -31,7 +31,7 @@ -(void)dealloc { __block id value = self->valueRef; self->valueRef = nil; - [queue dispatch:^{ + [_queue dispatch:^{ value = nil; }]; } diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Accumulate.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Accumulate.h index 1ea8a51ed0..8c17743017 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Accumulate.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Accumulate.h @@ -2,7 +2,7 @@ @interface SSignal (Accumulate) -- (SSignal *)reduceLeft:(id)value with:(id (^)(id, id))f; -- (SSignal *)reduceLeftWithPassthrough:(id)value with:(id (^)(id, id, void (^)(id)))f; +- (SSignal * _Nonnull)reduceLeft:(id _Nullable)value with:(id _Nullable (^ _Nonnull)(id _Nullable, id _Nullable))f; +- (SSignal * _Nonnull)reduceLeftWithPassthrough:(id _Nullable)value with:(id _Nullable (^ _Nonnull)(id _Nullable, id _Nullable, void (^ _Nonnull)(id _Nullable)))f; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Catch.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Catch.h index dc8898c062..43e8029ab0 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Catch.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Catch.h @@ -2,8 +2,8 @@ @interface SSignal (Catch) -- (SSignal *)catch:(SSignal *(^)(id error))f; -- (SSignal *)restart; -- (SSignal *)retryIf:(bool (^)(id error))predicate; +- (SSignal * _Nonnull)catch:(SSignal * _Nonnull (^ _Nonnull )(id _Nullable error))f; +- (SSignal * _Nonnull)restart; +- (SSignal * _Nonnull)retryIf:(bool (^ _Nonnull)(id _Nullable error))predicate; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Combine.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Combine.h index d84e065311..2038e06e52 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Combine.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Combine.h @@ -2,9 +2,9 @@ @interface SSignal (Combine) -+ (SSignal *)combineSignals:(NSArray *)signals; -+ (SSignal *)combineSignals:(NSArray *)signals withInitialStates:(NSArray *)initialStates; ++ (SSignal * _Nonnull)combineSignals:(NSArray * _Nonnull)signals; ++ (SSignal * _Nonnull)combineSignals:(NSArray * _Nonnull)signals withInitialStates:(NSArray * _Nullable)initialStates; -+ (SSignal *)mergeSignals:(NSArray *)signals; ++ (SSignal * _Nonnull)mergeSignals:(NSArray * _Nonnull)signals; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Dispatch.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Dispatch.h index a22679b372..21d0bfb17e 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Dispatch.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Dispatch.h @@ -5,10 +5,10 @@ @interface SSignal (Dispatch) -- (SSignal *)deliverOn:(SQueue *)queue; -- (SSignal *)deliverOnThreadPool:(SThreadPool *)threadPool; -- (SSignal *)startOn:(SQueue *)queue; -- (SSignal *)startOnThreadPool:(SThreadPool *)threadPool; -- (SSignal *)throttleOn:(SQueue *)queue delay:(NSTimeInterval)delay; +- (SSignal * _Nonnull)deliverOn:(SQueue * _Nonnull)queue; +- (SSignal * _Nonnull)deliverOnThreadPool:(SThreadPool * _Nonnull)threadPool; +- (SSignal * _Nonnull)startOn:(SQueue * _Nonnull)queue; +- (SSignal * _Nonnull)startOnThreadPool:(SThreadPool * _Nonnull)threadPool; +- (SSignal * _Nonnull)throttleOn:(SQueue * _Nonnull)queue delay:(NSTimeInterval)delay; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Mapping.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Mapping.h index 81a16816e2..75fb909dea 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Mapping.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Mapping.h @@ -2,8 +2,8 @@ @interface SSignal (Mapping) -- (SSignal *)map:(id (^)(id))f; -- (SSignal *)filter:(bool (^)(id))f; -- (SSignal *)ignoreRepeated; +- (SSignal * _Nonnull)map:(id _Nullable (^ _Nonnull)(id _Nullable))f; +- (SSignal * _Nonnull)filter:(bool (^ _Nonnull)(id _Nullable))f; +- (SSignal * _Nonnull)ignoreRepeated; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Meta.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Meta.h index 2d90df13f8..0e1893fc02 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Meta.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Meta.h @@ -4,19 +4,19 @@ @interface SSignal (Meta) -- (SSignal *)switchToLatest; -- (SSignal *)mapToSignal:(SSignal *(^)(id))f; -- (SSignal *)mapToQueue:(SSignal *(^)(id))f; -- (SSignal *)mapToThrottled:(SSignal *(^)(id))f; -- (SSignal *)then:(SSignal *)signal; -- (SSignal *)queue; -- (SSignal *)throttled; -+ (SSignal *)defer:(SSignal *(^)())generator; +- (SSignal * _Nonnull)switchToLatest; +- (SSignal * _Nonnull)mapToSignal:(SSignal * _Nonnull (^ _Nonnull)(id _Nullable))f; +- (SSignal * _Nonnull)mapToQueue:(SSignal * _Nonnull (^ _Nonnull)(id _Nullable))f; +- (SSignal * _Nonnull)mapToThrottled:(SSignal * _Nonnull (^ _Nonnull)(id _Nullable))f; +- (SSignal * _Nonnull)then:(SSignal * _Nonnull)signal; +- (SSignal * _Nonnull)queue; +- (SSignal * _Nonnull)throttled; ++ (SSignal * _Nonnull)defer:(SSignal * _Nonnull(^ _Nonnull)())generator; @end @interface SSignalQueue : NSObject -- (SSignal *)enqueue:(SSignal *)signal; +- (SSignal * _Nonnull)enqueue:(SSignal * _Nonnull)signal; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Multicast.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Multicast.h index e0720cc103..817f3a77d0 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Multicast.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Multicast.h @@ -2,6 +2,6 @@ @interface SSignal (Multicast) -- (SSignal *)multicast; +- (SSignal * _Nonnull)multicast; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Pipe.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Pipe.h index 2a21ee4cf7..fa24fdf597 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Pipe.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Pipe.h @@ -2,10 +2,10 @@ @interface SPipe : NSObject -@property (nonatomic, copy, readonly) SSignal *(^signalProducer)(); -@property (nonatomic, copy, readonly) void (^sink)(id); +@property (nonatomic, copy, readonly) SSignal * _Nonnull (^ _Nonnull signalProducer)(); +@property (nonatomic, copy, readonly) void (^ _Nonnull sink)(id _Nullable); -- (instancetype)initWithReplay:(bool)replay; +- (instancetype _Nonnull)initWithReplay:(bool)replay; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+SideEffects.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+SideEffects.h index 8107fcf4cb..9c7ba05910 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+SideEffects.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+SideEffects.h @@ -2,12 +2,12 @@ @interface SSignal (SideEffects) -- (SSignal *)onStart:(void (^)())f; -- (SSignal *)onNext:(void (^)(id next))f; -- (SSignal *)afterNext:(void (^)(id next))f; -- (SSignal *)onError:(void (^)(id error))f; -- (SSignal *)onCompletion:(void (^)())f; -- (SSignal *)afterCompletion:(void (^)())f; -- (SSignal *)onDispose:(void (^)())f; +- (SSignal * _Nonnull)onStart:(void (^ _Nonnull)())f; +- (SSignal * _Nonnull)onNext:(void (^ _Nonnull)(id _Nullable next))f; +- (SSignal * _Nonnull)afterNext:(void (^ _Nonnull)(id _Nullable next))f; +- (SSignal * _Nonnull)onError:(void (^ _Nonnull)(id _Nullable error))f; +- (SSignal * _Nonnull)onCompletion:(void (^ _Nonnull)())f; +- (SSignal * _Nonnull)afterCompletion:(void (^ _Nonnull)())f; +- (SSignal * _Nonnull)onDispose:(void (^ _Nonnull)())f; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Single.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Single.h index 75f48ff569..46693c0b08 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Single.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Single.h @@ -2,9 +2,9 @@ @interface SSignal (Single) -+ (SSignal *)single:(id)next; -+ (SSignal *)fail:(id)error; -+ (SSignal *)never; -+ (SSignal *)complete; ++ (SSignal * _Nonnull)single:(id _Nullable)next; ++ (SSignal * _Nonnull)fail:(id _Nullable)error; ++ (SSignal * _Nonnull)never; ++ (SSignal * _Nonnull)complete; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Take.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Take.h index a4d1ff23eb..9128e4a7f7 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Take.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Take.h @@ -2,8 +2,8 @@ @interface SSignal (Take) -- (SSignal *)take:(NSUInteger)count; -- (SSignal *)takeLast; -- (SSignal *)takeUntilReplacement:(SSignal *)replacement; +- (SSignal * _Nonnull)take:(NSUInteger)count; +- (SSignal * _Nonnull)takeLast; +- (SSignal * _Nonnull)takeUntilReplacement:(SSignal * _Nonnull)replacement; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Timing.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Timing.h index 4b5d50c90e..ae4c7d4d2a 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Timing.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal+Timing.h @@ -4,8 +4,8 @@ @interface SSignal (Timing) -- (SSignal *)delay:(NSTimeInterval)seconds onQueue:(SQueue *)queue; -- (SSignal *)timeout:(NSTimeInterval)seconds onQueue:(SQueue *)queue orSignal:(SSignal *)signal; -- (SSignal *)wait:(NSTimeInterval)seconds; +- (SSignal * _Nonnull)delay:(NSTimeInterval)seconds onQueue:(SQueue * _Nonnull)queue; +- (SSignal * _Nonnull)timeout:(NSTimeInterval)seconds onQueue:(SQueue * _Nonnull)queue orSignal:(SSignal * _Nonnull)signal; +- (SSignal * _Nonnull)wait:(NSTimeInterval)seconds; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal.h index 46b7385d36..f80728062d 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSignal.h @@ -3,16 +3,16 @@ @interface SSignal : NSObject { @public - id (^_generator)(SSubscriber *); + id _Nullable (^ _Nonnull _generator)(SSubscriber * _Nonnull); } -- (instancetype)initWithGenerator:(id (^)(SSubscriber *))generator; +- (instancetype _Nonnull)initWithGenerator:(id _Nullable (^ _Nonnull)(SSubscriber * _Nonnull))generator; -- (id)startWithNext:(void (^)(id next))next error:(void (^)(id error))error completed:(void (^)())completed; -- (id)startWithNext:(void (^)(id next))next; -- (id)startWithNext:(void (^)(id next))next completed:(void (^)())completed; +- (id _Nullable)startWithNext:(void (^ _Nullable)(id _Nullable next))next error:(void (^ _Nullable)(id _Nullable error))error completed:(void (^ _Nullable)())completed; +- (id _Nullable)startWithNext:(void (^ _Nullable)(id _Nullable next))next; +- (id _Nullable)startWithNext:(void (^ _Nullable)(id _Nullable next))next completed:(void (^ _Nullable)())completed; -- (SSignal *)trace:(NSString *)name; +- (SSignal * _Nonnull)trace:(NSString * _Nonnull)name; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSubscriber.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSubscriber.h index ce4fee5678..fdf0460993 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSubscriber.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SSubscriber.h @@ -4,19 +4,19 @@ { } -- (instancetype)initWithNext:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed; +- (instancetype _Nonnull)initWithNext:(void (^ _Nullable)(id _Nullable))next error:(void (^ _Nullable)(id _Nullable))error completed:(void (^ _Nullable)())completed; -- (void)_assignDisposable:(id)disposable; +- (void)_assignDisposable:(id _Nullable)disposable; - (void)_markTerminatedWithoutDisposal; -- (void)putNext:(id)next; -- (void)putError:(id)error; +- (void)putNext:(id _Nullable)next; +- (void)putError:(id _Nullable)error; - (void)putCompletion; @end @interface STracingSubscriber : SSubscriber -- (instancetype)initWithName:(NSString *)name next:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed; +- (instancetype _Nonnull)initWithName:(NSString * _Nonnull)name next:(void (^ _Nullable)(id _Nullable))next error:(void (^ _Nullable)(id _Nullable))error completed:(void (^ _Nullable)())completed; -@end \ No newline at end of file +@end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SThreadPool.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SThreadPool.h index 69d0565938..b8978eb4f3 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SThreadPool.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SThreadPool.h @@ -5,11 +5,11 @@ @interface SThreadPool : NSObject -- (instancetype)initWithThreadCount:(NSUInteger)threadCount threadPriority:(double)threadPriority; +- (instancetype _Nonnull)initWithThreadCount:(NSUInteger)threadCount threadPriority:(double)threadPriority; -- (void)addTask:(SThreadPoolTask *)task; +- (void)addTask:(SThreadPoolTask * _Nonnull)task; -- (SThreadPoolQueue *)nextQueue; -- (void)_workOnQueue:(SThreadPoolQueue *)queue block:(void (^)())block; +- (SThreadPoolQueue * _Nonnull)nextQueue; +- (void)_workOnQueue:(SThreadPoolQueue * _Nonnull)queue block:(void (^ _Nonnull)())block; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SThreadPoolQueue.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SThreadPoolQueue.h index 3d8d53b00c..51d63009f0 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SThreadPoolQueue.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SThreadPoolQueue.h @@ -5,9 +5,9 @@ @interface SThreadPoolQueue : NSObject -- (instancetype)initWithThreadPool:(SThreadPool *)threadPool; -- (void)addTask:(SThreadPoolTask *)task; -- (SThreadPoolTask *)_popFirstTask; +- (instancetype _Nonnull)initWithThreadPool:(SThreadPool * _Nonnull)threadPool; +- (void)addTask:(SThreadPoolTask * _Nonnull)task; +- (SThreadPoolTask * _Nullable)_popFirstTask; - (bool)_hasTasks; @end diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SThreadPoolTask.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SThreadPoolTask.h index e8da985ca0..bb9f476cce 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SThreadPoolTask.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SThreadPoolTask.h @@ -2,7 +2,7 @@ @interface SThreadPoolTask : NSObject -- (instancetype)initWithBlock:(void (^)(bool (^)()))block; +- (instancetype _Nonnull)initWithBlock:(void (^ _Nonnull)(bool (^ _Nonnull)()))block; - (void)execute; - (void)cancel; diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/STimer.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/STimer.h index 621e4232b2..0ad33b4569 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/STimer.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/STimer.h @@ -4,8 +4,8 @@ @interface STimer : NSObject -- (id)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(dispatch_block_t)completion queue:(SQueue *)queue; -- (id)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(dispatch_block_t)completion nativeQueue:(dispatch_queue_t)nativeQueue; +- (instancetype _Nonnull)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(dispatch_block_t _Nonnull)completion queue:(SQueue * _Nonnull)queue; +- (instancetype _Nonnull)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(dispatch_block_t _Nonnull)completion nativeQueue:(dispatch_queue_t _Nonnull)nativeQueue; - (void)start; - (void)invalidate; diff --git a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SVariable.h b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SVariable.h index 47d51a1608..ffd3fe7a62 100644 --- a/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SVariable.h +++ b/submodules/SSignalKit/SSignalKit/Source/SSignalKit/SVariable.h @@ -4,9 +4,9 @@ @interface SVariable : NSObject -- (instancetype)init; +- (instancetype _Nonnull)init; -- (void)set:(SSignal *)signal; -- (SSignal *)signal; +- (void)set:(SSignal * _Nonnull)signal; +- (SSignal * _Nonnull)signal; @end diff --git a/submodules/SSignalKit/SwiftSignalKit/BUILD b/submodules/SSignalKit/SwiftSignalKit/BUILD index 16720552fe..b3c12dbee1 100644 --- a/submodules/SSignalKit/SwiftSignalKit/BUILD +++ b/submodules/SSignalKit/SwiftSignalKit/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Source/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], visibility = [ "//visibility:public", ], diff --git a/submodules/SSignalKit/SwiftSignalKit/Source/Disposable.swift b/submodules/SSignalKit/SwiftSignalKit/Source/Disposable.swift index 8a9b9ce65a..28fa733282 100644 --- a/submodules/SSignalKit/SwiftSignalKit/Source/Disposable.swift +++ b/submodules/SSignalKit/SwiftSignalKit/Source/Disposable.swift @@ -1,6 +1,6 @@ import Foundation -public protocol Disposable: class { +public protocol Disposable: AnyObject { func dispose() } diff --git a/submodules/SSignalKit/SwiftSignalKit/Source/Multicast.swift b/submodules/SSignalKit/SwiftSignalKit/Source/Multicast.swift index b1ddec5a3c..8aff97ac08 100644 --- a/submodules/SSignalKit/SwiftSignalKit/Source/Multicast.swift +++ b/submodules/SSignalKit/SwiftSignalKit/Source/Multicast.swift @@ -48,9 +48,6 @@ public final class Multicast { subscriber(next) } }, error: { _ in - self.lock.locked { - let _ = self.instances.removeValue(forKey: key) - } }, completed: { self.lock.locked { self.instances.removeValue(forKey: key) diff --git a/submodules/SSignalKit/SwiftSignalKit/Source/Signal_Materialize.swift b/submodules/SSignalKit/SwiftSignalKit/Source/Signal_Materialize.swift index 92e7a9b3c9..e0e8330dbf 100644 --- a/submodules/SSignalKit/SwiftSignalKit/Source/Signal_Materialize.swift +++ b/submodules/SSignalKit/SwiftSignalKit/Source/Signal_Materialize.swift @@ -32,7 +32,6 @@ public func materialize(signal: Signal, NoError>) -> Sig subscriber.putCompletion() } }, error: { _ in - subscriber.putCompletion() }, completed: { subscriber.putCompletion() }) diff --git a/submodules/SaveToCameraRoll/BUILD b/submodules/SaveToCameraRoll/BUILD index 7776ead2dc..d7db544f62 100644 --- a/submodules/SaveToCameraRoll/BUILD +++ b/submodules/SaveToCameraRoll/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/Postbox:Postbox", diff --git a/submodules/ScreenCaptureDetection/BUILD b/submodules/ScreenCaptureDetection/BUILD index 81af8cfe2a..39f82dfb7d 100644 --- a/submodules/ScreenCaptureDetection/BUILD +++ b/submodules/ScreenCaptureDetection/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", ], diff --git a/submodules/SearchBarNode/BUILD b/submodules/SearchBarNode/BUILD index 1a7e748afb..5545d3b679 100644 --- a/submodules/SearchBarNode/BUILD +++ b/submodules/SearchBarNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/SearchPeerMembers/BUILD b/submodules/SearchPeerMembers/BUILD index cb4a8fb27a..2ba9d1ef0e 100644 --- a/submodules/SearchPeerMembers/BUILD +++ b/submodules/SearchPeerMembers/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/Postbox:Postbox", diff --git a/submodules/SearchUI/BUILD b/submodules/SearchUI/BUILD index 6a7bb45b4c..7210ffaa38 100644 --- a/submodules/SearchUI/BUILD +++ b/submodules/SearchUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/SectionHeaderItem/BUILD b/submodules/SectionHeaderItem/BUILD index e8089be45a..8d7a15d7fc 100644 --- a/submodules/SectionHeaderItem/BUILD +++ b/submodules/SectionHeaderItem/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/SegmentedControlNode/BUILD b/submodules/SegmentedControlNode/BUILD index f85e3f63b3..ff054555a3 100644 --- a/submodules/SegmentedControlNode/BUILD +++ b/submodules/SegmentedControlNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/SelectablePeerNode/BUILD b/submodules/SelectablePeerNode/BUILD index d770eaf552..752fbcac44 100644 --- a/submodules/SelectablePeerNode/BUILD +++ b/submodules/SelectablePeerNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/Postbox:Postbox", diff --git a/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift b/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift index 95daf00db5..48643b1b3b 100644 --- a/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift +++ b/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift @@ -2,7 +2,6 @@ import Foundation import UIKit import AsyncDisplayKit import Display -import Postbox import TelegramCore import SwiftSignalKit import TelegramPresentationData @@ -83,7 +82,7 @@ public final class SelectablePeerNode: ASDisplayNode { private var currentSelected = false - private var peer: RenderedPeer? + private var peer: EngineRenderedPeer? public var theme: SelectablePeerNodeTheme = SelectablePeerNodeTheme(textColor: .black, secretTextColor: .green, selectedTextColor: .blue, checkBackgroundColor: .white, checkFillColor: .blue, checkColor: .white, avatarPlaceholderColor: .white) { didSet { @@ -136,7 +135,7 @@ public final class SelectablePeerNode: ASDisplayNode { } } - public func setup(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: RenderedPeer, online: Bool = false, numberOfLines: Int = 2, synchronousLoad: Bool) { + public func setup(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: EngineRenderedPeer, online: Bool = false, numberOfLines: Int = 2, synchronousLoad: Bool) { let isFirstTime = self.peer == nil self.peer = peer guard let mainPeer = peer.chatMainPeer else { diff --git a/submodules/SemanticStatusNode/BUILD b/submodules/SemanticStatusNode/BUILD index 43a601eeee..e4a4b8d840 100644 --- a/submodules/SemanticStatusNode/BUILD +++ b/submodules/SemanticStatusNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Display:Display", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift b/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift index 970de65856..9852a8d08c 100644 --- a/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift +++ b/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift @@ -40,7 +40,7 @@ private protocol SemanticStatusNodeStateDrawingState: NSObjectProtocol { func draw(context: CGContext, size: CGSize, foregroundColor: UIColor) } -private protocol SemanticStatusNodeStateContext: class { +private protocol SemanticStatusNodeStateContext: AnyObject { var isAnimating: Bool { get } var requestUpdate: () -> Void { get set } @@ -273,6 +273,7 @@ private final class SemanticStatusNodeIconContext: SemanticStatusNodeStateContex strongSelf.requestUpdate() } } + self.animationNode?.enqueueState(self.icon == .play ? .play : .pause, animated: false) self.iconImage = self.animationNode?.image self.iconOffset = 1.5 } diff --git a/submodules/SettingsUI/Sources/Data and Storage/StorageUsageController.swift b/submodules/SettingsUI/Sources/Data and Storage/StorageUsageController.swift index 92dbf9794b..f3cdb336b6 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/StorageUsageController.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/StorageUsageController.swift @@ -715,7 +715,7 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P } var items: [ActionSheetItem] = [] - items.append(DeleteChatPeerActionSheetItem(context: context, peer: peer, chatPeer: peer, action: .clearCache, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder)) + items.append(DeleteChatPeerActionSheetItem(context: context, peer: EnginePeer(peer), chatPeer: EnginePeer(peer), action: .clearCache, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder)) let validCategories: [PeerCacheUsageCategory] = [.image, .video, .audio, .file] diff --git a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift index 1cb821a38b..6743534d02 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift @@ -270,7 +270,7 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode { } if let peer = item.peer { - strongSelf.avatarNode.setPeer(context: item.context, theme: item.theme, peer: peer, authorOfMessage: nil, overrideImage: nil, emptyColor: nil, clipStyle: .none, synchronousLoad: false) + strongSelf.avatarNode.setPeer(context: item.context, theme: item.theme, peer: EnginePeer(peer), authorOfMessage: nil, overrideImage: nil, emptyColor: nil, clipStyle: .none, synchronousLoad: false) } let revealOffset = strongSelf.revealOffset diff --git a/submodules/SettingsUI/Sources/TabBarAccountSwitchControllerNode.swift b/submodules/SettingsUI/Sources/TabBarAccountSwitchControllerNode.swift index e5583881d4..4c12bdf00c 100644 --- a/submodules/SettingsUI/Sources/TabBarAccountSwitchControllerNode.swift +++ b/submodules/SettingsUI/Sources/TabBarAccountSwitchControllerNode.swift @@ -189,7 +189,7 @@ private final class SwitchAccountItemNode: ASDisplayNode, AbstractSwitchAccountI return (titleSize.width + leftInset + rightInset, height, { width in let avatarSize = CGSize(width: 30.0, height: 30.0) self.avatarNode.frame = CGRect(origin: CGPoint(x: floor((leftInset - avatarSize.width) / 2.0), y: floor((height - avatarSize.height) / 2.0)), size: avatarSize) - self.avatarNode.setPeer(context: self.context, theme: self.presentationData.theme, peer: self.peer) + self.avatarNode.setPeer(context: self.context, theme: self.presentationData.theme, peer: EnginePeer(self.peer)) self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: floor((height - titleSize.height) / 2.0)), size: titleSize) diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index 5b35f2b901..9ca40e2478 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -210,7 +210,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) { var items: [ChatListItem] = [] - let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in + let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in gesture?.cancel() diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index 69574a18dc..d3148cca6b 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -778,7 +778,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) { var items: [ChatListItem] = [] - let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in + let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in gesture?.cancel() diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index 4ebba51e1e..2a49094e7f 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -355,7 +355,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) { var items: [ChatListItem] = [] - let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in + let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in gesture?.cancel() diff --git a/submodules/ShareController/BUILD b/submodules/ShareController/BUILD index 44333ca34d..16c1e671b9 100644 --- a/submodules/ShareController/BUILD +++ b/submodules/ShareController/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", @@ -27,6 +30,7 @@ swift_library( "//submodules/AccountContext:AccountContext", "//submodules/SegmentedControlNode:SegmentedControlNode", "//submodules/WallpaperBackgroundNode:WallpaperBackgroundNode", + "//submodules/ShimmerEffect:ShimmerEffect", ], visibility = [ "//visibility:public", diff --git a/submodules/ShareController/Sources/ShareContentContainerNode.swift b/submodules/ShareController/Sources/ShareContentContainerNode.swift index e3e7a612a9..8526858de6 100644 --- a/submodules/ShareController/Sources/ShareContentContainerNode.swift +++ b/submodules/ShareController/Sources/ShareContentContainerNode.swift @@ -3,7 +3,7 @@ import UIKit import Display import Postbox -public protocol ShareContentContainerNode: class { +public protocol ShareContentContainerNode: AnyObject { func activate() func deactivate() func setEnsurePeerVisibleOnLayout(_ peerId: PeerId?) diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index d836ddb15c..62c62c5be4 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -748,23 +748,7 @@ public final class ShareController: ViewController { } } - var activities: [UIActivity]? - if false, #available(iOS 10.0, *), strongSelf.sharedContext.applicationBindings.canOpenUrl("instagram-stories://"), case let .messages(messages) = strongSelf.subject, let message = messages.first, let peer = message.peers[message.id.peerId] { - let shareToInstagram = ShareToInstagramActivity(action: { sharedItems in - let renderer = MessageStoryRenderer(context: strongSelf.currentContext, messages: messages) - - let layout = ContainerViewLayout(size: CGSize(width: 414.0, height: 896.0), metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact), deviceMetrics: .iPhoneX, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), additionalInsets: UIEdgeInsets(), statusBarHeight: 0.0, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false) - renderer.update(layout: layout) { image in - if let data = image?.pngData() { - let pasteboardItems: [[String: Any]] = [["com.instagram.sharedSticker.backgroundImage": data, - "com.instagram.sharedSticker.contentURL": "https://t.me/\(peer.addressName ?? "")/\(message.id.id)"]] - UIPasteboard.general.setItems(pasteboardItems, options: [.expirationDate: Date().addingTimeInterval(5 * 60)]) - strongSelf.sharedContext.applicationBindings.openUrl("instagram-stories://share") - } - } - }) - activities = [shareToInstagram] - } + let activities: [UIActivity]? = nil let _ = (strongSelf.didAppearPromise.get() |> filter { $0 } @@ -803,7 +787,7 @@ public final class ShareController: ViewController { } var items: [ActionSheetItem] = [] for info in strongSelf.switchableAccounts { - items.append(ActionSheetPeerItem(context: strongSelf.sharedContext.makeTempAccountContext(account: info.account), peer: info.peer, title: info.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), isSelected: info.account.id == strongSelf.currentAccount.id, strings: presentationData.strings, theme: presentationData.theme, action: { [weak self] in + items.append(ActionSheetPeerItem(context: strongSelf.sharedContext.makeTempAccountContext(account: info.account), peer: EnginePeer(info.peer), title: info.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), isSelected: info.account.id == strongSelf.currentAccount.id, strings: presentationData.strings, theme: presentationData.theme, action: { [weak self] in dismissAction() self?.switchToAccount(account: info.account, animateIn: true) })) diff --git a/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift b/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift index 09636a9dc6..909d4ad7aa 100644 --- a/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift +++ b/submodules/ShareController/Sources/ShareControllerPeerGridItem.swift @@ -10,6 +10,7 @@ import TelegramStringFormatting import SelectablePeerNode import PeerPresenceStatusManager import AccountContext +import ShimmerEffect final class ShareControllerInteraction { var foundPeers: [RenderedPeer] = [] @@ -89,14 +90,14 @@ final class ShareControllerPeerGridItem: GridItem { let context: AccountContext let theme: PresentationTheme let strings: PresentationStrings - let peer: RenderedPeer + let peer: RenderedPeer? let presence: PeerPresence? let controllerInteraction: ShareControllerInteraction let search: Bool let section: GridSection? - init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: RenderedPeer, presence: PeerPresence?, controllerInteraction: ShareControllerInteraction, sectionTitle: String? = nil, search: Bool = false) { + init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: RenderedPeer?, presence: PeerPresence?, controllerInteraction: ShareControllerInteraction, sectionTitle: String? = nil, search: Bool = false) { self.context = context self.theme = theme self.strings = strings @@ -130,12 +131,15 @@ final class ShareControllerPeerGridItem: GridItem { } final class ShareControllerPeerGridItemNode: GridItemNode { - private var currentState: (AccountContext, PresentationTheme, PresentationStrings, RenderedPeer, Bool, PeerPresence?)? + private var currentState: (AccountContext, PresentationTheme, PresentationStrings, RenderedPeer?, Bool, PeerPresence?)? private let peerNode: SelectablePeerNode private var presenceManager: PeerPresenceStatusManager? var controllerInteraction: ShareControllerInteraction? + private var placeholderNode: ShimmerEffectNode? + private var absoluteLocation: (CGRect, CGSize)? + override init() { self.peerNode = SelectablePeerNode() @@ -143,7 +147,7 @@ final class ShareControllerPeerGridItemNode: GridItemNode { self.peerNode.toggleSelection = { [weak self] in if let strongSelf = self { - if let (_, _, _, peer, search, _) = strongSelf.currentState { + if let (_, _, _, maybePeer, search, _) = strongSelf.currentState, let peer = maybePeer { if let _ = peer.peers[peer.peerId] { strongSelf.controllerInteraction?.togglePeer(peer, search) } @@ -159,21 +163,61 @@ final class ShareControllerPeerGridItemNode: GridItemNode { }) } - func setup(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: RenderedPeer, presence: PeerPresence?, search: Bool, synchronousLoad: Bool, force: Bool) { + override func updateAbsoluteRect(_ absoluteRect: CGRect, within containerSize: CGSize) { + let rect = absoluteRect + self.absoluteLocation = (rect, containerSize) + if let shimmerNode = self.placeholderNode { + shimmerNode.updateAbsoluteRect(rect, within: containerSize) + } + } + + func setup(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: RenderedPeer?, presence: PeerPresence?, search: Bool, synchronousLoad: Bool, force: Bool) { if force || self.currentState == nil || self.currentState!.0 !== context || self.currentState!.3 != peer || !arePeerPresencesEqual(self.currentState!.5, presence) { let itemTheme = SelectablePeerNodeTheme(textColor: theme.actionSheet.primaryTextColor, secretTextColor: theme.chatList.secretTitleColor, selectedTextColor: theme.actionSheet.controlAccentColor, checkBackgroundColor: theme.actionSheet.opaqueItemBackgroundColor, checkFillColor: theme.actionSheet.controlAccentColor, checkColor: theme.actionSheet.checkContentColor, avatarPlaceholderColor: theme.list.mediaPlaceholderColor) let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) var online = false - if let peer = peer.peer as? TelegramUser, let presence = presence as? TelegramUserPresence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != context.account.peerId { - let relativeStatus = relativeUserPresenceStatus(presence, relativeTo: timestamp) + if let peer = peer?.peer as? TelegramUser, let presence = presence as? TelegramUserPresence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != context.account.peerId { + let relativeStatus = relativeUserPresenceStatus(EnginePeer.Presence(presence), relativeTo: timestamp) if case .online = relativeStatus { online = true } } self.peerNode.theme = itemTheme - self.peerNode.setup(context: context, theme: theme, strings: strings, peer: peer, online: online, synchronousLoad: synchronousLoad) + if let peer = peer { + self.peerNode.setup(context: context, theme: theme, strings: strings, peer: EngineRenderedPeer(peer), online: online, synchronousLoad: synchronousLoad) + if let shimmerNode = self.placeholderNode { + self.placeholderNode = nil + shimmerNode.removeFromSupernode() + } + } else { + let shimmerNode: ShimmerEffectNode + if let current = self.placeholderNode { + shimmerNode = current + } else { + shimmerNode = ShimmerEffectNode() + self.placeholderNode = shimmerNode + self.addSubnode(shimmerNode) + } + shimmerNode.frame = self.bounds + if let (rect, size) = self.absoluteLocation { + shimmerNode.updateAbsoluteRect(rect, within: size) + } + + var shapes: [ShimmerEffectNode.Shape] = [] + + let titleLineWidth: CGFloat = 56.0 + let lineDiameter: CGFloat = 10.0 + + let iconFrame = CGRect(x: 13.0, y: 4.0, width: 60.0, height: 60.0) + shapes.append(.circle(iconFrame)) + + let titleFrame = CGRect(x: 15.0, y: 70.0, width: 56.0, height: 10.0) + shapes.append(.roundedRectLine(startPoint: CGPoint(x: titleFrame.minX, y: titleFrame.minY + floor((titleFrame.height - lineDiameter) / 2.0)), width: titleLineWidth, diameter: lineDiameter)) + + shimmerNode.update(backgroundColor: theme.list.itemBlocksBackgroundColor, foregroundColor: theme.list.mediaPlaceholderColor, shimmeringColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: shapes, horizontal: true, size: self.bounds.size) + } self.currentState = (context, theme, strings, peer, search, presence) self.setNeedsLayout() if let presence = presence as? TelegramUserPresence { @@ -185,7 +229,7 @@ final class ShareControllerPeerGridItemNode: GridItemNode { func updateSelection(animated: Bool) { var selected = false - if let controllerInteraction = self.controllerInteraction, let (_, _, _, peer, _, _) = self.currentState { + if let controllerInteraction = self.controllerInteraction, let (_, _, _, maybePeer, _, _) = self.currentState, let peer = maybePeer { selected = controllerInteraction.selectedPeerIds.contains(peer.peerId) } @@ -197,5 +241,21 @@ final class ShareControllerPeerGridItemNode: GridItemNode { let bounds = self.bounds self.peerNode.frame = bounds + self.placeholderNode?.frame = bounds + + if let (_, theme, _, _, _, _) = self.currentState, let shimmerNode = self.placeholderNode { + var shapes: [ShimmerEffectNode.Shape] = [] + + let titleLineWidth: CGFloat = 56.0 + let lineDiameter: CGFloat = 10.0 + + let iconFrame = CGRect(x: (bounds.width - 60.0) / 2.0, y: 4.0, width: 60.0, height: 60.0) + shapes.append(.circle(iconFrame)) + + let titleFrame = CGRect(x: (bounds.width - titleLineWidth) / 2.0, y: 70.0, width: titleLineWidth, height: 10.0) + shapes.append(.roundedRectLine(startPoint: CGPoint(x: titleFrame.minX, y: titleFrame.minY + floor((titleFrame.height - lineDiameter) / 2.0)), width: titleLineWidth, diameter: lineDiameter)) + + shimmerNode.update(backgroundColor: theme.list.itemBlocksBackgroundColor, foregroundColor: theme.list.mediaPlaceholderColor, shimmeringColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: shapes, horizontal: true, size: self.bounds.size) + } } } diff --git a/submodules/ShareController/Sources/ShareControllerRecentPeersGridItem.swift b/submodules/ShareController/Sources/ShareControllerRecentPeersGridItem.swift index b0f39917d7..6f395eee22 100644 --- a/submodules/ShareController/Sources/ShareControllerRecentPeersGridItem.swift +++ b/submodules/ShareController/Sources/ShareControllerRecentPeersGridItem.swift @@ -61,7 +61,7 @@ final class ShareControllerRecentPeersGridItemNode: GridItemNode { peersNode.updateThemeAndStrings(theme: theme, strings: strings) } else { peersNode = ChatListSearchRecentPeersNode(context: context, theme: theme, mode: .actionSheet, strings: strings, peerSelected: { [weak self] peer in - self?.controllerInteraction?.togglePeer(RenderedPeer(peer: peer), true) + self?.controllerInteraction?.togglePeer(RenderedPeer(peer: peer._asPeer()), true) }, peerContextAction: { _, _, gesture in gesture?.cancel() }, isPeerSelected: { [weak self] peerId in return self?.controllerInteraction?.selectedPeerIds.contains(peerId) ?? false }, share: true) diff --git a/submodules/ShareController/Sources/SharePeersContainerNode.swift b/submodules/ShareController/Sources/SharePeersContainerNode.swift index 8811f62f31..1fefaac8f0 100644 --- a/submodules/ShareController/Sources/SharePeersContainerNode.swift +++ b/submodules/ShareController/Sources/SharePeersContainerNode.swift @@ -172,7 +172,7 @@ final class SharePeersContainerNode: ASDisplayNode, ShareContentContainerNode { var hasOtherAccounts = false if switchableAccounts.count > 1, let info = switchableAccounts.first(where: { $0.account.id == context.account.id }) { hasOtherAccounts = true - self.contentTitleAccountNode.setPeer(context: context, theme: theme, peer: info.peer, emptyColor: nil, synchronousLoad: false) + self.contentTitleAccountNode.setPeer(context: context, theme: theme, peer: EnginePeer(info.peer), emptyColor: nil, synchronousLoad: false) } else { self.contentTitleAccountNode.isHidden = true } diff --git a/submodules/ShareController/Sources/ShareSearchContainerNode.swift b/submodules/ShareController/Sources/ShareSearchContainerNode.swift index 6580453523..82dc40011d 100644 --- a/submodules/ShareController/Sources/ShareSearchContainerNode.swift +++ b/submodules/ShareController/Sources/ShareSearchContainerNode.swift @@ -32,15 +32,6 @@ private enum ShareSearchRecentEntryStableId: Hashable { } } } - - var hashValue: Int { - switch self { - case .topPeers: - return 0 - case let .peerId(peerId): - return peerId.hashValue - } - } } private enum ShareSearchRecentEntry: Comparable, Identifiable { @@ -110,13 +101,17 @@ private enum ShareSearchRecentEntry: Comparable, Identifiable { private struct ShareSearchPeerEntry: Comparable, Identifiable { let index: Int32 - let peer: RenderedPeer + let peer: RenderedPeer? let presence: PeerPresence? let theme: PresentationTheme let strings: PresentationStrings var stableId: Int64 { - return self.peer.peerId.toInt64() + if let peer = self.peer { + return peer.peerId.toInt64() + } else { + return Int64(index) + } } static func ==(lhs: ShareSearchPeerEntry, rhs: ShareSearchPeerEntry) -> Bool { @@ -134,7 +129,7 @@ private struct ShareSearchPeerEntry: Comparable, Identifiable { } func item(context: AccountContext, interfaceInteraction: ShareControllerInteraction) -> GridItem { - return ShareControllerPeerGridItem(context: context, theme: self.theme, strings: self.strings, peer: peer, presence: self.presence, controllerInteraction: interfaceInteraction, search: true) + return ShareControllerPeerGridItem(context: context, theme: self.theme, strings: self.strings, peer: self.peer, presence: self.presence, controllerInteraction: interfaceInteraction, search: true) } } @@ -246,10 +241,13 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode { if !query.isEmpty { let accountPeer = context.account.postbox.loadedPeerWithId(context.account.peerId) |> take(1) let foundLocalPeers = context.account.postbox.searchPeers(query: query.lowercased()) - let foundRemotePeers: Signal<([FoundPeer], [FoundPeer]), NoError> = .single(([], [])) + let foundRemotePeers: Signal<([FoundPeer], [FoundPeer], Bool), NoError> = .single(([], [], true)) |> then( context.engine.peers.searchPeers(query: query) |> delay(0.2, queue: Queue.concurrentDefaultQueue()) + |> map { a, b -> ([FoundPeer], [FoundPeer], Bool) in + return (a, b, false) + } ) return combineLatest(accountPeer, foundLocalPeers, foundRemotePeers) @@ -278,21 +276,28 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode { } } - for foundPeer in foundRemotePeers.0 { - let peer = foundPeer.peer - if !existingPeerIds.contains(peer.id) && canSendMessagesToPeer(peer) { - existingPeerIds.insert(peer.id) - entries.append(ShareSearchPeerEntry(index: index, peer: RenderedPeer(peer: foundPeer.peer), presence: nil, theme: theme, strings: strings)) + if foundRemotePeers.2 { + for _ in 0 ..< 4 { + entries.append(ShareSearchPeerEntry(index: index, peer: nil, presence: nil, theme: theme, strings: strings)) index += 1 } - } - - for foundPeer in foundRemotePeers.1 { - let peer = foundPeer.peer - if !existingPeerIds.contains(peer.id) && canSendMessagesToPeer(peer) { - existingPeerIds.insert(peer.id) - entries.append(ShareSearchPeerEntry(index: index, peer: RenderedPeer(peer: peer), presence: nil, theme: theme, strings: strings)) - index += 1 + } else { + for foundPeer in foundRemotePeers.0 { + let peer = foundPeer.peer + if !existingPeerIds.contains(peer.id) && canSendMessagesToPeer(peer) { + existingPeerIds.insert(peer.id) + entries.append(ShareSearchPeerEntry(index: index, peer: RenderedPeer(peer: foundPeer.peer), presence: nil, theme: theme, strings: strings)) + index += 1 + } + } + + for foundPeer in foundRemotePeers.1 { + let peer = foundPeer.peer + if !existingPeerIds.contains(peer.id) && canSendMessagesToPeer(peer) { + existingPeerIds.insert(peer.id) + entries.append(ShareSearchPeerEntry(index: index, peer: RenderedPeer(peer: peer), presence: nil, theme: theme, strings: strings)) + index += 1 + } } } @@ -436,7 +441,7 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode { var scrollToItem: GridNodeScrollToItem? if !self.contentGridNode.isHidden, let ensurePeerVisibleOnLayout = self.ensurePeerVisibleOnLayout { self.ensurePeerVisibleOnLayout = nil - if let index = self.entries.firstIndex(where: { $0.peer.peerId == ensurePeerVisibleOnLayout }) { + if let index = self.entries.firstIndex(where: { $0.peer?.peerId == ensurePeerVisibleOnLayout }) { scrollToItem = GridNodeScrollToItem(index: index, position: .visible, transition: transition, directionHint: .up, adjustForSection: false) } } diff --git a/submodules/ShareItems/BUILD b/submodules/ShareItems/BUILD index b660a08c21..313b5ce3cc 100644 --- a/submodules/ShareItems/BUILD +++ b/submodules/ShareItems/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/Postbox:Postbox", diff --git a/submodules/ShareItems/Sources/ShareItems.swift b/submodules/ShareItems/Sources/ShareItems.swift index 422b6a56cf..851be6db77 100644 --- a/submodules/ShareItems/Sources/ShareItems.swift +++ b/submodules/ShareItems/Sources/ShareItems.swift @@ -99,7 +99,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri var cropRect = CGRect(origin: CGPoint(), size: size) if abs(width - height) < CGFloat.ulpOfOne { cropRect = cropRect.insetBy(dx: 13.0, dy: 13.0) - cropRect.offsetBy(dx: 2.0, dy: 3.0) + cropRect = cropRect.offsetBy(dx: 2.0, dy: 3.0) } else { let shortestSide = min(size.width, size.height) cropRect = CGRect(x: (size.width - shortestSide) / 2.0, y: (size.height - shortestSide) / 2.0, width: shortestSide, height: shortestSide) @@ -119,7 +119,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri finalDuration = adjustments.trimEndValue - adjustments.trimStartValue } - let adjustmentsData = MemoryBuffer(data: NSKeyedArchiver.archivedData(withRootObject: adjustments.dictionary())) + let adjustmentsData = MemoryBuffer(data: NSKeyedArchiver.archivedData(withRootObject: adjustments.dictionary()!)) let digest = MemoryBuffer(data: adjustmentsData.md5Digest()) resourceAdjustments = VideoMediaResourceAdjustments(data: adjustmentsData, digest: digest) } @@ -151,7 +151,10 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri if !treatAsFile, let image = UIImage(data: data) { var isGif = false if data.count > 4 { - data.withUnsafeBytes { (bytes: UnsafePointer) -> Void in + data.withUnsafeBytes { buffer -> Void in + guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } if bytes.advanced(by: 0).pointee == 71 // G && bytes.advanced(by: 1).pointee == 73 // I && bytes.advanced(by: 2).pointee == 70 // F diff --git a/submodules/ShimmerEffect/BUILD b/submodules/ShimmerEffect/BUILD index 5ac01ccfdd..56cf4806fb 100644 --- a/submodules/ShimmerEffect/BUILD +++ b/submodules/ShimmerEffect/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/ShimmerEffect/Sources/StickerShimmerEffectNode.swift b/submodules/ShimmerEffect/Sources/StickerShimmerEffectNode.swift index af464d3222..a5b37eb8c3 100644 --- a/submodules/ShimmerEffect/Sources/StickerShimmerEffectNode.swift +++ b/submodules/ShimmerEffect/Sources/StickerShimmerEffectNode.swift @@ -330,6 +330,9 @@ private func renderPath(_ segments: [PathSegment], context: CGContext) { cubicPoint = nil quadrPoint = nil } + + let _ = initialPoint + let _ = quadrPoint for segment in segments { var data = segment.data diff --git a/submodules/SinglePhoneInputNode/BUILD b/submodules/SinglePhoneInputNode/BUILD index 444fca3d20..f2f4b9b70f 100644 --- a/submodules/SinglePhoneInputNode/BUILD +++ b/submodules/SinglePhoneInputNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/SlotMachineAnimationNode/BUILD b/submodules/SlotMachineAnimationNode/BUILD index 1d83bf32ee..9ebdcc588e 100644 --- a/submodules/SlotMachineAnimationNode/BUILD +++ b/submodules/SlotMachineAnimationNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/SolidRoundedButtonNode/BUILD b/submodules/SolidRoundedButtonNode/BUILD index a22f23414d..96b30de710 100644 --- a/submodules/SolidRoundedButtonNode/BUILD +++ b/submodules/SolidRoundedButtonNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/Speak/BUILD b/submodules/Speak/BUILD index 2cdcc0121b..face717fdb 100644 --- a/submodules/Speak/BUILD +++ b/submodules/Speak/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Display:Display", ], diff --git a/submodules/StatisticsUI/BUILD b/submodules/StatisticsUI/BUILD index 1bbd44f86b..c09203364d 100644 --- a/submodules/StatisticsUI/BUILD +++ b/submodules/StatisticsUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/StatisticsUI/Sources/StatsMessageItem.swift b/submodules/StatisticsUI/Sources/StatsMessageItem.swift index 7229f6488c..512eac52d2 100644 --- a/submodules/StatisticsUI/Sources/StatsMessageItem.swift +++ b/submodules/StatisticsUI/Sources/StatsMessageItem.swift @@ -358,8 +358,7 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { } let contentImageSize = CGSize(width: 40.0, height: 40.0) - - var contentImageNodeAppeared = false + if let dimensions = dimensions { let makeImageLayout = strongSelf.contentImageNode.asyncLayout() let imageSize = contentImageSize @@ -371,7 +370,6 @@ public class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { strongSelf.contentImageNode.setSignal(updateImageSignal) if currentContentImageMedia == nil { strongSelf.contentImageNode.isHidden = false - contentImageNodeAppeared = true } } } else { diff --git a/submodules/StickerPackPreviewUI/BUILD b/submodules/StickerPackPreviewUI/BUILD index 328333dfcd..548a88134f 100644 --- a/submodules/StickerPackPreviewUI/BUILD +++ b/submodules/StickerPackPreviewUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift index d53eb66333..cfddc92382 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift @@ -130,7 +130,6 @@ public final class StickerPackPreviewController: ViewController, StandalonePrese return } - let account = strongSelf.context.account strongSelf.openMentionDisposable.set((strongSelf.context.engine.peers.resolvePeerByName(name: mention) |> mapToSignal { peer -> Signal in if let peer = peer { diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewControllerNode.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewControllerNode.swift index c1e5d016ba..75671b66c8 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewControllerNode.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewControllerNode.swift @@ -522,8 +522,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol } @objc func installActionButtonPressed() { - let dismissOnAction = true - if let stickerPack = self.stickerPack, let stickerSettings = self.stickerSettings { + if let stickerPack = self.stickerPack, let _ = self.stickerSettings { switch stickerPack { case let .result(info, items, installed): if installed { @@ -534,19 +533,11 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol } strongSelf.actionPerformed?(info, items, .remove(positionInList: positionInList)) }) - if !dismissOnAction { - self.updateStickerPack(.result(info: info, items: items, installed: false), stickerSettings: stickerSettings) - } } else { let _ = self.context.engine.stickers.addStickerPackInteractively(info: info, items: items).start() - if !dismissOnAction { - self.updateStickerPack(.result(info: info, items: items, installed: true), stickerSettings: stickerSettings) - } self.actionPerformed?(info, items, .add) } - if dismissOnAction { - self.cancelButtonPressed() - } + self.cancelButtonPressed() default: break } diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift index aa10218b2d..598bbe8c24 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift @@ -412,7 +412,7 @@ private final class StickerPackContainer: ASDisplayNode { } case let .result(info, items, installed): if !items.isEmpty && self.currentStickerPack == nil { - if let (_, _, _, gridInsets) = self.validLayout, abs(self.expandScrollProgress - 1.0) < .ulpOfOne { + if let _ = self.validLayout, abs(self.expandScrollProgress - 1.0) < .ulpOfOne { scrollToItem = GridNodeScrollToItem(index: 0, position: .top(0.0), transition: .immediate, directionHint: .up, adjustForSection: false) } } diff --git a/submodules/StickerResources/BUILD b/submodules/StickerResources/BUILD index a8ed7fe661..a24e729b2e 100644 --- a/submodules/StickerResources/BUILD +++ b/submodules/StickerResources/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramCore:TelegramCore", "//submodules/Postbox:Postbox", diff --git a/submodules/StickerResources/Sources/StickerResources.swift b/submodules/StickerResources/Sources/StickerResources.swift index 3bba99d499..3703f2a8c2 100644 --- a/submodules/StickerResources/Sources/StickerResources.swift +++ b/submodules/StickerResources/Sources/StickerResources.swift @@ -83,8 +83,7 @@ private func chatMessageStickerDatas(postbox: Postbox, file: TelegramMediaFile, return Tuple(thumbnailData.complete ? try? Data(contentsOf: URL(fileURLWithPath: thumbnailData.path)) : nil, fullSizeData.0, fullSizeData.1) }).start(next: { next in subscriber.putNext(next) - }, error: { error in - subscriber.putError(error) + }, error: { _ in }, completed: { subscriber.putCompletion() }) @@ -135,8 +134,7 @@ public func chatMessageAnimatedStickerDatas(postbox: Postbox, file: TelegramMedi return Tuple(thumbnailData.complete ? try? Data(contentsOf: URL(fileURLWithPath: thumbnailData.path)) : nil, fullSizeData.0, fullSizeData.1) }).start(next: { next in subscriber.putNext(next) - }, error: { error in - subscriber.putError(error) + }, error: { _ in }, completed: { subscriber.putCompletion() }) @@ -173,8 +171,7 @@ private func chatMessageStickerThumbnailData(postbox: Postbox, file: TelegramMed return thumbnailData.complete ? try? Data(contentsOf: URL(fileURLWithPath: thumbnailData.path)) : nil }).start(next: { next in subscriber.putNext(next) - }, error: { error in - subscriber.putError(error) + }, error: { _ in }, completed: { subscriber.putCompletion() }) @@ -214,8 +211,7 @@ private func chatMessageStickerPackThumbnailData(postbox: Postbox, resource: Med let fetch: Disposable? = nil let disposable = fullSizeData.start(next: { next in subscriber.putNext(next.0) - }, error: { error in - subscriber.putError(error) + }, error: { _ in }, completed: { subscriber.putCompletion() }) diff --git a/submodules/StringPluralization/BUILD b/submodules/StringPluralization/BUILD index 98fb1d6975..76103e678b 100644 --- a/submodules/StringPluralization/BUILD +++ b/submodules/StringPluralization/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/NumberPluralizationForm:NumberPluralizationForm", "//submodules/AppBundle:AppBundle", diff --git a/submodules/SwipeToDismissGesture/BUILD b/submodules/SwipeToDismissGesture/BUILD index 0e224570ac..c60d8cf192 100644 --- a/submodules/SwipeToDismissGesture/BUILD +++ b/submodules/SwipeToDismissGesture/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], visibility = [ "//visibility:public", ], diff --git a/submodules/SwitchNode/BUILD b/submodules/SwitchNode/BUILD index 6df0d8776a..26a8788fa1 100644 --- a/submodules/SwitchNode/BUILD +++ b/submodules/SwitchNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/TelegramAnimatedStickerNode/BUILD b/submodules/TelegramAnimatedStickerNode/BUILD index bfb4779824..f67d3887d4 100644 --- a/submodules/TelegramAnimatedStickerNode/BUILD +++ b/submodules/TelegramAnimatedStickerNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Postbox:Postbox", "//submodules/TelegramCore:TelegramCore", diff --git a/submodules/TelegramAnimatedStickerNode/Sources/AnimatedStickerUtils.swift b/submodules/TelegramAnimatedStickerNode/Sources/AnimatedStickerUtils.swift index bc5316602b..3599fc09f5 100644 --- a/submodules/TelegramAnimatedStickerNode/Sources/AnimatedStickerUtils.swift +++ b/submodules/TelegramAnimatedStickerNode/Sources/AnimatedStickerUtils.swift @@ -39,7 +39,7 @@ public func fetchCompressedLottieFirstFrameAJpeg(data: Data, size: CGSize, fitzM assert(yuvaPixelsPerAlphaRow % 2 == 0) let yuvaLength = Int(size.width) * Int(size.height) * 2 + yuvaPixelsPerAlphaRow * Int(size.height) / 2 - var yuvaFrameData = malloc(yuvaLength)! + let yuvaFrameData = malloc(yuvaLength)! memset(yuvaFrameData, 0, yuvaLength) defer { @@ -109,7 +109,6 @@ private let threadPool: ThreadPool = { return ThreadPool(threadCount: 3, threadPriority: 0.5) }() -@available(iOS 9.0, *) public func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: CGSize, fitzModifier: EmojiFitzModifier? = nil, cacheKey: String) -> Signal { return Signal({ subscriber in let cancelled = Atomic(value: false) @@ -118,8 +117,7 @@ public func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: C if cancelled.with({ $0 }) { return } - - let startTime = CACurrentMediaTime() + var drawingTime: Double = 0 var appendingTime: Double = 0 var deltaTime: Double = 0 @@ -146,7 +144,7 @@ public func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: C } func writeData(_ data: UnsafeRawPointer, length: Int) { - file.write(data, count: length) + let _ = file.write(data, count: length) } func commitData() { @@ -234,7 +232,10 @@ public func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: C deltaTime += CACurrentMediaTime() - deltaStartTime let compressionStartTime = CACurrentMediaTime() - compressedFrameData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + compressedFrameData.withUnsafeMutableBytes { buffer -> Void in + guard let bytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } let length = compression_encode_buffer(bytes, compressedFrameDataLength, previousYuvaFrameData.assumingMemoryBound(to: UInt8.self), yuvaLength, scratchData, COMPRESSION_LZFSE) var frameLengthValue: Int32 = Int32(length) writeData(&frameLengthValue, length: 4) diff --git a/submodules/TelegramAudio/BUILD b/submodules/TelegramAudio/BUILD index 8273a42b1d..319722988c 100644 --- a/submodules/TelegramAudio/BUILD +++ b/submodules/TelegramAudio/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", ], diff --git a/submodules/TelegramAudio/Sources/ManagedAudioSession.swift b/submodules/TelegramAudio/Sources/ManagedAudioSession.swift index ffe4017e73..b6dfdf05f6 100644 --- a/submodules/TelegramAudio/Sources/ManagedAudioSession.swift +++ b/submodules/TelegramAudio/Sources/ManagedAudioSession.swift @@ -21,6 +21,7 @@ public enum ManagedAudioSessionType: Equatable { case record(speaker: Bool) case voiceCall case videoCall + case recordWithOthers var isPlay: Bool { switch self { @@ -38,7 +39,7 @@ private func nativeCategoryForType(_ type: ManagedAudioSessionType, headphones: return .ambient case .play: return .playback - case .record, .voiceCall, .videoCall: + case .record, .recordWithOthers, .voiceCall, .videoCall: return .playAndRecord case .playWithPossiblePortOverride: if headphones { @@ -114,7 +115,7 @@ private final class HolderRecord { let audioSessionType: ManagedAudioSessionType let control: ManagedAudioSessionControl let activate: (ManagedAudioSessionControl) -> Void - let deactivate: () -> Signal + let deactivate: (Bool) -> Signal let headsetConnectionStatusChanged: (Bool) -> Void let availableOutputsChanged: ([AudioSessionOutput], AudioSessionOutput?) -> Void let once: Bool @@ -122,7 +123,7 @@ private final class HolderRecord { var active: Bool = false var deactivatingDisposable: Disposable? = nil - init(id: Int32, audioSessionType: ManagedAudioSessionType, control: ManagedAudioSessionControl, activate: @escaping (ManagedAudioSessionControl) -> Void, deactivate: @escaping () -> Signal, headsetConnectionStatusChanged: @escaping (Bool) -> Void, availableOutputsChanged: @escaping ([AudioSessionOutput], AudioSessionOutput?) -> Void, once: Bool, outputMode: AudioSessionOutputMode) { + init(id: Int32, audioSessionType: ManagedAudioSessionType, control: ManagedAudioSessionControl, activate: @escaping (ManagedAudioSessionControl) -> Void, deactivate: @escaping (Bool) -> Signal, headsetConnectionStatusChanged: @escaping (Bool) -> Void, availableOutputsChanged: @escaping ([AudioSessionOutput], AudioSessionOutput?) -> Void, once: Bool, outputMode: AudioSessionOutputMode) { self.id = id self.audioSessionType = audioSessionType self.control = control @@ -431,7 +432,7 @@ public final class ManagedAudioSession { return AVAudioSession.sharedInstance().secondaryAudioShouldBeSilencedHint } - public func push(audioSessionType: ManagedAudioSessionType, outputMode: AudioSessionOutputMode = .system, once: Bool = false, activate: @escaping (AudioSessionActivationState) -> Void, deactivate: @escaping () -> Signal) -> Disposable { + public func push(audioSessionType: ManagedAudioSessionType, outputMode: AudioSessionOutputMode = .system, once: Bool = false, activate: @escaping (AudioSessionActivationState) -> Void, deactivate: @escaping (Bool) -> Signal) -> Disposable { return self.push(audioSessionType: audioSessionType, once: once, manualActivate: { control in control.setupAndActivate(synchronous: false, { state in activate(state) @@ -439,7 +440,7 @@ public final class ManagedAudioSession { }, deactivate: deactivate) } - public func push(audioSessionType: ManagedAudioSessionType, outputMode: AudioSessionOutputMode = .system, once: Bool = false, activateImmediately: Bool = false, manualActivate: @escaping (ManagedAudioSessionControl) -> Void, deactivate: @escaping () -> Signal, headsetConnectionStatusChanged: @escaping (Bool) -> Void = { _ in }, availableOutputsChanged: @escaping ([AudioSessionOutput], AudioSessionOutput?) -> Void = { _, _ in }) -> Disposable { + public func push(audioSessionType: ManagedAudioSessionType, outputMode: AudioSessionOutputMode = .system, once: Bool = false, activateImmediately: Bool = false, manualActivate: @escaping (ManagedAudioSessionControl) -> Void, deactivate: @escaping (Bool) -> Signal, headsetConnectionStatusChanged: @escaping (Bool) -> Void = { _ in }, availableOutputsChanged: @escaping ([AudioSessionOutput], AudioSessionOutput?) -> Void = { _, _ in }) -> Disposable { let id = OSAtomicIncrement32(&self.nextId) let queue = self.queue queue.async { @@ -566,9 +567,16 @@ public final class ManagedAudioSession { } index += 1 } + + var lastIsRecordWithOthers = false // self.holders.last?.audioSessionType == .recordWithOthers + if "".count != 0 { + // Silence warning + lastIsRecordWithOthers = true + } if !deactivating { if let activeIndex = activeIndex { var deactivate = false + var temporary = false if interruption { if self.holders[activeIndex].audioSessionType != .voiceCall { @@ -576,7 +584,10 @@ public final class ManagedAudioSession { } } else { if activeIndex != self.holders.count - 1 { - if self.holders[activeIndex].audioSessionType == .voiceCall { + if lastIsRecordWithOthers { + deactivate = true + temporary = true + } else if self.holders[activeIndex].audioSessionType == .voiceCall { deactivate = false } else { deactivate = true @@ -587,7 +598,7 @@ public final class ManagedAudioSession { if deactivate { self.holders[activeIndex].active = false let id = self.holders[activeIndex].id - self.holders[activeIndex].deactivatingDisposable = (self.holders[activeIndex].deactivate() + self.holders[activeIndex].deactivatingDisposable = (self.holders[activeIndex].deactivate(temporary) |> deliverOn(self.queue)).start(completed: { [weak self] in guard let strongSelf = self else { return @@ -726,21 +737,24 @@ public final class ManagedAudioSession { options.insert(.allowBluetooth) } } - case .record, .voiceCall, .videoCall: + case .record, .recordWithOthers, .voiceCall, .videoCall: options.insert(.allowBluetooth) } managedAudioSessionLog("ManagedAudioSession setting active true") let mode: AVAudioSession.Mode - switch type { - case .voiceCall: - mode = .voiceChat - options.insert(.mixWithOthers) - case .videoCall: - mode = .videoChat - options.insert(.mixWithOthers) - default: - mode = .default - } + switch type { + case .voiceCall: + mode = .voiceChat + options.insert(.mixWithOthers) + case .videoCall: + mode = .videoChat + options.insert(.mixWithOthers) +// case .recordWithOthers: +// mode = .videoRecording +// options.insert(.mixWithOthers) + default: + mode = .default + } if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { try AVAudioSession.sharedInstance().setCategory(nativeCategory, mode: mode, policy: .default, options: options) } else { diff --git a/submodules/TelegramBaseController/BUILD b/submodules/TelegramBaseController/BUILD index 5dd334fcf5..1d6b6bf6f8 100644 --- a/submodules/TelegramBaseController/BUILD +++ b/submodules/TelegramBaseController/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/Postbox:Postbox", diff --git a/submodules/TelegramBaseController/Sources/LocationBroadcastActionSheetItem.swift b/submodules/TelegramBaseController/Sources/LocationBroadcastActionSheetItem.swift index 054ab6a489..ab090a4db7 100644 --- a/submodules/TelegramBaseController/Sources/LocationBroadcastActionSheetItem.swift +++ b/submodules/TelegramBaseController/Sources/LocationBroadcastActionSheetItem.swift @@ -105,7 +105,7 @@ public class LocationBroadcastActionSheetItemNode: ActionSheetItemNode { let textColor: UIColor = self.theme.primaryTextColor self.label.attributedText = NSAttributedString(string: item.title, font: defaultFont, textColor: textColor) - self.avatarNode.setPeer(context: item.context, theme: (item.context.sharedContext.currentPresentationData.with { $0 }).theme, peer: item.peer) + self.avatarNode.setPeer(context: item.context, theme: (item.context.sharedContext.currentPresentationData.with { $0 }).theme, peer: EnginePeer(item.peer)) self.timerNode.update(backgroundColor: self.theme.controlAccentColor.withAlphaComponent(0.4), foregroundColor: self.theme.controlAccentColor, textColor: self.theme.controlAccentColor, beginTimestamp: item.beginTimestamp, timeout: item.timeout, strings: item.strings) } diff --git a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryContainerNode.swift b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryContainerNode.swift index ba6070ab82..db322c564d 100644 --- a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryContainerNode.swift +++ b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryContainerNode.swift @@ -24,7 +24,7 @@ public final class MediaNavigationAccessoryContainerNode: ASDisplayNode, UIGestu self.backgroundNode = ASDisplayNode() self.separatorNode = ASDisplayNode() - self.headerNode = MediaNavigationAccessoryHeaderNode(presentationData: self.presentationData) + self.headerNode = MediaNavigationAccessoryHeaderNode(context: context) super.init() diff --git a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift index f7cbb240a7..f40dead068 100644 --- a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift +++ b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryHeaderNode.swift @@ -10,6 +10,7 @@ import UniversalMediaPlayer import AccountContext import TelegramStringFormatting import ManagedAnimationNode +import ContextUI private let titleFont = Font.regular(12.0) private let subtitleFont = Font.regular(10.0) @@ -130,6 +131,7 @@ private func generateMaskImage(color: UIColor) -> UIImage? { public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollViewDelegate { public static let minimizedHeight: CGFloat = 37.0 + private let context: AccountContext private var theme: PresentationTheme private var strings: PresentationStrings private var dateTimeFormat: PresentationDateTimeFormat @@ -148,7 +150,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi private let closeButton: HighlightableButtonNode private let actionButton: HighlightTrackingButtonNode private let playPauseIconNode: PlayPauseIconNode - private let rateButton: HighlightableButtonNode + private let rateButton: RateButton private let accessibilityAreaNode: AccessibilityAreaNode private let scrubbingNode: MediaPlayerScrubbingNode @@ -167,24 +169,31 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi public var tapAction: (() -> Void)? public var close: (() -> Void)? - public var toggleRate: (() -> Void)? + public var setRate: ((AudioPlaybackRate) -> Void)? public var togglePlayPause: (() -> Void)? public var playPrevious: (() -> Void)? public var playNext: (() -> Void)? + public var getController: (() -> ViewController?)? + public var presentInGlobalOverlay: ((ViewController) -> Void)? + public var playbackBaseRate: AudioPlaybackRate? = nil { didSet { guard self.playbackBaseRate != oldValue, let playbackBaseRate = self.playbackBaseRate else { return } switch playbackBaseRate { + case .x0_5: + self.rateButton.setContent(.image(optionsRateImage(rate: "0.5X", color: self.theme.rootController.navigationBar.accentTextColor))) case .x1: - self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerRateInactiveIcon(self.theme), for: []) + self.rateButton.setContent(.image(optionsRateImage(rate: "2X", color: self.theme.rootController.navigationBar.controlColor))) self.rateButton.accessibilityLabel = self.strings.VoiceOver_Media_PlaybackRate self.rateButton.accessibilityValue = self.strings.VoiceOver_Media_PlaybackRateNormal self.rateButton.accessibilityHint = self.strings.VoiceOver_Media_PlaybackRateChange + case .x1_5: + self.rateButton.setContent(.image(optionsRateImage(rate: "1.5X", color: self.theme.rootController.navigationBar.accentTextColor))) case .x2: - self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerRateActiveIcon(self.theme), for: []) + self.rateButton.setContent(.image(optionsRateImage(rate: "2X", color: self.theme.rootController.navigationBar.accentTextColor))) self.rateButton.accessibilityLabel = self.strings.VoiceOver_Media_PlaybackRate self.rateButton.accessibilityValue = self.strings.VoiceOver_Media_PlaybackRateFast self.rateButton.accessibilityHint = self.strings.VoiceOver_Media_PlaybackRateChange @@ -208,7 +217,9 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi } } - public init(presentationData: PresentationData) { + public init(context: AccountContext) { + self.context = context + let presentationData = context.sharedContext.currentPresentationData.with { $0 } self.theme = presentationData.theme self.strings = presentationData.strings self.dateTimeFormat = presentationData.dateTimeFormat @@ -236,7 +247,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi self.closeButton.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 2.0) self.closeButton.displaysAsynchronously = false - self.rateButton = HighlightableButtonNode() + self.rateButton = RateButton() self.rateButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -4.0, bottom: -8.0, right: -4.0) self.rateButton.displaysAsynchronously = false @@ -265,9 +276,6 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi self.scrollNode.addSubnode(self.previousItemNode) self.scrollNode.addSubnode(self.nextItemNode) - //self.addSubnode(self.leftMaskNode) - //self.addSubnode(self.rightMaskNode) - self.addSubnode(self.closeButton) self.addSubnode(self.rateButton) self.addSubnode(self.accessibilityAreaNode) @@ -276,9 +284,13 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi self.addSubnode(self.actionButton) self.closeButton.addTarget(self, action: #selector(self.closeButtonPressed), forControlEvents: .touchUpInside) - self.rateButton.addTarget(self, action: #selector(self.rateButtonPressed), forControlEvents: .touchUpInside) self.actionButton.addTarget(self, action: #selector(self.actionButtonPressed), forControlEvents: .touchUpInside) + self.rateButton.addTarget(self, action: #selector(self.rateButtonPressed), forControlEvents: .touchUpInside) + self.rateButton.contextAction = { [weak self] sourceNode, gesture in + self?.openRateMenu(sourceNode: sourceNode, gesture: gesture) + } + self.addSubnode(self.scrubbingNode) self.addSubnode(self.separatorNode) @@ -300,13 +312,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi return } if let status = status { - let baseRate: AudioPlaybackRate - if status.baseRate.isEqual(to: 1.0) { - baseRate = .x1 - } else { - baseRate = .x2 - } - strongSelf.playbackBaseRate = baseRate + strongSelf.playbackBaseRate = AudioPlaybackRate(status.baseRate) } else { strongSelf.playbackBaseRate = .x1 } @@ -365,10 +371,14 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi if let playbackBaseRate = self.playbackBaseRate { switch playbackBaseRate { + case .x0_5: + self.rateButton.setContent(.image(optionsRateImage(rate: "0.5X", color: self.theme.rootController.navigationBar.accentTextColor))) case .x1: - self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerRateInactiveIcon(self.theme), for: []) + self.rateButton.setContent(.image(optionsRateImage(rate: "2X", color: self.theme.rootController.navigationBar.controlColor))) + case .x1_5: + self.rateButton.setContent(.image(optionsRateImage(rate: "1.5X", color: self.theme.rootController.navigationBar.accentTextColor))) case .x2: - self.rateButton.setImage(PresentationResourcesRootController.navigationPlayerRateActiveIcon(self.theme), for: []) + self.rateButton.setContent(.image(optionsRateImage(rate: "2X", color: self.theme.rootController.navigationBar.accentTextColor))) default: break } @@ -475,8 +485,8 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi let bounds = CGRect(origin: CGPoint(), size: size) let closeButtonSize = self.closeButton.measure(CGSize(width: 100.0, height: 100.0)) transition.updateFrame(node: self.closeButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 44.0 - rightInset, y: 0.0), size: CGSize(width: 44.0, height: minHeight))) - let rateButtonSize = CGSize(width: 24.0, height: minHeight) - transition.updateFrame(node: self.rateButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 18.0 - closeButtonSize.width - 17.0 - rateButtonSize.width - rightInset, y: 0.0), size: rateButtonSize)) + let rateButtonSize = CGSize(width: 30.0, height: minHeight) + transition.updateFrame(node: self.rateButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 33.0 - closeButtonSize.width - rateButtonSize.width - rightInset, y: -4.0), size: rateButtonSize)) transition.updateFrame(node: self.playPauseIconNode, frame: CGRect(origin: CGPoint(x: 6.0, y: 4.0 + UIScreenPixel), size: CGSize(width: 28.0, height: 28.0))) transition.updateFrame(node: self.actionButton, frame: CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: 40.0, height: 37.0))) transition.updateFrame(node: self.scrubbingNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 37.0 - 2.0), size: CGSize(width: size.width, height: 2.0))) @@ -491,7 +501,58 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, UIScrollVi } @objc public func rateButtonPressed() { - self.toggleRate?() + let nextRate: AudioPlaybackRate + if let rate = self.playbackBaseRate { + switch rate { + case .x1: + nextRate = .x2 + default: + nextRate = .x1 + } + } else { + nextRate = .x2 + } + self.setRate?(nextRate) + } + + private func speedList(strings: PresentationStrings) -> [(String, String, AudioPlaybackRate)] { + let speedList: [(String, String, AudioPlaybackRate)] = [ + ("0.5x", "0.5x", .x0_5), + (strings.PlaybackSpeed_Normal, "1x", .x1), + ("1.5x", "1.5x", .x1_5), + ("2x", "2x", .x2) + ] + return speedList + } + + private func contextMenuSpeedItems() -> Signal<[ContextMenuItem], NoError> { + var items: [ContextMenuItem] = [] + + for (text, _, rate) in self.speedList(strings: self.strings) { + let isSelected = self.playbackBaseRate == rate + items.append(.action(ContextMenuActionItem(text: text, icon: { theme in + if isSelected { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) + } else { + return nil + } + }, action: { [weak self] _, f in + f(.default) + + self?.setRate?(rate) + }))) + } + + return .single(items) + } + + private func openRateMenu(sourceNode: ASDisplayNode, gesture: ContextGesture?) { + guard let controller = self.getController?() else { + return + } + let items: Signal<[ContextMenuItem], NoError> = self.contextMenuSpeedItems() + let contextController = ContextController(account: self.context.account, presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.rateButton.referenceNode)), items: items, reactionItems: [], gesture: gesture) + self.presentInGlobalOverlay?(contextController) } @objc public func actionButtonPressed() { @@ -554,3 +615,163 @@ private final class PlayPauseIconNode: ManagedAnimationNode { } } } + +private func optionsRateImage(rate: String, color: UIColor = .white) -> UIImage? { + return generateImage(CGSize(width: 30.0, height: 16.0), rotatedContext: { size, context in + UIGraphicsPushContext(context) + + context.clear(CGRect(origin: CGPoint(), size: size)) + + let lineWidth = 1.0 + UIScreenPixel + context.setLineWidth(lineWidth) + context.setStrokeColor(color.cgColor) + + + let string = NSMutableAttributedString(string: rate, font: Font.with(size: 11.0, design: .round, weight: .bold), textColor: color) + + var offset = CGPoint(x: 1.0, y: 0.0) + var width: CGFloat + if rate.count >= 3 { + if rate == "0.5X" { + string.addAttribute(.kern, value: -0.8 as NSNumber, range: NSRange(string.string.startIndex ..< string.string.endIndex, in: string.string)) + offset.x += -0.5 + } else { + string.addAttribute(.kern, value: -0.5 as NSNumber, range: NSRange(string.string.startIndex ..< string.string.endIndex, in: string.string)) + offset.x += -0.3 + } + width = 29.0 + } else { + string.addAttribute(.kern, value: -0.5 as NSNumber, range: NSRange(string.string.startIndex ..< string.string.endIndex, in: string.string)) + width = 19.0 + offset.x += -0.3 + } + + let path = UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - width) / 2.0), y: 0.0, width: width, height: 16.0).insetBy(dx: lineWidth / 2.0, dy: lineWidth / 2.0), byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 2.0, height: 2.0)) + context.addPath(path.cgPath) + context.strokePath() + + let boundingRect = string.boundingRect(with: size, options: [], context: nil) + string.draw(at: CGPoint(x: offset.x + floor((size.width - boundingRect.width) / 2.0), y: offset.y + UIScreenPixel + floor((size.height - boundingRect.height) / 2.0))) + + UIGraphicsPopContext() + }) +} + +private final class RateButton: HighlightableButtonNode { + enum Content { + case image(UIImage?) + } + + let referenceNode: ContextReferenceContentNode + let containerNode: ContextControllerSourceNode + private let iconNode: ASImageNode + + var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? + + private let wide: Bool + + init(wide: Bool = false) { + self.wide = wide + + self.referenceNode = ContextReferenceContentNode() + self.containerNode = ContextControllerSourceNode() + self.containerNode.animateScale = false + self.iconNode = ASImageNode() + self.iconNode.displaysAsynchronously = false + self.iconNode.displayWithoutProcessing = true + self.iconNode.contentMode = .scaleToFill + + super.init() + + self.containerNode.addSubnode(self.referenceNode) + self.referenceNode.addSubnode(self.iconNode) + self.addSubnode(self.containerNode) + + self.containerNode.shouldBegin = { [weak self] location in + guard let strongSelf = self, let _ = strongSelf.contextAction else { + return false + } + return true + } + self.containerNode.activated = { [weak self] gesture, _ in + guard let strongSelf = self else { + return + } + strongSelf.contextAction?(strongSelf.containerNode, gesture) + } + + self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 26.0, height: 44.0)) + self.referenceNode.frame = self.containerNode.bounds + + if let image = self.iconNode.image { + self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) + } + + self.hitTestSlop = UIEdgeInsets(top: 0.0, left: -4.0, bottom: 0.0, right: -4.0) + } + + private var content: Content? + func setContent(_ content: Content, animated: Bool = false) { + if animated { + if let snapshotView = self.referenceNode.view.snapshotContentTree() { + snapshotView.frame = self.referenceNode.frame + self.view.addSubview(snapshotView) + + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + snapshotView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.3, removeOnCompletion: false) + + self.iconNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + self.iconNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.3) + } + + switch content { + case let .image(image): + if let image = image { + self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) + } + + self.iconNode.image = image + self.iconNode.isHidden = false + } + } else { + self.content = content + switch content { + case let .image(image): + if let image = image { + self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) + } + + self.iconNode.image = image + self.iconNode.isHidden = false + } + } + } + + override func didLoad() { + super.didLoad() + self.view.isOpaque = false + } + + override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { + return CGSize(width: wide ? 32.0 : 22.0, height: 44.0) + } + + func onLayout() { + } +} + +private final class HeaderContextReferenceContentSource: ContextReferenceContentSource { + private let controller: ViewController + private let sourceNode: ContextReferenceContentNode + + init(controller: ViewController, sourceNode: ContextReferenceContentNode) { + self.controller = controller + self.sourceNode = sourceNode + } + + func transitionInfo() -> ContextControllerReferenceViewInfo? { + return ContextControllerReferenceViewInfo(referenceNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds) + } +} diff --git a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryPanel.swift b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryPanel.swift index 9e1e41a976..a7ab775591 100644 --- a/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryPanel.swift +++ b/submodules/TelegramBaseController/Sources/MediaNavigationAccessoryPanel.swift @@ -4,17 +4,21 @@ import Display import AsyncDisplayKit import TelegramCore import AccountContext +import TelegramUIPreferences public final class MediaNavigationAccessoryPanel: ASDisplayNode { public let containerNode: MediaNavigationAccessoryContainerNode public var close: (() -> Void)? - public var toggleRate: (() -> Void)? + public var setRate: ((AudioPlaybackRate) -> Void)? public var togglePlayPause: (() -> Void)? public var tapAction: (() -> Void)? public var playPrevious: (() -> Void)? public var playNext: (() -> Void)? + public var getController: (() -> ViewController?)? + public var presentInGlobalOverlay: ((ViewController) -> Void)? + public init(context: AccountContext, displayBackground: Bool = false) { self.containerNode = MediaNavigationAccessoryContainerNode(context: context, displayBackground: displayBackground) @@ -27,8 +31,8 @@ public final class MediaNavigationAccessoryPanel: ASDisplayNode { close() } } - self.containerNode.headerNode.toggleRate = { [weak self] in - self?.toggleRate?() + self.containerNode.headerNode.setRate = { [weak self] rate in + self?.setRate?(rate) } self.containerNode.headerNode.togglePlayPause = { [weak self] in if let strongSelf = self, let togglePlayPause = strongSelf.togglePlayPause { @@ -50,6 +54,20 @@ public final class MediaNavigationAccessoryPanel: ASDisplayNode { playNext() } } + + self.containerNode.headerNode.getController = { [weak self] in + if let strongSelf = self, let getController = strongSelf.getController { + return getController() + } else { + return nil + } + } + + self.containerNode.headerNode.presentInGlobalOverlay = { [weak self] c in + if let strongSelf = self, let presentInGlobalOverlay = strongSelf.presentInGlobalOverlay { + presentInGlobalOverlay(c) + } + } } public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { diff --git a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift index a48571c327..ba568b1fc3 100644 --- a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift +++ b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift @@ -288,7 +288,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { let disposable = MetaDisposable() callContextCache.impl.syncWith { impl in - let callContext = impl.get(account: context.account, engine: context.engine, peerId: peerId, call: activeCall) + let callContext = impl.get(account: context.account, engine: context.engine, peerId: peerId, call: EngineGroupCallDescription(activeCall)) disposable.set((callContext.context.panelData |> deliverOnMainQueue).start(next: { panelData in callContext.keep() @@ -405,7 +405,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { strongSelf.joinGroupCall( peerId: groupCallPanelData.peerId, invite: nil, - activeCall: CachedChannelData.ActiveCall(id: groupCallPanelData.info.id, accessHash: groupCallPanelData.info.accessHash, title: groupCallPanelData.info.title, scheduleTimestamp: groupCallPanelData.info.scheduleTimestamp, subscribedToScheduled: groupCallPanelData.info.subscribedToScheduled) + activeCall: EngineGroupCallDescription(id: groupCallPanelData.info.id, accessHash: groupCallPanelData.info.accessHash, title: groupCallPanelData.info.title, scheduleTimestamp: groupCallPanelData.info.scheduleTimestamp, subscribedToScheduled: groupCallPanelData.info.subscribedToScheduled) ) }) self.navigationBar?.additionalContentNode.addSubnode(groupCallAccessoryPanel) @@ -645,32 +645,28 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { let mediaAccessoryPanel = MediaNavigationAccessoryPanel(context: self.context) mediaAccessoryPanel.containerNode.headerNode.displayScrubber = item.playbackData?.type != .instantVideo + mediaAccessoryPanel.getController = { [weak self] in + return self + } + mediaAccessoryPanel.presentInGlobalOverlay = { [weak self] c in + self?.presentInGlobalOverlay(c) + } mediaAccessoryPanel.close = { [weak self] in if let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType { strongSelf.context.sharedContext.mediaManager.setPlaylist(nil, type: type, control: SharedMediaPlayerControlAction.playback(.pause)) } } - mediaAccessoryPanel.toggleRate = { - [weak self] in + mediaAccessoryPanel.setRate = { [weak self] rate in guard let strongSelf = self else { return } let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> AudioPlaybackRate in let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings) as? MusicPlaybackSettings ?? MusicPlaybackSettings.defaultSettings - let nextRate: AudioPlaybackRate - switch settings.voicePlaybackRate { - case .x1: - nextRate = .x2 - case .x2: - nextRate = .x1 - default: - nextRate = .x1 - } transaction.updateSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings, { _ in - return settings.withUpdatedVoicePlaybackRate(nextRate) + return settings.withUpdatedVoicePlaybackRate(rate) }) - return nextRate + return rate } |> deliverOnMainQueue).start(next: { baseRate in guard let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType else { @@ -688,22 +684,31 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { }) let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let slowdown = baseRate == .x1 - strongSelf.present( - UndoOverlayController( - presentationData: presentationData, - content: .audioRate( - slowdown: slowdown, - text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp + let slowdown: Bool? + if baseRate == .x1 { + slowdown = true + } else if baseRate == .x2 { + slowdown = false + } else { + slowdown = nil + } + if let slowdown = slowdown { + strongSelf.present( + UndoOverlayController( + presentationData: presentationData, + content: .audioRate( + slowdown: slowdown, + text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp + ), + elevatedLayout: false, + animateInAsReplacement: hasTooltip, + action: { action in + return true + } ), - elevatedLayout: false, - animateInAsReplacement: hasTooltip, - action: { action in - return true - } - ), - in: .current - ) + in: .current + ) + } }) } mediaAccessoryPanel.togglePlayPause = { [weak self] in @@ -847,7 +852,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { })] } - open func joinGroupCall(peerId: PeerId, invite: String?, activeCall: CachedChannelData.ActiveCall) { + open func joinGroupCall(peerId: PeerId, invite: String?, activeCall: EngineGroupCallDescription) { let context = self.context let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } diff --git a/submodules/TelegramCallsUI/BUILD b/submodules/TelegramCallsUI/BUILD index 24b48b2c18..0865e3101d 100644 --- a/submodules/TelegramCallsUI/BUILD +++ b/submodules/TelegramCallsUI/BUILD @@ -46,6 +46,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], data = [ ":TelegramCallsUIBundle", ], diff --git a/submodules/TelegramCallsUI/Sources/CallController.swift b/submodules/TelegramCallsUI/Sources/CallController.swift index 0f13530122..dc1a46bbf3 100644 --- a/submodules/TelegramCallsUI/Sources/CallController.swift +++ b/submodules/TelegramCallsUI/Sources/CallController.swift @@ -14,7 +14,7 @@ import TelegramNotices import AppBundle import TooltipUI -protocol CallControllerNodeProtocol: class { +protocol CallControllerNodeProtocol: AnyObject { var isMuted: Bool { get set } var toggleMute: (() -> Void)? { get set } diff --git a/submodules/TelegramCallsUI/Sources/CallControllerButton.swift b/submodules/TelegramCallsUI/Sources/CallControllerButton.swift index e4e4be2b67..eb83d75458 100644 --- a/submodules/TelegramCallsUI/Sources/CallControllerButton.swift +++ b/submodules/TelegramCallsUI/Sources/CallControllerButton.swift @@ -369,7 +369,7 @@ final class CallControllerButtonItemNode: HighlightTrackingButtonNode { fillColor = UIColor(rgb: 0xd92326).withMultipliedBrightnessBy(0.2).withAlphaComponent(0.2) case .green: fillColor = UIColor(rgb: 0x74db58).withMultipliedBrightnessBy(0.2).withAlphaComponent(0.2) - case let .custom(color, alpha): + case let .custom(color, _): fillColor = UIColor(rgb: color).withMultipliedBrightnessBy(0.2).withAlphaComponent(0.2) } } diff --git a/submodules/TelegramCallsUI/Sources/CallControllerNode.swift b/submodules/TelegramCallsUI/Sources/CallControllerNode.swift index ae96cfc131..05e561d4c4 100644 --- a/submodules/TelegramCallsUI/Sources/CallControllerNode.swift +++ b/submodules/TelegramCallsUI/Sources/CallControllerNode.swift @@ -191,7 +191,7 @@ private final class CallVideoNode: ASDisplayNode, PreviewVideoNode { self.currentCornerRadius = cornerRadius var rotationAngle: CGFloat - if isOutgoing && isCompactLayout { + if false && isOutgoing && isCompactLayout { rotationAngle = CGFloat.pi / 2.0 } else { switch self.currentOrientation { @@ -227,7 +227,10 @@ private final class CallVideoNode: ASDisplayNode, PreviewVideoNode { additionalAngle = 0.0 } rotationAngle += additionalAngle - if abs(rotationAngle - (-CGFloat.pi)) < 1.0 { + if abs(rotationAngle - CGFloat.pi * 3.0 / 2.0) < 0.01 { + rotationAngle = -CGFloat.pi / 2.0 + } + if abs(rotationAngle - (-CGFloat.pi)) < 0.01 { rotationAngle = -CGFloat.pi + 0.001 } } @@ -1656,7 +1659,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro self.animationForExpandedVideoSnapshotView = nil } minimizedVideoTransition.updateFrame(node: minimizedVideoNode, frame: previewVideoFrame) - minimizedVideoNode.updateLayout(size: previewVideoFrame.size, cornerRadius: interpolate(from: 14.0, to: 24.0, value: self.pictureInPictureTransitionFraction), isOutgoing: minimizedVideoNode === self.outgoingVideoNodeValue, deviceOrientation: mappedDeviceOrientation, isCompactLayout: false, transition: minimizedVideoTransition) + minimizedVideoNode.updateLayout(size: previewVideoFrame.size, cornerRadius: interpolate(from: 14.0, to: 24.0, value: self.pictureInPictureTransitionFraction), isOutgoing: minimizedVideoNode === self.outgoingVideoNodeValue, deviceOrientation: mappedDeviceOrientation, isCompactLayout: layout.metrics.widthClass == .compact, transition: minimizedVideoTransition) if transition.isAnimated && didAppear { minimizedVideoNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.5) } diff --git a/submodules/TelegramCallsUI/Sources/CallStatusBarNode.swift b/submodules/TelegramCallsUI/Sources/CallStatusBarNode.swift index a1dcb9e96d..ec1a7572a7 100644 --- a/submodules/TelegramCallsUI/Sources/CallStatusBarNode.swift +++ b/submodules/TelegramCallsUI/Sources/CallStatusBarNode.swift @@ -138,7 +138,6 @@ private class CallStatusBarBackgroundNode: ASDisplayNode { } private func setupGradientAnimations() { - return if let _ = self.foregroundGradientLayer.animation(forKey: "movement") { } else { let previousValue = self.foregroundGradientLayer.startPoint diff --git a/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift b/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift index d5b992caed..91736b1655 100644 --- a/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift +++ b/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift @@ -357,7 +357,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { } self.currentText = membersText - self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { $0.peer }, animated: false) + self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { EnginePeer($0.peer) }, animated: false) self.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: self.theme.chat.inputPanel.secondaryTextColor) @@ -381,7 +381,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { } strongSelf.currentText = membersText - strongSelf.avatarsContent = strongSelf.avatarsContext.update(peers: summaryState.topParticipants.map { $0.peer }, animated: false) + strongSelf.avatarsContent = strongSelf.avatarsContext.update(peers: summaryState.topParticipants.map { EnginePeer($0.peer) }, animated: false) if let (size, leftInset, rightInset) = strongSelf.validLayout { strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .immediate) @@ -458,7 +458,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { } self.currentText = membersText - self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { $0.peer }, animated: false) + self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.map { EnginePeer($0.peer) }, animated: false) updateAudioLevels = true } diff --git a/submodules/TelegramCallsUI/Sources/GroupVideoNode.swift b/submodules/TelegramCallsUI/Sources/GroupVideoNode.swift index 7899d9855c..282a384d67 100644 --- a/submodules/TelegramCallsUI/Sources/GroupVideoNode.swift +++ b/submodules/TelegramCallsUI/Sources/GroupVideoNode.swift @@ -24,6 +24,8 @@ final class GroupVideoNode: ASDisplayNode, PreviewVideoNode { private let containerNode: ASDisplayNode private let videoViewContainer: UIView private let videoView: VideoRenderingView + + private let debugTextNode: ImmediateTextNode private let backdropVideoViewContainer: UIView private let backdropVideoView: VideoRenderingView? @@ -55,12 +57,20 @@ final class GroupVideoNode: ASDisplayNode, PreviewVideoNode { self.backdropVideoViewContainer = UIView() self.backdropVideoViewContainer.isUserInteractionEnabled = false self.backdropVideoView = backdropVideoView + + self.debugTextNode = ImmediateTextNode() super.init() - + + if let backdropVideoView = backdropVideoView { + self.backdropVideoViewContainer.addSubview(backdropVideoView) + self.view.addSubview(self.backdropVideoViewContainer) + } + self.videoViewContainer.addSubview(self.videoView) self.addSubnode(self.sourceContainerNode) self.containerNode.view.addSubview(self.videoViewContainer) + self.containerNode.addSubnode(self.debugTextNode) self.sourceContainerNode.contentNode.addSubnode(self.containerNode) self.clipsToBounds = true @@ -194,9 +204,24 @@ final class GroupVideoNode: ASDisplayNode, PreviewVideoNode { } return rotatedAspect } + + func updateDebugInfo(text: String) { + self.debugTextNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white) + if let (size, layoutMode) = self.validLayout { + self.updateLayout(size: size, layoutMode: layoutMode, transition: .immediate) + } + } func updateLayout(size: CGSize, layoutMode: VideoNodeLayoutMode, transition: ContainedViewLayoutTransition) { self.validLayout = (size, layoutMode) + + let debugTextSize = self.debugTextNode.updateLayout(CGSize(width: 200.0, height: 200.0)) + if size.height > size.width + 100.0 { + self.debugTextNode.frame = CGRect(origin: CGPoint(x: 5.0, y: 44.0), size: debugTextSize) + } else { + self.debugTextNode.frame = CGRect(origin: CGPoint(x: 5.0, y: 5.0), size: debugTextSize) + } + let bounds = CGRect(origin: CGPoint(), size: size) self.sourceContainerNode.update(size: size, transition: .immediate) transition.updateFrameAsPositionAndBounds(node: self.sourceContainerNode, frame: bounds) @@ -299,7 +324,20 @@ final class GroupVideoNode: ASDisplayNode, PreviewVideoNode { let normalizedVideoSize = rotatedVideoFrame.size.aspectFilled(CGSize(width: 1080.0, height: 1080.0)) - self.backdropVideoView?.updateIsEnabled(self.isEnabled && self.isBlurEnabled) + let effectiveBlurEnabled = self.isEnabled && self.isBlurEnabled + + if effectiveBlurEnabled { + self.backdropVideoView?.updateIsEnabled(true) + } + + transition.updatePosition(layer: backdropVideoView.layer, position: rotatedVideoFrame.center, force: true, completion: { [weak self] value in + guard let strongSelf = self, value else { + return + } + if !(strongSelf.isEnabled && strongSelf.isBlurEnabled) { + strongSelf.backdropVideoView?.updateIsEnabled(false) + } + }) transition.updateBounds(layer: backdropVideoView.layer, bounds: CGRect(origin: CGPoint(), size: normalizedVideoSize)) diff --git a/submodules/TelegramCallsUI/Sources/MetalVideoRenderingView.swift b/submodules/TelegramCallsUI/Sources/MetalVideoRenderingView.swift index 54545769af..13f4974b7a 100644 --- a/submodules/TelegramCallsUI/Sources/MetalVideoRenderingView.swift +++ b/submodules/TelegramCallsUI/Sources/MetalVideoRenderingView.swift @@ -27,12 +27,21 @@ private func getCubeVertexData( frameWidth: Int, frameHeight: Int, rotation: Int, + mirrorHorizontally: Bool, + mirrorVertically: Bool, buffer: UnsafeMutablePointer ) { - let cropLeft = Float(cropX) / Float(frameWidth) - let cropRight = Float(cropX + cropWidth) / Float(frameWidth) - let cropTop = Float(cropY) / Float(frameHeight) - let cropBottom = Float(cropY + cropHeight) / Float(frameHeight) + var cropLeft = Float(cropX) / Float(frameWidth) + var cropRight = Float(cropX + cropWidth) / Float(frameWidth) + var cropTop = Float(cropY) / Float(frameHeight) + var cropBottom = Float(cropY + cropHeight) / Float(frameHeight) + + if mirrorHorizontally { + swap(&cropLeft, &cropRight) + } + if mirrorVertically { + swap(&cropTop, &cropBottom) + } switch rotation { default: @@ -49,6 +58,8 @@ private func getCubeVertexData( @available(iOS 13.0, *) private protocol FrameBufferRenderingState { var frameSize: CGSize? { get } + var mirrorHorizontally: Bool { get } + var mirrorVertically: Bool { get } func encode(renderingContext: MetalVideoRenderingContext, vertexBuffer: MTLBuffer, renderEncoder: MTLRenderCommandEncoder) -> Bool } @@ -73,6 +84,9 @@ private final class NV12FrameBufferRenderingState: FrameBufferRenderingState { private var yTexture: MTLTexture? private var uvTexture: MTLTexture? + private(set) var mirrorHorizontally: Bool = false + private(set) var mirrorVertically: Bool = false + var frameSize: CGSize? { if let yTexture = self.yTexture { return CGSize(width: yTexture.width, height: yTexture.height) @@ -81,7 +95,7 @@ private final class NV12FrameBufferRenderingState: FrameBufferRenderingState { } } - func updateTextureBuffers(renderingContext: MetalVideoRenderingContext, frameBuffer: OngoingGroupCallContext.VideoFrameData.NativeBuffer) { + func updateTextureBuffers(renderingContext: MetalVideoRenderingContext, frameBuffer: OngoingGroupCallContext.VideoFrameData.NativeBuffer, mirrorHorizontally: Bool, mirrorVertically: Bool) { let pixelBuffer = frameBuffer.pixelBuffer var lumaTexture: MTLTexture? @@ -112,6 +126,9 @@ private final class NV12FrameBufferRenderingState: FrameBufferRenderingState { self.yTexture = nil self.uvTexture = nil } + + self.mirrorHorizontally = mirrorHorizontally + self.mirrorVertically = mirrorVertically } func encode(renderingContext: MetalVideoRenderingContext, vertexBuffer: MTLBuffer, renderEncoder: MTLRenderCommandEncoder) -> Bool { @@ -142,6 +159,9 @@ private final class I420FrameBufferRenderingState: FrameBufferRenderingState { private var lumaTextureDescriptor: MTLTextureDescriptor? private var chromaTextureDescriptor: MTLTextureDescriptor? + private(set) var mirrorHorizontally: Bool = false + private(set) var mirrorVertically: Bool = false + var frameSize: CGSize? { if let yTexture = self.yTexture { return CGSize(width: yTexture.width, height: yTexture.height) @@ -318,7 +338,7 @@ final class MetalVideoRenderingView: UIView, VideoRenderingView { renderingState = NV12FrameBufferRenderingState() self.frameBufferRenderingState = renderingState } - renderingState.updateTextureBuffers(renderingContext: renderingContext, frameBuffer: buffer) + renderingState.updateTextureBuffers(renderingContext: renderingContext, frameBuffer: buffer, mirrorHorizontally: videoFrameData.mirrorHorizontally, mirrorVertically: videoFrameData.mirrorVertically) self.needsRedraw = true case let .i420(buffer): let renderingState: I420FrameBufferRenderingState @@ -350,6 +370,8 @@ final class MetalVideoRenderingView: UIView, VideoRenderingView { guard let frameSize = frameBufferRenderingState.frameSize else { return nil } + let mirrorHorizontally = frameBufferRenderingState.mirrorHorizontally + let mirrorVertically = frameBufferRenderingState.mirrorVertically let drawableSize: CGSize if self.blur { @@ -382,19 +404,20 @@ final class MetalVideoRenderingView: UIView, VideoRenderingView { if self.metalLayer.drawableSize != drawableSize { self.metalLayer.drawableSize = drawableSize - - getCubeVertexData( - cropX: 0, - cropY: 0, - cropWidth: Int(drawableSize.width), - cropHeight: Int(drawableSize.height), - frameWidth: Int(drawableSize.width), - frameHeight: Int(drawableSize.height), - rotation: 0, - buffer: self.vertexBuffer.contents().assumingMemoryBound(to: Float.self) - ) } + getCubeVertexData( + cropX: 0, + cropY: 0, + cropWidth: Int(drawableSize.width), + cropHeight: Int(drawableSize.height), + frameWidth: Int(drawableSize.width), + frameHeight: Int(drawableSize.height), + rotation: 0, + mirrorHorizontally: mirrorHorizontally, + mirrorVertically: mirrorVertically, + buffer: self.vertexBuffer.contents().assumingMemoryBound(to: Float.self) + ) guard let drawable = self.metalLayer.nextDrawable() else { return nil diff --git a/submodules/TelegramCallsUI/Sources/PresentationCall.swift b/submodules/TelegramCallsUI/Sources/PresentationCall.swift index 59d438a608..0eed053664 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationCall.swift @@ -98,7 +98,10 @@ final class PresentationCallToneRenderer { var blockBuffer: CMBlockBuffer? let bytes = malloc(frameSize)! - toneData.withUnsafeBytes { (dataBytes: UnsafePointer) -> Void in + toneData.withUnsafeBytes { dataBuffer -> Void in + guard let dataBytes = dataBuffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } var takenCount = 0 while takenCount < frameSize { let dataOffset = (takeOffset + takenCount) % toneData.count @@ -391,7 +394,7 @@ public final class PresentationCallImpl: PresentationCall { } } } - }, deactivate: { [weak self] in + }, deactivate: { [weak self] _ in return Signal { subscriber in Queue.mainQueue().async { if let strongSelf = self { diff --git a/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift b/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift index eb09290450..d1dfdfd0c6 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift @@ -728,7 +728,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager { return .success } - public func joinGroupCall(context: AccountContext, peerId: PeerId, invite: String?, requestJoinAsPeerId: ((@escaping (PeerId?) -> Void) -> Void)?, initialCall: CachedChannelData.ActiveCall, endCurrentIfAny: Bool) -> JoinGroupCallManagerResult { + public func joinGroupCall(context: AccountContext, peerId: PeerId, invite: String?, requestJoinAsPeerId: ((@escaping (PeerId?) -> Void) -> Void)?, initialCall: EngineGroupCallDescription, endCurrentIfAny: Bool) -> JoinGroupCallManagerResult { let begin: () -> Void = { [weak self] in if let requestJoinAsPeerId = requestJoinAsPeerId { requestJoinAsPeerId({ joinAsPeerId in @@ -772,7 +772,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager { peerId: PeerId, invite: String?, joinAsPeerId: PeerId?, - initialCall: CachedChannelData.ActiveCall, + initialCall: EngineGroupCallDescription, internalId: CallSessionInternalId = CallSessionInternalId() ) -> Signal { let (presentationData, present, openSettings) = self.getDeviceAccessData() diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index ddcff8958e..3499f3dee9 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -91,7 +91,7 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext { return self.panelDataPromise.get() } - public init(account: Account, engine: TelegramEngine, peerId: PeerId, call: CachedChannelData.ActiveCall) { + public init(account: Account, engine: TelegramEngine, peerId: PeerId, call: EngineGroupCallDescription) { self.panelDataPromise.set(.single(GroupCallPanelData( peerId: peerId, info: GroupCallInfo( @@ -183,7 +183,7 @@ public final class AccountGroupCallContextCacheImpl: AccountGroupCallContextCach self.queue = queue } - public func get(account: Account, engine: TelegramEngine, peerId: PeerId, call: CachedChannelData.ActiveCall) -> AccountGroupCallContextImpl.Proxy { + public func get(account: Account, engine: TelegramEngine, peerId: PeerId, call: EngineGroupCallDescription) -> AccountGroupCallContextImpl.Proxy { let result: Record if let current = self.contexts[call.id] { result = current @@ -245,7 +245,8 @@ private extension PresentationGroupCallState { raisedHand: false, scheduleTimestamp: scheduleTimestamp, subscribedToScheduled: subscribedToScheduled, - isVideoEnabled: false + isVideoEnabled: false, + isVideoWatchersLimitReached: false ) } } @@ -381,7 +382,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { private let getDeviceAccessData: () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void) - private var initialCall: CachedChannelData.ActiveCall? + private var initialCall: EngineGroupCallDescription? public let internalId: CallSessionInternalId public let peerId: PeerId private var invite: String? @@ -620,7 +621,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { audioSession: ManagedAudioSession, callKitIntegration: CallKitIntegration?, getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void), - initialCall: CachedChannelData.ActiveCall?, + initialCall: EngineGroupCallDescription?, internalId: CallSessionInternalId, peerId: PeerId, invite: String?, @@ -662,7 +663,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { strongSelf.updateSessionState(internalState: strongSelf.internalState, audioSessionControl: control) } } - }, deactivate: { [weak self] in + }, deactivate: { [weak self] _ in return Signal { subscriber in Queue.mainQueue().async { if let strongSelf = self { @@ -821,7 +822,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { }) if let initialCall = initialCall, let temporaryParticipantsContext = (self.accountContext.cachedGroupCallContexts as? AccountGroupCallContextCacheImpl)?.impl.syncWith({ impl in - impl.get(account: accountContext.account, engine: accountContext.engine, peerId: peerId, call: CachedChannelData.ActiveCall(id: initialCall.id, accessHash: initialCall.accessHash, title: initialCall.title, scheduleTimestamp: initialCall.scheduleTimestamp, subscribedToScheduled: initialCall.subscribedToScheduled)) + impl.get(account: accountContext.account, engine: accountContext.engine, peerId: peerId, call: EngineGroupCallDescription(id: initialCall.id, accessHash: initialCall.accessHash, title: initialCall.title, scheduleTimestamp: initialCall.scheduleTimestamp, subscribedToScheduled: initialCall.subscribedToScheduled)) }) { self.switchToTemporaryParticipantsContext(sourceContext: temporaryParticipantsContext.context.participantsContext, oldMyPeerId: self.joinAsPeerId) } else { @@ -1385,8 +1386,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { outgoingAudioBitrateKbit = value } - let enableNoiseSuppression = accountContext.sharedContext.immediateExperimentalUISettings.enableNoiseSuppression - genericCallContext = OngoingGroupCallContext(video: self.videoCapturer, requestMediaChannelDescriptions: { [weak self] ssrcs, completion in let disposable = MetaDisposable() Queue.mainQueue().async { @@ -1405,7 +1404,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { strongSelf.requestCall(movingFromBroadcastToRtc: false) } } - }, outgoingAudioBitrateKbit: outgoingAudioBitrateKbit, videoContentType: self.isVideoEnabled ? .generic : .none, enableNoiseSuppression: enableNoiseSuppression) + }, outgoingAudioBitrateKbit: outgoingAudioBitrateKbit, videoContentType: self.isVideoEnabled ? .generic : .none, enableNoiseSuppression: false) self.genericCallContext = genericCallContext self.stateVersionValue += 1 @@ -1781,7 +1780,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { guard let strongSelf = self else { return } - + let appConfiguration = strongSelf.accountContext.currentAppConfiguration.with({ $0 }) + let configuration = VoiceChatConfiguration.with(appConfiguration: appConfiguration) + strongSelf.participantsContext?.updateAdminIds(adminIds) var topParticipants: [GroupCallParticipantsContext.Participant] = [] @@ -1859,6 +1860,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } var otherParticipantsWithVideo = 0 + var videoWatchingParticipants = 0 for participant in participants { var participant = participant @@ -1950,6 +1952,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { strongSelf.stateValue.muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false) strongSelf.genericCallContext?.setIsMuted(true) } + + if participant.joinedVideo { + videoWatchingParticipants += 1 + } } else { if let ssrc = participant.ssrc { if let volume = participant.volume { @@ -1969,6 +1975,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { if participant.videoDescription != nil || participant.presentationDescription != nil { otherParticipantsWithVideo += 1 } + if participant.joinedVideo { + videoWatchingParticipants += 1 + } } if let index = updatedInvitedPeers.firstIndex(of: participant.peer.id) { @@ -1994,6 +2003,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { strongSelf.stateValue.title = state.title strongSelf.stateValue.scheduleTimestamp = state.scheduleTimestamp strongSelf.stateValue.isVideoEnabled = state.isVideoEnabled && otherParticipantsWithVideo < state.unmutedVideoLimit + strongSelf.stateValue.isVideoWatchersLimitReached = videoWatchingParticipants >= configuration.videoParticipantsMaxCount strongSelf.summaryInfoState.set(.single(SummaryInfoState(info: GroupCallInfo( id: callInfo.id, @@ -2596,8 +2606,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } } - func requestVideo(capturer: OngoingCallVideoCapturer) { + func requestVideo(capturer: OngoingCallVideoCapturer, useFrontCamera: Bool = true) { self.videoCapturer = capturer + self.useFrontCamera = useFrontCamera self.hasVideo = true if let videoCapturer = self.videoCapturer { @@ -2873,7 +2884,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { case generic } - let account = self.account let context = self.accountContext let currentCall: Signal if let initialCall = self.initialCall { @@ -2924,7 +2934,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } if let value = value { - strongSelf.initialCall = CachedChannelData.ActiveCall(id: value.id, accessHash: value.accessHash, title: value.title, scheduleTimestamp: nil, subscribedToScheduled: false) + strongSelf.initialCall = EngineGroupCallDescription(id: value.id, accessHash: value.accessHash, title: value.title, scheduleTimestamp: nil, subscribedToScheduled: false) strongSelf.updateSessionState(internalState: .active(value), audioSessionControl: strongSelf.audioSessionControl) } else { @@ -3189,4 +3199,24 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { public func loadMoreMembers(token: String) { self.participantsContext?.loadMore(token: token) } + + func getStats() -> Signal { + return Signal { [weak self] subscriber in + guard let strongSelf = self else { + subscriber.putCompletion() + return EmptyDisposable + } + if let genericCallContext = strongSelf.genericCallContext { + genericCallContext.getStats(completion: { stats in + subscriber.putNext(stats) + subscriber.putCompletion() + }) + } else { + subscriber.putCompletion() + } + + return EmptyDisposable + } + |> runOn(.mainQueue()) + } } diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatCameraPreviewController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatCameraPreviewController.swift index c0bc5010e7..bdbf531757 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatCameraPreviewController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatCameraPreviewController.swift @@ -213,7 +213,7 @@ private class VoiceChatCameraPreviewControllerNode: ViewControllerTracingNode, U self.placeholderIconNode.contentMode = .scaleAspectFit self.placeholderIconNode.displaysAsynchronously = false - self.wheelNode = WheelControlNode(items: [WheelControlNode.Item(title: self.presentationData.strings.VoiceChat_VideoPreviewPhoneScreen), WheelControlNode.Item(title: self.presentationData.strings.VoiceChat_VideoPreviewFrontCamera), WheelControlNode.Item(title: self.presentationData.strings.VoiceChat_VideoPreviewBackCamera)], selectedIndex: self.selectedTabIndex) + self.wheelNode = WheelControlNode(items: [WheelControlNode.Item(title: UIDevice.current.model == "iPad" ? self.presentationData.strings.VoiceChat_VideoPreviewTabletScreen : self.presentationData.strings.VoiceChat_VideoPreviewPhoneScreen), WheelControlNode.Item(title: self.presentationData.strings.VoiceChat_VideoPreviewFrontCamera), WheelControlNode.Item(title: self.presentationData.strings.VoiceChat_VideoPreviewBackCamera)], selectedIndex: self.selectedTabIndex) super.init() diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index 68f4f30e93..a2578444b8 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -43,26 +43,6 @@ let bottomAreaHeight: CGFloat = 206.0 private let fullscreenBottomAreaHeight: CGFloat = 80.0 private let bottomGradientHeight: CGFloat = 70.0 -public struct VoiceChatConfiguration { - static var defaultValue: VoiceChatConfiguration { - return VoiceChatConfiguration(videoParticipantsMaxCount: 30) - } - - public let videoParticipantsMaxCount: Int32 - - fileprivate init(videoParticipantsMaxCount: Int32) { - self.videoParticipantsMaxCount = videoParticipantsMaxCount - } - - static func with(appConfiguration: AppConfiguration) -> VoiceChatConfiguration { - if let data = appConfiguration.data, let value = data["groupcall_video_participants_max"] as? Double { - return VoiceChatConfiguration(videoParticipantsMaxCount: Int32(value)) - } else { - return .defaultValue - } - } -} - func decorationCornersImage(top: Bool, bottom: Bool, dark: Bool) -> UIImage? { if !top && !bottom { return nil @@ -379,7 +359,7 @@ public final class VoiceChatController: ViewController { } private enum ListEntry: Comparable, Identifiable { - case tiles([VoiceChatTileItem], VoiceChatTileLayoutMode) + case tiles([VoiceChatTileItem], VoiceChatTileLayoutMode, Int32, Bool) case invite(PresentationTheme, PresentationStrings, String, Bool) case peer(VoiceChatPeerEntry, Int32) @@ -396,8 +376,8 @@ public final class VoiceChatController: ViewController { static func ==(lhs: ListEntry, rhs: ListEntry) -> Bool { switch lhs { - case let .tiles(lhsTiles, lhsLayoutMode): - if case let .tiles(rhsTiles, rhsLayoutMode) = rhs, lhsTiles == rhsTiles, lhsLayoutMode == rhsLayoutMode { + case let .tiles(lhsTiles, lhsLayoutMode, lhsVideoLimit, lhsReachedLimit): + if case let .tiles(rhsTiles, rhsLayoutMode, rhsVideoLimit, rhsReachedLimit) = rhs, lhsTiles == rhsTiles, lhsLayoutMode == rhsLayoutMode, lhsVideoLimit == rhsVideoLimit, lhsReachedLimit == rhsReachedLimit { return true } else { return false @@ -644,8 +624,8 @@ public final class VoiceChatController: ViewController { func item(context: AccountContext, presentationData: PresentationData, interaction: Interaction) -> ListViewItem { switch self { - case let .tiles(tiles, layoutMode): - return VoiceChatTilesGridItem(context: context, tiles: tiles, layoutMode: layoutMode, getIsExpanded: { + case let .tiles(tiles, layoutMode, videoLimit, reachedLimit): + return VoiceChatTilesGridItem(context: context, tiles: tiles, layoutMode: layoutMode, videoLimit: videoLimit, reachedLimit: reachedLimit, getIsExpanded: { return interaction.isExpanded }) case let .invite(_, _, text, isLink): @@ -965,6 +945,8 @@ public final class VoiceChatController: ViewController { return false } } + + private var statsDisposable: Disposable? init(controller: VoiceChatController, sharedContext: SharedAccountContext, call: PresentationGroupCall) { self.controller = controller @@ -1704,7 +1686,7 @@ public final class VoiceChatController: ViewController { let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme)) var items: [ActionSheetItem] = [] - items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: peer, chatPeer: chatPeer, action: .removeFromGroup, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) + items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: EnginePeer(peer), chatPeer: EnginePeer(chatPeer), action: .removeFromGroup, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.VoiceChat_RemovePeerRemove, color: .destructive, action: { [weak actionSheet] in actionSheet?.dismissAnimated() @@ -2358,6 +2340,27 @@ public final class VoiceChatController: ViewController { } strongSelf.appIsActive = active }) + + if self.context.sharedContext.immediateExperimentalUISettings.enableDebugDataDisplay { + self.statsDisposable = ((call as! PresentationGroupCallImpl).getStats() + |> deliverOnMainQueue + |> then(.complete() |> delay(1.0, queue: .mainQueue())) + |> restart).start(next: { [weak self] stats in + guard let strongSelf = self else { + return + } + for (endpointId, videoNode) in strongSelf.videoNodes { + if let incomingVideoStats = stats.incomingVideoStats[endpointId] { + videoNode.updateDebugInfo(text: "in: \(incomingVideoStats.receivingQuality)\n srv: \(incomingVideoStats.availableQuality)") + } + } + if let (_, maybeEndpointId, _, _, _) = strongSelf.mainStageNode.currentPeer, let endpointId = maybeEndpointId { + if let incomingVideoStats = stats.incomingVideoStats[endpointId] { + strongSelf.mainStageNode.currentVideoNode?.updateDebugInfo(text: "in: \(incomingVideoStats.receivingQuality)\n srv: \(incomingVideoStats.availableQuality)") + } + } + }) + } } deinit { @@ -2381,6 +2384,7 @@ public final class VoiceChatController: ViewController { self.readyVideoDisposables.dispose() self.applicationStateDisposable?.dispose() self.myPeerVideoReadyDisposable.dispose() + self.statsDisposable?.dispose() } private func openSettingsMenu(sourceNode: ASDisplayNode, gesture: ContextGesture?) { @@ -3501,23 +3505,27 @@ public final class VoiceChatController: ViewController { guard let strongSelf = self, ready else { return } + var isFrontCamera = true let videoCapturer = OngoingCallVideoCapturer() let input = videoCapturer.video() if let videoView = strongSelf.videoRenderingContext.makeView(input: input, blur: false) { + videoView.updateIsEnabled(true) + let cameraNode = GroupVideoNode(videoView: videoView, backdropVideoView: nil) let controller = VoiceChatCameraPreviewController(sharedContext: strongSelf.context.sharedContext, cameraNode: cameraNode, shareCamera: { [weak self] _, unmuted in if let strongSelf = self { strongSelf.call.setIsMuted(action: unmuted ? .unmuted : .muted(isPushToTalkActive: false)) - (strongSelf.call as! PresentationGroupCallImpl).requestVideo(capturer: videoCapturer) + (strongSelf.call as! PresentationGroupCallImpl).requestVideo(capturer: videoCapturer, useFrontCamera: isFrontCamera) if let (layout, navigationHeight) = strongSelf.validLayout { strongSelf.animatingButtonsSwap = true strongSelf.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.4, curve: .spring)) } } - }, switchCamera: { [weak self] in + }, switchCamera: { Queue.mainQueue().after(0.1) { - self?.call.switchVideoCamera() + isFrontCamera = !isFrontCamera + videoCapturer.switchVideoInput(isFront: isFrontCamera) } }) strongSelf.controller?.present(controller, in: .window(.root)) @@ -4792,7 +4800,6 @@ public final class VoiceChatController: ViewController { let speakingPeersUpdated = self.currentSpeakingPeers != speakingPeers self.currentCallMembers = callMembers - self.currentSpeakingPeers = speakingPeers self.currentInvitedPeers = invitedPeers var entries: [ListEntry] = [] @@ -5019,12 +5026,16 @@ public final class VoiceChatController: ViewController { self.joinedVideo = joinedVideo + let configuration = self.configuration ?? VoiceChatConfiguration.defaultValue + var reachedLimit = false + if !joinedVideo && (!tileItems.isEmpty || !gridTileItems.isEmpty), let peer = self.peer { tileItems.removeAll() gridTileItems.removeAll() - let configuration = self.configuration ?? VoiceChatConfiguration.defaultValue tileItems.append(VoiceChatTileItem(account: self.context.account, peer: peer, videoEndpointId: "", videoReady: false, videoTimeouted: true, isVideoLimit: true, videoLimit: configuration.videoParticipantsMaxCount, isPaused: false, isOwnScreencast: false, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder, speaking: false, secondary: false, isTablet: false, icon: .none, text: .none, additionalText: nil, action: {}, contextAction: nil, getVideo: { _ in return nil }, getAudioLevel: nil)) + } else if let callState = self.callState, !tileItems.isEmpty && callState.isVideoWatchersLimitReached && self.connectedOnce && (callState.canManageCall || callState.adminIds.contains(self.context.account.peerId)) { + reachedLimit = true } for member in callMembers.0 { @@ -5093,9 +5104,10 @@ public final class VoiceChatController: ViewController { self.updateMainVideo(waitForFullSize: true, entries: fullscreenEntries, force: true) return } - + self.updateRequestedVideoChannels() + self.currentSpeakingPeers = speakingPeers self.peerIdToEndpointId = peerIdToEndpointId var updateLayout = false @@ -5108,11 +5120,11 @@ public final class VoiceChatController: ViewController { updateLayout = true self.currentTileItems = gridTileItems if displayPanelVideos && !tileItems.isEmpty { - entries.insert(.tiles(tileItems, .pairs), at: 0) + entries.insert(.tiles(tileItems, .pairs, configuration.videoParticipantsMaxCount, reachedLimit), at: 0) } } else { if !tileItems.isEmpty { - entries.insert(.tiles(tileItems, .pairs), at: 0) + entries.insert(.tiles(tileItems, .pairs, configuration.videoParticipantsMaxCount, reachedLimit), at: 0) } } @@ -6032,15 +6044,15 @@ public final class VoiceChatController: ViewController { } else if let url = asset as? URL, let data = try? Data(contentsOf: url, options: [.mappedRead]), let image = UIImage(data: data), let entityRenderer = entityRenderer { let durationSignal: SSignal = SSignal(generator: { subscriber in let disposable = (entityRenderer.duration()).start(next: { duration in - subscriber?.putNext(duration) - subscriber?.putCompletion() + subscriber.putNext(duration) + subscriber.putCompletion() }) return SBlockDisposable(block: { disposable.dispose() }) }) - signal = durationSignal.map(toSignal: { duration -> SSignal? in + signal = durationSignal.map(toSignal: { duration -> SSignal in if let duration = duration as? Double { return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, watcher: nil, entityRenderer: entityRenderer)! } else { diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatFullscreenParticipantItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatFullscreenParticipantItem.swift index e7fa1d94ed..209ef94c91 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatFullscreenParticipantItem.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatFullscreenParticipantItem.swift @@ -38,10 +38,18 @@ private var fadeImage: UIImage? = { return generateImage(CGSize(width: fadeHeight, height: fadeHeight), rotatedContext: { size, context in let bounds = CGRect(origin: CGPoint(), size: size) context.clear(bounds) - - let colorsArray = [fadeColor.withAlphaComponent(0.0).cgColor, fadeColor.cgColor] as CFArray - var locations: [CGFloat] = [1.0, 0.0] - let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray, locations: &locations)! + + let stepCount = 10 + var colors: [CGColor] = [] + var locations: [CGFloat] = [] + + for i in 0 ... stepCount { + let t = CGFloat(i) / CGFloat(stepCount) + colors.append(fadeColor.withAlphaComponent((1.0 - t * t) * 0.7).cgColor) + locations.append(t) + } + + let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colors as CFArray, locations: &locations)! context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions()) }) }() @@ -606,7 +614,7 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode { strongSelf.containerNode.isGestureEnabled = item.contextAction != nil strongSelf.accessibilityLabel = titleAttributedString?.string - var combinedValueString = "" + let combinedValueString = "" // if let statusString = statusAttributedString?.string, !statusString.isEmpty { // combinedValueString.append(statusString) // } @@ -692,7 +700,7 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode { strongSelf.audioLevelView = audioLevelView strongSelf.offsetContainerNode.view.insertSubview(audioLevelView, at: 0) - if let item = strongSelf.item, strongSelf.videoNode != nil && !active { + if let _ = strongSelf.item, strongSelf.videoNode != nil && !active { audioLevelView.alpha = 0.0 } } @@ -744,7 +752,7 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode { if item.peer.isDeleted { overrideImage = .deletedIcon } - strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad, storeUnrounded: true) + strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: EnginePeer(item.peer), overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad, storeUnrounded: true) var hadMicrophoneNode = false var hadRaiseHandNode = false diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatHeaderButton.swift b/submodules/TelegramCallsUI/Sources/VoiceChatHeaderButton.swift index 92b9af8cb2..0e3b0a0126 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatHeaderButton.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatHeaderButton.swift @@ -3,6 +3,7 @@ import UIKit import AsyncDisplayKit import Display import Postbox +import TelegramCore import AccountContext import TelegramPresentationData import AvatarNode @@ -156,7 +157,7 @@ final class VoiceChatHeaderButton: HighlightableButtonNode { self.iconNode.isHidden = false self.avatarNode.isHidden = true case let .avatar(peer): - self.avatarNode.setPeer(context: self.context, theme: self.theme, peer: peer) + self.avatarNode.setPeer(context: self.context, theme: self.theme, peer: EnginePeer(peer)) self.iconNode.isHidden = true self.avatarNode.isHidden = false self.animationNode?.isHidden = true @@ -174,7 +175,7 @@ final class VoiceChatHeaderButton: HighlightableButtonNode { self.iconNode.isHidden = false self.avatarNode.isHidden = true case let .avatar(peer): - self.avatarNode.setPeer(context: self.context, theme: self.theme, peer: peer) + self.avatarNode.setPeer(context: self.context, theme: self.theme, peer: EnginePeer(peer)) self.iconNode.isHidden = true self.avatarNode.isHidden = false self.animationNode?.isHidden = true diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatJoinScreen.swift b/submodules/TelegramCallsUI/Sources/VoiceChatJoinScreen.swift index 19adc39f85..81195fa8fa 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatJoinScreen.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatJoinScreen.swift @@ -669,7 +669,7 @@ final class VoiceChatPreviewContentNode: ASDisplayNode, ShareContentContainerNod super.init() self.addSubnode(self.avatarNode) - self.avatarNode.setPeer(context: context, theme: theme, peer: peer, emptyColor: theme.list.mediaPlaceholderColor) + self.avatarNode.setPeer(context: context, theme: theme, peer: EnginePeer(peer), emptyColor: theme.list.mediaPlaceholderColor) self.addSubnode(self.titleNode) self.titleNode.attributedText = NSAttributedString(string: title ?? peer.displayTitle(strings: strings, displayOrder: displayOrder), font: Font.semibold(16.0), textColor: theme.actionSheet.primaryTextColor) diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatMainStageNode.swift b/submodules/TelegramCallsUI/Sources/VoiceChatMainStageNode.swift index fe25af4cf8..7c0cc6f84d 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatMainStageNode.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatMainStageNode.swift @@ -95,12 +95,12 @@ private class VoiceChatPinButtonNode: HighlightTrackingButtonNode { final class VoiceChatMainStageNode: ASDisplayNode { private let context: AccountContext private let call: PresentationGroupCall - private var currentPeer: (PeerId, String?, Bool, Bool, Bool)? + private(set) var currentPeer: (PeerId, String?, Bool, Bool, Bool)? private var currentPeerEntry: VoiceChatPeerEntry? var callState: PresentationGroupCallState? - private var currentVideoNode: GroupVideoNode? + private(set) var currentVideoNode: GroupVideoNode? private let backgroundNode: ASDisplayNode private let topFadeNode: ASDisplayNode @@ -161,10 +161,18 @@ final class VoiceChatMainStageNode: ASDisplayNode { if let image = generateImage(CGSize(width: fadeHeight, height: fadeHeight), rotatedContext: { size, context in let bounds = CGRect(origin: CGPoint(), size: size) context.clear(bounds) - - let colorsArray = [fadeColor.cgColor, fadeColor.withAlphaComponent(0.0).cgColor] as CFArray - var locations: [CGFloat] = [1.0, 0.0] - let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray, locations: &locations)! + + let stepCount = 10 + var colors: [CGColor] = [] + var locations: [CGFloat] = [] + + for i in 0 ... stepCount { + let t = CGFloat(i) / CGFloat(stepCount) + colors.append(fadeColor.withAlphaComponent(t * t).cgColor) + locations.append(t) + } + + let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colors as CFArray, locations: &locations)! context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions()) }) { self.topFadeNode.backgroundColor = UIColor(patternImage: image) @@ -181,6 +189,7 @@ final class VoiceChatMainStageNode: ASDisplayNode { let colorsArray = [fadeColor.withAlphaComponent(0.0).cgColor, fadeColor.cgColor] as CFArray var locations: [CGFloat] = [1.0, 0.0] let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray, locations: &locations)! + context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions()) }) { self.bottomGradientNode.backgroundColor = UIColor(patternImage: image) @@ -628,7 +637,7 @@ final class VoiceChatMainStageNode: ASDisplayNode { } let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - strongSelf.speakingAvatarNode.setPeer(context: strongSelf.context, theme: presentationData.theme, peer: peer) + strongSelf.speakingAvatarNode.setPeer(context: strongSelf.context, theme: presentationData.theme, peer: EnginePeer(peer)) let bodyAttributes = MarkdownAttributeSet(font: Font.regular(15.0), textColor: .white, additionalAttributes: [:]) let boldAttributes = MarkdownAttributeSet(font: Font.semibold(15.0), textColor: .white, additionalAttributes: [:]) diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift index 9bf559bcea..952e316641 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift @@ -937,7 +937,7 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode { strongSelf.containerNode.isGestureEnabled = item.contextAction != nil strongSelf.accessibilityLabel = titleAttributedString?.string - var combinedValueString = "" + let combinedValueString = "" // if let statusString = statusAttributedString?.string, !statusString.isEmpty { // combinedValueString.append(statusString) // } @@ -1105,7 +1105,7 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode { if item.peer.isDeleted { overrideImage = .deletedIcon } - strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad, storeUnrounded: true) + strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: EnginePeer(item.peer), overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad, storeUnrounded: true) strongSelf.highlightContainerNode.frame = CGRect(origin: CGPoint(x: params.leftInset, y: -UIScreenPixel), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: layout.contentSize.height + UIScreenPixel + UIScreenPixel + 11.0)) diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatPeerActionSheetItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatPeerActionSheetItem.swift index 12419fd9fa..bd1eb8bb1f 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatPeerActionSheetItem.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatPeerActionSheetItem.swift @@ -120,7 +120,7 @@ public class VoiceChatPeerActionSheetItemNode: ActionSheetItemNode { self.subtitleNode.attributedText = NSAttributedString(string: item.subtitle, font: subtitleFont, textColor: self.theme.secondaryTextColor) let theme = item.context.sharedContext.currentPresentationData.with { $0 }.theme - self.avatarNode.setPeer(context: item.context, theme: theme, peer: item.peer) + self.avatarNode.setPeer(context: item.context, theme: theme, peer: EnginePeer(item.peer)) self.accessibilityArea.accessibilityTraits = [.button] self.accessibilityArea.accessibilityLabel = item.title diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatPeerProfileNode.swift b/submodules/TelegramCallsUI/Sources/VoiceChatPeerProfileNode.swift index 40e0728036..5146e2c53f 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatPeerProfileNode.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatPeerProfileNode.swift @@ -440,7 +440,6 @@ final class VoiceChatPeerProfileNode: ASDisplayNode { } else if let targetNode = targetNode as? VoiceChatFullscreenParticipantItemNode { let backgroundTargetRect = targetRect - let initialSize = self.bounds self.updateInfo(size: targetRect.size, sourceSize: targetRect.size, animate: true) targetNode.avatarNode.isHidden = false diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatShareScreenContextItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatShareScreenContextItem.swift index a91ab9b17b..9b081c9a2a 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatShareScreenContextItem.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatShareScreenContextItem.swift @@ -160,7 +160,6 @@ private final class VoiceChatShareScreenContextItemNode: ASDisplayNode, ContextM self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize) - let subtextFont = Font.regular(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0) self.textNode.attributedText = NSAttributedString(string: self.textNode.attributedText?.string ?? "", font: textFont, textColor: presentationData.theme.contextMenu.primaryColor) } diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatTileGridNode.swift b/submodules/TelegramCallsUI/Sources/VoiceChatTileGridNode.swift index dca6322e0d..1b8b43df66 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatTileGridNode.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatTileGridNode.swift @@ -181,12 +181,16 @@ final class VoiceChatTilesGridItem: ListViewItem { let context: AccountContext let tiles: [VoiceChatTileItem] let layoutMode: VoiceChatTileLayoutMode + let videoLimit: Int32 + let reachedLimit: Bool let getIsExpanded: () -> Bool - init(context: AccountContext, tiles: [VoiceChatTileItem], layoutMode: VoiceChatTileLayoutMode, getIsExpanded: @escaping () -> Bool) { + init(context: AccountContext, tiles: [VoiceChatTileItem], layoutMode: VoiceChatTileLayoutMode, videoLimit: Int32, reachedLimit: Bool, getIsExpanded: @escaping () -> Bool) { self.context = context self.tiles = tiles self.layoutMode = layoutMode + self.videoLimit = videoLimit + self.reachedLimit = reachedLimit self.getIsExpanded = getIsExpanded } @@ -231,6 +235,8 @@ final class VoiceChatTilesGridItemNode: ListViewItemNode { let backgroundNode: ASDisplayNode let cornersNode: ASImageNode + let limitLabel: TextNode + private var absoluteLocation: (CGRect, CGSize)? var tileNodes: [VoiceChatTileItemNode] { @@ -248,10 +254,14 @@ final class VoiceChatTilesGridItemNode: ListViewItemNode { self.cornersNode.displaysAsynchronously = false self.cornersNode.image = decorationCornersImage(top: true, bottom: false, dark: false) + self.limitLabel = TextNode() + self.limitLabel.alpha = 0.0 + super.init(layerBacked: false, dynamicBounce: false) self.addSubnode(self.backgroundNode) self.addSubnode(self.cornersNode) + self.addSubnode(self.limitLabel) } override func animateFrameTransition(_ progress: CGFloat, _ currentValue: CGFloat) { @@ -274,14 +284,24 @@ final class VoiceChatTilesGridItemNode: ListViewItemNode { func asyncLayout() -> (_ item: VoiceChatTilesGridItem, _ params: ListViewItemLayoutParams) -> (ListViewItemNodeLayout, () -> Void) { let currentItem = self.item + let makeLabelLayout = TextNode.asyncLayout(self.limitLabel) + return { item, params in + let presentationData = item.context.sharedContext.currentPresentationData.with { $0 } + let (textLayout, textApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: presentationData.strings.VoiceChat_VideoParticipantsLimitExceededExtended(String(item.videoLimit)).string, font: Font.regular(13.0), textColor: UIColor(rgb: 0x8e8e93), paragraphAlignment: .center), maximumNumberOfLines: 3, truncationType: .end, constrainedSize: CGSize(width: params.width - 32.0, height: CGFloat.greatestFiniteMagnitude), lineSpacing: 0.25)) + let rowCount = ceil(CGFloat(item.tiles.count) / 2.0) - let contentSize = CGSize(width: params.width, height: rowCount * (tileHeight + tileSpacing)) + let gridSize = CGSize(width: params.width, height: rowCount * (tileHeight + tileSpacing)) + var contentSize = gridSize + if item.reachedLimit { + contentSize.height += 10.0 + textLayout.size.height + 10.0 + } let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: UIEdgeInsets()) + return (layout, { [weak self] in if let strongSelf = self { strongSelf.item = item - + let tileGridNode: VoiceChatTileGridNode if let current = strongSelf.tileGridNode { tileGridNode = current @@ -295,22 +315,33 @@ final class VoiceChatTilesGridItemNode: ListViewItemNode { strongSelf.tileGridNode = tileGridNode } - if let (rect, size) = strongSelf.absoluteLocation { tileGridNode.updateAbsoluteRect(rect, within: size) } let transition: ContainedViewLayoutTransition = currentItem == nil ? .immediate : .animated(duration: 0.3, curve: .easeInOut) let tileGridSize = tileGridNode.update(size: CGSize(width: params.width - params.leftInset - params.rightInset, height: params.availableHeight), layoutMode: item.layoutMode, items: item.tiles, transition: transition) + var backgroundSize = tileGridSize + if item.reachedLimit { + backgroundSize.height += 10.0 + textLayout.size.height + 10.0 + } if currentItem == nil { tileGridNode.frame = CGRect(x: params.leftInset, y: 0.0, width: tileGridSize.width, height: tileGridSize.height) - strongSelf.backgroundNode.frame = tileGridNode.frame + strongSelf.backgroundNode.frame = CGRect(origin: tileGridNode.frame.origin, size: backgroundSize) strongSelf.cornersNode.frame = CGRect(x: params.leftInset, y: layout.size.height, width: tileGridSize.width, height: 50.0) } else { transition.updateFrame(node: tileGridNode, frame: CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: tileGridSize)) - transition.updateFrame(node: strongSelf.backgroundNode, frame: CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: tileGridSize)) + transition.updateFrame(node: strongSelf.backgroundNode, frame: CGRect(origin: tileGridNode.frame.origin, size: backgroundSize)) strongSelf.cornersNode.frame = CGRect(x: params.leftInset, y: layout.size.height, width: tileGridSize.width, height: 50.0) } + + let _ = textApply() + if !transition.isAnimated && currentItem?.reachedLimit != item.reachedLimit { + strongSelf.backgroundNode.layer.removeAllAnimations() + strongSelf.limitLabel.layer.removeAllAnimations() + } + transition.updateFrame(node: strongSelf.limitLabel, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((params.width - textLayout.size.width) / 2.0), y: gridSize.height + 10.0), size: textLayout.size)) + transition.updateAlpha(node: strongSelf.limitLabel, alpha: item.reachedLimit ? 1.0 : 0.0) } }) } diff --git a/submodules/TelegramCore/Sources/Account/Account.swift b/submodules/TelegramCore/Sources/Account/Account.swift index 6d75f530c4..11556da21f 100644 --- a/submodules/TelegramCore/Sources/Account/Account.swift +++ b/submodules/TelegramCore/Sources/Account/Account.swift @@ -700,13 +700,11 @@ public enum AccountNetworkState: Equatable { } public final class AccountAuxiliaryMethods { - public let updatePeerChatInputState: (PeerChatInterfaceState?, SynchronizeableChatInputState?) -> PeerChatInterfaceState? public let fetchResource: (Account, MediaResource, Signal<[(Range, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal? public let fetchResourceMediaReferenceHash: (MediaResource) -> Signal public let prepareSecretThumbnailData: (MediaResourceData) -> (PixelDimensions, Data)? - public init(updatePeerChatInputState: @escaping (PeerChatInterfaceState?, SynchronizeableChatInputState?) -> PeerChatInterfaceState?, fetchResource: @escaping (Account, MediaResource, Signal<[(Range, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal?, fetchResourceMediaReferenceHash: @escaping (MediaResource) -> Signal, prepareSecretThumbnailData: @escaping (MediaResourceData) -> (PixelDimensions, Data)?) { - self.updatePeerChatInputState = updatePeerChatInputState + public init(fetchResource: @escaping (Account, MediaResource, Signal<[(Range, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal?, fetchResourceMediaReferenceHash: @escaping (MediaResource) -> Signal, prepareSecretThumbnailData: @escaping (MediaResourceData) -> (PixelDimensions, Data)?) { self.fetchResource = fetchResource self.fetchResourceMediaReferenceHash = fetchResourceMediaReferenceHash self.prepareSecretThumbnailData = prepareSecretThumbnailData diff --git a/submodules/TelegramCore/Sources/Network/MultipartFetch.swift b/submodules/TelegramCore/Sources/Network/MultipartFetch.swift index 0b0568b657..6ec67cecc1 100644 --- a/submodules/TelegramCore/Sources/Network/MultipartFetch.swift +++ b/submodules/TelegramCore/Sources/Network/MultipartFetch.swift @@ -364,7 +364,7 @@ private enum MultipartFetchSource { var ivOffset: Int32 = (offset / 16).bigEndian memcpy(bytes.advanced(by: partIvCount - 4), &ivOffset, 4) } - return .single(MTAesCtrDecrypt(bytes.makeData(), key, partIv)) + return .single(MTAesCtrDecrypt(bytes.makeData(), key, partIv)!) } } } diff --git a/submodules/TelegramCore/Sources/Network/Network.swift b/submodules/TelegramCore/Sources/Network/Network.swift index 9fa999c446..4f8872d0a3 100644 --- a/submodules/TelegramCore/Sources/Network/Network.swift +++ b/submodules/TelegramCore/Sources/Network/Network.swift @@ -493,7 +493,7 @@ func initializedNetwork(accountId: AccountRecordId, arguments: NetworkInitializa context = current context.updateApiEnvironment({ _ in return apiEnvironment}) } else { - context = MTContext(serialization: serialization, encryptionProvider: arguments.encryptionProvider, apiEnvironment: apiEnvironment, isTestingEnvironment: testingEnvironment, useTempAuthKeys: useTempAuthKeys)! + context = MTContext(serialization: serialization, encryptionProvider: arguments.encryptionProvider, apiEnvironment: apiEnvironment, isTestingEnvironment: testingEnvironment, useTempAuthKeys: useTempAuthKeys) store.contexts[key] = context } contextValue = context diff --git a/submodules/TelegramCore/Sources/SecretChats/SecretChatEncryption.swift b/submodules/TelegramCore/Sources/SecretChats/SecretChatEncryption.swift index a46edde5f6..376bca7183 100644 --- a/submodules/TelegramCore/Sources/SecretChats/SecretChatEncryption.swift +++ b/submodules/TelegramCore/Sources/SecretChats/SecretChatEncryption.swift @@ -14,7 +14,7 @@ private func messageKey(key: SecretChatKey, msgKey: UnsafeRawPointer, mode: Secr memcpy(bytes, msgKey, 16) memcpy(bytes.advanced(by: 16), key.key.memory.advanced(by: x), 32) } - let sha1A = MTSha1(sha1AData)! + let sha1A = MTSha1(sha1AData) var sha1BData = Data() sha1BData.count = 16 + 16 + 16 @@ -23,7 +23,7 @@ private func messageKey(key: SecretChatKey, msgKey: UnsafeRawPointer, mode: Secr memcpy(bytes.advanced(by: 16), msgKey, 16) memcpy(bytes.advanced(by: 16 + 16), key.key.memory.advanced(by: 48 + x), 16) } - let sha1B = MTSha1(sha1BData)! + let sha1B = MTSha1(sha1BData) var sha1CData = Data() sha1CData.count = 32 + 16 @@ -31,7 +31,7 @@ private func messageKey(key: SecretChatKey, msgKey: UnsafeRawPointer, mode: Secr memcpy(bytes, key.key.memory.advanced(by: 64 + x), 32) memcpy(bytes.advanced(by: 32), msgKey, 16) } - let sha1C = MTSha1(sha1CData)! + let sha1C = MTSha1(sha1CData) var sha1DData = Data() sha1DData.count = 16 + 32 @@ -39,7 +39,7 @@ private func messageKey(key: SecretChatKey, msgKey: UnsafeRawPointer, mode: Secr memcpy(bytes, msgKey, 16) memcpy(bytes.advanced(by: 16), key.key.memory.advanced(by: 96 + x), 32) } - let sha1D = MTSha1(sha1DData)! + let sha1D = MTSha1(sha1DData) var aesKey = Data() aesKey.count = 8 + 12 + 12 @@ -85,13 +85,13 @@ private func messageKey(key: SecretChatKey, msgKey: UnsafeRawPointer, mode: Secr sha256_a_data.append(msgKey.assumingMemoryBound(to: UInt8.self), count: 16) sha256_a_data.append(key.key.memory.assumingMemoryBound(to: UInt8.self).advanced(by: xValue), count: 36) - let sha256_a = MTSha256(sha256_a_data)! + let sha256_a = MTSha256(sha256_a_data) var sha256_b_data = Data() sha256_b_data.append(key.key.memory.assumingMemoryBound(to: UInt8.self).advanced(by: 40 + xValue), count: 36) sha256_b_data.append(msgKey.assumingMemoryBound(to: UInt8.self), count: 16) - let sha256_b = MTSha256(sha256_b_data)! + let sha256_b = MTSha256(sha256_b_data) var aesKey = Data() aesKey.append(sha256_a.subdata(in: 0 ..< (0 + 8))) @@ -136,7 +136,7 @@ func withDecryptedMessageContents(parameters: SecretChatEncryptionParameters, da return nil } - let calculatedMsgKeyData = MTSubdataSha1(decryptedData, 0, UInt(payloadLength) + 4)! + let calculatedMsgKeyData = MTSubdataSha1(decryptedData, 0, UInt(payloadLength) + 4) let msgKeyMatches = calculatedMsgKeyData.withUnsafeBytes { (bytes: UnsafePointer) -> Bool in return memcmp(bytes.advanced(by: calculatedMsgKeyData.count - 16), msgKey, 16) == 0 } @@ -184,7 +184,7 @@ func withDecryptedMessageContents(parameters: SecretChatEncryptionParameters, da keyLargeData.append(parameters.key.key.memory.assumingMemoryBound(to: UInt8.self).advanced(by: 88 + xValue), count: 32) keyLargeData.append(decryptedData) - let keyLarge = MTSha256(keyLargeData)! + let keyLarge = MTSha256(keyLargeData) let localMessageKey = keyLarge.subdata(in: 8 ..< (8 + 16)) let msgKeyData = Data(bytes: msgKey.assumingMemoryBound(to: UInt8.self), count: 16) @@ -230,7 +230,7 @@ func encryptedMessageContents(parameters: SecretChatEncryptionParameters, data: switch parameters.mode { case .v1: - var msgKey = MTSha1(payloadData)! + var msgKey = MTSha1(payloadData) msgKey.replaceSubrange(0 ..< (msgKey.count - 16), with: Data()) var randomBuf = malloc(16)! @@ -301,7 +301,7 @@ func encryptedMessageContents(parameters: SecretChatEncryptionParameters, data: keyData.append(decryptedData) - let keyLarge = MTSha256(keyData)! + let keyLarge = MTSha256(keyData) let msgKey = keyLarge.subdata(in: 8 ..< (8 + 16)) diff --git a/submodules/TelegramCore/Sources/SecretChats/SecretChatRekeySession.swift b/submodules/TelegramCore/Sources/SecretChats/SecretChatRekeySession.swift index 35aa275bd8..2230accf37 100644 --- a/submodules/TelegramCore/Sources/SecretChats/SecretChatRekeySession.swift +++ b/submodules/TelegramCore/Sources/SecretChats/SecretChatRekeySession.swift @@ -59,7 +59,7 @@ func secretChatAdvanceRekeySessionIfNeeded(encryptionProvider: EncryptionProvide } } - let keyHash = MTSha1(key)! + let keyHash = MTSha1(key) var keyFingerprint: Int64 = 0 keyHash.withUnsafeBytes { (bytes: UnsafePointer) -> Void in diff --git a/submodules/TelegramCore/Sources/SecretChats/UpdateSecretChat.swift b/submodules/TelegramCore/Sources/SecretChats/UpdateSecretChat.swift index 0ed557e1d1..759ee4495d 100644 --- a/submodules/TelegramCore/Sources/SecretChats/UpdateSecretChat.swift +++ b/submodules/TelegramCore/Sources/SecretChats/UpdateSecretChat.swift @@ -40,7 +40,7 @@ func updateSecretChat(encryptionProvider: EncryptionProvider, accountPeerId: Pee } } - let keyHash = MTSha1(key)! + let keyHash = MTSha1(key) var keyFingerprint: Int64 = 0 keyHash.withUnsafeBytes { (bytes: UnsafePointer) -> Void in diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index cb12685129..a6c066ec6b 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -1353,7 +1353,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo if let replyToMsgId = replyToMsgId { replyToMessageId = MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId) } - inputState = SynchronizeableChatInputState(replyToMessageId: replyToMessageId, text: message, entities: messageTextEntitiesFromApiEntities(entities ?? []), timestamp: date) + inputState = SynchronizeableChatInputState(replyToMessageId: replyToMessageId, text: message, entities: messageTextEntitiesFromApiEntities(entities ?? []), timestamp: date, textSelection: nil) } updatedState.addUpdateChatInputState(peerId: peer.peerId, state: inputState) case let .updatePhoneCall(phoneCall): @@ -2995,9 +2995,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP case .UpdateRecentGifs: syncRecentGifs = true case let .UpdateChatInputState(peerId, inputState): - transaction.updatePeerChatInterfaceState(peerId, update: { current in - return auxiliaryMethods.updatePeerChatInputState(current, inputState) - }) + _internal_updateChatInputState(transaction: transaction, peerId: peerId, inputState: inputState) case let .UpdateCall(call): updatedCalls.append(call) case let .AddCallSignalingData(callId, data): diff --git a/submodules/TelegramCore/Sources/State/CallSessionManager.swift b/submodules/TelegramCore/Sources/State/CallSessionManager.swift index 09d8bc3806..82ed3e0ab0 100644 --- a/submodules/TelegramCore/Sources/State/CallSessionManager.swift +++ b/submodules/TelegramCore/Sources/State/CallSessionManager.swift @@ -685,14 +685,14 @@ private final class CallSessionManagerContext { } } - let keyHash = MTSha1(key)! + let keyHash = MTSha1(key) var keyId: Int64 = 0 keyHash.withUnsafeBytes { (bytes: UnsafePointer) -> Void in memcpy(&keyId, bytes.advanced(by: keyHash.count - 8), 8) } - let keyVisualHash = MTSha256(key + gA)! + let keyVisualHash = MTSha256(key + gA) context.state = .confirming(id: id, accessHash: accessHash, key: key, keyId: keyId, keyVisualHash: keyVisualHash, disposable: (confirmCallSession(network: self.network, stableId: id, accessHash: accessHash, gA: gA, keyFingerprint: keyId, maxLayer: self.maxLayer, versions: selectedVersions) |> deliverOnMainQueue).start(next: { [weak self] updatedCall in if let strongSelf = self, let context = strongSelf.contexts[internalId], case .confirming = context.state { @@ -888,18 +888,18 @@ private final class CallSessionManagerContext { } } - let keyHash = MTSha1(key)! + let keyHash = MTSha1(key) var keyId: Int64 = 0 keyHash.withUnsafeBytes { (bytes: UnsafePointer) -> Void in memcpy(&keyId, bytes.advanced(by: keyHash.count - 8), 8) } - if MTSha256(gA)! != gAHash { + if MTSha256(gA) != gAHash { return nil } - let keyVisualHash = MTSha256(key + gA)! + let keyVisualHash = MTSha256(key + gA) return (key, keyId, keyVisualHash) } @@ -1161,7 +1161,7 @@ private func requestCallSession(postbox: Postbox, network: Network, peerId: Peer return .single(.failed(.generic)) } - let gAHash = MTSha256(ga)! + let gAHash = MTSha256(ga) var callFlags: Int32 = 0 if isVideo { diff --git a/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift index b55a6d9a1c..146d0ac618 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift @@ -219,7 +219,7 @@ private func initialHandshakeAccept(postbox: Postbox, network: Network, peerId: } } - let keyHash = MTSha1(key)! + let keyHash = MTSha1(key) var keyFingerprint: Int64 = 0 keyHash.withUnsafeBytes { (bytes: UnsafePointer) -> Void in @@ -321,7 +321,7 @@ private func pfsAcceptKey(postbox: Postbox, network: Network, peerId: PeerId, la } } - let keyHash = MTSha1(key)! + let keyHash = MTSha1(key) var keyFingerprint: Int64 = 0 keyHash.withUnsafeBytes { (bytes: UnsafePointer) -> Void in diff --git a/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift index 817beee648..c6112c68de 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift @@ -126,7 +126,11 @@ func managedSynchronizeChatInputStateOperations(postbox: Postbox, network: Netwo } private func synchronizeChatInputState(transaction: Transaction, postbox: Postbox, network: Network, peerId: PeerId, operation: SynchronizeChatInputStateOperation) -> Signal { - let inputState = (transaction.getPeerChatInterfaceState(peerId) as? SynchronizeableChatInterfaceState)?.synchronizeableInputState + var inputState: SynchronizeableChatInputState? + if let peerChatInterfaceState = transaction.getPeerChatInterfaceState(peerId), let data = peerChatInterfaceState.data { + inputState = (try? AdaptedPostboxDecoder().decode(InternalChatInterfaceState.self, from: data))?.synchronizeableInputState + } + if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { var flags: Int32 = 0 if let inputState = inputState { diff --git a/submodules/TelegramCore/Sources/State/Serialization.swift b/submodules/TelegramCore/Sources/State/Serialization.swift index f23220932c..54bf243d69 100644 --- a/submodules/TelegramCore/Sources/State/Serialization.swift +++ b/submodules/TelegramCore/Sources/State/Serialization.swift @@ -258,7 +258,7 @@ public class Serialization: NSObject, MTSerialization { let restrictToTcp = (flags & (1 << 2)) != 0 let isCdn = (flags & (1 << 3)) != 0 let preferForProxy = (flags & (1 << 4)) != 0 - addressDict[id as NSNumber]!.append(MTDatacenterAddress(ip: ipAddress, port: UInt16(port), preferForMedia: preferForMedia, restrictToTcp: restrictToTcp, cdn: isCdn, preferForProxy: preferForProxy, secret: secret?.makeData())!) + addressDict[id as NSNumber]!.append(MTDatacenterAddress(ip: ipAddress, port: UInt16(port), preferForMedia: preferForMedia, restrictToTcp: restrictToTcp, cdn: isCdn, preferForProxy: preferForProxy, secret: secret?.makeData())) break } } diff --git a/submodules/TelegramCore/Sources/State/SynchronizeChatInputStateOperation.swift b/submodules/TelegramCore/Sources/State/SynchronizeChatInputStateOperation.swift index 7c49544c23..544d797775 100644 --- a/submodules/TelegramCore/Sources/State/SynchronizeChatInputStateOperation.swift +++ b/submodules/TelegramCore/Sources/State/SynchronizeChatInputStateOperation.swift @@ -1,7 +1,6 @@ import Foundation import Postbox - func addSynchronizeChatInputStateOperation(transaction: Transaction, peerId: PeerId) { var updateLocalIndex: Int32? let tag: PeerOperationLogTag = OperationLogTags.SynchronizeChatInputStates @@ -17,8 +16,8 @@ func addSynchronizeChatInputStateOperation(transaction: Transaction, peerId: Pee var previousState: SynchronizeableChatInputState? if let previousOperation = previousOperation { previousState = previousOperation.previousState - } else if let peerChatInterfaceState = transaction.getPeerChatInterfaceState(peerId) as? SynchronizeableChatInterfaceState { - previousState = peerChatInterfaceState.synchronizeableInputState + } else if let peerChatInterfaceState = transaction.getPeerChatInterfaceState(peerId), let data = peerChatInterfaceState.data { + previousState = (try? AdaptedPostboxDecoder().decode(InternalChatInterfaceState.self, from: data))?.synchronizeableInputState } let operationContents = SynchronizeChatInputStateOperation(previousState: previousState) if let updateLocalIndex = updateLocalIndex { diff --git a/submodules/TelegramCore/Sources/State/SynchronizePeerReadState.swift b/submodules/TelegramCore/Sources/State/SynchronizePeerReadState.swift index 9f213ac37e..1cfa73aaa3 100644 --- a/submodules/TelegramCore/Sources/State/SynchronizePeerReadState.swift +++ b/submodules/TelegramCore/Sources/State/SynchronizePeerReadState.swift @@ -213,8 +213,10 @@ private func pushPeerReadState(network: Network, postbox: Postbox, stateManager: return .single(readState) case let .indexBased(maxIncomingReadIndex, _, _, _): return network.request(Api.functions.messages.readEncryptedHistory(peer: inputPeer, maxDate: maxIncomingReadIndex.timestamp)) - |> retryRequest - |> mapToSignalPromotingError { _ -> Signal in + |> mapError { _ in + return PeerReadStateValidationError.retry + } + |> mapToSignal { _ -> Signal in return .single(readState) } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeChatInputStateOperation.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeChatInputStateOperation.swift index 04ddc11ef2..7f3ad4bb48 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeChatInputStateOperation.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeChatInputStateOperation.swift @@ -8,12 +8,12 @@ public final class SynchronizeChatInputStateOperation: PostboxCoding { } public init(decoder: PostboxDecoder) { - self.previousState = decoder.decodeObjectForKey("p", decoder: { SynchronizeableChatInputState(decoder: $0) }) as? SynchronizeableChatInputState + self.previousState = decoder.decode(SynchronizeableChatInputState.self, forKey: "p") } public func encode(_ encoder: PostboxEncoder) { if let previousState = self.previousState { - encoder.encodeObject(previousState, forKey: "p") + encoder.encode(previousState, forKey: "p") } else { encoder.encodeNil(forKey: "p") } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeableChatInputState.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeableChatInputState.swift index 6211d31c87..f291ba736a 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeableChatInputState.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeableChatInputState.swift @@ -1,42 +1,49 @@ import Foundation import Postbox -public struct SynchronizeableChatInputState: PostboxCoding, Equatable { +public struct SynchronizeableChatInputState: Codable, Equatable { public let replyToMessageId: MessageId? public let text: String public let entities: [MessageTextEntity] public let timestamp: Int32 + public let textSelection: Range? - public init(replyToMessageId: MessageId?, text: String, entities: [MessageTextEntity], timestamp: Int32) { + public init(replyToMessageId: MessageId?, text: String, entities: [MessageTextEntity], timestamp: Int32, textSelection: Range?) { self.replyToMessageId = replyToMessageId self.text = text self.entities = entities self.timestamp = timestamp + self.textSelection = textSelection } - public init(decoder: PostboxDecoder) { - self.text = decoder.decodeStringForKey("t", orElse: "") - self.entities = decoder.decodeObjectArrayWithDecoderForKey("e") - self.timestamp = decoder.decodeInt32ForKey("s", orElse: 0) - if let messageIdPeerId = decoder.decodeOptionalInt64ForKey("m.p"), let messageIdNamespace = decoder.decodeOptionalInt32ForKey("m.n"), let messageIdId = decoder.decodeOptionalInt32ForKey("m.i") { + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + self.text = (try? container.decode(String.self, forKey: "t")) ?? "" + self.entities = (try? container.decode([MessageTextEntity].self, forKey: "e")) ?? [] + self.timestamp = (try? container.decode(Int32.self, forKey: "s")) ?? 0 + + if let messageIdPeerId = try? container.decodeIfPresent(Int64.self, forKey: "m.p"), let messageIdNamespace = try? container.decodeIfPresent(Int32.self, forKey: "m.n"), let messageIdId = try? container.decodeIfPresent(Int32.self, forKey: "m.i") { self.replyToMessageId = MessageId(peerId: PeerId(messageIdPeerId), namespace: messageIdNamespace, id: messageIdId) } else { self.replyToMessageId = nil } + self.textSelection = nil } - public func encode(_ encoder: PostboxEncoder) { - encoder.encodeString(self.text, forKey: "t") - encoder.encodeObjectArray(self.entities, forKey: "e") - encoder.encodeInt32(self.timestamp, forKey: "s") + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + try container.encode(self.text, forKey: "t") + try container.encode(self.entities, forKey: "e") + try container.encode(self.timestamp, forKey: "s") if let replyToMessageId = self.replyToMessageId { - encoder.encodeInt64(replyToMessageId.peerId.toInt64(), forKey: "m.p") - encoder.encodeInt32(replyToMessageId.namespace, forKey: "m.n") - encoder.encodeInt32(replyToMessageId.id, forKey: "m.i") + try container.encode(replyToMessageId.peerId.toInt64(), forKey: "m.p") + try container.encode(replyToMessageId.namespace, forKey: "m.n") + try container.encode(replyToMessageId.id, forKey: "m.i") } else { - encoder.encodeNil(forKey: "m.p") - encoder.encodeNil(forKey: "m.n") - encoder.encodeNil(forKey: "m.i") + try container.encodeNil(forKey: "m.p") + try container.encodeNil(forKey: "m.n") + try container.encodeNil(forKey: "m.i") } } @@ -53,11 +60,46 @@ public struct SynchronizeableChatInputState: PostboxCoding, Equatable { if lhs.timestamp != rhs.timestamp { return false } + if lhs.textSelection != rhs.textSelection { + return false + } return true } } -public protocol SynchronizeableChatInterfaceState: PeerChatInterfaceState { - var synchronizeableInputState: SynchronizeableChatInputState? { get } - func withUpdatedSynchronizeableInputState(_ state: SynchronizeableChatInputState?) -> SynchronizeableChatInterfaceState +class InternalChatInterfaceState: Codable { + let synchronizeableInputState: SynchronizeableChatInputState? + let historyScrollMessageIndex: MessageIndex? + let opaqueData: Data? + + init( + synchronizeableInputState: SynchronizeableChatInputState?, + historyScrollMessageIndex: MessageIndex?, + opaqueData: Data? + ) { + self.synchronizeableInputState = synchronizeableInputState + self.historyScrollMessageIndex = historyScrollMessageIndex + self.opaqueData = opaqueData + } +} + +func _internal_updateChatInputState(transaction: Transaction, peerId: PeerId, inputState: SynchronizeableChatInputState?) { + var previousState: InternalChatInterfaceState? + if let peerChatInterfaceState = transaction.getPeerChatInterfaceState(peerId), let data = peerChatInterfaceState.data { + previousState = (try? AdaptedPostboxDecoder().decode(InternalChatInterfaceState.self, from: data)) + } + + if let updatedStateData = try? AdaptedPostboxEncoder().encode(InternalChatInterfaceState( + synchronizeableInputState: inputState, + historyScrollMessageIndex: previousState?.historyScrollMessageIndex, + opaqueData: previousState?.opaqueData + )) { + let storedState = StoredPeerChatInterfaceState( + overrideChatTimestamp: inputState?.timestamp, + historyScrollMessageIndex: previousState?.historyScrollMessageIndex, + associatedMessageIds: (inputState?.replyToMessageId).flatMap({ [$0] }) ?? [], + data: updatedStateData + ) + transaction.setPeerChatInterfaceState(peerId, state: storedState) + } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift index ac746b6613..2fc3af8f68 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift @@ -1,6 +1,6 @@ import Postbox -public struct TelegramChatAdminRightsFlags: OptionSet { +public struct TelegramChatAdminRightsFlags: OptionSet, Hashable { public var rawValue: Int32 public init(rawValue: Int32) { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatBannedRights.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatBannedRights.swift index ef804d8858..1bb9f99b39 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatBannedRights.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatBannedRights.swift @@ -1,6 +1,6 @@ import Postbox -public struct TelegramChatBannedRightsFlags: OptionSet { +public struct TelegramChatBannedRightsFlags: OptionSet, Hashable { public var rawValue: Int32 public init(rawValue: Int32) { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift index 7ad29dd436..ea805fcfe9 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift @@ -251,6 +251,13 @@ public final class TelegramMediaAction: Media { } public let action: TelegramMediaActionType + + public func preventsAutomaticMessageSendingFailure() -> Bool { + if case .historyScreenshot = self.action { + return true + } + return false + } public init(action: TelegramMediaActionType) { self.action = action diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift index e0c3f565a9..f1503ed736 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift @@ -23,7 +23,7 @@ public enum MessageTextEntityType: Equatable { case Custom(type: CustomEntityType) } -public struct MessageTextEntity: PostboxCoding, Equatable { +public struct MessageTextEntity: PostboxCoding, Codable, Equatable { public let range: Range public let type: MessageTextEntityType @@ -74,6 +74,60 @@ public struct MessageTextEntity: PostboxCoding, Equatable { self.type = .Unknown } } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + + let rangeStart: Int32 = (try? container.decode(Int32.self, forKey: "start")) ?? 0 + var rangeEnd: Int32 = (try? container.decode(Int32.self, forKey: "end")) ?? 0 + rangeEnd = max(rangeEnd, rangeStart) + + let type: Int32 = (try? container.decode(Int32.self, forKey: "_rawValue")) ?? 0 + + self.range = Int(rangeStart) ..< Int(rangeEnd) + + switch type { + case 1: + self.type = .Mention + case 2: + self.type = .Hashtag + case 3: + self.type = .BotCommand + case 4: + self.type = .Url + case 5: + self.type = .Email + case 6: + self.type = .Bold + case 7: + self.type = .Italic + case 8: + self.type = .Code + case 9: + self.type = .Pre + case 10: + let url = (try? container.decode(String.self, forKey: "url")) ?? "" + self.type = .TextUrl(url: url) + case 11: + let peerId = (try? container.decode(Int64.self, forKey: "peerId")) ?? 0 + self.type = .TextMention(peerId: PeerId(peerId)) + case 12: + self.type = .PhoneNumber + case 13: + self.type = .Strikethrough + case 14: + self.type = .BlockQuote + case 15: + self.type = .Underline + case 16: + self.type = .BankCard + case Int32.max: + let customType: Int32 = (try? container.decode(Int32.self, forKey: "type")) ?? 0 + self.type = .Custom(type: customType) + default: + self.type = .Unknown + } + } public func encode(_ encoder: PostboxEncoder) { encoder.encodeInt32(Int32(self.range.lowerBound), forKey: "start") @@ -120,6 +174,54 @@ public struct MessageTextEntity: PostboxCoding, Equatable { encoder.encodeInt32(type, forKey: "type") } } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + + try container.encode(Int32(self.range.lowerBound), forKey: "start") + try container.encode(Int32(self.range.upperBound), forKey: "end") + switch self.type { + case .Unknown: + try container.encode(0 as Int32, forKey: "_rawValue") + case .Mention: + try container.encode(1 as Int32, forKey: "_rawValue") + case .Hashtag: + try container.encode(2 as Int32, forKey: "_rawValue") + case .BotCommand: + try container.encode(3 as Int32, forKey: "_rawValue") + case .Url: + try container.encode(4 as Int32, forKey: "_rawValue") + case .Email: + try container.encode(5 as Int32, forKey: "_rawValue") + case .Bold: + try container.encode(6 as Int32, forKey: "_rawValue") + case .Italic: + try container.encode(7 as Int32, forKey: "_rawValue") + case .Code: + try container.encode(8 as Int32, forKey: "_rawValue") + case .Pre: + try container.encode(9 as Int32, forKey: "_rawValue") + case let .TextUrl(url): + try container.encode(10 as Int32, forKey: "_rawValue") + try container.encode(url, forKey: "url") + case let .TextMention(peerId): + try container.encode(11 as Int32, forKey: "_rawValue") + try container.encode(peerId.toInt64(), forKey: "peerId") + case .PhoneNumber: + try container.encode(12 as Int32, forKey: "_rawValue") + case .Strikethrough: + try container.encode(13 as Int32, forKey: "_rawValue") + case .BlockQuote: + try container.encode(14 as Int32, forKey: "_rawValue") + case .Underline: + try container.encode(15 as Int32, forKey: "_rawValue") + case .BankCard: + try container.encode(16 as Int32, forKey: "_rawValue") + case let .Custom(type): + try container.encode(Int32.max as Int32, forKey: "_rawValue") + try container.encode(type as Int32, forKey: "type") + } + } public static func ==(lhs: MessageTextEntity, rhs: MessageTextEntity) -> Bool { return lhs.range == rhs.range && lhs.type == rhs.type diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Calls/TelegramEngineCalls.swift b/submodules/TelegramCore/Sources/TelegramEngine/Calls/TelegramEngineCalls.swift index 21be454285..7e734808ab 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Calls/TelegramEngineCalls.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Calls/TelegramEngineCalls.swift @@ -89,8 +89,11 @@ public extension TelegramEngine { return _internal_cachedGroupCallDisplayAsAvailablePeers(account: self.account, peerId: peerId) } - public func updatedCurrentPeerGroupCall(peerId: PeerId) -> Signal { + public func updatedCurrentPeerGroupCall(peerId: PeerId) -> Signal { return _internal_updatedCurrentPeerGroupCall(account: self.account, peerId: peerId) + |> map { activeCall -> EngineGroupCallDescription? in + return activeCall.flatMap(EngineGroupCallDescription.init) + } } public func getAudioBroadcastDataSource(callId: Int64, accessHash: Int64) -> Signal { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeerSummary.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeerSummary.swift index c8135eb9c5..57efef204b 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeerSummary.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeerSummary.swift @@ -57,5 +57,36 @@ public extension TelegramEngine.EngineData.Item { } } } + + public struct GroupCallDescription: TelegramEngineDataItem, PostboxViewDataItem { + public typealias Result = Optional + + fileprivate var id: EnginePeer.Id + + public init(id: EnginePeer.Id) { + self.id = id + } + + var key: PostboxViewKey { + return .cachedPeerData(peerId: self.id) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? CachedPeerDataView else { + preconditionFailure() + } + guard let cachedPeerData = view.cachedPeerData else { + return nil + } + switch cachedPeerData { + case let channel as CachedChannelData: + return channel.activeCall.flatMap(EngineGroupCallDescription.init) + case let group as CachedGroupData: + return group.activeCall.flatMap(EngineGroupCallDescription.init) + default: + return nil + } + } + } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/TelegramEngineData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/TelegramEngineData.swift index 25c8e66626..f28ae11346 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/TelegramEngineData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/TelegramEngineData.swift @@ -33,7 +33,7 @@ public extension TelegramEngine { } private func _subscribe(items: [AnyPostboxViewDataItem]) -> Signal<[Any], NoError> { - return self.account.postbox.combinedView(keys: items.map(\.key)) + return self.account.postbox.combinedView(keys: Array(Set(items.map(\.key)))) |> map { views -> [Any] in var results: [Any] = [] diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/CallList.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/CallList.swift new file mode 100644 index 0000000000..21c66527e5 --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/CallList.swift @@ -0,0 +1,27 @@ +import Postbox + +public final class EngineCallList { + public enum Scope { + case all + case missed + } + + public enum Item { + case message(message: EngineMessage, group: [EngineMessage]) + case hole(EngineMessage.Index) + } + + public let items: [Item] + public let hasEarlier: Bool + public let hasLater: Bool + + init( + items: [Item], + hasEarlier: Bool, + hasLater: Bool + ) { + self.items = items + self.hasEarlier = hasEarlier + self.hasLater = hasLater + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift new file mode 100644 index 0000000000..bf2a88a5e6 --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift @@ -0,0 +1,163 @@ +import Postbox + +public final class EngineChatList { + public enum Group { + case root + case archive + } + + public final class Item { + public typealias Index = ChatListIndex + + public let index: Index + public let messages: [EngineMessage] + public let readState: EngineReadState? + public let isMuted: Bool + public let draftText: String? + public let renderedPeer: EngineRenderedPeer + public let presence: EnginePeer.Presence? + public let hasUnseenMentions: Bool + public let hasFailed: Bool + public let isContact: Bool + + public init( + index: Index, + messages: [EngineMessage], + readState: EngineReadState?, + isMuted: Bool, + draftText: String?, + renderedPeer: EngineRenderedPeer, + presence: EnginePeer.Presence?, + hasUnseenMentions: Bool, + hasFailed: Bool, + isContact: Bool + ) { + self.index = index + self.messages = messages + self.readState = readState + self.isMuted = isMuted + self.draftText = draftText + self.renderedPeer = renderedPeer + self.presence = presence + self.hasUnseenMentions = hasUnseenMentions + self.hasFailed = hasFailed + self.isContact = isContact + } + } + + public final class GroupItem { + public final class Item { + public let peer: EngineRenderedPeer + public let isUnread: Bool + + public init(peer: EngineRenderedPeer, isUnread: Bool) { + self.peer = peer + self.isUnread = isUnread + } + } + + public let id: Group + public let topMessage: EngineMessage? + public let items: [Item] + public let unreadCount: Int + + public init( + id: Group, + topMessage: EngineMessage?, + items: [Item], + unreadCount: Int + ) { + self.id = id + self.topMessage = topMessage + self.items = items + self.unreadCount = unreadCount + } + } + + public let items: [Item] + public let groupItems: [GroupItem] + public let hasEarlier: Bool + public let hasLater: Bool + + init( + items: [Item], + groupItems: [GroupItem], + hasEarlier: Bool, + hasLater: Bool + ) { + self.items = items + self.groupItems = groupItems + self.hasEarlier = hasEarlier + self.hasLater = hasLater + } +} + +public extension EngineChatList.Group { + init(_ group: PeerGroupId) { + switch group { + case .root: + self = .root + case let .group(value): + assert(value == Namespaces.PeerGroup.archive.rawValue) + self = .archive + } + } + + func _asGroup() -> PeerGroupId { + switch self { + case .root: + return .root + case .archive: + return Namespaces.PeerGroup.archive + } + } +} + +extension EngineChatList.Item { + convenience init?(_ entry: ChatListEntry) { + switch entry { + case let .MessageEntry(index, messages, readState, isRemovedFromTotalUnreadCount, embeddedInterfaceState, renderedPeer, presence, summaryInfo, hasFailed, isContact): + self.init( + index: index, + messages: messages.map(EngineMessage.init), + readState: readState.flatMap(EngineReadState.init), + isMuted: isRemovedFromTotalUnreadCount, + draftText: nil, + renderedPeer: EngineRenderedPeer(renderedPeer), + presence: presence.flatMap(EnginePeer.Presence.init), + hasUnseenMentions: (summaryInfo.tagSummaryCount ?? 0) > (summaryInfo.actionsSummaryCount ?? 0), + hasFailed: hasFailed, + isContact: isContact + ) + case .HoleEntry: + return nil + } + } +} + +extension EngineChatList.GroupItem { + convenience init(_ entry: ChatListGroupReferenceEntry) { + self.init( + id: EngineChatList.Group(entry.groupId), + topMessage: entry.message.flatMap(EngineMessage.init), + items: entry.renderedPeers.map { peer in + return EngineChatList.GroupItem.Item( + peer: EngineRenderedPeer(peer.peer), + isUnread: peer.isUnread + ) + }, + unreadCount: Int(entry.unreadState.count(countingCategory: .chats, mutedCategory: .all)) + ) + } +} + +extension EngineChatList { + convenience init(_ view: ChatListView) { + self.init( + items: view.entries.compactMap(EngineChatList.Item.init), + groupItems: view.groupEntries.map(EngineChatList.GroupItem.init), + hasEarlier: view.earlierIndex != nil, + hasLater: view.laterIndex != nil + ) + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift index 0205e1f3cb..131cdef8bb 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ClearCloudDrafts.swift @@ -41,13 +41,7 @@ func _internal_clearCloudDraftsInteractively(postbox: Postbox, network: Network, updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) var signals: [Signal] = [] for peerId in peerIds { - transaction.updatePeerChatInterfaceState(peerId, update: { current in - if let current = current as? SynchronizeableChatInterfaceState { - return current.withUpdatedSynchronizeableInputState(nil) - } else { - return nil - } - }) + _internal_updateChatInputState(transaction: transaction, peerId: peerId, inputState: nil) if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { signals.append(network.request(Api.functions.messages.saveDraft(flags: 0, replyToMsgId: nil, peer: inputPeer, message: "", entities: nil)) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/EngineGroupCallDescription.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/EngineGroupCallDescription.swift new file mode 100644 index 0000000000..a4685f9193 --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/EngineGroupCallDescription.swift @@ -0,0 +1,35 @@ +import Postbox + +public final class EngineGroupCallDescription { + public let id: Int64 + public let accessHash: Int64 + public let title: String? + public let scheduleTimestamp: Int32? + public let subscribedToScheduled: Bool + + public init( + id: Int64, + accessHash: Int64, + title: String?, + scheduleTimestamp: Int32?, + subscribedToScheduled: Bool + ) { + self.id = id + self.accessHash = accessHash + self.title = title + self.scheduleTimestamp = scheduleTimestamp + self.subscribedToScheduled = subscribedToScheduled + } +} + +public extension EngineGroupCallDescription { + convenience init(_ activeCall: CachedChannelData.ActiveCall) { + self.init( + id: activeCall.id, + accessHash: activeCall.accessHash, + title: activeCall.title, + scheduleTimestamp: activeCall.scheduleTimestamp, + subscribedToScheduled: activeCall.subscribedToScheduled + ) + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReadState.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReadState.swift new file mode 100644 index 0000000000..d499b6071b --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ReadState.swift @@ -0,0 +1,23 @@ +import Postbox + +public struct EngineReadState: Equatable { + public var unreadCount: Int + public var isMarkedAsUnread: Bool + + public init(unreadCount: Int, isMarkedAsUnread: Bool) { + self.unreadCount = unreadCount + self.isMarkedAsUnread = isMarkedAsUnread + } +} + +public extension EngineReadState { + var isUnread: Bool { + return self.unreadCount != 0 || self.isMarkedAsUnread + } +} + +public extension EngineReadState { + init(_ readState: CombinedPeerReadState) { + self.init(unreadCount: Int(readState.count), isMarkedAsUnread: readState.markedUnread) + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index db4844b206..6cda567415 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -170,5 +170,34 @@ public extension TelegramEngine { public func topPeerActiveLiveLocationMessages(peerId: PeerId) -> Signal<(Peer?, [Message]), NoError> { return _internal_topPeerActiveLiveLocationMessages(viewTracker: self.account.viewTracker, accountPeerId: self.account.peerId, peerId: peerId) } + + public func chatList(group: EngineChatList.Group, count: Int) -> Signal { + return self.account.postbox.tailChatListView(groupId: group._asGroup(), count: count, summaryComponents: ChatListEntrySummaryComponents()) + |> map { view -> EngineChatList in + return EngineChatList(view.0) + } + } + + public func callList(scope: EngineCallList.Scope, index: EngineMessage.Index, itemCount: Int) -> Signal { + return self.account.viewTracker.callListView( + type: scope == .all ? .all : .missed, + index: index, + count: itemCount + ) + |> map { view -> EngineCallList in + return EngineCallList( + items: view.entries.map { entry -> EngineCallList.Item in + switch entry { + case let .message(message, group): + return .message(message: EngineMessage(message), group: group.map(EngineMessage.init)) + case let .hole(index): + return .hole(index) + } + }, + hasEarlier: view.earlier != nil, + hasLater: view.later != nil + ) + } + } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/OpaqueChatState.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/OpaqueChatState.swift new file mode 100644 index 0000000000..db80a6bd81 --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/OpaqueChatState.swift @@ -0,0 +1,5 @@ +import Postbox + +public protocol EngineOpaqueChatState: AnyObject, Codable { + func isEqual(to other: EngineOpaqueChatState) -> Bool +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift index 359e5c1c64..501c75e0f1 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift @@ -3,6 +3,24 @@ import Postbox public enum EnginePeer: Equatable { public typealias Id = PeerId + public struct Presence: Equatable { + public enum Status: Equatable { + case present(until: Int32) + case recently + case lastWeek + case lastMonth + case longTimeAgo + } + + public var status: Status + public var lastActivity: Int32 + + public init(status: Status, lastActivity: Int32) { + self.status = status + self.lastActivity = lastActivity + } + } + case user(TelegramUser) case legacyGroup(TelegramGroup) case channel(TelegramChannel) @@ -38,6 +56,30 @@ public enum EnginePeer: Equatable { } } +public extension EnginePeer.Presence { + init(_ presence: PeerPresence) { + if let presence = presence as? TelegramUserPresence { + let mappedStatus: Status + switch presence.status { + case .none: + mappedStatus = .longTimeAgo + case let .present(until): + mappedStatus = .present(until: until) + case .recently: + mappedStatus = .recently + case .lastWeek: + mappedStatus = .lastWeek + case .lastMonth: + mappedStatus = .lastMonth + } + + self.init(status: mappedStatus, lastActivity: presence.lastActivity) + } else { + preconditionFailure() + } + } +} + public extension EnginePeer { var id: Id { return self._asPeer().id @@ -50,6 +92,56 @@ public extension EnginePeer { var indexName: PeerIndexNameRepresentation { return self._asPeer().indexName } + + var debugDisplayTitle: String { + return self._asPeer().debugDisplayTitle + } + + func restrictionText(platform: String, contentSettings: ContentSettings) -> String? { + return self._asPeer().restrictionText(platform: platform, contentSettings: contentSettings) + } + + var displayLetters: [String] { + return self._asPeer().displayLetters + } + + var profileImageRepresentations: [TelegramMediaImageRepresentation] { + return self._asPeer().profileImageRepresentations + } + + var smallProfileImage: TelegramMediaImageRepresentation? { + return self._asPeer().smallProfileImage + } + + var largeProfileImage: TelegramMediaImageRepresentation? { + return self._asPeer().largeProfileImage + } + + var isDeleted: Bool { + return self._asPeer().isDeleted + } + + var isScam: Bool { + return self._asPeer().isScam + } + + var isFake: Bool { + return self._asPeer().isFake + } + + var isVerified: Bool { + return self._asPeer().isVerified + } + + var isService: Bool { + if case let .user(peer) = self { + if peer.id.isReplies { + return true + } + return (peer.id.namespace == Namespaces.Peer.CloudUser && (peer.id.id._internalGetInt32Value() == 777000 || peer.id.id._internalGetInt32Value() == 333000)) + } + return false + } } public extension EnginePeer { @@ -81,3 +173,54 @@ public extension EnginePeer { } } } + +public final class EngineRenderedPeer { + public let peerId: EnginePeer.Id + public let peers: [EnginePeer.Id: EnginePeer] + + public init(peerId: EnginePeer.Id, peers: [EnginePeer.Id: EnginePeer]) { + self.peerId = peerId + self.peers = peers + } + + public init(peer: EnginePeer) { + self.peerId = peer.id + self.peers = [peer.id: peer] + } + + public static func ==(lhs: EngineRenderedPeer, rhs: EngineRenderedPeer) -> Bool { + if lhs.peerId != rhs.peerId { + return false + } + if lhs.peers != rhs.peers { + return false + } + return true + } + + public var peer: EnginePeer? { + return self.peers[self.peerId] + } + + public var chatMainPeer: EnginePeer? { + if let peer = self.peers[self.peerId] { + if case let .secretChat(secretChat) = peer { + return self.peers[secretChat.regularPeerId] + } else { + return peer + } + } else { + return nil + } + } +} + +public extension EngineRenderedPeer { + convenience init(_ renderedPeer: RenderedPeer) { + var mappedPeers: [EnginePeer.Id: EnginePeer] = [:] + for (id, peer) in renderedPeer.peers { + mappedPeers[id] = EnginePeer(peer) + } + self.init(peerId: renderedPeer.peerId, peers: mappedPeers) + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RemovePeerChat.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RemovePeerChat.swift index 71e835ee0e..76a96034af 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/RemovePeerChat.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/RemovePeerChat.swift @@ -25,13 +25,7 @@ func _internal_terminateSecretChat(transaction: Transaction, peerId: PeerId, req func _internal_removePeerChat(account: Account, transaction: Transaction, mediaBox: MediaBox, peerId: PeerId, reportChatSpam: Bool, deleteGloballyIfPossible: Bool) { if let _ = transaction.getPeerChatInterfaceState(peerId) { - transaction.updatePeerChatInterfaceState(peerId, update: { current in - if let current = current { - return account.auxiliaryMethods.updatePeerChatInputState(current, nil) - } else { - return nil - } - }) + transaction.setPeerChatInterfaceState(peerId, state: nil) } _internal_updateChatListFiltersInteractively(transaction: transaction, { filters in var filters = filters diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 876dd90374..d4edba3fa9 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -8,6 +8,22 @@ public enum AddressNameValidationStatus: Equatable { case availability(AddressNameAvailability) } +public final class OpaqueChatInterfaceState { + public let opaqueData: Data? + public let historyScrollMessageIndex: MessageIndex? + public let synchronizeableInputState: SynchronizeableChatInputState? + + public init( + opaqueData: Data?, + historyScrollMessageIndex: MessageIndex?, + synchronizeableInputState: SynchronizeableChatInputState? + ) { + self.opaqueData = opaqueData + self.historyScrollMessageIndex = historyScrollMessageIndex + self.synchronizeableInputState = synchronizeableInputState + } +} + public extension TelegramEngine { final class Peers { private let account: Account @@ -478,5 +494,120 @@ public extension TelegramEngine { public func updatePeerDescription(peerId: PeerId, description: String?) -> Signal { return _internal_updatePeerDescription(account: self.account, peerId: peerId, description: description) } + + public func getNextUnreadChannel(peerId: PeerId, filter: ChatListFilterPredicate?) -> Signal { + return self.account.postbox.transaction { transaction -> EnginePeer? in + var results: [(EnginePeer, Int32)] = [] + + var peerIds: [PeerId] = [] + peerIds.append(contentsOf: transaction.getUnreadChatListPeerIds(groupId: .root, filterPredicate: filter)) + peerIds.append(contentsOf: transaction.getUnreadChatListPeerIds(groupId: Namespaces.PeerGroup.archive, filterPredicate: filter)) + + for listId in peerIds { + guard let peer = transaction.getPeer(listId) else { + continue + } + guard let channel = peer as? TelegramChannel, case .broadcast = channel.info else { + continue + } + if channel.id == peerId { + continue + } + guard let readState = transaction.getCombinedPeerReadState(channel.id), readState.count != 0 else { + continue + } + guard let topMessageIndex = transaction.getTopPeerMessageIndex(peerId: channel.id) else { + continue + } + + results.append((EnginePeer(channel), topMessageIndex.timestamp)) + } + + results.sort(by: { $0.1 > $1.1 }) + + return results.first?.0 + } + } + + public func getOpaqueChatInterfaceState(peerId: PeerId, threadId: Int64?) -> Signal { + return self.account.postbox.transaction { transaction -> OpaqueChatInterfaceState? in + let storedState: StoredPeerChatInterfaceState? + if let threadId = threadId { + storedState = transaction.getPeerChatThreadInterfaceState(peerId, threadId: threadId) + } else { + storedState = transaction.getPeerChatInterfaceState(peerId) + } + + guard let state = storedState, let data = state.data else { + return nil + } + guard let internalState = try? AdaptedPostboxDecoder().decode(InternalChatInterfaceState.self, from: data) else { + return nil + } + return OpaqueChatInterfaceState( + opaqueData: internalState.opaqueData, + historyScrollMessageIndex: internalState.historyScrollMessageIndex, + synchronizeableInputState: internalState.synchronizeableInputState + ) + } + } + + public func setOpaqueChatInterfaceState(peerId: PeerId, threadId: Int64?, state: OpaqueChatInterfaceState) -> Signal { + return self.account.postbox.transaction { transaction -> Void in + guard let data = try? AdaptedPostboxEncoder().encode(InternalChatInterfaceState( + synchronizeableInputState: state.synchronizeableInputState, + historyScrollMessageIndex: state.historyScrollMessageIndex, + opaqueData: state.opaqueData + )) else { + return + } + + #if DEBUG + let _ = try! AdaptedPostboxDecoder().decode(InternalChatInterfaceState.self, from: data) + #endif + + let storedState = StoredPeerChatInterfaceState( + overrideChatTimestamp: state.synchronizeableInputState?.timestamp, + historyScrollMessageIndex: state.historyScrollMessageIndex, + associatedMessageIds: (state.synchronizeableInputState?.replyToMessageId).flatMap({ [$0] }) ?? [], + data: data + ) + + if let threadId = threadId { + transaction.setPeerChatThreadInterfaceState(peerId, threadId: threadId, state: storedState) + } else { + var currentInputState: SynchronizeableChatInputState? + if let peerChatInterfaceState = transaction.getPeerChatInterfaceState(peerId), let data = peerChatInterfaceState.data { + currentInputState = (try? AdaptedPostboxDecoder().decode(InternalChatInterfaceState.self, from: data))?.synchronizeableInputState + } + let updatedInputState = state.synchronizeableInputState + + if currentInputState != updatedInputState { + if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudChannel || peerId.namespace == Namespaces.Peer.CloudGroup { + addSynchronizeChatInputStateOperation(transaction: transaction, peerId: peerId) + } + } + transaction.setPeerChatInterfaceState( + peerId, + state: storedState + ) + } + } + |> ignoreValues + } } } + +public func _internal_decodeStoredChatInterfaceState(state: StoredPeerChatInterfaceState) -> OpaqueChatInterfaceState? { + guard let data = state.data else { + return nil + } + guard let internalState = try? AdaptedPostboxDecoder().decode(InternalChatInterfaceState.self, from: data) else { + return nil + } + return OpaqueChatInterfaceState( + opaqueData: internalState.opaqueData, + historyScrollMessageIndex: internalState.historyScrollMessageIndex, + synchronizeableInputState: internalState.synchronizeableInputState + ) +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Utils/StringCodingKey.swift b/submodules/TelegramCore/Sources/TelegramEngine/Utils/StringCodingKey.swift new file mode 100644 index 0000000000..07cd9767c3 --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Utils/StringCodingKey.swift @@ -0,0 +1,24 @@ + +public struct StringCodingKey: CodingKey, ExpressibleByStringLiteral { + public var stringValue: String + + public init?(stringValue: String) { + self.stringValue = stringValue + } + + public init(_ stringValue: String) { + self.stringValue = stringValue + } + + public init(stringLiteral: String) { + self.stringValue = stringLiteral + } + + public var intValue: Int? { + return nil + } + + public init?(intValue: Int) { + return nil + } +} diff --git a/submodules/TelegramCore/Sources/UpdatePeerChatInterfaceState.swift b/submodules/TelegramCore/Sources/UpdatePeerChatInterfaceState.swift index 9a1429d590..8b13789179 100644 --- a/submodules/TelegramCore/Sources/UpdatePeerChatInterfaceState.swift +++ b/submodules/TelegramCore/Sources/UpdatePeerChatInterfaceState.swift @@ -1,26 +1 @@ -import Foundation -import Postbox -import SwiftSignalKit - -public func updatePeerChatInterfaceState(account: Account, peerId: PeerId, threadId: Int64?, state: SynchronizeableChatInterfaceState) -> Signal { - return account.postbox.transaction { transaction -> Void in - if let threadId = threadId { - transaction.updatePeerChatThreadInterfaceState(peerId, threadId: threadId, update: { _ in - return state - }) - } else { - let currentInputState = (transaction.getPeerChatInterfaceState(peerId) as? SynchronizeableChatInterfaceState)?.synchronizeableInputState - let updatedInputState = state.synchronizeableInputState - - if currentInputState != updatedInputState { - if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudChannel || peerId.namespace == Namespaces.Peer.CloudGroup { - addSynchronizeChatInputStateOperation(transaction: transaction, peerId: peerId) - } - } - transaction.updatePeerChatInterfaceState(peerId, update: { _ in - return state - }) - } - } -} diff --git a/submodules/TelegramCore/Sources/Utils/Coding.swift b/submodules/TelegramCore/Sources/Utils/Coding.swift new file mode 100644 index 0000000000..e6770f323f --- /dev/null +++ b/submodules/TelegramCore/Sources/Utils/Coding.swift @@ -0,0 +1,14 @@ +import Foundation +import Postbox + +public final class EngineEncoder { + public static func encode(_ value: Encodable) throws -> Data { + return try AdaptedPostboxEncoder().encode(value) + } +} + +public final class EngineDecoder { + public static func decode(_ type: T.Type, from data: Data) throws -> T where T : Decodable { + return try AdaptedPostboxDecoder().decode(type, from: data) + } +} diff --git a/submodules/TelegramIntents/BUILD b/submodules/TelegramIntents/BUILD index 68285800aa..a9539a5ca1 100644 --- a/submodules/TelegramIntents/BUILD +++ b/submodules/TelegramIntents/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramCore:TelegramCore", "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", diff --git a/submodules/TelegramNotices/BUILD b/submodules/TelegramNotices/BUILD index d0b8191524..351f442adf 100644 --- a/submodules/TelegramNotices/BUILD +++ b/submodules/TelegramNotices/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Postbox:Postbox", "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", diff --git a/submodules/TelegramNotices/Sources/Notices.swift b/submodules/TelegramNotices/Sources/Notices.swift index a65f6ac38f..38957258e6 100644 --- a/submodules/TelegramNotices/Sources/Notices.swift +++ b/submodules/TelegramNotices/Sources/Notices.swift @@ -137,6 +137,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 { case callsTabTip = 18 case chatFolderTips = 19 case locationProximityAlertTip = 20 + case nextChatSuggestionTip = 21 var key: ValueBoxKey { let v = ValueBoxKey(length: 4) @@ -268,6 +269,10 @@ private struct ApplicationSpecificNoticeKeys { static func locationProximityAlertTip() -> NoticeEntryKey { return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.locationProximityAlertTip.key) } + + static func nextChatSuggestionTip() -> NoticeEntryKey { + return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.nextChatSuggestionTip.key) + } } public struct ApplicationSpecificNotice { @@ -735,6 +740,28 @@ public struct ApplicationSpecificNotice { transaction.setNotice(ApplicationSpecificNoticeKeys.chatMessageOptionsTip(), ApplicationSpecificCounterNotice(value: currentValue)) } } + + public static func getNextChatSuggestionTip(accountManager: AccountManager) -> Signal { + return accountManager.transaction { transaction -> Int32 in + if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.nextChatSuggestionTip()) as? ApplicationSpecificCounterNotice { + return value.value + } else { + return 0 + } + } + } + + public static func incrementNextChatSuggestionTip(accountManager: AccountManager, count: Int32 = 1) -> Signal { + return accountManager.transaction { transaction -> Void in + var currentValue: Int32 = 0 + if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.nextChatSuggestionTip()) as? ApplicationSpecificCounterNotice { + currentValue = value.value + } + currentValue += count + + transaction.setNotice(ApplicationSpecificNoticeKeys.nextChatSuggestionTip(), ApplicationSpecificCounterNotice(value: currentValue)) + } + } public static func reset(accountManager: AccountManager) -> Signal { return accountManager.transaction { transaction -> Void in diff --git a/submodules/TelegramPermissions/BUILD b/submodules/TelegramPermissions/BUILD index 8225a6d321..5c9010b5d7 100644 --- a/submodules/TelegramPermissions/BUILD +++ b/submodules/TelegramPermissions/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Postbox:Postbox", "//submodules/TelegramCore:TelegramCore", diff --git a/submodules/TelegramPermissionsUI/BUILD b/submodules/TelegramPermissionsUI/BUILD index acbc6e3dd5..ec40edd53e 100644 --- a/submodules/TelegramPermissionsUI/BUILD +++ b/submodules/TelegramPermissionsUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/TelegramPresentationData/BUILD b/submodules/TelegramPresentationData/BUILD index 9a8b2f60a5..d15b450263 100644 --- a/submodules/TelegramPresentationData/BUILD +++ b/submodules/TelegramPresentationData/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramCore:TelegramCore", "//submodules/Postbox:Postbox", diff --git a/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift b/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift index 5dd0703bd0..7f339b778f 100644 --- a/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift +++ b/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift @@ -72,20 +72,20 @@ public func chatControllerBackgroundImage(theme: PresentationTheme?, wallpaper i backgroundImage = UIImage(contentsOfFile: path)?.precomposed() } } - case let .file(file): + case let .file(_, _, _, _, _, _, _, file, settings): if wallpaper.isPattern { backgroundImage = nil } else { - if file.settings.blur && composed { + if settings.blur && composed { var image: UIImage? - let _ = mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true, attemptSynchronously: true).start(next: { data in + let _ = mediaBox.cachedResourceRepresentation(file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true, attemptSynchronously: true).start(next: { data in if data.complete { image = UIImage(contentsOfFile: data.path)?.precomposed() } }) backgroundImage = image } - if backgroundImage == nil, let path = mediaBox.completedResourcePath(file.file.resource) { + if backgroundImage == nil, let path = mediaBox.completedResourcePath(file.resource) { succeed = false backgroundImage = UIImage(contentsOfFile: path)?.precomposed() } @@ -166,17 +166,17 @@ public func chatControllerBackgroundImageSignal(wallpaper: TelegramWallpaper, me } } } - case let .file(file): + case let .file(_, _, _, _, _, _, slug, file, settings): if wallpaper.isPattern { return .single((nil, true)) } else { - if file.settings.blur { + if settings.blur { let representation = CachedBlurredWallpaperRepresentation() - if FileManager.default.fileExists(atPath: mediaBox.cachedRepresentationCompletePath(file.file.resource.id, representation: representation)) { + if FileManager.default.fileExists(atPath: mediaBox.cachedRepresentationCompletePath(file.resource.id, representation: representation)) { let effectiveMediaBox = mediaBox - return effectiveMediaBox.cachedResourceRepresentation(file.file.resource, representation: representation, complete: true, fetch: true, attemptSynchronously: true) + return effectiveMediaBox.cachedResourceRepresentation(file.resource, representation: representation, complete: true, fetch: true, attemptSynchronously: true) |> map { data -> (UIImage?, Bool)? in if data.complete { return (UIImage(contentsOfFile: data.path)?.precomposed(), true) @@ -189,17 +189,17 @@ public func chatControllerBackgroundImageSignal(wallpaper: TelegramWallpaper, me } } else { return Signal { subscriber in - let fetch = fetchedMediaResource(mediaBox: accountMediaBox, reference: MediaResourceReference.wallpaper(wallpaper: WallpaperReference.slug(file.slug), resource: file.file.resource)).start() + let fetch = fetchedMediaResource(mediaBox: accountMediaBox, reference: MediaResourceReference.wallpaper(wallpaper: WallpaperReference.slug(slug), resource: file.resource)).start() var didOutputBlurred = false - let data = accountMediaBox.cachedResourceRepresentation(file.file.resource, representation: representation, complete: true, fetch: true, attemptSynchronously: true).start(next: { data in + let data = accountMediaBox.cachedResourceRepresentation(file.resource, representation: representation, complete: true, fetch: true, attemptSynchronously: true).start(next: { data in if data.complete { if let image = UIImage(contentsOfFile: data.path)?.precomposed() { - mediaBox.copyResourceData(file.file.resource.id, fromTempPath: data.path) + mediaBox.copyResourceData(file.resource.id, fromTempPath: data.path) subscriber.putNext((image, true)) } } else if !didOutputBlurred { didOutputBlurred = true - if let immediateThumbnailData = file.file.immediateThumbnailData, let decodedData = decodeTinyThumbnail(data: immediateThumbnailData) { + if let immediateThumbnailData = file.immediateThumbnailData, let decodedData = decodeTinyThumbnail(data: immediateThumbnailData) { if let image = UIImage(data: decodedData)?.precomposed() { subscriber.putNext((image, false)) } @@ -215,9 +215,9 @@ public func chatControllerBackgroundImageSignal(wallpaper: TelegramWallpaper, me } } else { var path: String? - if let maybePath = mediaBox.completedResourcePath(file.file.resource) { + if let maybePath = mediaBox.completedResourcePath(file.resource) { path = maybePath - } else if let maybePath = accountMediaBox.completedResourcePath(file.file.resource) { + } else if let maybePath = accountMediaBox.completedResourcePath(file.resource) { path = maybePath } if let path = path { @@ -227,17 +227,17 @@ public func chatControllerBackgroundImageSignal(wallpaper: TelegramWallpaper, me } } else { return Signal { subscriber in - let fetch = fetchedMediaResource(mediaBox: accountMediaBox, reference: MediaResourceReference.wallpaper(wallpaper: WallpaperReference.slug(file.slug), resource: file.file.resource)).start() + let fetch = fetchedMediaResource(mediaBox: accountMediaBox, reference: MediaResourceReference.wallpaper(wallpaper: WallpaperReference.slug(slug), resource: file.resource)).start() var didOutputBlurred = false - let data = accountMediaBox.resourceData(file.file.resource).start(next: { data in + let data = accountMediaBox.resourceData(file.resource).start(next: { data in if data.complete { if let image = UIImage(contentsOfFile: data.path)?.precomposed() { - mediaBox.copyResourceData(file.file.resource.id, fromTempPath: data.path) + mediaBox.copyResourceData(file.resource.id, fromTempPath: data.path) subscriber.putNext((image, true)) } } else if !didOutputBlurred { didOutputBlurred = true - if let immediateThumbnailData = file.file.immediateThumbnailData, let decodedData = decodeTinyThumbnail(data: immediateThumbnailData) { + if let immediateThumbnailData = file.immediateThumbnailData, let decodedData = decodeTinyThumbnail(data: immediateThumbnailData) { if let image = UIImage(data: decodedData)?.precomposed() { subscriber.putNext((image, false)) } diff --git a/submodules/TelegramPresentationData/Sources/ChatMessageBubbleImages.swift b/submodules/TelegramPresentationData/Sources/ChatMessageBubbleImages.swift index a59ae2b960..4df9b65770 100644 --- a/submodules/TelegramPresentationData/Sources/ChatMessageBubbleImages.swift +++ b/submodules/TelegramPresentationData/Sources/ChatMessageBubbleImages.swift @@ -215,7 +215,6 @@ public func messageBubbleImage(maxCornerRadius: CGFloat, minCornerRadius: CGFloa if drawTail { let outlineBottomEllipse = bottomEllipse.insetBy(dx: -borderOffset, dy: -borderOffset) let outlineInnerTopEllipse = topEllipse.insetBy(dx: borderOffset, dy: borderOffset) - let outlineTopEllipse = topEllipse.insetBy(dx: -borderOffset, dy: -borderOffset) context.setBlendMode(.copy) context.setFillColor(UIColor.clear.cgColor) diff --git a/submodules/TelegramPresentationData/Sources/ChatPresentationData.swift b/submodules/TelegramPresentationData/Sources/ChatPresentationData.swift index 1fbb5bc4bd..094d3bd857 100644 --- a/submodules/TelegramPresentationData/Sources/ChatPresentationData.swift +++ b/submodules/TelegramPresentationData/Sources/ChatPresentationData.swift @@ -2,7 +2,6 @@ import Foundation import UIKit import Display import TelegramCore -import TelegramPresentationData import TelegramUIPreferences public final class ChatPresentationThemeData: Equatable { diff --git a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift index 00da547761..cca43e6c1b 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift @@ -140,7 +140,6 @@ public func customizeDefaultDarkPresentationTheme(theme: PresentationTheme, edit var outgoingBubbleFillColor: UIColor? var outgoingBubbleFillGradientColor: UIColor? - var outgoingBubbleHighlightedFillColor: UIColor? var outgoingPrimaryTextColor: UIColor? var outgoingSecondaryTextColor: UIColor? var outgoingLinkTextColor: UIColor? diff --git a/submodules/TelegramPresentationData/Sources/NumericFormat.swift b/submodules/TelegramPresentationData/Sources/NumericFormat.swift index fefc92766a..ae55a9b9d5 100644 --- a/submodules/TelegramPresentationData/Sources/NumericFormat.swift +++ b/submodules/TelegramPresentationData/Sources/NumericFormat.swift @@ -111,8 +111,10 @@ public func shortTimeIntervalString(strings: PresentationStrings, value: Int32) return strings.MessageTimer_ShortHours(max(1, value / (60 * 60))) } else if value < 60 * 60 * 24 * 7 { return strings.MessageTimer_ShortDays(max(1, value / (60 * 60 * 24))) - } else { + } else if value < 60 * 60 * 24 * 31 { return strings.MessageTimer_ShortWeeks(max(1, value / (60 * 60 * 24 * 7))) + } else { + return strings.MessageTimer_ShortMonths(max(1, value / (60 * 60 * 24 * 7 * 30))) } } diff --git a/submodules/TelegramPresentationData/Sources/PresentationData.swift b/submodules/TelegramPresentationData/Sources/PresentationData.swift index 61c25cc6c9..726ddce651 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationData.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationData.swift @@ -457,12 +457,12 @@ public func serviceColor(for wallpaper: (TelegramWallpaper, UIImage?)) -> UIColo } else { return UIColor(rgb: 0x000000, alpha: 0.3) } - case let .file(file): + case let .file(_, _, _, _, _, _, _, _, settings): if wallpaper.0.isPattern { - if file.settings.colors.count >= 1 && file.settings.colors.count <= 2 { - var mixedColor = UIColor(argb: file.settings.colors[0]) - if file.settings.colors.count >= 2 { - mixedColor = mixedColor.mixedWith(UIColor(argb: file.settings.colors[1]), alpha: 0.5) + if settings.colors.count >= 1 && settings.colors.count <= 2 { + var mixedColor = UIColor(argb: settings.colors[0]) + if settings.colors.count >= 2 { + mixedColor = mixedColor.mixedWith(UIColor(argb: settings.colors[1]), alpha: 0.5) } return serviceColor(with: mixedColor) } else { @@ -531,12 +531,12 @@ public func chatServiceBackgroundColor(wallpaper: TelegramWallpaper, mediaBox: M } else { return .single(UIColor(rgb: 0x000000, alpha: 0.3)) } - case let .file(file): + case let .file(_, _, _, _, _, _, _, file, settings): if wallpaper.isPattern { - if file.settings.colors.count >= 1 && file.settings.colors.count <= 2 { - var mixedColor = UIColor(argb: file.settings.colors[0]) - if file.settings.colors.count >= 2 { - mixedColor = mixedColor.mixedWith(UIColor(argb: file.settings.colors[1]), alpha: 0.5) + if settings.colors.count >= 1 && settings.colors.count <= 2 { + var mixedColor = UIColor(argb: settings.colors[0]) + if settings.colors.count >= 2 { + mixedColor = mixedColor.mixedWith(UIColor(argb: settings.colors[1]), alpha: 0.5) } return .single(serviceColor(with: mixedColor)) } else { @@ -544,7 +544,7 @@ public func chatServiceBackgroundColor(wallpaper: TelegramWallpaper, mediaBox: M } } else { return Signal { subscriber in - let data = serviceColor(for: mediaBox.resourceData(file.file.resource)).start(next: { next in + let data = serviceColor(for: mediaBox.resourceData(file.resource)).start(next: { next in subscriber.putNext(next) }, completed: { subscriber.putCompletion() @@ -625,7 +625,7 @@ public func updatedPresentationData(accountManager: AccountManager, applicationI switch effectiveChatWallpaper { case .builtin, .color, .gradient: effectiveChatWallpaper = themeValue.chat.defaultWallpaper - case let .file(file): + case .file: if effectiveChatWallpaper.isPattern { effectiveChatWallpaper = themeValue.chat.defaultWallpaper } diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift index 0fcd1a94aa..a9a4a31a22 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift @@ -132,33 +132,33 @@ extension TelegramWallpaper: Codable { components.append("blur") } try container.encode(components.joined(separator: " ")) - case let .file(file): + case let .file(_, _, _, _, _, _, slug, _, settings): var components: [String] = [] - components.append(file.slug) + components.append(slug) if self.isPattern { - if file.settings.colors.count >= 1 { - components.append(String(format: "%06x", file.settings.colors[0])) + if settings.colors.count >= 1 { + components.append(String(format: "%06x", settings.colors[0])) } - if let intensity = file.settings.intensity { + if let intensity = settings.intensity { components.append("\(intensity)") } - if file.settings.colors.count >= 2 { - components.append(String(format: "%06x", file.settings.colors[1])) + if settings.colors.count >= 2 { + components.append(String(format: "%06x", settings.colors[1])) } - if file.settings.colors.count >= 3 { - components.append(String(format: "%06x", file.settings.colors[2])) + if settings.colors.count >= 3 { + components.append(String(format: "%06x", settings.colors[2])) } - if file.settings.colors.count >= 4 { - components.append(String(format: "%06x", file.settings.colors[3])) + if settings.colors.count >= 4 { + components.append(String(format: "%06x", settings.colors[3])) } - if let rotation = file.settings.rotation, rotation != 0 { + if let rotation = settings.rotation, rotation != 0 { components.append("\(rotation)") } } - if file.settings.motion { + if settings.motion { components.append("motion") } - if file.settings.blur { + if settings.blur { components.append("blur") } try container.encode(components.joined(separator: " ")) diff --git a/submodules/TelegramPresentationData/Sources/WallpaperUtils.swift b/submodules/TelegramPresentationData/Sources/WallpaperUtils.swift index cb821727cc..e2e8423c67 100644 --- a/submodules/TelegramPresentationData/Sources/WallpaperUtils.swift +++ b/submodules/TelegramPresentationData/Sources/WallpaperUtils.swift @@ -31,8 +31,8 @@ public extension TelegramWallpaper { var isPattern: Bool { switch self { - case let .file(file): - return file.isPattern || file.file.mimeType == "application/x-tgwallpattern" + case let .file(_, _, _, _, isPattern, _, _, file, _): + return isPattern || file.mimeType == "application/x-tgwallpattern" default: return false } @@ -48,8 +48,8 @@ public extension TelegramWallpaper { } var dimensions: CGSize? { - if case let .file(file) = self { - return file.file.dimensions?.cgSize + if case let .file(_, _, _, _, _, _, _, file, _) = self { + return file.dimensions?.cgSize } else { return nil } diff --git a/submodules/TelegramStringFormatting/BUILD b/submodules/TelegramStringFormatting/BUILD index d0e98b73dd..9c917401d3 100644 --- a/submodules/TelegramStringFormatting/BUILD +++ b/submodules/TelegramStringFormatting/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramCore:TelegramCore", "//submodules/Postbox:Postbox", diff --git a/submodules/TelegramStringFormatting/Sources/PresenceStrings.swift b/submodules/TelegramStringFormatting/Sources/PresenceStrings.swift index 6621d58bad..edf9e67ab2 100644 --- a/submodules/TelegramStringFormatting/Sources/PresenceStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/PresenceStrings.swift @@ -1,5 +1,4 @@ import Foundation -import Postbox import TelegramCore import TelegramPresentationData @@ -222,9 +221,9 @@ public enum RelativeUserPresenceStatus { case lastMonth } -public func relativeUserPresenceStatus(_ presence: TelegramUserPresence, relativeTo timestamp: Int32) -> RelativeUserPresenceStatus { +public func relativeUserPresenceStatus(_ presence: EnginePeer.Presence, relativeTo timestamp: Int32) -> RelativeUserPresenceStatus { switch presence.status { - case .none: + case .longTimeAgo: return .offline case let .present(statusTimestamp): if statusTimestamp >= timestamp { diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD index 586fc7fb26..7e22f4728c 100644 --- a/submodules/TelegramUI/BUILD +++ b/submodules/TelegramUI/BUILD @@ -238,6 +238,7 @@ swift_library( "//submodules/ImportStickerPackUI:ImportStickerPackUI", "//submodules/GradientBackground:GradientBackground", "//submodules/WallpaperBackgroundNode:WallpaperBackgroundNode", + "//submodules/ComponentFlow:ComponentFlow", ] + select({ "@build_bazel_rules_apple//apple:ios_armv7": [], "@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets, diff --git a/submodules/TelegramUI/Sources/AccountContext.swift b/submodules/TelegramUI/Sources/AccountContext.swift index b2d11f4816..30015f24d7 100644 --- a/submodules/TelegramUI/Sources/AccountContext.swift +++ b/submodules/TelegramUI/Sources/AccountContext.swift @@ -305,7 +305,7 @@ public final class AccountContextImpl: AccountContext { let _ = self.sharedContext.callManager?.scheduleGroupCall(context: self, peerId: peerId, endCurrentIfAny: true) } - public func joinGroupCall(peerId: PeerId, invite: String?, requestJoinAsPeerId: ((@escaping (PeerId?) -> Void) -> Void)?, activeCall: CachedChannelData.ActiveCall) { + public func joinGroupCall(peerId: PeerId, invite: String?, requestJoinAsPeerId: ((@escaping (PeerId?) -> Void) -> Void)?, activeCall: EngineGroupCallDescription) { let callResult = self.sharedContext.callManager?.joinGroupCall(context: self, peerId: peerId, invite: invite, requestJoinAsPeerId: requestJoinAsPeerId, initialCall: activeCall, endCurrentIfAny: false) if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult { if currentPeerId == peerId { diff --git a/submodules/TelegramUI/Sources/AuthorizationSequenceSplashController.swift b/submodules/TelegramUI/Sources/AuthorizationSequenceSplashController.swift index 5b344155b8..b0acbe208f 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequenceSplashController.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequenceSplashController.swift @@ -57,10 +57,10 @@ final class AuthorizationSequenceSplashController: ViewController { if let available = localization.availableLocalizations.first, available.languageCode != "en" { let value = TGSuggestedLocalization(info: TGAvailableLocalization(title: available.title, localizedTitle: available.localizedTitle, code: available.languageCode), continueWithLanguageString: continueWithLanguageString, chooseLanguageString: "Choose Language", chooseLanguageOtherString: "Choose Language", englishLanguageNameString: "English") - subscriber?.putNext(value) + subscriber.putNext(value) } }, completed: { - subscriber?.putCompletion() + subscriber.putCompletion() }) return SBlockDisposable(block: { diff --git a/submodules/TelegramUI/Sources/ChatAvatarNavigationNode.swift b/submodules/TelegramUI/Sources/ChatAvatarNavigationNode.swift index c26dcc45f3..48169b0556 100644 --- a/submodules/TelegramUI/Sources/ChatAvatarNavigationNode.swift +++ b/submodules/TelegramUI/Sources/ChatAvatarNavigationNode.swift @@ -52,4 +52,33 @@ final class ChatAvatarNavigationNode: ASDisplayNode { func onLayout() { } + + final class SnapshotState { + fileprivate let snapshotView: UIView + + fileprivate init(snapshotView: UIView) { + self.snapshotView = snapshotView + } + } + + func prepareSnapshotState() -> SnapshotState { + let snapshotView = self.avatarNode.view.snapshotView(afterScreenUpdates: false)! + return SnapshotState( + snapshotView: snapshotView + ) + } + + func animateFromSnapshot(_ snapshotState: SnapshotState) { + self.avatarNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + self.avatarNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: true) + + snapshotState.snapshotView.frame = self.frame + self.containerNode.view.addSubview(snapshotState.snapshotView) + + let snapshotView = snapshotState.snapshotView + snapshotState.snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + snapshotView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) + } } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index cfda44c958..f192cd8395 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -64,6 +64,7 @@ import TelegramPermissionsUI import Speak import UniversalMediaPlayer import WallpaperBackgroundNode +import ChatListUI #if DEBUG import os.signpost @@ -212,6 +213,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G private var validLayout: ContainerViewLayout? public weak var parentController: ViewController? + + private let currentChatListFilter: ChatListFilterData? public var peekActions: ChatControllerPeekActions = .standard private var didSetup3dTouch: Bool = false @@ -282,6 +285,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G private var preloadHistoryPeerId: PeerId? private let preloadHistoryPeerIdDisposable = MetaDisposable() + + private var preloadNextChatPeerId: PeerId? + private let preloadNextChatPeerIdDisposable = MetaDisposable() private let botCallbackAlertMessage = Promise(nil) private var botCallbackAlertMessageDisposable: Disposable? @@ -442,6 +448,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return self.chatLocation } + private var scheduledScrollToMessageId: (MessageId, Double?)? + public var purposefulAction: (() -> Void)? var updatedClosedPinnedMessageId: ((MessageId) -> Void)? var requestedUnpinAllMessages: ((Int, MessageId) -> Void)? @@ -454,8 +462,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } private var importStateDisposable: Disposable? + + private var nextChannelToReadDisposable: Disposable? - public init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic = Atomic(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, mode: ChatControllerPresentationMode = .standard(previewing: false), peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil) { + public init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic = Atomic(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, mode: ChatControllerPresentationMode = .standard(previewing: false), peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, chatListFilter: ChatListFilterData? = nil) { let _ = ChatControllerCount.modify { value in return value + 1 } @@ -466,6 +476,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.subject = subject self.botStart = botStart self.peekData = peekData + self.currentChatListFilter = chatListFilter var useSharedAnimationPhase = false switch mode { @@ -591,7 +602,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } case .groupPhoneCall, .inviteToGroupPhoneCall: if let activeCall = strongSelf.presentationInterfaceState.activeGroupCallInfo?.activeCall { - strongSelf.joinGroupCall(peerId: message.id.peerId, invite: nil, activeCall: CachedChannelData.ActiveCall(id: activeCall.id, accessHash: activeCall.accessHash, title: activeCall.title, scheduleTimestamp: activeCall.scheduleTimestamp, subscribedToScheduled: activeCall.subscribedToScheduled)) + strongSelf.joinGroupCall(peerId: message.id.peerId, invite: nil, activeCall: EngineGroupCallDescription(id: activeCall.id, accessHash: activeCall.accessHash, title: activeCall.title, scheduleTimestamp: activeCall.scheduleTimestamp, subscribedToScheduled: activeCall.subscribedToScheduled)) } else { var canManageGroupCalls = false if let channel = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramChannel { @@ -625,7 +636,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self else { return } - strongSelf.joinGroupCall(peerId: message.id.peerId, invite: nil, activeCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: info.scheduleTimestamp, subscribedToScheduled: info.subscribedToScheduled)) + strongSelf.joinGroupCall(peerId: message.id.peerId, invite: nil, activeCall: EngineGroupCallDescription(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: info.scheduleTimestamp, subscribedToScheduled: info.subscribedToScheduled)) }, error: { [weak self] error in dismissStatus?() @@ -2423,7 +2434,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } if !found { - let _ = strongSelf.controllerInteraction?.openMessage(message, .timecode(Double(timestamp))) + if let _ = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) { + let _ = strongSelf.controllerInteraction?.openMessage(message, .timecode(Double(timestamp))) + } else { + strongSelf.navigateToMessage(messageLocation: .id(message.id, Double(timestamp)), animated: true, forceInCurrentChat: true) + } } } }, scheduleCurrentMessage: { [weak self] in @@ -3060,7 +3075,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } else { imageOverride = nil } - (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.avatarNode.setPeer(context: strongSelf.context, theme: strongSelf.presentationData.theme, peer: peer, overrideImage: imageOverride) + (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.avatarNode.setPeer(context: strongSelf.context, theme: strongSelf.presentationData.theme, peer: EnginePeer(peer), overrideImage: imageOverride) (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.contextActionIsEnabled = peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil strongSelf.chatInfoNavigationButton?.buttonItem.accessibilityLabel = presentationInterfaceState.strings.Conversation_ContextMenuOpenProfile } @@ -3295,6 +3310,40 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }.updatedIsNotAccessible(isNotAccessible).updatedContactStatus(contactStatus).updatedHasBots(hasBots).updatedHasBotCommands(hasBotCommands).updatedIsArchived(isArchived).updatedPeerIsMuted(peerIsMuted).updatedPeerDiscussionId(peerDiscussionId).updatedPeerGeoLocation(peerGeoLocation).updatedExplicitelyCanPinMessages(explicitelyCanPinMessages).updatedHasScheduledMessages(hasScheduledMessages) .updatedAutoremoveTimeout(autoremoveTimeout) }) + + if let channel = renderedPeer?.chatMainPeer as? TelegramChannel, case .broadcast = channel.info { + if strongSelf.nextChannelToReadDisposable == nil { + strongSelf.nextChannelToReadDisposable = (combineLatest(queue: .mainQueue(), + strongSelf.context.engine.peers.getNextUnreadChannel(peerId: channel.id, filter: strongSelf.currentChatListFilter.flatMap(chatListFilterPredicate)), + ApplicationSpecificNotice.getNextChatSuggestionTip(accountManager: strongSelf.context.sharedContext.accountManager) + ) + |> then(.complete() |> delay(1.0, queue: .mainQueue())) + |> restart).start(next: { nextPeer, nextChatSuggestionTip in + guard let strongSelf = self else { + return + } + + strongSelf.chatDisplayNode.historyNode.offerNextChannelToRead = true + strongSelf.chatDisplayNode.historyNode.nextChannelToRead = nextPeer + strongSelf.chatDisplayNode.historyNode.nextChannelToReadDisplayName = nextChatSuggestionTip >= 3 + + let nextPeerId = nextPeer?.id + + if strongSelf.preloadNextChatPeerId != nextPeerId { + strongSelf.preloadNextChatPeerId = nextPeerId + if let nextPeerId = nextPeerId { + let combinedDisposable = DisposableSet() + strongSelf.preloadNextChatPeerIdDisposable.set(combinedDisposable) + combinedDisposable.add(strongSelf.context.account.viewTracker.polledChannel(peerId: nextPeerId).start()) + combinedDisposable.add(strongSelf.context.account.addAdditionalPreloadHistoryPeerId(peerId: nextPeerId)) + } else { + strongSelf.preloadNextChatPeerIdDisposable.set(nil) + } + } + }) + } + } + if !strongSelf.didSetChatLocationInfoReady { strongSelf.didSetChatLocationInfoReady = true strongSelf._chatLocationInfoReady.set(.single(true)) @@ -3778,7 +3827,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }) - if case let .peer(peerId) = self.chatLocation { + /*if case let .peer(peerId) = self.chatLocation { self.importStateDisposable = (ChatHistoryImportTasks.importState(peerId: peerId) |> distinctUntilChanged |> deliverOnMainQueue).start(next: { [weak self] state in @@ -3792,7 +3841,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G $0.updatedImportState(mappedState) }) }) - } + }*/ } required public init(coder aDecoder: NSCoder) { @@ -3853,6 +3902,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.shareStatusDisposable?.dispose() self.context.sharedContext.mediaManager.galleryHiddenMediaManager.removeTarget(self) self.preloadHistoryPeerIdDisposable.dispose() + self.preloadNextChatPeerIdDisposable.dispose() self.reportIrrelvantGeoDisposable?.dispose() self.reminderActivity?.invalidate() self.updateSlowmodeStatusDisposable.dispose() @@ -3867,6 +3917,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.selectAddMemberDisposable.dispose() self.addMemberDisposable.dispose() self.importStateDisposable?.dispose() + self.nextChannelToReadDisposable?.dispose() } public func updatePresentationMode(_ mode: ChatControllerPresentationMode) { @@ -4256,7 +4307,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self, let combinedInitialData = combinedInitialData else { return } - if let interfaceState = combinedInitialData.initialData?.chatInterfaceState as? ChatInterfaceState { + + if let opaqueState = (combinedInitialData.initialData?.storedInterfaceState).flatMap(_internal_decodeStoredChatInterfaceState) { + let interfaceState = ChatInterfaceState.parse(opaqueState) + var pinnedMessageId: MessageId? var peerIsBlocked: Bool = false var callsAvailable: Bool = true @@ -4612,7 +4666,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } })) - if case let .message(_, _, maybeTimecode) = strongSelf.subject, let timecode = maybeTimecode, initial { + if let (messageId, maybeTimecode) = strongSelf.scheduledScrollToMessageId { + strongSelf.scheduledScrollToMessageId = nil + if let timecode = maybeTimecode, message.id == messageId { + Queue.mainQueue().after(0.2) { + let _ = strongSelf.controllerInteraction?.openMessage(message, .timecode(timecode)) + } + } + } else if case let .message(_, _, maybeTimecode) = strongSelf.subject, let timecode = maybeTimecode, initial { Queue.mainQueue().after(0.2) { let _ = strongSelf.controllerInteraction?.openMessage(message, .timecode(timecode)) } @@ -6730,7 +6791,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else { return } - strongSelf.joinGroupCall(peerId: peer.id, invite: nil, activeCall: activeCall) + strongSelf.joinGroupCall(peerId: peer.id, invite: nil, activeCall: EngineGroupCallDescription(activeCall)) }, presentInviteMembers: { [weak self] in guard let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else { return @@ -7011,9 +7072,32 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } }) + + self.chatDisplayNode.historyNode.openNextChannelToRead = { [weak self] peer in + guard let strongSelf = self else { + return + } + if let navigationController = strongSelf.effectiveNavigationController { + ApplicationSpecificNotice.incrementNextChatSuggestionTip(accountManager: strongSelf.context.sharedContext.accountManager).start() + + let snapshotState = strongSelf.chatDisplayNode.prepareSnapshotState( + titleViewSnapshotState: strongSelf.chatTitleView?.prepareSnapshotState(), + avatarSnapshotState: (strongSelf.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.prepareSnapshotState() + ) + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id), animated: false, chatListFilter: strongSelf.currentChatListFilter, completion: { nextController in + (nextController as! ChatControllerImpl).animateFromPreviousController(snapshotState: snapshotState) + })) + } + } self.displayNodeDidLoad() } + + private var storedAnimateFromSnapshotState: ChatControllerNode.SnapshotState? + + private func animateFromPreviousController(snapshotState: ChatControllerNode.SnapshotState) { + self.storedAnimateFromSnapshotState = snapshotState + } override public func viewWillAppear(_ animated: Bool) { #if DEBUG @@ -7067,7 +7151,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.didAppear = true - self.chatDisplayNode.historyNode.preloadPages = true self.chatDisplayNode.historyNode.experimentalSnapScrollToItem = false self.chatDisplayNode.historyNode.canReadHistory.set(combineLatest(context.sharedContext.applicationBindings.applicationInForeground, self.canReadHistory.get()) |> map { a, b in return a && b @@ -7415,6 +7498,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return state.updatedInputMode({ _ in .text }) }) } + + if let snapshotState = self.storedAnimateFromSnapshotState { + self.storedAnimateFromSnapshotState = nil + + if let titleViewSnapshotState = snapshotState.titleViewSnapshotState { + self.chatTitleView?.animateFromSnapshot(titleViewSnapshotState) + } + if let avatarSnapshotState = snapshotState.avatarSnapshotState { + (self.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.animateFromSnapshot(avatarSnapshotState) + } + self.chatDisplayNode.animateFromSnapshot(snapshotState) + } } override public func viewWillDisappear(_ animated: Bool) { @@ -7450,7 +7545,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G interfaceState = interfaceState.withUpdatedHistoryScrollState(scrollState) } interfaceState = interfaceState.withUpdatedInputLanguage(self.chatDisplayNode.currentTextInputLanguage) - let _ = updatePeerChatInterfaceState(account: self.context.account, peerId: peerId, threadId: threadId, state: interfaceState).start() + let _ = ChatInterfaceState.update(engine: self.context.engine, peerId: peerId, threadId: threadId, { _ in + return interfaceState + }).start() } override public func viewDidDisappear(_ animated: Bool) { @@ -8205,7 +8302,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G })) } else { if let _ = canClearForMyself ?? canClearForEveryone { - items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .clearHistory(canClearCache: canClearCache), strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) + items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: EnginePeer(mainPeer), chatPeer: EnginePeer(chatPeer), action: .clearHistory(canClearCache: canClearCache), strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder)) if let canClearForEveryone = canClearForEveryone { let text: String @@ -8394,7 +8491,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } var items: [ActionSheetItem] = [] - items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: peer, chatPeer: peer, action: .clearCache, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder)) + items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: EnginePeer(peer), chatPeer: EnginePeer(peer), action: .clearCache, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder)) let validCategories: [PeerCacheUsageCategory] = [.image, .video, .audio, .file] @@ -10729,6 +10826,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } else if case let .index(index) = messageLocation, index.id.id == 0, index.timestamp > 0, case .scheduledMessages = self.presentationInterfaceState.subject { self.chatDisplayNode.historyNode.scrollToMessage(from: scrollFromIndex, to: index, animated: animated, scrollPosition: scrollPosition) } else { + if case let .id(messageId, maybeTimecode) = messageLocation, let timecode = maybeTimecode { + self.scheduledScrollToMessageId = (messageId, timecode) + } self.loadingMessage.set(.single(statusSubject) |> delay(0.1, queue: .mainQueue())) let searchLocation: ChatHistoryInitialSearchLocation switch messageLocation { @@ -10910,7 +11010,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var attemptSelectionImpl: ((Peer) -> Void)? let controller = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: filter, attemptSelection: { peer in attemptSelectionImpl?(peer) - }, multipleSelection: true)) + }, multipleSelection: true, forwardedMessagesCount: messages.count)) let context = self.context attemptSelectionImpl = { [weak controller] peer in guard let controller = controller else { @@ -10925,7 +11025,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } controller.present(textAlertController(context: context, title: nil, text: presentationData.strings.Forward_ErrorDisabledForChat, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } - controller.multiplePeersSelected = { [weak self, weak controller] peers, messageText, mode in + controller.multiplePeersSelected = { [weak self, weak controller] peers, peerMap, messageText, mode in guard let strongSelf = self, let strongController = controller else { return } @@ -10951,6 +11051,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) let commit: ([EnqueueMessage]) -> Void = { result in + var displayPeers: [Peer] = [] for peer in peers { let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: result) |> deliverOnMainQueue).start(next: { messageIds in @@ -10977,30 +11078,38 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }) - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let text: String - var savedMessages = false - if peers.count == 1, let peerId = peers.first?.id, peerId == strongSelf.context.account.peerId { - text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many - savedMessages = true - } else { - if peers.count == 1, let peer = peers.first { - let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string - } else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last { - let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string - } else if let peer = peers.first { - let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(peers.count - 1)").string - } else { - text = "" + if let secretPeer = peer as? TelegramSecretChat { + if let peer = peerMap[secretPeer.regularPeerId] { + displayPeers.append(peer) } + } else { + displayPeers.append(peer) } - - strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) } + + let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + let text: String + var savedMessages = false + if displayPeers.count == 1, let peerId = displayPeers.first?.id, peerId == strongSelf.context.account.peerId { + text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many + savedMessages = true + } else { + if displayPeers.count == 1, let peer = displayPeers.first { + let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string + } else if displayPeers.count == 2, let firstPeer = displayPeers.first, let secondPeer = displayPeers.last { + let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string + } else if let peer = displayPeers.first { + let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(displayPeers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(displayPeers.count - 1)").string + } else { + text = "" + } + } + + strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) } switch mode { @@ -11096,16 +11205,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } } - - let _ = (strongSelf.context.account.postbox.transaction({ transaction -> Void in - transaction.updatePeerChatInterfaceState(peerId, update: { currentState in - if let currentState = currentState as? ChatInterfaceState { - return currentState.withUpdatedForwardMessageIds(messages.map { $0.id }) - } else { - return ChatInterfaceState().withUpdatedForwardMessageIds(messages.map { $0.id }) - } - }) - }) |> deliverOnMainQueue).start(completed: { + + let _ = (ChatInterfaceState.update(engine: strongSelf.context.engine, peerId: peerId, threadId: nil, { currentState in + return currentState.withUpdatedForwardMessageIds(messages.map { $0.id }) + }) + |> deliverOnMainQueue).start(completed: { if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withoutSelectionState() }) }) @@ -11189,14 +11293,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G })) case let .chat(textInputState, subject, peekData): if let textInputState = textInputState { - let _ = (self.context.account.postbox.transaction({ transaction -> Void in - transaction.updatePeerChatInterfaceState(peerId, update: { currentState in - if let currentState = currentState as? ChatInterfaceState { - return currentState.withUpdatedComposeInputState(textInputState) - } else { - return ChatInterfaceState().withUpdatedComposeInputState(textInputState) - } - }) + let _ = (ChatInterfaceState.update(engine: self.context.engine, peerId: peerId, threadId: nil, { currentState in + return currentState.withUpdatedComposeInputState(textInputState) }) |> deliverOnMainQueue).start(completed: { [weak self] in if let strongSelf = self, let navigationController = strongSelf.effectiveNavigationController { @@ -11231,15 +11329,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) strongController.dismiss() } else { - let _ = (strongSelf.context.account.postbox.transaction({ transaction -> Void in - transaction.updatePeerChatInterfaceState(peerId, update: { currentState in - if let currentState = currentState as? ChatInterfaceState { - return currentState.withUpdatedComposeInputState(textInputState) - } else { - return ChatInterfaceState().withUpdatedComposeInputState(textInputState) - } - }) - }) |> deliverOnMainQueue).start(completed: { + let _ = (ChatInterfaceState.update(engine: strongSelf.context.engine, peerId: peerId, threadId: nil, { currentState in + return currentState.withUpdatedComposeInputState(textInputState) + }) + |> deliverOnMainQueue).start(completed: { if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withoutSelectionState() }) }) @@ -11672,7 +11765,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self?.controllerInteraction?.requestMessageActionUrlAuth(url, subject) } }, joinVoiceChat: { [weak self] peerId, invite, call in - self?.joinGroupCall(peerId: peerId, invite: invite, activeCall: call) + self?.joinGroupCall(peerId: peerId, invite: invite, activeCall: EngineGroupCallDescription(call)) }, present: { [weak self] c, a in self?.present(c, in: .window(.root), with: a) }, dismissInput: { [weak self] in @@ -12044,7 +12137,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let actionSheet = ActionSheetController(presentationData: self.presentationData) var items: [ActionSheetItem] = [] - items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: peer, chatPeer: peer, action: .clearCacheSuggestion, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder)) + items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: EnginePeer(peer), chatPeer: EnginePeer(peer), action: .clearCacheSuggestion, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder)) var presented = false items.append(ActionSheetButtonItem(title: self.presentationData.strings.ClearCache_FreeSpace, color: .accent, action: { [weak self, weak actionSheet] in @@ -12381,7 +12474,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return inputShortcuts + otherShortcuts } - public override func joinGroupCall(peerId: PeerId, invite: String?, activeCall: CachedChannelData.ActiveCall) { + public override func joinGroupCall(peerId: PeerId, invite: String?, activeCall: EngineGroupCallDescription) { let proceed = { super.joinGroupCall(peerId: peerId, invite: invite, activeCall: activeCall) } diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index d5b569a1dd..34fc8fcffc 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -2498,4 +2498,86 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { return false } } + + final class SnapshotState { + fileprivate let historySnapshotState: ChatHistoryListNode.SnapshotState + let titleViewSnapshotState: ChatTitleView.SnapshotState? + let avatarSnapshotState: ChatAvatarNavigationNode.SnapshotState? + let navigationButtonsSnapshotState: ChatHistoryNavigationButtons.SnapshotState + let titleAccessoryPanelSnapshot: UIView? + let navigationBarHeight: CGFloat + + fileprivate init( + historySnapshotState: ChatHistoryListNode.SnapshotState, + titleViewSnapshotState: ChatTitleView.SnapshotState?, + avatarSnapshotState: ChatAvatarNavigationNode.SnapshotState?, + navigationButtonsSnapshotState: ChatHistoryNavigationButtons.SnapshotState, + titleAccessoryPanelSnapshot: UIView?, + navigationBarHeight: CGFloat + ) { + self.historySnapshotState = historySnapshotState + self.titleViewSnapshotState = titleViewSnapshotState + self.avatarSnapshotState = avatarSnapshotState + self.navigationButtonsSnapshotState = navigationButtonsSnapshotState + self.titleAccessoryPanelSnapshot = titleAccessoryPanelSnapshot + self.navigationBarHeight = navigationBarHeight + } + } + + func prepareSnapshotState( + titleViewSnapshotState: ChatTitleView.SnapshotState?, + avatarSnapshotState: ChatAvatarNavigationNode.SnapshotState? + ) -> SnapshotState { + var titleAccessoryPanelSnapshot: UIView? + if let titleAccessoryPanelNode = self.titleAccessoryPanelNode, let snapshot = titleAccessoryPanelNode.view.snapshotView(afterScreenUpdates: false) { + snapshot.frame = titleAccessoryPanelNode.frame + titleAccessoryPanelSnapshot = snapshot + } + return SnapshotState( + historySnapshotState: self.historyNode.prepareSnapshotState(), + titleViewSnapshotState: titleViewSnapshotState, + avatarSnapshotState: avatarSnapshotState, + navigationButtonsSnapshotState: self.navigateButtons.prepareSnapshotState(), + titleAccessoryPanelSnapshot: titleAccessoryPanelSnapshot, + navigationBarHeight: self.navigationBar?.backgroundNode.bounds.height ?? 0.0 + ) + } + + func animateFromSnapshot(_ snapshotState: SnapshotState) { + self.historyNode.animateFromSnapshot(snapshotState.historySnapshotState) + self.navigateButtons.animateFromSnapshot(snapshotState.navigationButtonsSnapshotState) + + if let titleAccessoryPanelSnapshot = snapshotState.titleAccessoryPanelSnapshot { + self.titleAccessoryPanelContainer.view.addSubview(titleAccessoryPanelSnapshot) + if let _ = self.titleAccessoryPanelNode { + titleAccessoryPanelSnapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak titleAccessoryPanelSnapshot] _ in + titleAccessoryPanelSnapshot?.removeFromSuperview() + }) + titleAccessoryPanelSnapshot.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -10.0), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) + } else { + titleAccessoryPanelSnapshot.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -titleAccessoryPanelSnapshot.bounds.height), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true, completion: { [weak titleAccessoryPanelSnapshot] _ in + titleAccessoryPanelSnapshot?.removeFromSuperview() + }) + } + } + + if let titleAccessoryPanelNode = self.titleAccessoryPanelNode { + if let _ = snapshotState.titleAccessoryPanelSnapshot { + titleAccessoryPanelNode.layer.animatePosition(from: CGPoint(x: 0.0, y: 10.0), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: true, additive: true) + titleAccessoryPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, removeOnCompletion: true) + } else { + titleAccessoryPanelNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -titleAccessoryPanelNode.bounds.height), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: true, additive: true) + } + } + + if let navigationBar = self.navigationBar { + let currentFrame = navigationBar.backgroundNode.frame + var previousFrame = currentFrame + previousFrame.size.height = snapshotState.navigationBarHeight + if previousFrame != currentFrame { + navigationBar.backgroundNode.update(size: previousFrame.size, transition: .immediate) + navigationBar.backgroundNode.update(size: currentFrame.size, transition: .animated(duration: 0.5, curve: .spring)) + } + } + } } diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index b1189bdf07..52872c42e7 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -17,6 +17,7 @@ import ListMessageItem import AccountContext import ChatInterfaceState import ChatListUI +import ComponentFlow extension ChatReplyThreadMessage { var effectiveTopId: MessageId { @@ -546,6 +547,14 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { let topVisibleMessageRange = ValuePromise(nil, ignoreRepeated: true) var isSelectionGestureEnabled = true + + private var overscrollView: ComponentHostView? + var nextChannelToRead: EnginePeer? + var offerNextChannelToRead: Bool = false + var nextChannelToReadDisplayName: Bool = false + private var currentOverscrollExpandProgress: CGFloat = 0.0 + private var feedback: HapticFeedback? + var openNextChannelToRead: ((EnginePeer) -> Void)? private let clientId: Atomic @@ -577,6 +586,8 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { nextClientId += 1 super.init() + + self.clipsToBounds = false self.accessibilityPageScrolledString = { [weak self] row, count in if let strongSelf = self { @@ -617,7 +628,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } } - self.preloadPages = false + self.preloadPages = true switch self.mode { case .bubbles: self.transform = CATransform3DMakeRotation(CGFloat(Double.pi), 0.0, 0.0, 1.0) @@ -1115,11 +1126,14 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { if strongSelf.tagMask == nil { var atBottom = false + var offsetFromBottom: CGFloat? switch offset { case let .known(offsetValue): if offsetValue.isLessThanOrEqualTo(0.0) { atBottom = true + offsetFromBottom = offsetValue } + //print("offsetValue: \(offsetValue)") default: break } @@ -1130,6 +1144,8 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { strongSelf.isScrollAtBottomPositionUpdated?() } + + strongSelf.maybeUpdateOverscrollAction(offset: offsetFromBottom) } } } @@ -1150,10 +1166,22 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { self?.isInteractivelyScrollingPromise.set(true) self?.beganDragging?() } + + self.endedInteractiveDragging = { [weak self] in + guard let strongSelf = self else { + return + } + if let channel = strongSelf.nextChannelToRead, strongSelf.currentOverscrollExpandProgress >= 0.99 { + strongSelf.openNextChannelToRead?(channel) + } + } self.didEndScrolling = { [weak self] in - self?.isInteractivelyScrollingValue = false - self?.isInteractivelyScrollingPromise.set(false) + guard let strongSelf = self else { + return + } + strongSelf.isInteractivelyScrollingValue = false + strongSelf.isInteractivelyScrollingPromise.set(false) } let selectionRecognizer = ChatHistoryListSelectionRecognizer(target: self, action: #selector(self.selectionPanGesture(_:))) @@ -1177,6 +1205,74 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { public func setLoadStateUpdated(_ f: @escaping (ChatHistoryNodeLoadState, Bool) -> Void) { self.loadStateUpdated = f } + + private func maybeUpdateOverscrollAction(offset: CGFloat?) { + if let offset = offset, offset < 0.0, self.offerNextChannelToRead { + let overscrollView: ComponentHostView + if let current = self.overscrollView { + overscrollView = current + } else { + overscrollView = ComponentHostView() + overscrollView.layer.sublayerTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) + self.overscrollView = overscrollView + self.view.addSubview(overscrollView) + } + + let expandDistance = max(-offset - 12.0, 0.0) + let expandProgress: CGFloat = min(1.0, expandDistance / 90.0) + + let text: String + if let nextChannelToRead = nextChannelToRead { + if self.nextChannelToReadDisplayName { + if expandProgress >= 0.99 { + //TODO:localize + text = "Release to go to \(nextChannelToRead.compactDisplayTitle)" + } else { + text = "Swipe up to go to \(nextChannelToRead.compactDisplayTitle)" + } + } else { + if expandProgress >= 0.99 { + //TODO:localize + text = "Release to go to the next unread channel" + } else { + text = "Swipe up to go to the next unread channel" + } + } + + let previousType = self.currentOverscrollExpandProgress >= 0.99 + let currentType = expandProgress >= 0.99 + + if previousType != currentType { + if self.feedback == nil { + self.feedback = HapticFeedback() + } + self.feedback?.tap() + } + + self.currentOverscrollExpandProgress = expandProgress + } else { + text = "You have no unread channels" + } + + let overscrollSize = overscrollView.update( + transition: .immediate, + component: AnyComponent(ChatOverscrollControl( + text: text, + backgroundColor: selectDateFillStaticColor(theme: self.currentPresentationData.theme.theme, wallpaper: self.currentPresentationData.theme.wallpaper), + foregroundColor: bubbleVariableColor(variableColor: self.currentPresentationData.theme.theme.chat.serviceMessage.dateTextColor, wallpaper: self.currentPresentationData.theme.wallpaper), + peer: self.nextChannelToRead, + context: self.context, + expandDistance: expandDistance + )), + environment: {}, + containerSize: CGSize(width: self.bounds.width, height: 200.0) + ) + overscrollView.frame = CGRect(origin: CGPoint(x: floor((self.bounds.width - overscrollSize.width) / 2.0), y: -offset + self.insets.top - overscrollSize.height - 10.0), size: overscrollSize) + } else if let overscrollView = self.overscrollView { + self.overscrollView = nil + overscrollView.removeFromSuperview() + } + } func refreshPollActionsForVisibleMessages() { let _ = self.clientId.swap(nextClientId) @@ -2218,6 +2314,13 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } private func handlePanSelection(location: CGPoint) { + var location = location + if location.y < self.insets.top { + location.y = self.insets.top + 5.0 + } else if location.y > self.frame.height - self.insets.bottom { + location.y = self.frame.height - self.insets.bottom - 5.0 + } + if let state = self.selectionPanState { if let messages = self.messagesAtPoint(location), let message = messages.first { if message.id == state.initialMessageId { @@ -2317,4 +2420,76 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } var animationCorrelationMessageFound: ((ChatMessageItemView, Int64?) -> Void)? + + final class SnapshotState { + fileprivate let snapshotTopInset: CGFloat + fileprivate let snapshotBottomInset: CGFloat + fileprivate let snapshotView: UIView + + fileprivate init( + snapshotTopInset: CGFloat, + snapshotBottomInset: CGFloat, + snapshotView: UIView + ) { + self.snapshotTopInset = snapshotTopInset + self.snapshotBottomInset = snapshotBottomInset + self.snapshotView = snapshotView + } + } + + func prepareSnapshotState() -> SnapshotState { + var snapshotTopInset: CGFloat = 0.0 + var snapshotBottomInset: CGFloat = 0.0 + self.forEachItemNode { itemNode in + let topOverflow = itemNode.frame.maxY - self.bounds.height + snapshotTopInset = max(snapshotTopInset, topOverflow) + + if itemNode.frame.minY < 0.0 { + snapshotBottomInset = max(snapshotBottomInset, -itemNode.frame.minY) + } + } + let snapshotView = self.view.snapshotView(afterScreenUpdates: false)! + + let currentSnapshotView = self.view.snapshotView(afterScreenUpdates: false)! + currentSnapshotView.frame = self.view.bounds + if let sublayers = self.layer.sublayers { + for sublayer in sublayers { + sublayer.isHidden = true + } + } + self.view.addSubview(currentSnapshotView) + + return SnapshotState( + snapshotTopInset: snapshotTopInset, + snapshotBottomInset: snapshotBottomInset, + snapshotView: snapshotView + ) + } + + func animateFromSnapshot(_ snapshotState: SnapshotState) { + var snapshotTopInset: CGFloat = 0.0 + var snapshotBottomInset: CGFloat = 0.0 + self.forEachItemNode { itemNode in + let topOverflow = itemNode.frame.maxY - self.bounds.height + snapshotTopInset = max(snapshotTopInset, topOverflow) + + if itemNode.frame.minY < 0.0 { + snapshotBottomInset = max(snapshotBottomInset, -itemNode.frame.minY) + } + } + + let snapshotParentView = UIView() + snapshotParentView.addSubview(snapshotState.snapshotView) + snapshotParentView.layer.sublayerTransform = CATransform3DMakeRotation(CGFloat(Double.pi), 0.0, 0.0, 1.0) + snapshotParentView.frame = self.view.frame + + snapshotState.snapshotView.frame = snapshotParentView.bounds + self.view.superview?.insertSubview(snapshotParentView, belowSubview: self.view) + + snapshotParentView.layer.animatePosition(from: CGPoint(x: 0.0, y: 0.0), to: CGPoint(x: 0.0, y: -self.view.bounds.height - snapshotState.snapshotBottomInset - snapshotTopInset), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true, completion: { [weak snapshotParentView] _ in + snapshotParentView?.removeFromSuperview() + }) + + self.view.layer.animatePosition(from: CGPoint(x: 0.0, y: self.view.bounds.height + snapshotTopInset), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: true, additive: true) + } } diff --git a/submodules/TelegramUI/Sources/ChatHistoryNavigationButtons.swift b/submodules/TelegramUI/Sources/ChatHistoryNavigationButtons.swift index 888f2dcfe1..77538962d5 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryNavigationButtons.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryNavigationButtons.swift @@ -163,4 +163,34 @@ final class ChatHistoryNavigationButtons: ASDisplayNode { } } } + + final class SnapshotState { + fileprivate let downButtonSnapshotView: UIView? + + fileprivate init( + downButtonSnapshotView: UIView? + ) { + self.downButtonSnapshotView = downButtonSnapshotView + } + } + + func prepareSnapshotState() -> SnapshotState { + var downButtonSnapshotView: UIView? + if !self.downButton.isHidden { + downButtonSnapshotView = self.downButton.view.snapshotView(afterScreenUpdates: false)! + } + return SnapshotState( + downButtonSnapshotView: downButtonSnapshotView + ) + } + + func animateFromSnapshot(_ snapshotState: SnapshotState) { + if self.downButton.isHidden != (snapshotState.downButtonSnapshotView == nil) { + if self.downButton.isHidden { + } else { + self.downButton.layer.animateAlpha(from: 0.0, to: self.downButton.alpha, duration: 0.3) + self.downButton.layer.animateScale(from: 0.1, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: true) + } + } + } } diff --git a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift index e8c9e35068..612fb3f74a 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift @@ -149,7 +149,7 @@ func chatHistoryViewForLocation(_ location: ChatHistoryLocationInput, context: A } } } - } else if view.isAddedToChatList, let historyScrollState = (initialData?.chatInterfaceState as? ChatInterfaceState)?.historyScrollState, tagMask == nil { + } else if view.isAddedToChatList, tagMask == nil, let historyScrollState = (initialData?.storedInterfaceState).flatMap(_internal_decodeStoredChatInterfaceState).flatMap(ChatInterfaceState.parse)?.historyScrollState { scrollPosition = .positionRestoration(index: historyScrollState.messageIndex, relativeOffset: CGFloat(historyScrollState.relativeOffset)) } else { if case .peer = chatLocation, !view.isAddedToChatList { diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index eb11078a3f..fde14f3b61 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -1059,6 +1059,7 @@ func chatAvailableMessageActionsImpl(postbox: Postbox, accountPeerId: PeerId, me var banPeer: Peer? var hadPersonalIncoming = false var hadBanPeerId = false + var disableDelete = false func getPeer(_ peerId: PeerId) -> Peer? { if let peer = transaction.getPeer(peerId) { @@ -1229,6 +1230,15 @@ func chatAvailableMessageActionsImpl(postbox: Postbox, accountPeerId: PeerId, me if canDeleteGlobally { optionsMap[id]!.insert(.deleteGlobally) } + for media in message.media { + if let action = media as? TelegramMediaAction { + if case .historyScreenshot = action.action { + optionsMap[id]!.remove(.deleteLocally) + optionsMap[id]!.remove(.deleteGlobally) + disableDelete = true + } + } + } if user.botInfo != nil && message.flags.contains(.Incoming) && !user.id.isReplies && !isAction { optionsMap[id]!.insert(.report) } @@ -1239,6 +1249,7 @@ func chatAvailableMessageActionsImpl(postbox: Postbox, accountPeerId: PeerId, me switch action.action { case .historyScreenshot: isNonRemovableServiceAction = true + disableDelete = true default: break } @@ -1265,9 +1276,9 @@ func chatAvailableMessageActionsImpl(postbox: Postbox, accountPeerId: PeerId, me if hadPersonalIncoming && optionsMap.values.contains(where: { $0.contains(.deleteGlobally) }) && !reducedOptions.contains(.deleteGlobally) { reducedOptions.insert(.unsendPersonal) } - return ChatAvailableMessageActions(options: reducedOptions, banAuthor: banPeer) + return ChatAvailableMessageActions(options: reducedOptions, banAuthor: banPeer, disableDelete: disableDelete) } else { - return ChatAvailableMessageActions(options: [], banAuthor: nil) + return ChatAvailableMessageActions(options: [], banAuthor: nil, disableDelete: false) } } } diff --git a/submodules/TelegramUI/Sources/ChatMediaInputGifPane.swift b/submodules/TelegramUI/Sources/ChatMediaInputGifPane.swift index 0b158eb7b6..00e647e07f 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputGifPane.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputGifPane.swift @@ -246,7 +246,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate { } multiplexedNode.didScroll = { [weak self] offset, height in - guard let strongSelf = self else { + guard let strongSelf = self, let multiplexedNode = strongSelf.multiplexedNode else { return } let absoluteOffset = -offset + 60.0 diff --git a/submodules/TelegramUI/Sources/ChatMediaInputNode.swift b/submodules/TelegramUI/Sources/ChatMediaInputNode.swift index 243f33012b..c20077f39a 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputNode.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputNode.swift @@ -761,16 +761,20 @@ final class ChatMediaInputNode: ChatInputNode { let inputNodeInteraction = self.inputNodeInteraction! let peerSpecificPack: Signal<(PeerSpecificPackData?, CanInstallPeerSpecificPack), NoError> if let peerId = peerId { - self.dismissedPeerSpecificStickerPack.set(context.account.postbox.transaction { transaction -> Bool in - guard let state = transaction.getPeerChatInterfaceState(peerId) as? ChatInterfaceState else { + self.dismissedPeerSpecificStickerPack.set( + context.engine.peers.getOpaqueChatInterfaceState(peerId: peerId, threadId: nil) + |> map { opaqueState -> Bool in + guard let opaqueState = opaqueState else { + return false + } + let interfaceState = ChatInterfaceState.parse(opaqueState) + + if interfaceState.messageActionsState.closedPeerSpecificPackSetup { + return true + } return false } - if state.messageActionsState.closedPeerSpecificPackSetup { - return true - } - - return false - }) + ) peerSpecificPack = combineLatest(context.engine.peers.peerSpecificStickerPack(peerId: peerId), context.account.postbox.multiplePeersView([peerId]), self.dismissedPeerSpecificStickerPack.get()) |> map { packData, peersView, dismissedPeerSpecificPack -> (PeerSpecificPackData?, CanInstallPeerSpecificPack) in if let peer = peersView.peers[peerId] { @@ -2094,17 +2098,11 @@ final class ChatMediaInputNode: ChatInputNode { return } self.dismissedPeerSpecificStickerPack.set(.single(true)) - let _ = (self.context.account.postbox.transaction { transaction -> Void in - transaction.updatePeerChatInterfaceState(peerId, update: { current in - if let current = current as? ChatInterfaceState { - return current.withUpdatedMessageActionsState({ value in - var value = value - value.closedPeerSpecificPackSetup = true - return value - }) - } else { - return current - } + let _ = ChatInterfaceState.update(engine: self.context.engine, peerId: peerId, threadId: nil, { current in + return current.withUpdatedMessageActionsState({ value in + var value = value + value.closedPeerSpecificPackSetup = true + return value }) }).start() } diff --git a/submodules/TelegramUI/Sources/ChatMediaInputPeerSpecificItem.swift b/submodules/TelegramUI/Sources/ChatMediaInputPeerSpecificItem.swift index 1e588c200b..37b192bbfa 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputPeerSpecificItem.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputPeerSpecificItem.swift @@ -150,7 +150,7 @@ final class ChatMediaInputPeerSpecificItemNode: ListViewItemNode { self.avatarNode.frame = self.avatarNode.frame expandTransition.updateFrame(node: self.highlightNode, frame: expanded ? titleFrame.insetBy(dx: -7.0, dy: -2.0) : CGRect(origin: CGPoint(x: self.avatarNode.position.x - highlightSize.width / 2.0, y: self.avatarNode.position.y - highlightSize.height / 2.0), size: highlightSize)) - self.avatarNode.setPeer(context: context, theme: theme, peer: peer) + self.avatarNode.setPeer(context: context, theme: theme, peer: EnginePeer(peer)) } func updateIsHighlighted() { diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index 6cb1ee9204..0354afcac3 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -1080,7 +1080,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { let viaBotNode = viaBotApply() if strongSelf.viaBotNode == nil { strongSelf.viaBotNode = viaBotNode - strongSelf.addSubnode(viaBotNode) + strongSelf.contextSourceNode.contentNode.addSubnode(viaBotNode) } let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 15.0) : (params.width - params.rightInset - viaBotLayout.size.width - layoutConstants.bubble.edgeInset - 14.0)), y: 8.0), size: viaBotLayout.size) viaBotNode.frame = viaBotFrame @@ -1161,7 +1161,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } else { let forwardBackgroundNode = NavigationBackgroundNode(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper)) strongSelf.forwardBackgroundNode = forwardBackgroundNode - strongSelf.addSubnode(forwardBackgroundNode) + strongSelf.contextSourceNode.contentNode.addSubnode(forwardBackgroundNode) } } @@ -1169,7 +1169,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { let forwardInfoNode = forwardInfoApply(forwardInfoSize.width) if strongSelf.forwardInfoNode == nil { strongSelf.forwardInfoNode = forwardInfoNode - strongSelf.addSubnode(forwardInfoNode) + strongSelf.contextSourceNode.contentNode.addSubnode(forwardInfoNode) } let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 12.0) : (params.width - params.rightInset - forwardInfoSize.width - layoutConstants.bubble.edgeInset - 12.0)), y: 8.0), size: forwardInfoSize) forwardInfoNode.frame = forwardInfoFrame diff --git a/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift index d1b73fc1a4..841078d66d 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift @@ -786,6 +786,8 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { var imageHeightAddition = size.height + 6.0 if textFrame.size.height > CGFloat.ulpOfOne { imageHeightAddition += 6.0 + } else { + imageHeightAddition += 7.0 } adjustedBoundingSize.height += imageHeightAddition + 5.0 @@ -931,7 +933,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { if let (_, flags) = mediaAndFlags, flags.contains(.preferMediaBeforeText) { contentFileNode.frame = CGRect(origin: CGPoint(x: insets.left, y: insets.top), size: contentFileSize) } else { - contentFileNode.frame = CGRect(origin: CGPoint(x: insets.left, y: textFrame.maxY + (textFrame.size.height > CGFloat.ulpOfOne ? 8.0 : 0.0)), size: contentFileSize) + contentFileNode.frame = CGRect(origin: CGPoint(x: insets.left, y: textFrame.maxY + (textFrame.size.height > CGFloat.ulpOfOne ? 8.0 : 7.0)), size: contentFileSize) } } else if let contentFileNode = strongSelf.contentFileNode { contentFileNode.removeFromSupernode() diff --git a/submodules/TelegramUI/Sources/ChatMessageAvatarAccessoryItem.swift b/submodules/TelegramUI/Sources/ChatMessageAvatarAccessoryItem.swift index 010ad7b8df..bf81f9ace3 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAvatarAccessoryItem.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAvatarAccessoryItem.swift @@ -178,6 +178,6 @@ final class ChatMessageAvatarAccessoryItemNode: ListViewAccessoryItemNode { if peer.isDeleted { overrideImage = .deletedIcon } - self.avatarNode.setPeer(context: context, theme: theme, peer: peer, authorOfMessage: authorOfMessage, overrideImage: overrideImage, emptyColor: emptyColor, synchronousLoad: synchronousLoad, displayDimensions: CGSize(width: 38.0, height: 38.0)) + self.avatarNode.setPeer(context: context, theme: theme, peer: EnginePeer(peer), authorOfMessage: authorOfMessage, overrideImage: overrideImage, emptyColor: emptyColor, synchronousLoad: synchronousLoad, displayDimensions: CGSize(width: 38.0, height: 38.0)) } } diff --git a/submodules/TelegramUI/Sources/ChatMessageCommentFooterContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageCommentFooterContentNode.swift index 973dbf4a56..d14942f98c 100644 --- a/submodules/TelegramUI/Sources/ChatMessageCommentFooterContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageCommentFooterContentNode.swift @@ -353,7 +353,7 @@ final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContentNode { } } - let avatarContent = strongSelf.avatarsContext.update(peers: replyPeers, animated: animation.isAnimated) + let avatarContent = strongSelf.avatarsContext.update(peers: replyPeers.map(EnginePeer.init), animated: animation.isAnimated) let avatarsSize = strongSelf.avatarsNode.update(context: item.context, content: avatarContent, animated: animation.isAnimated, synchronousLoad: synchronousLoad) let iconAlpha: CGFloat = avatarsSize.width.isZero ? 1.0 : 0.0 diff --git a/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift index 2b8b6c441f..f9cc25ac81 100644 --- a/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift @@ -322,7 +322,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { } if let peerId = selectedContact?.peerId, let peer = item.message.peers[peerId] { - strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme.theme, peer: peer, emptyColor: avatarPlaceholderColor, synchronousLoad: synchronousLoads) + strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme.theme, peer: EnginePeer(peer), emptyColor: avatarPlaceholderColor, synchronousLoad: synchronousLoads) } else { strongSelf.avatarNode.setCustomLetters(customLetters) } diff --git a/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift b/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift index afd4e1a69b..ec201e8fa3 100644 --- a/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift +++ b/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift @@ -437,7 +437,7 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode { if peer.isDeleted { overrideImage = .deletedIcon } - self.avatarNode.setPeer(context: context, theme: theme, peer: peer, authorOfMessage: authorOfMessage, overrideImage: overrideImage, emptyColor: emptyColor, synchronousLoad: synchronousLoad, displayDimensions: CGSize(width: 38.0, height: 38.0)) + self.avatarNode.setPeer(context: context, theme: theme, peer: EnginePeer(peer), authorOfMessage: authorOfMessage, overrideImage: overrideImage, emptyColor: emptyColor, synchronousLoad: synchronousLoad, displayDimensions: CGSize(width: 38.0, height: 38.0)) } override func didLoad() { diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift index e28ddf8f18..69be62c976 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift @@ -43,11 +43,11 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD } private var forwardInfoNode: ChatMessageForwardInfoNode? - private var forwardBackgroundNode: ASImageNode? + private var forwardBackgroundNode: NavigationBackgroundNode? private var viaBotNode: TextNode? private var replyInfoNode: ChatMessageReplyInfoNode? - private var replyBackgroundNode: ASImageNode? + private var replyBackgroundNode: NavigationBackgroundNode? private var actionButtonsNode: ChatMessageActionButtonsNode? @@ -70,6 +70,8 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD } } + private var wasPlaying = false + required init() { self.contextSourceNode = ContextExtractedContentContainingNode() self.containerNode = ContextControllerSourceNode() @@ -97,7 +99,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD return false } if strongSelf.appliedCurrentlyPlaying && !strongSelf.interactiveVideoNode.isPlaying { - return false + return strongSelf.interactiveVideoNode.frame.insetBy(dx: 0.15 * strongSelf.interactiveVideoNode.frame.width, dy: 0.15 * strongSelf.interactiveVideoNode.frame.height).contains(location) } if let action = strongSelf.gestureRecognized(gesture: .tap, location: location, recognizer: nil) { if case .action = action { @@ -126,10 +128,24 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD case let .openContextMenu(tapMessage, selectAll, subFrame): strongSelf.recognizer?.cancel() item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, gesture) + if strongSelf.appliedCurrentlyPlaying && strongSelf.interactiveVideoNode.isPlaying { + strongSelf.wasPlaying = true + strongSelf.interactiveVideoNode.pause() + } } } } + self.contextSourceNode.willUpdateIsExtractedToContextPreview = { [weak self] extracted, _ in + guard let strongSelf = self, let _ = strongSelf.item else { + return + } + if !extracted && strongSelf.wasPlaying { + strongSelf.wasPlaying = false + strongSelf.interactiveVideoNode.play() + } + } + self.containerNode.addSubnode(self.contextSourceNode) self.containerNode.targetNodeForActivationProgress = self.contextSourceNode.contentNode self.addSubnode(self.containerNode) @@ -373,11 +389,11 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD var viaBotApply: (TextNodeLayout, () -> TextNode)? var replyInfoApply: (CGSize, () -> ChatMessageReplyInfoNode)? - var updatedReplyBackgroundNode: ASImageNode? + var updatedReplyBackgroundNode: NavigationBackgroundNode? var replyBackgroundImage: UIImage? var replyMarkup: ReplyMarkupMessageAttribute? - let availableWidth = max(60.0, params.width - params.leftInset - params.rightInset - videoLayout.contentSize.width - 20.0 - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left) + let availableWidth = max(60.0, params.width - params.leftInset - params.rightInset - normalDisplaySize.width - 20.0 - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left) var ignoreForward = false var ignoreSource = false @@ -416,6 +432,8 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD let botString = addAttributesToStringWithRanges(item.presentationData.strings.Conversation_MessageViaUser("@\(inlineBotNameString)")._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes]) viaBotApply = viaBotLayout(TextNodeLayoutArguments(attributedString: botString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(0, availableWidth), height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + + ignoreForward = true } } @@ -448,11 +466,10 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD if let currentReplyBackgroundNode = currentReplyBackgroundNode { updatedReplyBackgroundNode = currentReplyBackgroundNode } else { - updatedReplyBackgroundNode = ASImageNode() + updatedReplyBackgroundNode = NavigationBackgroundNode(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper)) } - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) - replyBackgroundImage = graphics.chatFreeformContentAdditionalInfoBackgroundImage + updatedReplyBackgroundNode?.updateColor(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), transition: .immediate) } var updatedShareButtonNode: ChatMessageShareButton? @@ -471,7 +488,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD var forwardAuthorSignature: String? var forwardInfoSizeApply: (CGSize, (CGFloat) -> ChatMessageForwardInfoNode)? - var updatedForwardBackgroundNode: ASImageNode? + var updatedForwardBackgroundNode: NavigationBackgroundNode? var forwardBackgroundImage: UIImage? if !ignoreForward, let forwardInfo = item.message.forwardInfo { @@ -495,17 +512,16 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD forwardAuthorSignature = forwardInfo.authorSignature } } - let availableWidth = max(60.0, availableContentWidth - videoLayout.contentSize.width + 6.0) + let availableWidth = max(60.0, availableContentWidth - normalDisplaySize.width + 6.0) forwardInfoSizeApply = makeForwardInfoLayout(item.presentationData, item.presentationData.strings, .standalone, forwardSource, forwardAuthorSignature, forwardPsaType, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) if let currentForwardBackgroundNode = currentForwardBackgroundNode { updatedForwardBackgroundNode = currentForwardBackgroundNode } else { - updatedForwardBackgroundNode = ASImageNode() + updatedForwardBackgroundNode = NavigationBackgroundNode(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper)) } - let graphics = PresentationResourcesChat.additionalGraphics(item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) - forwardBackgroundImage = graphics.chatServiceBubbleFillImage + updatedForwardBackgroundNode?.updateColor(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), transition: .immediate) } var maxContentWidth = normalDisplaySize.width @@ -597,10 +613,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD if let updatedReplyBackgroundNode = updatedReplyBackgroundNode { if strongSelf.replyBackgroundNode == nil { strongSelf.replyBackgroundNode = updatedReplyBackgroundNode - strongSelf.addSubnode(updatedReplyBackgroundNode) - updatedReplyBackgroundNode.image = replyBackgroundImage - } else { - strongSelf.replyBackgroundNode?.image = replyBackgroundImage + strongSelf.contextSourceNode.contentNode.addSubnode(updatedReplyBackgroundNode) } } else if let replyBackgroundNode = strongSelf.replyBackgroundNode { replyBackgroundNode.removeFromSupernode() @@ -611,11 +624,13 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD let viaBotNode = viaBotApply() if strongSelf.viaBotNode == nil { strongSelf.viaBotNode = viaBotNode - strongSelf.addSubnode(viaBotNode) + strongSelf.contextSourceNode.contentNode.addSubnode(viaBotNode) } let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - viaBotLayout.size.width - layoutConstants.bubble.edgeInset - 10.0)), y: 8.0), size: viaBotLayout.size) viaBotNode.frame = viaBotFrame - strongSelf.replyBackgroundNode?.frame = CGRect(origin: CGPoint(x: viaBotFrame.minX - 4.0, y: viaBotFrame.minY - 2.0), size: CGSize(width: viaBotFrame.size.width + 8.0, height: viaBotFrame.size.height + 5.0)) + let replyBackgroundFrame = CGRect(origin: CGPoint(x: viaBotFrame.minX - 4.0, y: viaBotFrame.minY - 2.0), size: CGSize(width: viaBotFrame.size.width + 8.0, height: viaBotFrame.size.height + 5.0)) + strongSelf.replyBackgroundNode?.frame = replyBackgroundFrame + strongSelf.replyBackgroundNode?.update(size: replyBackgroundFrame.size, cornerRadius: 8.0, transition: .immediate) } else if let viaBotNode = strongSelf.viaBotNode { viaBotNode.removeFromSupernode() strongSelf.viaBotNode = nil @@ -625,7 +640,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD let replyInfoNode = replyInfoApply() if strongSelf.replyInfoNode == nil { strongSelf.replyInfoNode = replyInfoNode - strongSelf.addSubnode(replyInfoNode) + strongSelf.contextSourceNode.contentNode.addSubnode(replyInfoNode) } var viaBotSize = CGSize() if let viaBotNode = strongSelf.viaBotNode { @@ -638,7 +653,9 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD } } replyInfoNode.frame = replyInfoFrame - strongSelf.replyBackgroundNode?.frame = CGRect(origin: CGPoint(x: replyInfoFrame.minX - 4.0, y: replyInfoFrame.minY - viaBotSize.height - 2.0), size: CGSize(width: max(replyInfoFrame.size.width, viaBotSize.width) + 8.0, height: replyInfoFrame.size.height + viaBotSize.height + 5.0)) + let replyBackgroundFrame = CGRect(origin: CGPoint(x: replyInfoFrame.minX - 4.0, y: replyInfoFrame.minY - viaBotSize.height - 2.0), size: CGSize(width: max(replyInfoFrame.size.width, viaBotSize.width) + 8.0, height: replyInfoFrame.size.height + viaBotSize.height + 5.0)) + strongSelf.replyBackgroundNode?.frame = replyBackgroundFrame + strongSelf.replyBackgroundNode?.update(size: replyBackgroundFrame.size, cornerRadius: 8.0, transition: .immediate) } else if let replyInfoNode = strongSelf.replyInfoNode { replyInfoNode.removeFromSupernode() strongSelf.replyInfoNode = nil @@ -678,8 +695,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD if let updatedForwardBackgroundNode = updatedForwardBackgroundNode { if strongSelf.forwardBackgroundNode == nil { strongSelf.forwardBackgroundNode = updatedForwardBackgroundNode - strongSelf.addSubnode(updatedForwardBackgroundNode) - updatedForwardBackgroundNode.image = forwardBackgroundImage + strongSelf.contextSourceNode.contentNode.addSubnode(updatedForwardBackgroundNode) } } else if let forwardBackgroundNode = strongSelf.forwardBackgroundNode { forwardBackgroundNode.removeFromSupernode() @@ -690,7 +706,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD let forwardInfoNode = forwardInfoApply(forwardInfoSize.width) if strongSelf.forwardInfoNode == nil { strongSelf.forwardInfoNode = forwardInfoNode - strongSelf.addSubnode(forwardInfoNode) + strongSelf.contextSourceNode.contentNode.addSubnode(forwardInfoNode) forwardInfoNode.openPsa = { [weak strongSelf] type, sourceNode in guard let strongSelf = strongSelf, let item = strongSelf.item else { return @@ -700,7 +716,9 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD } let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 12.0) : (params.width - params.rightInset - forwardInfoSize.width - layoutConstants.bubble.edgeInset - 12.0)), y: 8.0), size: forwardInfoSize) forwardInfoNode.frame = forwardInfoFrame - strongSelf.forwardBackgroundNode?.frame = CGRect(origin: CGPoint(x: forwardInfoFrame.minX - 6.0, y: forwardInfoFrame.minY - 2.0), size: CGSize(width: forwardInfoFrame.size.width + 10.0, height: forwardInfoFrame.size.height + 4.0)) + let forwardBackgroundFrame = CGRect(origin: CGPoint(x: forwardInfoFrame.minX - 6.0, y: forwardInfoFrame.minY - 2.0), size: CGSize(width: forwardInfoFrame.size.width + 10.0, height: forwardInfoFrame.size.height + 4.0)) + strongSelf.forwardBackgroundNode?.frame = forwardBackgroundFrame + strongSelf.forwardBackgroundNode?.update(size: forwardBackgroundFrame.size, cornerRadius: 8.0, transition: .immediate) } else if let forwardInfoNode = strongSelf.forwardInfoNode { forwardInfoNode.removeFromSupernode() strongSelf.forwardInfoNode = nil diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift index a8a6218ba7..0ff7f5f6b9 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -199,6 +199,9 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { break } } + if item.message.id.namespace == Namespaces.Message.Local { + notConsumed = true + } var updatedPlaybackStatus: Signal? if let updatedFile = updatedFile, updatedMedia || updatedMessageId { @@ -774,9 +777,9 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { if !self.bounds.contains(point) { return nil } - if let playbackNode = self.playbackStatusNode, !self.isPlaying, !playbackNode.frame.insetBy(dx: 0.15 * playbackNode.frame.width, dy: 0.15 * playbackNode.frame.height).contains(point) { + if let playbackNode = self.playbackStatusNode, !self.isPlaying, !playbackNode.frame.insetBy(dx: 0.2 * playbackNode.frame.width, dy: 0.2 * playbackNode.frame.height).contains(point) { let distanceFromCenter = point.distanceTo(playbackNode.position) - if distanceFromCenter < 0.15 * playbackNode.frame.width { + if distanceFromCenter < 0.2 * playbackNode.frame.width { return self.view } else { return playbackNode.view @@ -917,6 +920,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } } + private var animatedFadeIn = false func animateFromSnapshot(snapshotView: UIView, transition: CombinedTransition) { guard let videoFrame = self.videoFrame else { return @@ -934,9 +938,12 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { transition.horizontal.animateTransformScale(node: self, from: 1.0 / scale) - self.dateAndStatusNode.layer.animateAlpha(from: 0.0, to: self.dateAndStatusNode.alpha, duration: 0.15, delay: 0.18) - if let durationNode = self.durationNode { - durationNode.layer.animateAlpha(from: 0.0, to: durationNode.alpha, duration: 0.15, delay: 0.18) + if !self.animatedFadeIn { + self.animatedFadeIn = true + self.dateAndStatusNode.layer.animateAlpha(from: 0.0, to: self.dateAndStatusNode.alpha, duration: 0.15, delay: 0.18) + if let durationNode = self.durationNode { + durationNode.layer.animateAlpha(from: 0.0, to: durationNode.alpha, duration: 0.15, delay: 0.18) + } } } } diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift index ee678fd115..3a65218147 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift @@ -899,6 +899,10 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio videoNode.ownsContentNodeUpdated = { [weak self] owns in if let strongSelf = self { strongSelf.videoNode?.isHidden = !owns + if owns { + strongSelf.videoNode?.setBaseRate(1.0) + strongSelf.videoNode?.continuePlayingWithoutSound() + } } } strongSelf.videoContent = videoContent diff --git a/submodules/TelegramUI/Sources/ChatMessageItemView.swift b/submodules/TelegramUI/Sources/ChatMessageItemView.swift index ae3870994d..2ae84cdfd3 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItemView.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItemView.swift @@ -879,6 +879,11 @@ public class ChatMessageItemView: ListViewItemNode { override public func attachedHeaderNodesUpdated() { self.updateAttachedAvatarNodeOffset(offset: self.attachedAvatarNodeOffset, transition: .immediate) + for headerNode in self.attachedHeaderNodes { + if let headerNode = headerNode as? ChatMessageAvatarHeaderNode { + headerNode.updateSelectionState(animated: false) + } + } } func updateAttachedAvatarNodeOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) { diff --git a/submodules/TelegramUI/Sources/ChatMessageMapBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageMapBubbleContentNode.swift index fc60e62bdf..74c1f9cd4d 100644 --- a/submodules/TelegramUI/Sources/ChatMessageMapBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageMapBubbleContentNode.swift @@ -151,7 +151,7 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode { var mode: ChatMessageLiveLocationPositionNode.Mode = .location(selectedMedia) if let selectedMedia = selectedMedia, let peer = item.message.author { if selectedMedia.liveBroadcastingTimeout != nil { - mode = .liveLocation(peer: peer, active: activeLiveBroadcastingTimeout != nil, latitude: selectedMedia.latitude, longitude: selectedMedia.longitude, heading: selectedMedia.heading) + mode = .liveLocation(peer: EnginePeer(peer), active: activeLiveBroadcastingTimeout != nil, latitude: selectedMedia.latitude, longitude: selectedMedia.longitude, heading: selectedMedia.heading) } } let (pinSize, pinApply) = makePinLayout(item.context, item.presentationData.theme.theme, mode) diff --git a/submodules/TelegramUI/Sources/ChatMessageNotificationItem.swift b/submodules/TelegramUI/Sources/ChatMessageNotificationItem.swift index f02214c67d..5a4117b696 100644 --- a/submodules/TelegramUI/Sources/ChatMessageNotificationItem.swift +++ b/submodules/TelegramUI/Sources/ChatMessageNotificationItem.swift @@ -150,7 +150,7 @@ final class ChatMessageNotificationItemNode: NotificationItemNode { if firstMessage.id.peerId.isReplies, let author = firstMessage.forwardInfo?.author { avatarPeer = author } - self.avatarNode.setPeer(context: item.context, theme: presentationData.theme, peer: avatarPeer, overrideImage: peer.id == item.context.account.peerId ? .savedMessagesIcon : nil, emptyColor: presentationData.theme.list.mediaPlaceholderColor) + self.avatarNode.setPeer(context: item.context, theme: presentationData.theme, peer: EnginePeer(avatarPeer), overrideImage: peer.id == item.context.account.peerId ? .savedMessagesIcon : nil, emptyColor: presentationData.theme.list.mediaPlaceholderColor) } var updatedMedia: Media? diff --git a/submodules/TelegramUI/Sources/ChatMessageSelectionInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatMessageSelectionInputPanelNode.swift index 48dc8bb01d..34fdd0cbb6 100644 --- a/submodules/TelegramUI/Sources/ChatMessageSelectionInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageSelectionInputPanelNode.swift @@ -154,7 +154,7 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { if self.peerMedia { self.deleteButton.isEnabled = !actions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty } else { - self.deleteButton.isEnabled = true + self.deleteButton.isEnabled = !actions.disableDelete } self.shareButton.isEnabled = !actions.options.intersection([.forward]).isEmpty self.reportButton.isEnabled = !actions.options.intersection([.report]).isEmpty diff --git a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift index 35aa60b1d2..40b90c78cc 100644 --- a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift @@ -719,7 +719,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { let viaBotNode = viaBotApply() if strongSelf.viaBotNode == nil { strongSelf.viaBotNode = viaBotNode - strongSelf.addSubnode(viaBotNode) + strongSelf.contextSourceNode.contentNode.addSubnode(viaBotNode) } viaBotNode.frame = viaBotFrame if let replyBackgroundNode = strongSelf.replyBackgroundNode { diff --git a/submodules/TelegramUI/Sources/ChatOverscrollControl.swift b/submodules/TelegramUI/Sources/ChatOverscrollControl.swift new file mode 100644 index 0000000000..99ba5e4da8 --- /dev/null +++ b/submodules/TelegramUI/Sources/ChatOverscrollControl.swift @@ -0,0 +1,537 @@ +import UIKit +import ComponentFlow +import Display +import TelegramCore +import Postbox +import AccountContext +import AvatarNode + +final class BlurredRoundedRectangle: Component { + let color: UIColor + + init(color: UIColor) { + self.color = color + } + + static func ==(lhs: BlurredRoundedRectangle, rhs: BlurredRoundedRectangle) -> Bool { + if !lhs.color.isEqual(rhs.color) { + return false + } + return true + } + + final class View: UIView { + private let background: NavigationBackgroundNode + + init() { + self.background = NavigationBackgroundNode(color: .clear) + + super.init(frame: CGRect()) + + self.addSubview(self.background.view) + } + + required init?(coder aDecoder: NSCoder) { + preconditionFailure() + } + + func update(component: BlurredRoundedRectangle, availableSize: CGSize, transition: Transition) -> CGSize { + transition.setFrame(view: self.background.view, frame: CGRect(origin: CGPoint(), size: availableSize)) + self.background.updateColor(color: component.color, transition: .immediate) + self.background.update(size: availableSize, cornerRadius: min(availableSize.width, availableSize.height) / 2.0, transition: .immediate) + + return availableSize + } + } + + func makeView() -> View { + return View() + } + + func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, transition: transition) + } +} + +final class RadialProgressComponent: Component { + let color: UIColor + let lineWidth: CGFloat + let value: CGFloat + + init( + color: UIColor, + lineWidth: CGFloat, + value: CGFloat + ) { + self.color = color + self.lineWidth = lineWidth + self.value = value + } + + static func ==(lhs: RadialProgressComponent, rhs: RadialProgressComponent) -> Bool { + if !lhs.color.isEqual(rhs.color) { + return false + } + if lhs.lineWidth != rhs.lineWidth { + return false + } + if lhs.value != rhs.value { + return false + } + return true + } + + final class View: UIView { + init() { + super.init(frame: CGRect()) + } + + required init?(coder aDecoder: NSCoder) { + preconditionFailure() + } + + func update(component: RadialProgressComponent, availableSize: CGSize, transition: Transition) -> CGSize { + func draw(context: CGContext) { + let diameter = availableSize.width + + context.saveGState() + + context.setBlendMode(.normal) + context.setFillColor(component.color.cgColor) + context.setStrokeColor(component.color.cgColor) + + var progress: CGFloat + var startAngle: CGFloat + var endAngle: CGFloat + + let value = component.value + + progress = value + startAngle = -CGFloat.pi / 2.0 + endAngle = CGFloat(progress) * 2.0 * CGFloat.pi + startAngle + + if progress > 1.0 { + progress = 2.0 - progress + let tmp = startAngle + startAngle = endAngle + endAngle = tmp + } + progress = min(1.0, progress) + + let lineWidth: CGFloat = component.lineWidth + + let pathDiameter: CGFloat + + pathDiameter = diameter - lineWidth + + var angle: Double = 0.0 + angle *= 4.0 + + context.translateBy(x: diameter / 2.0, y: diameter / 2.0) + context.rotate(by: CGFloat(angle.truncatingRemainder(dividingBy: Double.pi * 2.0))) + context.translateBy(x: -diameter / 2.0, y: -diameter / 2.0) + + let path = UIBezierPath(arcCenter: CGPoint(x: diameter / 2.0, y: diameter / 2.0), radius: pathDiameter / 2.0, startAngle: startAngle, endAngle: endAngle, clockwise: true) + path.lineWidth = lineWidth + path.lineCapStyle = .round + path.stroke() + + context.restoreGState() + } + + if #available(iOS 10.0, *) { + let renderer = UIGraphicsImageRenderer(bounds: CGRect(origin: CGPoint(), size: availableSize)) + let image = renderer.image { context in + UIGraphicsPushContext(context.cgContext) + draw(context: context.cgContext) + UIGraphicsPopContext() + } + self.layer.contents = image.cgImage + } else { + UIGraphicsBeginImageContextWithOptions(availableSize, false, 0.0) + draw(context: UIGraphicsGetCurrentContext()!) + self.layer.contents = UIGraphicsGetImageFromCurrentImageContext()?.cgImage + UIGraphicsEndImageContext() + } + + return availableSize + } + } + + func makeView() -> View { + return View() + } + + func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, transition: transition) + } +} + +final class CheckComponent: Component { + let color: UIColor + let lineWidth: CGFloat + let value: CGFloat + + init( + color: UIColor, + lineWidth: CGFloat, + value: CGFloat + ) { + self.color = color + self.lineWidth = lineWidth + self.value = value + } + + static func ==(lhs: CheckComponent, rhs: CheckComponent) -> Bool { + if !lhs.color.isEqual(rhs.color) { + return false + } + if lhs.lineWidth != rhs.lineWidth { + return false + } + if lhs.value != rhs.value { + return false + } + return true + } + + final class View: UIView { + init() { + super.init(frame: CGRect()) + } + + required init?(coder aDecoder: NSCoder) { + preconditionFailure() + } + + func update(component: CheckComponent, availableSize: CGSize, transition: Transition) -> CGSize { + func draw(context: CGContext) { + let size = availableSize + + let diameter = size.width + + let factor = diameter / 50.0 + + context.saveGState() + + context.setBlendMode(.normal) + context.setFillColor(component.color.cgColor) + context.setStrokeColor(component.color.cgColor) + + let center = CGPoint(x: diameter / 2.0, y: diameter / 2.0) + + let lineWidth = component.lineWidth + + context.setLineWidth(max(1.7, lineWidth * factor)) + context.setLineCap(.round) + context.setLineJoin(.round) + context.setMiterLimit(10.0) + + let progress = component.value + let firstSegment: CGFloat = max(0.0, min(1.0, progress * 3.0)) + + var s = CGPoint(x: center.x - 10.0 * factor, y: center.y + 1.0 * factor) + var p1 = CGPoint(x: 7.0 * factor, y: 7.0 * factor) + var p2 = CGPoint(x: 13.0 * factor, y: -15.0 * factor) + + if diameter < 36.0 { + s = CGPoint(x: center.x - 7.0 * factor, y: center.y + 1.0 * factor) + p1 = CGPoint(x: 4.5 * factor, y: 4.5 * factor) + p2 = CGPoint(x: 10.0 * factor, y: -11.0 * factor) + } + + if !firstSegment.isZero { + if firstSegment < 1.0 { + context.move(to: CGPoint(x: s.x + p1.x * firstSegment, y: s.y + p1.y * firstSegment)) + context.addLine(to: s) + } else { + let secondSegment = (progress - 0.33) * 1.5 + context.move(to: CGPoint(x: s.x + p1.x + p2.x * secondSegment, y: s.y + p1.y + p2.y * secondSegment)) + context.addLine(to: CGPoint(x: s.x + p1.x, y: s.y + p1.y)) + context.addLine(to: s) + } + } + context.strokePath() + + context.restoreGState() + } + + if #available(iOS 10.0, *) { + let renderer = UIGraphicsImageRenderer(bounds: CGRect(origin: CGPoint(), size: availableSize)) + let image = renderer.image { context in + UIGraphicsPushContext(context.cgContext) + draw(context: context.cgContext) + UIGraphicsPopContext() + } + self.layer.contents = image.cgImage + } else { + UIGraphicsBeginImageContextWithOptions(availableSize, false, 0.0) + draw(context: UIGraphicsGetCurrentContext()!) + self.layer.contents = UIGraphicsGetImageFromCurrentImageContext()?.cgImage + UIGraphicsEndImageContext() + } + + return availableSize + } + } + + func makeView() -> View { + return View() + } + + func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, transition: transition) + } +} + +final class AvatarComponent: Component { + let context: AccountContext + let peer: EnginePeer + + init(context: AccountContext, peer: EnginePeer) { + self.context = context + self.peer = peer + } + + static func ==(lhs: AvatarComponent, rhs: AvatarComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.peer != rhs.peer { + return false + } + return true + } + + final class View: UIView { + private let avatarNode: AvatarNode + + init() { + self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0)) + + super.init(frame: CGRect()) + + self.addSubview(self.avatarNode.view) + } + + required init?(coder aDecoder: NSCoder) { + preconditionFailure() + } + + func update(component: AvatarComponent, availableSize: CGSize, transition: Transition) -> CGSize { + self.avatarNode.frame = CGRect(origin: CGPoint(), size: availableSize) + self.avatarNode.setPeer(context: component.context, theme: component.context.sharedContext.currentPresentationData.with({ $0 }).theme, peer: component.peer, synchronousLoad: true) + + return availableSize + } + } + + func makeView() -> View { + return View() + } + + func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, transition: transition) + } +} + +final class ChatOverscrollControl: CombinedComponent { + let text: String + let backgroundColor: UIColor + let foregroundColor: UIColor + let peer: EnginePeer? + let context: AccountContext + let expandDistance: CGFloat + + init( + text: String, + backgroundColor: UIColor, + foregroundColor: UIColor, + peer: EnginePeer?, + context: AccountContext, + expandDistance: CGFloat + ) { + self.text = text + self.backgroundColor = backgroundColor + self.foregroundColor = foregroundColor + self.peer = peer + self.context = context + self.expandDistance = expandDistance + } + + static func ==(lhs: ChatOverscrollControl, rhs: ChatOverscrollControl) -> Bool { + if lhs.text != rhs.text { + return false + } + if !lhs.backgroundColor.isEqual(rhs.backgroundColor) { + return false + } + if !lhs.foregroundColor.isEqual(rhs.foregroundColor) { + return false + } + if lhs.peer != rhs.peer { + return false + } + if lhs.context !== rhs.context { + return false + } + if lhs.expandDistance != rhs.expandDistance { + return false + } + return true + } + + static var body: Body { + let avatarBackground = Child(BlurredRoundedRectangle.self) + let avatarExpandProgress = Child(RadialProgressComponent.self) + let avatarCheck = Child(CheckComponent.self) + let avatar = Child(AvatarComponent.self) + let textBackground = Child(BlurredRoundedRectangle.self) + let text = Child(Text.self) + + return { context in + let text = text.update( + component: Text( + text: context.component.text, + font: Font.regular(12.0), + color: context.component.foregroundColor + ), + availableSize: CGSize(width: context.availableSize.width, height: 100.0), + transition: context.transition + ) + + let textHorizontalPadding: CGFloat = 6.0 + let textVerticalPadding: CGFloat = 2.0 + let avatarSize: CGFloat = 48.0 + let avatarPadding: CGFloat = 8.0 + let avatarTextSpacing: CGFloat = 8.0 + let avatarProgressPadding: CGFloat = 2.5 + + let avatarBackgroundSize: CGFloat = context.component.peer != nil ? (avatarSize + avatarPadding * 2.0) : avatarSize + + let avatarBackground = avatarBackground.update( + component: BlurredRoundedRectangle( + color: context.component.backgroundColor + ), + availableSize: CGSize(width: avatarBackgroundSize, height: avatarBackgroundSize), + transition: context.transition + ) + + let avatarCheck = Condition(context.component.peer == nil, { () -> _UpdatedChildComponent in + let avatarCheckSize = avatarBackgroundSize + 2.0 + + return avatarCheck.update( + component: CheckComponent( + color: context.component.foregroundColor, + lineWidth: 2.5, + value: 1.0 + ), + availableSize: CGSize(width: avatarCheckSize, height: avatarCheckSize), + transition: context.transition + ) + }) + + let progressSize = avatarBackground.size.width - avatarProgressPadding * 2.0 + + let halfDistance = progressSize + let quarterDistance = halfDistance / 2.0 + + let clippedDistance = max(0.0, min(halfDistance * 2.0, context.component.expandDistance)) + + var mappedProgress: CGFloat + if clippedDistance <= quarterDistance { + mappedProgress = acos(1.0 - clippedDistance / quarterDistance) / (CGFloat.pi * 2.0) + } else if clippedDistance <= halfDistance { + let sectionDistance = halfDistance - clippedDistance + mappedProgress = 0.25 + asin(1.0 - sectionDistance / quarterDistance) / (CGFloat.pi * 2.0) + } else { + let restDistance = clippedDistance - halfDistance + mappedProgress = min(1.0, 0.5 + restDistance / 60.0) + } + mappedProgress = max(0.01, mappedProgress) + + let avatarExpandProgress = avatarExpandProgress.update( + component: RadialProgressComponent( + color: context.component.foregroundColor, + lineWidth: 2.5, + value: context.component.peer == nil ? 0.0 : mappedProgress + ), + availableSize: CGSize(width: progressSize, height: progressSize), + transition: context.transition + ) + + let textBackground = textBackground.update( + component: BlurredRoundedRectangle( + color: context.component.backgroundColor + ), + availableSize: CGSize(width: text.size.width + textHorizontalPadding * 2.0, height: text.size.height + textVerticalPadding * 2.0), + transition: context.transition + ) + + let size = CGSize(width: context.availableSize.width, height: avatarBackground.size.height + avatarTextSpacing + textBackground.size.height) + + let avatarBackgroundFrame = avatarBackground.size.topCentered(in: CGRect(origin: CGPoint(), size: size)) + + let avatar = context.component.peer.flatMap { peer in + avatar.update( + component: AvatarComponent( + context: context.component.context, + peer: peer + ), + availableSize: CGSize(width: avatarSize, height: avatarSize), + transition: context.transition + ) + } + + context.add(avatarBackground + .position(CGPoint( + x: avatarBackgroundFrame.midX, + y: avatarBackgroundFrame.midY + )) + ) + + if let avatarCheck = avatarCheck { + context.add(avatarCheck + .position(CGPoint( + x: avatarBackgroundFrame.midX, + y: avatarBackgroundFrame.midY + )) + ) + } + + context.add(avatarExpandProgress + .position(CGPoint( + x: avatarBackgroundFrame.midX, + y: avatarBackgroundFrame.midY + )) + ) + + if let avatar = avatar { + context.add(avatar + .position(CGPoint( + x: avatarBackgroundFrame.midX, + y: avatarBackgroundFrame.midY + )) + ) + } + + let textBackgroundFrame = textBackground.size.bottomCentered(in: CGRect(origin: CGPoint(), size: size)) + context.add(textBackground + .position(CGPoint( + x: textBackgroundFrame.midX, + y: textBackgroundFrame.midY + )) + ) + + let textFrame = text.size.centered(in: textBackgroundFrame) + context.add(text + .position(CGPoint( + x: textFrame.midX, + y: textFrame.midY + )) + ) + + return size + } + } +} diff --git a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift index ba76debd4d..901788194a 100644 --- a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift @@ -171,7 +171,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe } let interaction = ChatListNodeInteraction(activateSearch: { - }, peerSelected: { _, _ in + }, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in diff --git a/submodules/TelegramUI/Sources/ChatTitleView.swift b/submodules/TelegramUI/Sources/ChatTitleView.swift index 7986280ed6..d46fc70974 100644 --- a/submodules/TelegramUI/Sources/ChatTitleView.swift +++ b/submodules/TelegramUI/Sources/ChatTitleView.swift @@ -420,7 +420,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView { let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 for participant in participants.participants { if let presence = peerView.peerPresences[participant.peerId] as? TelegramUserPresence { - let relativeStatus = relativeUserPresenceStatus(presence, relativeTo: Int32(timestamp)) + let relativeStatus = relativeUserPresenceStatus(EnginePeer.Presence(presence), relativeTo: Int32(timestamp)) switch relativeStatus { case .online: onlineCount += 1 @@ -736,4 +736,33 @@ final class ChatTitleView: UIView, NavigationBarTitleView { } return super.hitTest(point, with: event) } + + final class SnapshotState { + fileprivate let snapshotView: UIView + + fileprivate init(snapshotView: UIView) { + self.snapshotView = snapshotView + } + } + + func prepareSnapshotState() -> SnapshotState { + let snapshotView = self.snapshotView(afterScreenUpdates: false)! + return SnapshotState( + snapshotView: snapshotView + ) + } + + func animateFromSnapshot(_ snapshotState: SnapshotState) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + self.layer.animatePosition(from: CGPoint(x: 0.0, y: 20.0), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: true, additive: true) + + snapshotState.snapshotView.frame = self.frame + self.superview?.insertSubview(snapshotState.snapshotView, belowSubview: self) + + let snapshotView = snapshotState.snapshotView + snapshotState.snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -20.0), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) + } } diff --git a/submodules/TelegramUI/Sources/CommandChatInputPanelItem.swift b/submodules/TelegramUI/Sources/CommandChatInputPanelItem.swift index bd800e6984..4339046032 100644 --- a/submodules/TelegramUI/Sources/CommandChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/CommandChatInputPanelItem.swift @@ -164,7 +164,7 @@ final class CommandChatInputPanelItemNode: ListViewItemNode { strongSelf.arrowNode.setImage(iconImage, for: []) - strongSelf.avatarNode.setPeer(context: item.context, theme: item.theme, peer: item.command.peer, emptyColor: item.theme.list.mediaPlaceholderColor) + strongSelf.avatarNode.setPeer(context: item.context, theme: item.theme, peer: EnginePeer(item.command.peer), emptyColor: item.theme.list.mediaPlaceholderColor) let _ = textApply() diff --git a/submodules/TelegramUI/Sources/CreateChannelController.swift b/submodules/TelegramUI/Sources/CreateChannelController.swift index 8373ee9fa2..9e907e7507 100644 --- a/submodules/TelegramUI/Sources/CreateChannelController.swift +++ b/submodules/TelegramUI/Sources/CreateChannelController.swift @@ -369,15 +369,15 @@ public func createChannelController(context: AccountContext) -> ViewController { } else if let url = asset as? URL, let data = try? Data(contentsOf: url, options: [.mappedRead]), let image = UIImage(data: data), let entityRenderer = entityRenderer { let durationSignal: SSignal = SSignal(generator: { subscriber in let disposable = (entityRenderer.duration()).start(next: { duration in - subscriber?.putNext(duration) - subscriber?.putCompletion() + subscriber.putNext(duration) + subscriber.putCompletion() }) return SBlockDisposable(block: { disposable.dispose() }) }) - signal = durationSignal.map(toSignal: { duration -> SSignal? in + signal = durationSignal.map(toSignal: { duration -> SSignal in if let duration = duration as? Double { return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, watcher: nil, entityRenderer: entityRenderer)! } else { diff --git a/submodules/TelegramUI/Sources/CreateGroupController.swift b/submodules/TelegramUI/Sources/CreateGroupController.swift index fa66130050..35c148000b 100644 --- a/submodules/TelegramUI/Sources/CreateGroupController.swift +++ b/submodules/TelegramUI/Sources/CreateGroupController.swift @@ -617,15 +617,15 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] } else if let url = asset as? URL, let data = try? Data(contentsOf: url, options: [.mappedRead]), let image = UIImage(data: data), let entityRenderer = entityRenderer { let durationSignal: SSignal = SSignal(generator: { subscriber in let disposable = (entityRenderer.duration()).start(next: { duration in - subscriber?.putNext(duration) - subscriber?.putCompletion() + subscriber.putNext(duration) + subscriber.putCompletion() }) return SBlockDisposable(block: { disposable.dispose() }) }) - signal = durationSignal.map(toSignal: { duration -> SSignal? in + signal = durationSignal.map(toSignal: { duration -> SSignal in if let duration = duration as? Double { return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, watcher: nil, entityRenderer: entityRenderer)! } else { diff --git a/submodules/TelegramUI/Sources/DeclareEncodables.swift b/submodules/TelegramUI/Sources/DeclareEncodables.swift index 2800e6a5f8..5eaaa38693 100644 --- a/submodules/TelegramUI/Sources/DeclareEncodables.swift +++ b/submodules/TelegramUI/Sources/DeclareEncodables.swift @@ -15,8 +15,6 @@ import ChatInterfaceState private var telegramUIDeclaredEncodables: Void = { declareEncodable(InAppNotificationSettings.self, f: { InAppNotificationSettings(decoder: $0) }) - declareEncodable(ChatInterfaceState.self, f: { ChatInterfaceState(decoder: $0) }) - declareEncodable(ChatEmbeddedInterfaceState.self, f: { ChatEmbeddedInterfaceState(decoder: $0) }) declareEncodable(VideoLibraryMediaResource.self, f: { VideoLibraryMediaResource(decoder: $0) }) declareEncodable(LocalFileVideoMediaResource.self, f: { LocalFileVideoMediaResource(decoder: $0) }) declareEncodable(LocalFileGifMediaResource.self, f: { LocalFileGifMediaResource(decoder: $0) }) diff --git a/submodules/TelegramUI/Sources/DrawingStickersScreen.swift b/submodules/TelegramUI/Sources/DrawingStickersScreen.swift index 5bc72657e9..051f603f4f 100644 --- a/submodules/TelegramUI/Sources/DrawingStickersScreen.swift +++ b/submodules/TelegramUI/Sources/DrawingStickersScreen.swift @@ -1037,21 +1037,21 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode { transition.updateFrame(view: self.blurView, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: maximumHeight))) transition.updateFrame(node: self.collectionListContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: maximumHeight + contentVerticalOffset - bottomPanelHeight - bottomInset), size: CGSize(width: width, height: max(0.0, bottomPanelHeight + UIScreenPixel + bottomInset)))) - transition.updateFrame(node: self.collectionListPanel, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: bottomPanelHeight + bottomInset))) + transition.updateFrame(node: self.collectionListPanel, frame: CGRect(origin: CGPoint(x: 0.0, y: 41.0), size: CGSize(width: width, height: bottomPanelHeight + bottomInset))) transition.updateFrame(node: self.topSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset + topPanelHeight), size: CGSize(width: width, height: separatorHeight))) transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: maximumHeight + contentVerticalOffset - bottomPanelHeight - bottomInset), size: CGSize(width: width, height: separatorHeight))) let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition) - let listPosition = CGPoint(x: width / 2.0, y: (bottomPanelHeight - collectionListPanelOffset) / 2.0) - self.stickerListView.bounds = CGRect(x: 0.0, y: 0.0, width: bottomPanelHeight, height: width) + let listPosition = CGPoint(x: width / 2.0, y: (bottomPanelHeight - collectionListPanelOffset) / 2.0 + 5.0) + self.stickerListView.bounds = CGRect(x: 0.0, y: 0.0, width: bottomPanelHeight + 31.0, height: width) transition.updatePosition(node: self.stickerListView, position: listPosition) - self.maskListView.bounds = CGRect(x: 0.0, y: 0.0, width: bottomPanelHeight, height: width) + self.maskListView.bounds = CGRect(x: 0.0, y: 0.0, width: bottomPanelHeight + 31.0, height: width) transition.updatePosition(node: self.maskListView, position: listPosition) - let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: CGSize(width: bottomPanelHeight, height: width), insets: UIEdgeInsets(top: 4.0 + leftInset, left: 0.0, bottom: 4.0 + rightInset, right: 0.0), duration: duration, curve: curve) + let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: CGSize(width: bottomPanelHeight + 31.0, height: width), insets: UIEdgeInsets(top: 4.0 + leftInset, left: 0.0, bottom: 4.0 + rightInset, right: 0.0), duration: duration, curve: curve) self.stickerListView.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) self.maskListView.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) diff --git a/submodules/TelegramUI/Sources/FetchVideoMediaResource.swift b/submodules/TelegramUI/Sources/FetchVideoMediaResource.swift index 5404666e0d..a9b3b6855a 100644 --- a/submodules/TelegramUI/Sources/FetchVideoMediaResource.swift +++ b/submodules/TelegramUI/Sources/FetchVideoMediaResource.swift @@ -357,8 +357,8 @@ func fetchLocalFileVideoMediaResource(account: Account, resource: LocalFileVideo if let data = try? Data(contentsOf: URL(fileURLWithPath: filteredPath), options: [.mappedRead]), let image = UIImage(data: data) { let durationSignal: SSignal = SSignal(generator: { subscriber in let disposable = (entityRenderer.duration()).start(next: { duration in - subscriber?.putNext(duration) - subscriber?.putCompletion() + subscriber.putNext(duration) + subscriber.putCompletion() }) return SBlockDisposable(block: { @@ -366,7 +366,7 @@ func fetchLocalFileVideoMediaResource(account: Account, resource: LocalFileVideo }) }) - signal = durationSignal.map(toSignal: { duration -> SSignal? in + signal = durationSignal.map(toSignal: { duration -> SSignal in if let duration = duration as? Double { return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, watcher: VideoConversionWatcher(update: { path, size in var value = stat() diff --git a/submodules/TelegramUI/Sources/InstantVideoRadialStatusNode.swift b/submodules/TelegramUI/Sources/InstantVideoRadialStatusNode.swift index 4e1b625b2f..2827f864c0 100644 --- a/submodules/TelegramUI/Sources/InstantVideoRadialStatusNode.swift +++ b/submodules/TelegramUI/Sources/InstantVideoRadialStatusNode.swift @@ -11,12 +11,16 @@ private final class InstantVideoRadialStatusNodeParameters: NSObject { let progress: CGFloat let dimProgress: CGFloat let playProgress: CGFloat + let blinkProgress: CGFloat + let hasSeek: Bool - init(color: UIColor, progress: CGFloat, dimProgress: CGFloat, playProgress: CGFloat) { + init(color: UIColor, progress: CGFloat, dimProgress: CGFloat, playProgress: CGFloat, blinkProgress: CGFloat, hasSeek: Bool) { self.color = color self.progress = progress self.dimProgress = dimProgress self.playProgress = playProgress + self.blinkProgress = blinkProgress + self.hasSeek = hasSeek } } @@ -62,6 +66,12 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele } } + private var effectiveBlinkProgress: CGFloat = 0.0 { + didSet { + self.setNeedsDisplay() + } + } + private var _statusValue: MediaPlayerStatus? private var statusValue: MediaPlayerStatus? { get { @@ -141,7 +151,7 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele let center = CGPoint(x: self.bounds.width / 2.0, y: self.bounds.height / 2.0) let location = gestureRecognizer.location(in: self.view) let distanceFromCenter = location.distanceTo(center) - if distanceFromCenter < self.bounds.width * 0.15 { + if distanceFromCenter < self.bounds.width * 0.2 { return false } return true @@ -194,12 +204,46 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele if let seekingProgress = self.seekingProgress { if seekingProgress > 0.98 && fraction > 0.0 && fraction < 0.05 { self.hapticFeedback.impact(.light) + + let blinkAnimation = POPBasicAnimation() + blinkAnimation.property = POPAnimatableProperty.property(withName: "blinkProgress", initializer: { property in + property?.readBlock = { node, values in + values?.pointee = (node as! InstantVideoRadialStatusNode).effectiveBlinkProgress + } + property?.writeBlock = { node, values in + (node as! InstantVideoRadialStatusNode).effectiveBlinkProgress = values!.pointee + } + property?.threshold = 0.01 + }) as? POPAnimatableProperty + blinkAnimation.fromValue = 1.0 as NSNumber + blinkAnimation.toValue = 0.0 as NSNumber + blinkAnimation.duration = 0.5 + self.pop_add(blinkAnimation, forKey: "blinkProgress") } else if seekingProgress > 0.0 && seekingProgress < 0.05 && fraction > 0.98 { self.hapticFeedback.impact(.light) + + let blinkAnimation = POPBasicAnimation() + blinkAnimation.property = POPAnimatableProperty.property(withName: "blinkProgress", initializer: { property in + property?.readBlock = { node, values in + values?.pointee = (node as! InstantVideoRadialStatusNode).effectiveBlinkProgress + } + property?.writeBlock = { node, values in + (node as! InstantVideoRadialStatusNode).effectiveBlinkProgress = values!.pointee + } + property?.threshold = 0.01 + }) as? POPAnimatableProperty + blinkAnimation.fromValue = -1.0 as NSNumber + blinkAnimation.toValue = 0.0 as NSNumber + blinkAnimation.duration = 0.5 + self.pop_add(blinkAnimation, forKey: "blinkProgress") } } - self.seekTo?(min(0.99, fraction), false) - self.seekingProgress = CGFloat(fraction) + let newProgress = min(0.99, fraction) + if let seekingProgress = self.seekingProgress, abs(seekingProgress - CGFloat(newProgress)) < 0.005 { + } else { + self.seekTo?(newProgress, false) + self.seekingProgress = CGFloat(fraction) + } case .ended, .cancelled: self.seeking = false self.seekTo?(min(0.99, fraction), true) @@ -210,7 +254,7 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele } override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? { - return InstantVideoRadialStatusNodeParameters(color: self.color, progress: self.effectiveProgress, dimProgress: self.effectiveDimProgress, playProgress: self.effectivePlayProgress) + return InstantVideoRadialStatusNodeParameters(color: self.color, progress: self.effectiveProgress, dimProgress: self.effectiveDimProgress, playProgress: self.effectivePlayProgress, blinkProgress: self.effectiveBlinkProgress, hasSeek: self.hasSeek) } @objc override class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) { @@ -225,9 +269,23 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele if let parameters = parameters as? InstantVideoRadialStatusNodeParameters { context.setStrokeColor(parameters.color.cgColor) + context.addEllipse(in: bounds) + context.clip() + if !parameters.dimProgress.isZero { - context.setFillColor(UIColor(rgb: 0x000000, alpha: 0.35 * min(1.0, parameters.dimProgress)).cgColor) - context.fillEllipse(in: bounds) + if parameters.playProgress == 1.0 { + context.setFillColor(UIColor(rgb: 0x000000, alpha: 0.35 * min(1.0, parameters.dimProgress)).cgColor) + context.fillEllipse(in: bounds) + } else { + var locations: [CGFloat] = [0.0, 0.8, 1.0] + let alpha: CGFloat = 0.2 + 0.15 * parameters.playProgress + let colors: [CGColor] = [UIColor(rgb: 0x000000, alpha: alpha * min(1.0, parameters.dimProgress * parameters.playProgress)).cgColor, UIColor(rgb: 0x000000, alpha: alpha * min(1.0, parameters.dimProgress * parameters.playProgress)).cgColor, UIColor(rgb: 0x000000, alpha: alpha * min(1.0, parameters.dimProgress)).cgColor] + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)! + + let center = bounds.center + context.drawRadialGradient(gradient, startCenter: center, startRadius: 0.0, endCenter: center, endRadius: bounds.width / 2.0, options: .drawsAfterEndLocation) + } } context.setBlendMode(.normal) @@ -239,20 +297,40 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele progress = min(1.0, progress) var lineWidth: CGFloat = 4.0 - lineWidth += 1.0 * parameters.dimProgress + if parameters.hasSeek { + lineWidth += 1.0 * parameters.dimProgress + } var pathDiameter = bounds.size.width - lineWidth - 8.0 - pathDiameter -= (18.0 * 2.0) * parameters.dimProgress - - if !parameters.dimProgress.isZero { - context.setStrokeColor(parameters.color.withAlphaComponent(0.2 * parameters.dimProgress).cgColor) - context.setLineWidth(lineWidth) - context.strokeEllipse(in: CGRect(x: (bounds.size.width - pathDiameter) / 2.0 , y: (bounds.size.height - pathDiameter) / 2.0, width: pathDiameter, height: pathDiameter)) + if parameters.hasSeek { + pathDiameter -= (18.0 * 2.0) * parameters.dimProgress + } + if !parameters.dimProgress.isZero { + context.setLineWidth(lineWidth) + + if parameters.blinkProgress > 0.0 { + context.setStrokeColor(parameters.color.withAlphaComponent(0.2 * parameters.blinkProgress).cgColor) + context.strokeEllipse(in: CGRect(x: (bounds.size.width - pathDiameter) / 2.0 , y: (bounds.size.height - pathDiameter) / 2.0, width: pathDiameter, height: pathDiameter)) + } + + if parameters.hasSeek { + var progress = parameters.dimProgress + if parameters.blinkProgress < 0.0 { + progress = parameters.dimProgress + parameters.blinkProgress + } + context.setStrokeColor(parameters.color.withAlphaComponent(0.2 * progress).cgColor) + context.strokeEllipse(in: CGRect(x: (bounds.size.width - pathDiameter) / 2.0 , y: (bounds.size.height - pathDiameter) / 2.0, width: pathDiameter, height: pathDiameter)) + } + if !parameters.playProgress.isZero { context.saveGState() context.translateBy(x: bounds.width / 2.0, y: bounds.height / 2.0) - context.scaleBy(x: 1.0 + 1.4 * parameters.playProgress, y: 1.0 + 1.4 * parameters.playProgress) + if parameters.hasSeek { + context.scaleBy(x: 1.0 + 1.4 * parameters.playProgress, y: 1.0 + 1.4 * parameters.playProgress) + } else { + context.scaleBy(x: 1.0 + 0.7 * parameters.playProgress, y: 1.0 + 0.7 * parameters.playProgress) + } context.translateBy(x: -bounds.width / 2.0, y: -bounds.height / 2.0) let iconSize = CGSize(width: 15.0, height: 18.0) @@ -272,12 +350,14 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele path.lineCapStyle = .round path.stroke() - let handleSide = 16.0 * min(1.0, (parameters.dimProgress * 2.0)) - let handleSize = CGSize(width: handleSide, height: handleSide) - let handlePosition = CGPoint(x: 0.5 * pathDiameter * cos(endAngle), y: 0.5 * pathDiameter * sin(endAngle)).offsetBy(dx: bounds.size.width / 2.0, dy: bounds.size.height / 2.0) - let handleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(handlePosition.x - handleSize.width / 2.0), y: floorToScreenPixels(handlePosition.y - handleSize.height / 2.0)), size: handleSize) - context.setFillColor(UIColor.white.cgColor) - context.fillEllipse(in: handleFrame) + if parameters.hasSeek { + let handleSide = 16.0 * min(1.0, (parameters.dimProgress * 2.0)) + let handleSize = CGSize(width: handleSide, height: handleSide) + let handlePosition = CGPoint(x: 0.5 * pathDiameter * cos(endAngle), y: 0.5 * pathDiameter * sin(endAngle)).offsetBy(dx: bounds.size.width / 2.0, dy: bounds.size.height / 2.0) + let handleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(handlePosition.x - handleSize.width / 2.0), y: floorToScreenPixels(handlePosition.y - handleSize.height / 2.0)), size: handleSize) + context.setFillColor(UIColor.white.cgColor) + context.fillEllipse(in: handleFrame) + } } } @@ -296,11 +376,7 @@ final class InstantVideoRadialStatusNode: ASDisplayNode, UIGestureRecognizerDele if self.seeking { dimmed = true } - - if !self.hasSeek { - dimmed = false - } - + if dimmed != self.dimmed { self.dimmed = dimmed diff --git a/submodules/TelegramUI/Sources/ManageSharedAccountInfo.swift b/submodules/TelegramUI/Sources/ManageSharedAccountInfo.swift index 7745eb5493..dcf97d441c 100644 --- a/submodules/TelegramUI/Sources/ManageSharedAccountInfo.swift +++ b/submodules/TelegramUI/Sources/ManageSharedAccountInfo.swift @@ -24,9 +24,9 @@ private func accountInfo(account: Account) -> Signal if let authInfo = context.authInfoForDatacenter(withId: id, selector: .persistent), let authKey = authInfo.authKey { let transportScheme = context.chooseTransportSchemeForConnection(toDatacenterId: id, schemes: context.transportSchemesForDatacenter(withId: id, media: true, enforceMedia: false, isProxy: false)) var addressList: [AccountDatacenterAddress] = [] - if let transportScheme = transportScheme, let address = transportScheme.address, let host = address.host { - let secret: Data? = address.secret - addressList.append(AccountDatacenterAddress(host: host, port: Int32(address.port), isMedia: address.preferForMedia, secret: secret)) + if let transportScheme = transportScheme, let host = transportScheme.address.host { + let secret: Data? = transportScheme.address.secret + addressList.append(AccountDatacenterAddress(host: host, port: Int32(transportScheme.address.port), isMedia: transportScheme.address.preferForMedia, secret: secret)) } var ephemeralMainKey: AccountDatacenterKey? diff --git a/submodules/TelegramUI/Sources/ManagedAudioRecorder.swift b/submodules/TelegramUI/Sources/ManagedAudioRecorder.swift index ab3b23d82a..73a70d40ec 100644 --- a/submodules/TelegramUI/Sources/ManagedAudioRecorder.swift +++ b/submodules/TelegramUI/Sources/ManagedAudioRecorder.swift @@ -402,7 +402,7 @@ final class ManagedAudioRecorderContext { strongSelf.audioSessionAcquired(headset: state.isHeadsetConnected) } } - }, deactivate: { [weak self] in + }, deactivate: { [weak self] _ in return Signal { subscriber in queue.async { if let strongSelf = self { diff --git a/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift b/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift index 1605756d1f..3c3373f2fa 100644 --- a/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift @@ -200,7 +200,7 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { strongSelf.backgroundColor = item.presentationData.theme.list.plainBackgroundColor strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor - strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor) + strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: EnginePeer(item.peer), emptyColor: item.presentationData.theme.list.mediaPlaceholderColor) let _ = textApply() diff --git a/submodules/TelegramUI/Sources/MultiplexedVideoNode.swift b/submodules/TelegramUI/Sources/MultiplexedVideoNode.swift index 9ebfa1d597..ae35bcee76 100644 --- a/submodules/TelegramUI/Sources/MultiplexedVideoNode.swift +++ b/submodules/TelegramUI/Sources/MultiplexedVideoNode.swift @@ -193,6 +193,7 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate { class DisplayLinkProxy: NSObject { weak var target: MultiplexedVideoNode? + init(target: MultiplexedVideoNode) { self.target = target } @@ -258,24 +259,6 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate { gesture.cancel() } } - - self.contextContainerNode.customActivationProgress = { [weak self] progress, update in - guard let _ = self, let _ = gestureLocation else { - return - } - /*let minScale: CGFloat = (strongSelf.bounds.width - 10.0) / strongSelf.bounds.width - let currentScale = 1.0 * (1.0 - progress) + minScale * progress - switch update { - case .update: - strongSelf.layer.sublayerTransform = CATransform3DMakeScale(currentScale, currentScale, 1.0) - case .begin: - strongSelf.layer.sublayerTransform = CATransform3DMakeScale(currentScale, currentScale, 1.0) - case let .ended(previousProgress): - let previousScale = 1.0 * (1.0 - previousProgress) + minScale * previousProgress - strongSelf.layer.sublayerTransform = CATransform3DMakeScale(currentScale, currentScale, 1.0) - strongSelf.layer.animateSpring(from: previousScale as NSNumber, to: currentScale as NSNumber, keyPath: "sublayerTransform.scale", duration: 0.5, delay: 0.0, initialVelocity: 0.0, damping: 90.0) - }*/ - } } required init?(coder aDecoder: NSCoder) { diff --git a/submodules/TelegramUI/Sources/NavigateToChatController.swift b/submodules/TelegramUI/Sources/NavigateToChatController.swift index da44828a2a..d3edf3e890 100644 --- a/submodules/TelegramUI/Sources/NavigateToChatController.swift +++ b/submodules/TelegramUI/Sources/NavigateToChatController.swift @@ -70,7 +70,7 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam }) } } else { - controller = ChatControllerImpl(context: params.context, chatLocation: params.chatLocation, chatLocationContextHolder: params.chatLocationContextHolder, subject: params.subject, botStart: params.botStart, peekData: params.peekData, peerNearbyData: params.peerNearbyData) + controller = ChatControllerImpl(context: params.context, chatLocation: params.chatLocation, chatLocationContextHolder: params.chatLocationContextHolder, subject: params.subject, botStart: params.botStart, peekData: params.peekData, peerNearbyData: params.peerNearbyData, chatListFilter: params.chatListFilter) } controller.purposefulAction = params.purposefulAction if let search = params.activateMessageSearch { diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index 0f2dd98798..f7a2fe8dfc 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -222,14 +222,8 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur } if let textInputState = textInputState { - let _ = (context.account.postbox.transaction({ transaction -> Void in - transaction.updatePeerChatInterfaceState(peerId, update: { currentState in - if let currentState = currentState as? ChatInterfaceState { - return currentState.withUpdatedComposeInputState(textInputState) - } else { - return ChatInterfaceState().withUpdatedComposeInputState(textInputState) - } - }) + let _ = (ChatInterfaceState.update(engine: context.engine, peerId: peerId, threadId: nil, { currentState in + return currentState.withUpdatedComposeInputState(textInputState) }) |> deliverOnMainQueue).start(completed: { navigationController?.pushViewController(ChatControllerImpl(context: context, chatLocation: .peer(peerId))) @@ -471,7 +465,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur return } navigationController.currentWindow?.present(VoiceChatJoinScreen(context: context, peerId: peerId, invite: invite, join: { [weak chatController] call in - chatController?.joinGroupCall(peerId: peerId, invite: invite, activeCall: call) + chatController?.joinGroupCall(peerId: peerId, invite: invite, activeCall: EngineGroupCallDescription(call)) }), on: .root, blockInteraction: false, completion: {}) })) } diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift index 6545a7e36d..c5bd2ad74a 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoListPaneNode.swift @@ -206,32 +206,28 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode { let mediaAccessoryPanel = MediaNavigationAccessoryPanel(context: self.context, displayBackground: true) mediaAccessoryPanel.containerNode.headerNode.displayScrubber = item.playbackData?.type != .instantVideo + mediaAccessoryPanel.getController = { [weak self] in + return self?.parentController + } + mediaAccessoryPanel.presentInGlobalOverlay = { [weak self] c in + self?.parentController?.presentInGlobalOverlay(c) + } mediaAccessoryPanel.close = { [weak self] in if let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType { strongSelf.context.sharedContext.mediaManager.setPlaylist(nil, type: type, control: SharedMediaPlayerControlAction.playback(.pause)) } } - mediaAccessoryPanel.toggleRate = { - [weak self] in + mediaAccessoryPanel.setRate = { [weak self] rate in guard let strongSelf = self else { return } let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> AudioPlaybackRate in let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings) as? MusicPlaybackSettings ?? MusicPlaybackSettings.defaultSettings - let nextRate: AudioPlaybackRate - switch settings.voicePlaybackRate { - case .x1: - nextRate = .x2 - case .x2: - nextRate = .x1 - default: - nextRate = .x1 - } transaction.updateSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings, { _ in - return settings.withUpdatedVoicePlaybackRate(nextRate) + return settings.withUpdatedVoicePlaybackRate(rate) }) - return nextRate + return rate } |> deliverOnMainQueue).start(next: { baseRate in guard let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType else { @@ -250,22 +246,31 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode { }) let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let slowdown = baseRate == .x1 - controller.present( - UndoOverlayController( - presentationData: presentationData, - content: .audioRate( - slowdown: slowdown, - text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp + let slowdown: Bool? + if baseRate == .x1 { + slowdown = true + } else if baseRate == .x2 { + slowdown = false + } else { + slowdown = nil + } + if let slowdown = slowdown { + controller.present( + UndoOverlayController( + presentationData: presentationData, + content: .audioRate( + slowdown: slowdown, + text: slowdown ? presentationData.strings.Conversation_AudioRateTooltipNormal : presentationData.strings.Conversation_AudioRateTooltipSpeedUp + ), + elevatedLayout: false, + animateInAsReplacement: hasTooltip, + action: { action in + return true + } ), - elevatedLayout: false, - animateInAsReplacement: hasTooltip, - action: { action in - return true - } - ), - in: .current - ) + in: .current + ) + } } }) } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift index 8b148d38de..676828b575 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift @@ -724,7 +724,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 for participant in participants.participants { if let presence = peerView.peerPresences[participant.peerId] as? TelegramUserPresence { - let relativeStatus = relativeUserPresenceStatus(presence, relativeTo: Int32(timestamp)) + let relativeStatus = relativeUserPresenceStatus(EnginePeer.Presence(presence), relativeTo: Int32(timestamp)) switch relativeStatus { case .online: onlineCount += 1 diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index 2571456842..8aac43656e 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -392,7 +392,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode { self.containerNode.isGestureEnabled = false } - self.avatarNode.setPeer(context: self.context, theme: theme, peer: peer, overrideImage: overrideImage, synchronousLoad: self.isFirstAvatarLoading, displayDimensions: CGSize(width: avatarSize, height: avatarSize), storeUnrounded: true) + self.avatarNode.setPeer(context: self.context, theme: theme, peer: EnginePeer(peer), overrideImage: overrideImage, synchronousLoad: self.isFirstAvatarLoading, displayDimensions: CGSize(width: avatarSize, height: avatarSize), storeUnrounded: true) self.isFirstAvatarLoading = false self.containerNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize)) @@ -691,7 +691,7 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode { overrideImage = nil } self.avatarNode.font = avatarPlaceholderFont(size: floor(avatarSize * 16.0 / 37.0)) - self.avatarNode.setPeer(context: self.context, theme: theme, peer: peer, overrideImage: overrideImage, synchronousLoad: false, displayDimensions: CGSize(width: avatarSize, height: avatarSize)) + self.avatarNode.setPeer(context: self.context, theme: theme, peer: EnginePeer(peer), overrideImage: overrideImage, synchronousLoad: false, displayDimensions: CGSize(width: avatarSize, height: avatarSize)) self.avatarNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize)) if let item = item { diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 724a704546..cd13d41ad4 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -3945,7 +3945,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD private func requestCall(isVideo: Bool, gesture: ContextGesture? = nil, contextController: ContextControllerProtocol? = nil, result: ((ContextMenuActionResult) -> Void)? = nil, backAction: ((ContextControllerProtocol) -> Void)? = nil) { let peerId = self.peerId - let requestCall: (PeerId?, CachedChannelData.ActiveCall?) -> Void = { [weak self] defaultJoinAsPeerId, activeCall in + let requestCall: (PeerId?, EngineGroupCallDescription?) -> Void = { [weak self] defaultJoinAsPeerId, activeCall in if let activeCall = activeCall { self?.context.joinGroupCall(peerId: peerId, invite: nil, requestJoinAsPeerId: { completion in if let defaultJoinAsPeerId = defaultJoinAsPeerId { @@ -3963,10 +3963,10 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD } if let cachedChannelData = self.data?.cachedData as? CachedChannelData { - requestCall(cachedChannelData.callJoinPeerId, cachedChannelData.activeCall) + requestCall(cachedChannelData.callJoinPeerId, cachedChannelData.activeCall.flatMap(EngineGroupCallDescription.init)) return } else if let cachedGroupData = self.data?.cachedData as? CachedGroupData { - requestCall(cachedGroupData.callJoinPeerId, cachedGroupData.activeCall) + requestCall(cachedGroupData.callJoinPeerId, cachedGroupData.activeCall.flatMap(EngineGroupCallDescription.init)) return } @@ -4024,7 +4024,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD } strongSelf.context.joinGroupCall(peerId: peerId, invite: nil, requestJoinAsPeerId: { result in result(joinAsPeerId) - }, activeCall: CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: nil, subscribedToScheduled: false)) + }, activeCall: EngineGroupCallDescription(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: nil, subscribedToScheduled: false)) }, error: { [weak self] error in guard let strongSelf = self else { return @@ -5093,15 +5093,15 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD } else if let url = asset as? URL, let data = try? Data(contentsOf: url, options: [.mappedRead]), let image = UIImage(data: data), let entityRenderer = entityRenderer { let durationSignal: SSignal = SSignal(generator: { subscriber in let disposable = (entityRenderer.duration()).start(next: { duration in - subscriber?.putNext(duration) - subscriber?.putCompletion() + subscriber.putNext(duration) + subscriber.putCompletion() }) return SBlockDisposable(block: { disposable.dispose() }) }) - signal = durationSignal.map(toSignal: { duration -> SSignal? in + signal = durationSignal.map(toSignal: { duration -> SSignal in if let duration = duration as? Double { return TGMediaVideoConverter.renderUIImage(image, duration: duration, adjustments: adjustments, watcher: nil, entityRenderer: entityRenderer)! } else { @@ -5649,7 +5649,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD func forwardMessages(messageIds: Set?) { if let messageIds = messageIds ?? self.state.selectedMessageIds, !messageIds.isEmpty { let peerSelectionController = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: [.onlyWriteable, .excludeDisabled], multipleSelection: true)) - peerSelectionController.multiplePeersSelected = { [weak self, weak peerSelectionController] peers, messageText, mode in + peerSelectionController.multiplePeersSelected = { [weak self, weak peerSelectionController] peers, peerMap, messageText, mode in guard let strongSelf = self, let strongController = peerSelectionController else { return } @@ -5674,6 +5674,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD return .forward(source: messageId, grouping: .auto, attributes: [], correlationId: nil) }) + var displayPeers: [Peer] = [] for peer in peers { let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: result) |> deliverOnMainQueue).start(next: { messageIds in @@ -5700,30 +5701,38 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD } }) - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let text: String - var savedMessages = false - if peers.count == 1, let peerId = peers.first?.id, peerId == strongSelf.context.account.peerId { - text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many - savedMessages = true - } else { - if peers.count == 1, let peer = peers.first { - let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string - } else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last { - let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string - } else if let peer = peers.first { - let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(peers.count - 1)").string - } else { - text = "" + if let secretPeer = peer as? TelegramSecretChat { + if let peer = peerMap[secretPeer.regularPeerId] { + displayPeers.append(peer) } + } else { + displayPeers.append(peer) } - - strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) } + + let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + let text: String + var savedMessages = false + if displayPeers.count == 1, let peerId = displayPeers.first?.id, peerId == strongSelf.context.account.peerId { + text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many + savedMessages = true + } else { + if displayPeers.count == 1, let peer = displayPeers.first { + let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string + } else if displayPeers.count == 2, let firstPeer = displayPeers.first, let secondPeer = displayPeers.last { + let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string + } else if let peer = displayPeers.first { + let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(displayPeers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(displayPeers.count - 1)").string + } else { + text = "" + } + } + + strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) } peerSelectionController.peerSelected = { [weak self, weak peerSelectionController] peer in let peerId = peer.id @@ -5762,15 +5771,10 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD peerSelectionController.dismiss() } } else { - let _ = (strongSelf.context.account.postbox.transaction({ transaction -> Void in - transaction.updatePeerChatInterfaceState(peerId, update: { currentState in - if let currentState = currentState as? ChatInterfaceState { - return currentState.withUpdatedForwardMessageIds(Array(messageIds)) - } else { - return ChatInterfaceState().withUpdatedForwardMessageIds(Array(messageIds)) - } - }) - }) |> deliverOnMainQueue).start(completed: { + let _ = (ChatInterfaceState.update(engine: strongSelf.context.engine, peerId: peerId, threadId: nil, { currentState in + return currentState.withUpdatedForwardMessageIds(Array(messageIds)) + }) + |> deliverOnMainQueue).start(completed: { if let strongSelf = self { strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone) diff --git a/submodules/TelegramUI/Sources/PeerSelectionController.swift b/submodules/TelegramUI/Sources/PeerSelectionController.swift index 9e8240e79c..2873c2ae74 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionController.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionController.swift @@ -19,7 +19,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon private var customTitle: String? public var peerSelected: ((Peer) -> Void)? - public var multiplePeersSelected: (([Peer], NSAttributedString, PeerSelectionControllerSendMode) -> Void)? + public var multiplePeersSelected: (([Peer], [PeerId: Peer], NSAttributedString, PeerSelectionControllerSendMode) -> Void)? private let filter: ChatListNodePeersFilter private let attemptSelection: ((Peer) -> Void)? @@ -58,6 +58,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon private let hasContactSelector: Bool private let hasGlobalSearch: Bool private let pretendPresentedInModal: Bool + private let forwardedMessagesCount: Int override public var _presentedInModal: Bool { get { @@ -85,6 +86,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon self.attemptSelection = params.attemptSelection self.createNewGroup = params.createNewGroup self.pretendPresentedInModal = params.pretendPresentedInModal + self.forwardedMessagesCount = params.forwardedMessagesCount super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) @@ -148,7 +150,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon } override public func loadDisplayNode() { - self.displayNode = PeerSelectionControllerNode(context: self.context, filter: self.filter, hasChatListSelector: self.hasChatListSelector, hasContactSelector: self.hasContactSelector, hasGlobalSearch: self.hasGlobalSearch, createNewGroup: self.createNewGroup, present: { [weak self] c, a in + self.displayNode = PeerSelectionControllerNode(context: self.context, filter: self.filter, hasChatListSelector: self.hasChatListSelector, hasContactSelector: self.hasContactSelector, hasGlobalSearch: self.hasGlobalSearch, forwardedMessagesCount: self.forwardedMessagesCount, createNewGroup: self.createNewGroup, present: { [weak self] c, a in self?.present(c, in: .window(.root), with: a) }, presentInGlobalOverlay: { [weak self] c, a in self?.presentInGlobalOverlay(c, with: a) @@ -158,8 +160,8 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon self.peerSelectionNode.navigationBar = self.navigationBar - self.peerSelectionNode.requestSend = { [weak self] peers, text, mode in - self?.multiplePeersSelected?(peers, text, mode) + self.peerSelectionNode.requestSend = { [weak self] peers, peerMap, text, mode in + self?.multiplePeersSelected?(peers, peerMap, text, mode) } self.peerSelectionNode.requestDeactivateSearch = { [weak self] in diff --git a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift index 75dfeaf130..c599ae7588 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift @@ -56,7 +56,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { var requestOpenDisabledPeer: ((Peer) -> Void)? var requestOpenPeerFromSearch: ((Peer) -> Void)? var requestOpenMessageFromSearch: ((Peer, MessageId) -> Void)? - var requestSend: (([Peer], NSAttributedString, PeerSelectionControllerSendMode) -> Void)? + var requestSend: (([Peer], [PeerId: Peer], NSAttributedString, PeerSelectionControllerSendMode) -> Void)? private var presentationData: PresentationData private var presentationDataDisposable: Disposable? @@ -66,7 +66,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { return self.readyValue.get() } - init(context: AccountContext, filter: ChatListNodePeersFilter, hasChatListSelector: Bool, hasContactSelector: Bool, hasGlobalSearch: Bool, createNewGroup: (() -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void) { + init(context: AccountContext, filter: ChatListNodePeersFilter, hasChatListSelector: Bool, hasContactSelector: Bool, hasGlobalSearch: Bool, forwardedMessagesCount: Int, createNewGroup: (() -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void) { self.context = context self.present = present self.presentInGlobalOverlay = presentInGlobalOverlay @@ -79,6 +79,16 @@ final class PeerSelectionControllerNode: ASDisplayNode { self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(PeerId(0)), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil) + var mockMessageIds: [MessageId]? + if forwardedMessagesCount > 0 { + var messageIds: [MessageId] = [] + for _ in 0 ..< forwardedMessagesCount { + messageIds.append(MessageId(peerId: PeerId(0), namespace: Namespaces.Message.Local, id: Int32.random(in: 0 ..< Int32.max))) + } + mockMessageIds = messageIds + } + self.presentationInterfaceState = self.presentationInterfaceState.updatedInterfaceState { $0.withUpdatedForwardMessageIds(mockMessageIds) } + if hasChatListSelector && hasContactSelector { self.toolbarBackgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.rootController.navigationBar.blurredBackgroundColor) @@ -276,13 +286,6 @@ final class PeerSelectionControllerNode: ASDisplayNode { guard let textInputNode = textInputPanelNode.textInputNode else { return } -// let previousSupportedOrientations = strongSelf.supportedOrientations -// if layout.size.width > layout.size.height { -// strongSelf.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .landscape) -// } else { -// strongSelf.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) -// } - let controller = ChatSendMessageActionSheetController(context: strongSelf.context, interfaceState: strongSelf.presentationInterfaceState, gesture: gesture, sourceSendButton: node, textInputNode: textInputNode, completion: { [weak self] in if let strongSelf = self { // strongSelf.supportedOrientations = previousSupportedOrientations @@ -292,7 +295,6 @@ final class PeerSelectionControllerNode: ASDisplayNode { }, schedule: { [weak textInputPanelNode] in textInputPanelNode?.sendMessage(.schedule) }) -// strongSelf.sendMessageActionsController = controller strongSelf.presentInGlobalOverlay(controller, nil) }, openScheduledMessages: { }, openPeersNearby: { @@ -348,13 +350,15 @@ final class PeerSelectionControllerNode: ASDisplayNode { let selectedContactPeers = strongSelf.contactListNode?.selectedPeers ?? [] let effectiveInputText = strongSelf.presentationInterfaceState.interfaceState.composeInputState.inputText var selectedPeers: [Peer] = [] + var selectedPeerMap: [PeerId: Peer] = [:] for contactPeer in selectedContactPeers { if case let .peer(peer, _, _) = contactPeer { selectedPeers.append(peer) + selectedPeerMap[peer.id] = peer } } if !selectedPeers.isEmpty { - strongSelf.requestSend?(selectedPeers, effectiveInputText, mode) + strongSelf.requestSend?(selectedPeers, selectedPeerMap, effectiveInputText, mode) } } else { var selectedPeerIds: [PeerId] = [] @@ -372,7 +376,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { selectedPeers.append(peer) } } - strongSelf.requestSend?(selectedPeers, effectiveInputText, mode) + strongSelf.requestSend?(selectedPeers, selectedPeerMap, effectiveInputText, mode) } } } @@ -492,7 +496,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { } if self.chatListNode.supernode != nil { - self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ChatListSearchContainerNode(context: self.context, filter: self.filter, groupId: .root, displaySearchFilters: false, openPeer: { [weak self] peer, _ in + self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ChatListSearchContainerNode(context: self.context, filter: self.filter, groupId: .root, displaySearchFilters: false, openPeer: { [weak self] peer, chatPeer, _ in guard let strongSelf = self else { return } @@ -505,15 +509,18 @@ final class PeerSelectionControllerNode: ASDisplayNode { var foundPeers = state.foundPeers var selectedPeerMap = state.selectedPeerMap selectedPeerMap[peer.id] = peer + if peer is TelegramSecretChat, let chatPeer = chatPeer { + selectedPeerMap[chatPeer.id] = chatPeer + } var exists = false for foundPeer in foundPeers { - if peer.id == foundPeer.id { + if peer.id == foundPeer.0.id { exists = true break } } if !exists { - foundPeers.insert(peer, at: 0) + foundPeers.insert((peer, chatPeer), at: 0) } if state.selectedPeerIds.contains(peer.id) { state.selectedPeerIds.remove(peer.id) diff --git a/submodules/TelegramUI/Sources/SharedMediaPlayer.swift b/submodules/TelegramUI/Sources/SharedMediaPlayer.swift index 0b96de1a52..2e2c8bdc09 100644 --- a/submodules/TelegramUI/Sources/SharedMediaPlayer.swift +++ b/submodules/TelegramUI/Sources/SharedMediaPlayer.swift @@ -290,7 +290,12 @@ final class SharedMediaPlayer { player.play() } case let .instantVideo(node): - node.playOnceWithSound(playAndRecord: controlPlaybackWithProximity) + if let scheduledStartTime = scheduledStartTime { + node.seek(scheduledStartTime) + node.playOnceWithSound(playAndRecord: controlPlaybackWithProximity) + } else { + node.playOnceWithSound(playAndRecord: controlPlaybackWithProximity) + } } case .pause: playbackItem.pause() diff --git a/submodules/TelegramUI/Sources/TelegramAccountAuxiliaryMethods.swift b/submodules/TelegramUI/Sources/TelegramAccountAuxiliaryMethods.swift index e0d2a72d05..3e06830a34 100644 --- a/submodules/TelegramUI/Sources/TelegramAccountAuxiliaryMethods.swift +++ b/submodules/TelegramUI/Sources/TelegramAccountAuxiliaryMethods.swift @@ -12,15 +12,7 @@ import WallpaperResources import AppBundle import SwiftSignalKit -public let telegramAccountAuxiliaryMethods = AccountAuxiliaryMethods(updatePeerChatInputState: { interfaceState, inputState -> PeerChatInterfaceState? in - if interfaceState == nil { - return ChatInterfaceState().withUpdatedSynchronizeableInputState(inputState) - } else if let interfaceState = interfaceState as? ChatInterfaceState { - return interfaceState.withUpdatedSynchronizeableInputState(inputState) - } else { - return interfaceState - } -}, fetchResource: { account, resource, ranges, _ in +public let telegramAccountAuxiliaryMethods = AccountAuxiliaryMethods(fetchResource: { account, resource, ranges, _ in if let resource = resource as? VideoLibraryMediaResource { return fetchVideoLibraryMediaResource(account: account, resource: resource) } else if let resource = resource as? LocalFileVideoMediaResource { diff --git a/submodules/TelegramUIPreferences/BUILD b/submodules/TelegramUIPreferences/BUILD index e3fafaf5a0..d3a5d7b5b1 100644 --- a/submodules/TelegramUIPreferences/BUILD +++ b/submodules/TelegramUIPreferences/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramCore:TelegramCore", "//submodules/Postbox:Postbox", diff --git a/submodules/TelegramUIPreferences/Sources/CallListSettings.swift b/submodules/TelegramUIPreferences/Sources/CallListSettings.swift index 991b7e6ddf..e40d334a9b 100644 --- a/submodules/TelegramUIPreferences/Sources/CallListSettings.swift +++ b/submodules/TelegramUIPreferences/Sources/CallListSettings.swift @@ -34,9 +34,7 @@ public struct CallListSettings: PreferencesEntry, Equatable { } public init(decoder: PostboxDecoder) { - var defaultValue = CallListSettings.defaultSettings.showTab if let alternativeDefaultValue = decoder.decodeOptionalInt32ForKey("defaultShowTab") { - defaultValue = alternativeDefaultValue != 0 self.defaultShowTab = alternativeDefaultValue != 0 } if let value = decoder.decodeOptionalInt32ForKey("showTab") { diff --git a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift index 1ae9c83d4c..d08fd83283 100644 --- a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift +++ b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift @@ -16,7 +16,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { public var enableVoipTcp: Bool public var demoVideoChats: Bool public var experimentalCompatibility: Bool - public var enableNoiseSuppression: Bool + public var enableDebugDataDisplay: Bool public static var defaultSettings: ExperimentalUISettings { return ExperimentalUISettings( @@ -33,7 +33,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { enableVoipTcp: false, demoVideoChats: false, experimentalCompatibility: false, - enableNoiseSuppression: false + enableDebugDataDisplay: false ) } @@ -51,7 +51,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { enableVoipTcp: Bool, demoVideoChats: Bool, experimentalCompatibility: Bool, - enableNoiseSuppression: Bool + enableDebugDataDisplay: Bool ) { self.keepChatNavigationStack = keepChatNavigationStack self.skipReadHistory = skipReadHistory @@ -66,7 +66,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { self.enableVoipTcp = enableVoipTcp self.demoVideoChats = demoVideoChats self.experimentalCompatibility = experimentalCompatibility - self.enableNoiseSuppression = enableNoiseSuppression + self.enableDebugDataDisplay = enableDebugDataDisplay } public init(decoder: PostboxDecoder) { @@ -83,7 +83,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { self.enableVoipTcp = decoder.decodeInt32ForKey("enableVoipTcp", orElse: 0) != 0 self.demoVideoChats = decoder.decodeInt32ForKey("demoVideoChats", orElse: 0) != 0 self.experimentalCompatibility = decoder.decodeInt32ForKey("experimentalCompatibility", orElse: 0) != 0 - self.enableNoiseSuppression = decoder.decodeInt32ForKey("enableNoiseSuppression", orElse: 0) != 0 + self.enableDebugDataDisplay = decoder.decodeInt32ForKey("enableDebugDataDisplay", orElse: 0) != 0 } public func encode(_ encoder: PostboxEncoder) { @@ -102,7 +102,7 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { encoder.encodeInt32(self.enableVoipTcp ? 1 : 0, forKey: "enableVoipTcp") encoder.encodeInt32(self.demoVideoChats ? 1 : 0, forKey: "demoVideoChats") encoder.encodeInt32(self.experimentalCompatibility ? 1 : 0, forKey: "experimentalCompatibility") - encoder.encodeInt32(self.enableNoiseSuppression ? 1 : 0, forKey: "enableNoiseSuppression") + encoder.encodeInt32(self.enableDebugDataDisplay ? 1 : 0, forKey: "enableDebugDataDisplay") } public func isEqual(to: PreferencesEntry) -> Bool { diff --git a/submodules/TelegramUIPreferences/Sources/PresentationThemeSettings.swift b/submodules/TelegramUIPreferences/Sources/PresentationThemeSettings.swift index cf0239ad40..01fec34c68 100644 --- a/submodules/TelegramUIPreferences/Sources/PresentationThemeSettings.swift +++ b/submodules/TelegramUIPreferences/Sources/PresentationThemeSettings.swift @@ -242,7 +242,7 @@ public enum PresentationThemeReference: PostboxCoding, Equatable { public func coloredThemeIndex(reference: PresentationThemeReference, accentColor: PresentationThemeAccentColor?) -> Int64 { if let accentColor = accentColor { - if case let .builtin(theme) = reference { + if case .builtin = reference { return reference.index * 1000 &+ Int64(accentColor.index) } else { return reference.index &+ Int64(accentColor.index) diff --git a/submodules/TelegramUniversalVideoContent/BUILD b/submodules/TelegramUniversalVideoContent/BUILD index 1bdd85e6a7..65030fcbf1 100644 --- a/submodules/TelegramUniversalVideoContent/BUILD +++ b/submodules/TelegramUniversalVideoContent/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/TelegramUniversalVideoContent/Sources/OverlayVideoDecoration.swift b/submodules/TelegramUniversalVideoContent/Sources/OverlayVideoDecoration.swift index e14bfd80ad..0f5b97e1b9 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/OverlayVideoDecoration.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/OverlayVideoDecoration.swift @@ -230,32 +230,27 @@ final class OverlayVideoDecoration: UniversalVideoDecoration { } let effect: UIBlurEffect? = edge != nil ? UIBlurEffect(style: .light) : nil - if true { - if let edge = edge { - self.minimizedBlurView?.isHidden = false - - switch edge { - case .left: - break - case .right: - break + + if let edge = edge { + self.minimizedBlurView?.isHidden = false + + switch edge { + case .left: + break + case .right: + break + } + } + + UIView.animate(withDuration: 0.35, animations: { + self.minimizedBlurView?.effect = effect + self.minimizedArrowView?.alpha = edge != nil ? 1.0 : 0.0; + }, completion: { [weak self] finished in + if let strongSelf = self { + if finished && edge == nil { + strongSelf.minimizedBlurView?.isHidden = true } } - - UIView.animate(withDuration: 0.35, animations: { - self.minimizedBlurView?.effect = effect - self.minimizedArrowView?.alpha = edge != nil ? 1.0 : 0.0; - }, completion: { [weak self] finished in - if let strongSelf = self { - if finished && edge == nil { - strongSelf.minimizedBlurView?.isHidden = true - } - } - }) - } else { - self.minimizedBlurView?.effect = effect; - self.minimizedBlurView?.isHidden = edge == nil - self.minimizedArrowView?.alpha = edge != nil ? 1.0 : 0.0 - } + }) } } diff --git a/submodules/TelegramUniversalVideoContent/Sources/PlatformVideoContent.swift b/submodules/TelegramUniversalVideoContent/Sources/PlatformVideoContent.swift index e4ec700341..7f9466a28f 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/PlatformVideoContent.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/PlatformVideoContent.swift @@ -343,7 +343,7 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte self.statusValue = MediaPlayerStatus(generationTimestamp: 0.0, duration: Double(self.approximateDuration), dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: status, soundEnabled: true) self._status.set(self.statusValue) } else if keyPath == "status" { - if let playerItem = self.playerItem, false { + /*if let playerItem = self.playerItem, false { switch playerItem.status { case .failed: switch self.content { @@ -356,7 +356,7 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte default: break } - } + }*/ } } @@ -386,7 +386,7 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte self.audioSessionDisposable.set(self.audioSessionManager.push(audioSessionType: .play, activate: { [weak self] _ in self?.hasAudioSession = true self?.player.play() - }, deactivate: { [weak self] in + }, deactivate: { [weak self] _ in self?.hasAudioSession = false self?.player.pause() return .complete() diff --git a/submodules/TelegramUniversalVideoContent/Sources/SystemVideoContent.swift b/submodules/TelegramUniversalVideoContent/Sources/SystemVideoContent.swift index 8f6c0980eb..93a4a58af6 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/SystemVideoContent.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/SystemVideoContent.swift @@ -224,7 +224,7 @@ private final class SystemVideoContentNode: ASDisplayNode, UniversalVideoContent self.audioSessionDisposable.set(self.audioSessionManager.push(audioSessionType: .play, activate: { [weak self] _ in self?.hasAudioSession = true self?.player.play() - }, deactivate: { [weak self] in + }, deactivate: { [weak self] _ in self?.hasAudioSession = false self?.player.pause() return .complete() diff --git a/submodules/TelegramUniversalVideoContent/Sources/UniversalVideoContentManager.swift b/submodules/TelegramUniversalVideoContent/Sources/UniversalVideoContentManager.swift index 1f65bbf906..3939b2f85a 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/UniversalVideoContentManager.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/UniversalVideoContentManager.swift @@ -154,7 +154,7 @@ public final class UniversalVideoManagerImpl: UniversalVideoManager { if let current = self.holders[content.id] { holder = current } else { - var foundHolder: UniversalVideoContentHolder? + let foundHolder: UniversalVideoContentHolder? = nil for (_, current) in self.holders { if current.content.isEqual(to: content) { //foundHolder = current diff --git a/submodules/TelegramUniversalVideoContent/Sources/VimeoEmbedImplementation.swift b/submodules/TelegramUniversalVideoContent/Sources/VimeoEmbedImplementation.swift index f5b911b5a0..f03ec1d5c0 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/VimeoEmbedImplementation.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/VimeoEmbedImplementation.swift @@ -216,6 +216,8 @@ final class VimeoEmbedImplementation: WebEmbedImplementation { } } } + + let _ = download if let position = position { if let ticksToIgnore = self.ignorePosition { diff --git a/submodules/TelegramUniversalVideoContent/Sources/YoutubeEmbedImplementation.swift b/submodules/TelegramUniversalVideoContent/Sources/YoutubeEmbedImplementation.swift index 6df3c2227c..454ea52507 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/YoutubeEmbedImplementation.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/YoutubeEmbedImplementation.swift @@ -285,6 +285,9 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation { } } } + + let _ = download + let _ = failed if let position = position { if self.ignoreEarlierTimestamps { diff --git a/submodules/TelegramUpdateUI/BUILD b/submodules/TelegramUpdateUI/BUILD index a86c8f314f..cea001af4c 100644 --- a/submodules/TelegramUpdateUI/BUILD +++ b/submodules/TelegramUpdateUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/TelegramVoip/BUILD b/submodules/TelegramVoip/BUILD index 662a872426..0e8a8cec3c 100644 --- a/submodules/TelegramVoip/BUILD +++ b/submodules/TelegramVoip/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/TelegramCore:TelegramCore", diff --git a/submodules/TelegramVoip/Sources/GroupCallContext.swift b/submodules/TelegramVoip/Sources/GroupCallContext.swift index cea1f85d45..62e7fb1687 100644 --- a/submodules/TelegramVoip/Sources/GroupCallContext.swift +++ b/submodules/TelegramVoip/Sources/GroupCallContext.swift @@ -290,6 +290,8 @@ public final class OngoingGroupCallContext { public let width: Int public let height: Int public let orientation: OngoingCallVideoOrientation + public let mirrorHorizontally: Bool + public let mirrorVertically: Bool init(frameData: CallVideoFrameData) { if let nativeBuffer = frameData.buffer as? CallVideoFrameNativePixelBuffer { @@ -305,8 +307,19 @@ public final class OngoingGroupCallContext { self.width = Int(frameData.width) self.height = Int(frameData.height) self.orientation = OngoingCallVideoOrientation(frameData.orientation) + self.mirrorHorizontally = frameData.mirrorHorizontally + self.mirrorVertically = frameData.mirrorVertically } } + + public struct Stats { + public struct IncomingVideoStats { + public var receivingQuality: Int + public var availableQuality: Int + } + + public var incomingVideoStats: [String: IncomingVideoStats] + } private final class Impl { let queue: Queue @@ -728,6 +741,16 @@ public final class OngoingGroupCallContext { func addExternalAudioData(data: Data) { self.context.addExternalAudioData(data) } + + func getStats(completion: @escaping (Stats) -> Void) { + self.context.getStats({ stats in + var incomingVideoStats: [String: Stats.IncomingVideoStats] = [:] + for (key, value) in stats.incomingVideoStats { + incomingVideoStats[key] = Stats.IncomingVideoStats(receivingQuality: Int(value.receivingQuality), availableQuality: Int(value.availableQuality)) + } + completion(Stats(incomingVideoStats: incomingVideoStats)) + }) + } } private let queue = Queue() @@ -905,4 +928,10 @@ public final class OngoingGroupCallContext { impl.addExternalAudioData(data: data) } } + + public func getStats(completion: @escaping (Stats) -> Void) { + self.impl.with { impl in + impl.getStats(completion: completion) + } + } } diff --git a/submodules/TemporaryCachedPeerDataManager/BUILD b/submodules/TemporaryCachedPeerDataManager/BUILD index d3795aa6d3..ecb048b8a6 100644 --- a/submodules/TemporaryCachedPeerDataManager/BUILD +++ b/submodules/TemporaryCachedPeerDataManager/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramStringFormatting:TelegramStringFormatting", "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", diff --git a/submodules/TemporaryCachedPeerDataManager/Sources/PeerChannelMemberCategoriesContextsManager.swift b/submodules/TemporaryCachedPeerDataManager/Sources/PeerChannelMemberCategoriesContextsManager.swift index dade7e25b3..b6b70ca702 100644 --- a/submodules/TemporaryCachedPeerDataManager/Sources/PeerChannelMemberCategoriesContextsManager.swift +++ b/submodules/TemporaryCachedPeerDataManager/Sources/PeerChannelMemberCategoriesContextsManager.swift @@ -522,7 +522,7 @@ public final class PeerChannelMemberCategoriesContextsManager { if let presences = (view.views[key] as? PeerPresencesView)?.presences { for (_, presence) in presences { if let presence = presence as? TelegramUserPresence { - let relativeStatus = relativeUserPresenceStatus(presence, relativeTo: Int32(timestamp)) + let relativeStatus = relativeUserPresenceStatus(EnginePeer.Presence(presence), relativeTo: Int32(timestamp)) switch relativeStatus { case .online: count += 1 diff --git a/submodules/TextFormat/BUILD b/submodules/TextFormat/BUILD index 42516f84ee..2585c4f6cd 100644 --- a/submodules/TextFormat/BUILD +++ b/submodules/TextFormat/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramCore:TelegramCore", "//submodules/Display:Display", diff --git a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift index fd01167f40..ae4ddfbff1 100644 --- a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift +++ b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift @@ -720,21 +720,23 @@ public func convertMarkdownToAttributes(_ text: NSAttributedString) -> NSAttribu let entity = string.substring(with: match.range(at: 7)) let substring = string.substring(with: match.range(at: 6)) + text + string.substring(with: match.range(at: 9)) + let textInputAttribute: NSAttributedString.Key? switch entity { case "`": - result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.monospace: true as NSNumber])) - offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2)) + textInputAttribute = ChatTextInputAttributes.monospace case "**": - result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.bold: true as NSNumber])) - offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2)) + textInputAttribute = ChatTextInputAttributes.bold case "__": - result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.italic: true as NSNumber])) - offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2)) + textInputAttribute = ChatTextInputAttributes.italic case "~~": - result.append(NSAttributedString(string: substring, attributes: [ChatTextInputAttributes.strikethrough: true as NSNumber])) - offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2)) + textInputAttribute = ChatTextInputAttributes.strikethrough default: - break + textInputAttribute = nil + } + + if let textInputAttribute = textInputAttribute { + result.append(NSAttributedString(string: substring, attributes: [textInputAttribute: true as NSNumber])) + offsetRanges.append((NSMakeRange(matchIndex + match.range(at: 6).length, text.count), match.range(at: 6).length * 2)) } stringOffset -= match.range(at: 7).length * 2 diff --git a/submodules/TextSelectionNode/BUILD b/submodules/TextSelectionNode/BUILD index 0795ef338f..05e706d364 100644 --- a/submodules/TextSelectionNode/BUILD +++ b/submodules/TextSelectionNode/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/TextSelectionNode/Sources/TextSelectionNode.swift b/submodules/TextSelectionNode/Sources/TextSelectionNode.swift index 0cd411fbad..f57f7c1c16 100644 --- a/submodules/TextSelectionNode/Sources/TextSelectionNode.swift +++ b/submodules/TextSelectionNode/Sources/TextSelectionNode.swift @@ -389,10 +389,7 @@ public final class TextSelectionNode: ASDisplayNode { } let point = CGPoint(x: (1.0 - progress) * startPoint.x + progress * endPoint.x, y: (1.0 - progress) * startPoint.y + progress * endPoint.y) strongSelf.recognizer?.moveKnob?(.right, point) - }, completion: { [weak self] in - guard let strongSelf = self else { - return - } + }, completion: { }) self.displayLinkAnimator = displayLinkAnimator } diff --git a/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h b/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h index 756283be39..55e3492af0 100644 --- a/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h +++ b/submodules/TgVoipWebrtc/PublicHeaders/TgVoipWebrtc/OngoingCallThreadLocalContext.h @@ -161,6 +161,8 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) { @property (nonatomic, readonly) int width; @property (nonatomic, readonly) int height; @property (nonatomic, readonly) OngoingCallVideoOrientationWebrtc orientation; +@property (nonatomic, readonly) bool mirrorHorizontally; +@property (nonatomic, readonly) bool mirrorVertically; @end @@ -316,6 +318,23 @@ typedef NS_ENUM(int32_t, OngoingGroupCallRequestedVideoQuality) { @end +@interface OngoingGroupCallIncomingVideoStats : NSObject + +@property (nonatomic, readonly) int receivingQuality; +@property (nonatomic, readonly) int availableQuality; + +- (instancetype _Nonnull)initWithReceivingQuality:(int)receivingQuality availableQuality:(int)availableQuality; + +@end + +@interface OngoingGroupCallStats : NSObject + +@property (nonatomic, strong, readonly) NSDictionary * _Nonnull incomingVideoStats; + +- (instancetype _Nonnull)initWithIncomingVideoStats:(NSDictionary * _Nonnull)incomingVideoStats; + +@end + @interface GroupCallThreadLocalContext : NSObject - (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue @@ -353,6 +372,8 @@ typedef NS_ENUM(int32_t, OngoingGroupCallRequestedVideoQuality) { - (void)addExternalAudioData:(NSData * _Nonnull)data; +- (void)getStats:(void (^ _Nonnull)(OngoingGroupCallStats * _Nonnull))completion; + @end #endif diff --git a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm index 622dffa96d..9a652771a2 100644 --- a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm +++ b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm @@ -34,6 +34,7 @@ #include "sdk/objc/native/src/objc_frame_buffer.h" #import "components/video_frame_buffer/RTCCVPixelBuffer.h" +#import "platform/darwin/TGRTCCVPixelBuffer.h" @implementation OngoingCallConnectionDescriptionWebrtc @@ -326,7 +327,7 @@ @implementation CallVideoFrameData -- (instancetype)initWithBuffer:(id)buffer frame:(webrtc::VideoFrame const &)frame { +- (instancetype)initWithBuffer:(id)buffer frame:(webrtc::VideoFrame const &)frame mirrorHorizontally:(bool)mirrorHorizontally mirrorVertically:(bool)mirrorVertically { self = [super init]; if (self != nil) { _buffer = buffer; @@ -356,6 +357,9 @@ break; } } + + _mirrorHorizontally = mirrorHorizontally; + _mirrorVertically = mirrorVertically; } return self; } @@ -400,12 +404,31 @@ private: _adapter.reset(new GroupCallVideoSinkAdapter(^(webrtc::VideoFrame const &videoFrame) { id mappedBuffer = nil; + bool mirrorHorizontally = false; + bool mirrorVertically = false; + if (videoFrame.video_frame_buffer()->type() == webrtc::VideoFrameBuffer::Type::kNative) { id nativeBuffer = static_cast(videoFrame.video_frame_buffer().get())->wrapped_frame_buffer(); if ([nativeBuffer isKindOfClass:[RTC_OBJC_TYPE(RTCCVPixelBuffer) class]]) { RTCCVPixelBuffer *pixelBuffer = (RTCCVPixelBuffer *)nativeBuffer; mappedBuffer = [[CallVideoFrameNativePixelBuffer alloc] initWithPixelBuffer:pixelBuffer.pixelBuffer]; } + if ([nativeBuffer isKindOfClass:[TGRTCCVPixelBuffer class]]) { + if (((TGRTCCVPixelBuffer *)nativeBuffer).shouldBeMirrored) { + switch (videoFrame.rotation()) { + case webrtc::kVideoRotation_0: + case webrtc::kVideoRotation_180: + mirrorHorizontally = true; + break; + case webrtc::kVideoRotation_90: + case webrtc::kVideoRotation_270: + mirrorVertically = true; + break; + default: + break; + } + } + } } else if (videoFrame.video_frame_buffer()->type() == webrtc::VideoFrameBuffer::Type::kNV12) { rtc::scoped_refptr nv12Buffer = (webrtc::NV12BufferInterface *)videoFrame.video_frame_buffer().get(); mappedBuffer = [[CallVideoFrameNV12Buffer alloc] initWithBuffer:nv12Buffer]; @@ -415,7 +438,7 @@ private: } if (storedSink && mappedBuffer) { - storedSink([[CallVideoFrameData alloc] initWithBuffer:mappedBuffer frame:videoFrame]); + storedSink([[CallVideoFrameData alloc] initWithBuffer:mappedBuffer frame:videoFrame mirrorHorizontally:mirrorHorizontally mirrorVertically:mirrorVertically]); } })); } @@ -446,6 +469,7 @@ private: _interface = interface; _isProcessingCustomSampleBuffer = [[IsProcessingCustomSampleBufferFlag alloc] init]; _croppingBuffer = std::make_shared>(); + _sinks = [[NSMutableDictionary alloc] init]; } return self; } @@ -460,6 +484,7 @@ private: resolvedId += std::string(":landscape"); } _interface = tgcalls::VideoCaptureInterface::Create(tgcalls::StaticThreads::getThreads(), resolvedId); + _sinks = [[NSMutableDictionary alloc] init]; } return self; } @@ -473,7 +498,7 @@ tgcalls::VideoCaptureInterfaceObject *GetVideoCaptureAssumingSameThread(tgcalls: } + (instancetype _Nonnull)capturerWithExternalSampleBufferProvider { - std::shared_ptr interface = tgcalls::VideoCaptureInterface::Create(tgcalls::StaticThreads::getThreads(), ":ios_custom"); + std::shared_ptr interface = tgcalls::VideoCaptureInterface::Create(tgcalls::StaticThreads::getThreads(), ":ios_custom", true); return [[OngoingCallThreadLocalContextVideoCapturer alloc] initWithInterface:interface]; } #endif @@ -1760,6 +1785,20 @@ private: } } +- (void)getStats:(void (^ _Nonnull)(OngoingGroupCallStats * _Nonnull))completion { + if (_instance) { + _instance->getStats([completion](tgcalls::GroupInstanceStats stats) { + NSMutableDictionary *incomingVideoStats = [[NSMutableDictionary alloc] init]; + + for (const auto &it : stats.incomingVideoStats) { + incomingVideoStats[[NSString stringWithUTF8String:it.first.c_str()]] = [[OngoingGroupCallIncomingVideoStats alloc] initWithReceivingQuality:it.second.receivingQuality availableQuality:it.second.availableQuality]; + } + + completion([[OngoingGroupCallStats alloc] initWithIncomingVideoStats:incomingVideoStats]); + }); + } +} + @end @implementation OngoingGroupCallMediaChannelDescription @@ -1821,3 +1860,28 @@ private: } @end + +@implementation OngoingGroupCallIncomingVideoStats + +- (instancetype _Nonnull)initWithReceivingQuality:(int)receivingQuality availableQuality:(int)availableQuality { + self = [super init]; + if (self != nil) { + _receivingQuality = receivingQuality; + _availableQuality = availableQuality; + } + return self; +} + +@end + +@implementation OngoingGroupCallStats + +- (instancetype _Nonnull)initWithIncomingVideoStats:(NSDictionary * _Nonnull)incomingVideoStats { + self = [super init]; + if (self != nil) { + _incomingVideoStats = incomingVideoStats; + } + return self; +} + +@end diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index e8f7d43930..68eadd0cb3 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit e8f7d439309abd4da1c15b97141c546295fcdcb4 +Subproject commit 68eadd0cb31ceedcfbf7f810dc86d2ebc7dabf9d diff --git a/submodules/TinyThumbnail/BUILD b/submodules/TinyThumbnail/BUILD index 390a77b9fe..cbedb8d1e9 100644 --- a/submodules/TinyThumbnail/BUILD +++ b/submodules/TinyThumbnail/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], visibility = [ "//visibility:public", ], diff --git a/submodules/TinyThumbnail/Sources/TinyThumbnail.swift b/submodules/TinyThumbnail/Sources/TinyThumbnail.swift index 4d3777c448..4bceb3e819 100644 --- a/submodules/TinyThumbnail/Sources/TinyThumbnail.swift +++ b/submodules/TinyThumbnail/Sources/TinyThumbnail.swift @@ -24,9 +24,12 @@ public func decodeTinyThumbnail(data: Data) -> Data? { resultData.append(tinyThumbnailHeaderPattern) resultData.append(data.subdata(in: 3 ..< data.count)) resultData.append(tinyThumbnailFooterPattern) - resultData.withUnsafeMutableBytes({ (resultBytes: UnsafeMutablePointer) -> Void in + resultData.withUnsafeMutableBytes { buffer -> Void in + guard let resultBytes = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else { + return + } resultBytes[164] = width resultBytes[166] = height - }) + } return resultData } diff --git a/submodules/TooltipUI/BUILD b/submodules/TooltipUI/BUILD index 59c221f744..fe81ac954d 100644 --- a/submodules/TooltipUI/BUILD +++ b/submodules/TooltipUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/TouchDownGesture/BUILD b/submodules/TouchDownGesture/BUILD index 04e076cb47..13657370f2 100644 --- a/submodules/TouchDownGesture/BUILD +++ b/submodules/TouchDownGesture/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], visibility = [ "//visibility:public", ], diff --git a/submodules/Tuples/BUILD b/submodules/Tuples/BUILD index b9e8a036dc..a1390c56ea 100644 --- a/submodules/Tuples/BUILD +++ b/submodules/Tuples/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], visibility = [ "//visibility:public", ], diff --git a/submodules/UIKitRuntimeUtils/BUILD b/submodules/UIKitRuntimeUtils/BUILD index 8086d92521..d189926c6c 100644 --- a/submodules/UIKitRuntimeUtils/BUILD +++ b/submodules/UIKitRuntimeUtils/BUILD @@ -6,6 +6,9 @@ objc_library( srcs = glob([ "Source/UIKitRuntimeUtils/*.m", ]), + copts = [ + "-Werror", + ], hdrs = glob([ "Source/UIKitRuntimeUtils/*.h", ]), diff --git a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.h b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.h index 96e40fc67d..f335aae7a1 100644 --- a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.h +++ b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.h @@ -7,5 +7,5 @@ CABasicAnimation * _Nonnull makeSpringAnimationImpl(NSString * _Nonnull keyPath) CABasicAnimation * _Nonnull makeSpringBounceAnimationImpl(NSString * _Nonnull keyPath, CGFloat initialVelocity, CGFloat damping); CGFloat springAnimationValueAtImpl(CABasicAnimation * _Nonnull animation, CGFloat t); -UIBlurEffect *makeCustomZoomBlurEffectImpl(bool isLight); +UIBlurEffect * _Nonnull makeCustomZoomBlurEffectImpl(bool isLight); void applySmoothRoundedCornersImpl(CALayer * _Nonnull layer); diff --git a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m index 4000be50b1..a59590d627 100644 --- a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m +++ b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIViewController+Navigation.m @@ -302,20 +302,6 @@ static bool notyfyingShiftState = false; @end -static NSString *TGEncodeText(NSString *string, int key) -{ - NSMutableString *result = [[NSMutableString alloc] init]; - - for (int i = 0; i < (int)[string length]; i++) - { - unichar c = [string characterAtIndex:i]; - c += key; - [result appendString:[NSString stringWithCharacters:&c length:1]]; - } - - return result; -} - void applyKeyboardAutocorrection(UITextView * _Nonnull textView) { NSRange rangeCopy = textView.selectedRange; NSRange fakeRange = rangeCopy; diff --git a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIWindow+OrientationChange.m b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIWindow+OrientationChange.m index 88d51c4cce..5b85419517 100644 --- a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIWindow+OrientationChange.m +++ b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIWindow+OrientationChange.m @@ -16,6 +16,12 @@ static NSMutableArray *postDeviceDidChangeOrientationBlocks() { static bool _isDeviceRotating = false; +@interface UIView (OrientationChangeDeps) + +- (void)_updateToInterfaceOrientation:(int)arg1 duration:(double)arg2 force:(BOOL)arg3; + +@end + @implementation UIWindow (OrientationChange) + (void)load { @@ -114,4 +120,8 @@ static bool _isDeviceRotating = false; return _isDeviceRotating; } +- (void)_updateToInterfaceOrientation:(int)arg1 duration:(double)arg2 force:(BOOL)arg3 { + [super _updateToInterfaceOrientation:arg1 duration:arg2 force:arg3]; +} + @end diff --git a/submodules/UndoUI/BUILD b/submodules/UndoUI/BUILD index 22bb401273..c3a6d09fe4 100644 --- a/submodules/UndoUI/BUILD +++ b/submodules/UndoUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift index 5c0e1d6143..f22d8dc8a3 100644 --- a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift +++ b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift @@ -504,7 +504,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in return nil }), textAlignment: .natural) self.textNode.attributedText = attributedText - self.avatarNode?.setPeer(context: context, theme: presentationData.theme, peer: peer, overrideImage: nil, emptyColor: presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: true) + self.avatarNode?.setPeer(context: context, theme: presentationData.theme, peer: EnginePeer(peer), overrideImage: nil, emptyColor: presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: true) displayUndo = false self.originalRemainingSeconds = 3 diff --git a/submodules/UrlEscaping/BUILD b/submodules/UrlEscaping/BUILD index 9e665e590d..ac14d9b3ff 100644 --- a/submodules/UrlEscaping/BUILD +++ b/submodules/UrlEscaping/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], visibility = [ "//visibility:public", ], diff --git a/submodules/UrlEscaping/Sources/UrlEscaping.swift b/submodules/UrlEscaping/Sources/UrlEscaping.swift index 59f89b869b..a4cb5d97bb 100644 --- a/submodules/UrlEscaping/Sources/UrlEscaping.swift +++ b/submodules/UrlEscaping/Sources/UrlEscaping.swift @@ -47,10 +47,9 @@ public func explicitUrl(_ url: String) -> String { public func urlEncodedStringFromString(_ string: String) -> String { var nsString: NSString = string as NSString - if let value = nsString.replacingPercentEscapes(using: String.Encoding.utf8.rawValue) { + if let value = nsString.removingPercentEncoding { nsString = value as NSString } - - let result = CFURLCreateStringByAddingPercentEscapes(nil, nsString as CFString, nil, "?!@#$^&%*+=,:;'\"`<>()[]{}/\\|~ " as CFString, CFStringConvertNSStringEncodingToEncoding(String.Encoding.utf8.rawValue))! - return result as String + + return nsString.addingPercentEncoding(withAllowedCharacters: CharacterSet(charactersIn: "?!@#$^&%*+=,:;'\"`<>()[]{}/\\|~ ")) ?? "" } diff --git a/submodules/UrlHandling/BUILD b/submodules/UrlHandling/BUILD index 9da093721e..4d6dcfb0a7 100644 --- a/submodules/UrlHandling/BUILD +++ b/submodules/UrlHandling/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/Postbox:Postbox", diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index 9599c8799c..bed77ec35c 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -475,15 +475,15 @@ public func parseProxyUrl(_ url: String) -> (host: String, port: Int32, username for scheme in schemes { let basePrefix = scheme + basePath + "/" if url.lowercased().hasPrefix(basePrefix) { - if let internalUrl = parseInternalUrl(query: String(url[basePrefix.endIndex...])), case let .proxy(proxy) = internalUrl { - return (proxy.host, proxy.port, proxy.username, proxy.password, proxy.secret) + if let internalUrl = parseInternalUrl(query: String(url[basePrefix.endIndex...])), case let .proxy(host, port, username, password, secret) = internalUrl { + return (host, port, username, password, secret) } } } } if let parsedUrl = URL(string: url), parsedUrl.scheme == "tg", let host = parsedUrl.host, let query = parsedUrl.query { - if let internalUrl = parseInternalUrl(query: host + "?" + query), case let .proxy(proxy) = internalUrl { - return (proxy.host, proxy.port, proxy.username, proxy.password, proxy.secret) + if let internalUrl = parseInternalUrl(query: host + "?" + query), case let .proxy(host, port, username, password, secret) = internalUrl { + return (host, port, username, password, secret) } } diff --git a/submodules/UrlWhitelist/BUILD b/submodules/UrlWhitelist/BUILD index dadbd3e80a..c68f3d9b49 100644 --- a/submodules/UrlWhitelist/BUILD +++ b/submodules/UrlWhitelist/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], visibility = [ "//visibility:public", ], diff --git a/submodules/WallpaperBackgroundNode/BUILD b/submodules/WallpaperBackgroundNode/BUILD index 2d047a9c80..1dbe792985 100644 --- a/submodules/WallpaperBackgroundNode/BUILD +++ b/submodules/WallpaperBackgroundNode/BUILD @@ -7,6 +7,7 @@ swift_library( "Sources/**/*.swift", ]), copts = [ + "-warnings-as-errors", ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/WallpaperResources/BUILD b/submodules/WallpaperResources/BUILD index bb2ec51701..50a52d3bdd 100644 --- a/submodules/WallpaperResources/BUILD +++ b/submodules/WallpaperResources/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramCore:TelegramCore", "//submodules/Postbox:Postbox", diff --git a/submodules/WallpaperResources/Sources/WallpaperCache.swift b/submodules/WallpaperResources/Sources/WallpaperCache.swift index f5e5d10ba1..fd4f6bef3e 100644 --- a/submodules/WallpaperResources/Sources/WallpaperCache.swift +++ b/submodules/WallpaperResources/Sources/WallpaperCache.swift @@ -71,12 +71,12 @@ public func cachedWallpaper(account: Account, slug: String, settings: WallpaperS } public func updateCachedWallpaper(account: Account, wallpaper: TelegramWallpaper) { - guard case let .file(file) = wallpaper, file.id != 0 else { + guard case let .file(id, _, _, _, _, _, slug, _, _) = wallpaper, id != 0 else { return } let _ = (account.postbox.transaction { transaction in let key = ValueBoxKey(length: 8) - key.setInt64(0, value: Int64(bitPattern: file.slug.persistentHashValue)) + key.setInt64(0, value: Int64(bitPattern: slug.persistentHashValue)) let id = ItemCacheEntryId(collectionId: ApplicationSpecificItemCacheCollectionId.cachedWallpapers, key: key) transaction.putItemCacheEntry(id: id, entry: CachedWallpaper(wallpaper: wallpaper), collectionSpec: collectionSpec) }).start() diff --git a/submodules/WallpaperResources/Sources/WallpaperResources.swift b/submodules/WallpaperResources/Sources/WallpaperResources.swift index 26425f5752..84466da289 100644 --- a/submodules/WallpaperResources/Sources/WallpaperResources.swift +++ b/submodules/WallpaperResources/Sources/WallpaperResources.swift @@ -87,28 +87,21 @@ public func wallpaperDatas(account: Account, accountManager: AccountManager, fil } } else { let fetchedThumbnail: Signal - if let _ = decodedThumbnailData, false { - fetchedThumbnail = .complete() - } else { - fetchedThumbnail = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: representations[smallestIndex].reference) - } + fetchedThumbnail = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: representations[smallestIndex].reference) let fetchedFullSize = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: representations[largestIndex].reference) let thumbnailData: Signal - if let decodedThumbnailData = decodedThumbnailData, false { - thumbnailData = .single(decodedThumbnailData) - } else { - thumbnailData = Signal { subscriber in - let fetchedDisposable = fetchedThumbnail.start() - let thumbnailDisposable = account.postbox.mediaBox.resourceData(smallestRepresentation.resource).start(next: { next in - subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: [])) - }, error: subscriber.putError, completed: subscriber.putCompletion) - - return ActionDisposable { - fetchedDisposable.dispose() - thumbnailDisposable.dispose() - } + + thumbnailData = Signal { subscriber in + let fetchedDisposable = fetchedThumbnail.start() + let thumbnailDisposable = account.postbox.mediaBox.resourceData(smallestRepresentation.resource).start(next: { next in + subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: [])) + }, error: subscriber.putError, completed: subscriber.putCompletion) + + return ActionDisposable { + fetchedDisposable.dispose() + thumbnailDisposable.dispose() } } @@ -889,8 +882,7 @@ public func telegramThemeData(account: Account, accountManager: AccountManager, accountManager.mediaBox.storeResourceData(reference.resource.id, data: data) } subscriber.putNext(next) - }, error: { error in - subscriber.putError(error) + }, error: { _ in }, completed: { subscriber.putCompletion() }) @@ -943,8 +935,8 @@ public func drawThemeImage(context c: CGContext, theme: PresentationTheme, wallp let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: drawingRect.height), end: CGPoint(x: 0.0, y: 0.0), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation]) } - case let .file(file): - if file.isPattern, let intensity = file.settings.intensity, intensity < 0 { + case let .file(_, _, _, _, isPattern, _, _, _, settings): + if isPattern, let intensity = settings.intensity, intensity < 0 { c.setFillColor(UIColor.black.cgColor) c.fill(CGRect(origin: CGPoint(), size: size)) } else { @@ -1119,8 +1111,7 @@ public func themeImage(account: Account, accountManager: AccountManager, source: accountManager.mediaBox.storeResourceData(reference.resource.id, data: data) } subscriber.putNext(next) - }, error: { error in - subscriber.putError(error) + }, error: { _ in }, completed: { subscriber.putCompletion() }) @@ -1152,31 +1143,31 @@ public func themeImage(account: Account, accountManager: AccountManager, source: let data = theme |> mapToSignal { (theme, thumbnailData) -> Signal<(PresentationTheme?, WallpaperImage?, Data?), NoError> in if let theme = theme { - if case let .file(file) = theme.chat.defaultWallpaper { - return cachedWallpaper(account: account, slug: file.slug, settings: file.settings) + if case let .file(_, _, _, _, _, _, slug, _, settings) = theme.chat.defaultWallpaper { + return cachedWallpaper(account: account, slug: slug, settings: settings) |> mapToSignal { wallpaper -> Signal<(PresentationTheme?, WallpaperImage?, Data?), NoError> in - if let wallpaper = wallpaper, case let .file(file) = wallpaper.wallpaper { + if let wallpaper = wallpaper, case let .file(_, _, _, _, _, _, slug, file, settings) = wallpaper.wallpaper { var convertedRepresentations: [ImageRepresentationWithReference] = [] - convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.file.resource, progressiveSizes: [], immediateThumbnailData: nil), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource))) - return wallpaperDatas(account: account, accountManager: accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false) + convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.resource, progressiveSizes: [], immediateThumbnailData: nil), reference: .wallpaper(wallpaper: .slug(slug), resource: file.resource))) + return wallpaperDatas(account: account, accountManager: accountManager, fileReference: .standalone(media: file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false) |> mapToSignal { _, fullSizeData, complete -> Signal<(PresentationTheme?, WallpaperImage?, Data?), NoError> in guard complete, let fullSizeData = fullSizeData else { return .complete() } - accountManager.mediaBox.storeResourceData(file.file.resource.id, data: fullSizeData) - let _ = accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 720.0, height: 720.0), mode: .aspectFit), complete: true, fetch: true).start() + accountManager.mediaBox.storeResourceData(file.resource.id, data: fullSizeData) + let _ = accountManager.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 720.0, height: 720.0), mode: .aspectFit), complete: true, fetch: true).start() - if wallpaper.wallpaper.isPattern, !file.settings.colors.isEmpty, let intensity = file.settings.intensity { - return accountManager.mediaBox.resourceData(file.file.resource) + if wallpaper.wallpaper.isPattern, !settings.colors.isEmpty, let intensity = settings.intensity { + return accountManager.mediaBox.resourceData(file.resource) |> mapToSignal { data in if data.complete, let imageData = try? Data(contentsOf: URL(fileURLWithPath: data.path)) { - return .single((theme, .pattern(data: imageData, colors: file.settings.colors, intensity: intensity), thumbnailData)) + return .single((theme, .pattern(data: imageData, colors: settings.colors, intensity: intensity), thumbnailData)) } else { return .complete() } } - } else if file.settings.blur { - return accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true) + } else if settings.blur { + return accountManager.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true) |> mapToSignal { data in if data.complete, let data = try? Data(contentsOf: URL(fileURLWithPath: data.path)), let image = UIImage(data: data) { return .single((theme, .image(image), thumbnailData)) @@ -1290,244 +1281,133 @@ public func themeImage(account: Account, accountManager: AccountManager, source: public func themeIconImage(account: Account, accountManager: AccountManager, theme: PresentationThemeReference, color: PresentationThemeAccentColor?, wallpaper: TelegramWallpaper? = nil) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { let colorsSignal: Signal<((UIColor, UIColor?, [UInt32]), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError> - if false, case let .builtin(theme) = theme { - let incomingColor: UIColor - let outgoingColor: (UIColor, UIColor) - var accentColor = color?.color - var bubbleColors = color?.plainBubbleColors - var topBackgroundColor: UIColor - var bottomBackgroundColor: UIColor? - switch theme { - case .dayClassic: - incomingColor = UIColor(rgb: 0xffffff) - if let accentColor = accentColor { - if let wallpaper = wallpaper, case let .file(file) = wallpaper { - topBackgroundColor = file.settings.colors.first.flatMap { UIColor(rgb: $0) } ?? UIColor(rgb: 0xd6e2ee) - if file.settings.colors.count >= 2 { - bottomBackgroundColor = UIColor(rgb: file.settings.colors[1]) - } - } else { - if let bubbleColors = bubbleColors { - topBackgroundColor = UIColor(rgb: 0xd6e2ee) - } else { - topBackgroundColor = accentColor.withMultiplied(hue: 1.019, saturation: 0.867, brightness: 0.965) - } - } - if let bubbleColors = bubbleColors { - topBackgroundColor = UIColor(rgb: 0xd6e2ee) - outgoingColor = bubbleColors - } else { - topBackgroundColor = accentColor.withMultiplied(hue: 1.019, saturation: 0.867, brightness: 0.965) - let hsb = accentColor.hsb - let bubbleColor = UIColor(hue: hsb.0, saturation: hsb.2 > 0.0 ? 0.14 : 0.0, brightness: 0.79 + hsb.2 * 0.21, alpha: 1.0) - outgoingColor = (bubbleColor, bubbleColor) - } - } else { - if let wallpaper = wallpaper, case let .file(file) = wallpaper { - topBackgroundColor = file.settings.colors.first.flatMap { UIColor(rgb: $0) } ?? UIColor(rgb: 0xd6e2ee) - if file.settings.colors.count >= 2 { - bottomBackgroundColor = UIColor(rgb: file.settings.colors[1]) - } - } else if let wallpaper = wallpaper, case let .gradient(_, colors, _) = wallpaper { - topBackgroundColor = colors.first.flatMap { UIColor(rgb: $0) } ?? UIColor(rgb: 0xd6e2ee) - if colors.count >= 2 { - bottomBackgroundColor = UIColor(rgb: colors[1]) - } - } else { - topBackgroundColor = defaultBuiltinWallpaperGradientColors[0] - bottomBackgroundColor = defaultBuiltinWallpaperGradientColors[1] - } - outgoingColor = (UIColor(rgb: 0xe1ffc7), UIColor(rgb: 0xe1ffc7)) - } - case .day: - topBackgroundColor = UIColor(rgb: 0xffffff) - incomingColor = UIColor(rgb: 0xd5dde6) - if accentColor == nil { - accentColor = UIColor(rgb: 0x007aff) - } - outgoingColor = bubbleColors ?? (accentColor!, accentColor!) - case .night: - topBackgroundColor = UIColor(rgb: 0x000000) - incomingColor = UIColor(rgb: 0x1f1f1f) - if accentColor == nil || accentColor?.rgb == 0xffffff { - accentColor = UIColor(rgb: 0x313131) - } - outgoingColor = bubbleColors ?? (accentColor!, accentColor!) - case .nightAccent: - let accentColor = accentColor ?? UIColor(rgb: 0x007aff) - topBackgroundColor = accentColor.withMultiplied(hue: 1.024, saturation: 0.573, brightness: 0.18) - incomingColor = accentColor.withMultiplied(hue: 1.024, saturation: 0.585, brightness: 0.25) - let accentBubbleColor = accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59) - outgoingColor = bubbleColors ?? (accentBubbleColor, accentBubbleColor) - } - var colors: [UInt32] = [] - var rotation: Int32? - if let wallpaper = wallpaper { + var reference: MediaResourceReference? + if case let .local(theme) = theme { + reference = .standalone(resource: theme.resource) + } else if case let .cloud(theme) = theme, let resource = theme.theme.file?.resource { + reference = .theme(theme: .slug(theme.theme.slug), resource: resource) + } + + let themeSignal: Signal + if case let .builtin(theme) = theme { + themeSignal = .single(makeDefaultPresentationTheme(reference: theme, serviceBackgroundColor: nil)) + } else if case let .cloud(theme) = theme, let settings = theme.theme.settings { + themeSignal = Signal { subscriber in + let theme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: .builtin(PresentationBuiltinThemeReference(baseTheme: settings.baseTheme)), accentColor: UIColor(argb: settings.accentColor), backgroundColors: [], bubbleColors: settings.messageColors.flatMap { (UIColor(argb: $0.top), UIColor(argb: $0.bottom)) }, wallpaper: settings.wallpaper, serviceBackgroundColor: nil, preview: false) + subscriber.putNext(theme) + subscriber.putCompletion() + + return EmptyDisposable + } + } else if let reference = reference { + themeSignal = telegramThemeData(account: account, accountManager: accountManager, reference: reference, synchronousLoad: false) + |> map { data -> PresentationTheme? in + if let data = data, let theme = makePresentationTheme(data: data) { + return theme + } else { + return nil + } + } + } else { + themeSignal = .never() + } + + colorsSignal = themeSignal + |> mapToSignal { theme -> Signal<((UIColor, UIColor?, [UInt32]), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError> in + if let theme = theme { + var wallpaperSignal: Signal<((UIColor, UIColor?, [UInt32]), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError> = .complete() + var rotation: Int32? + var backgroundColor: (UIColor, UIColor?, [UInt32]) + let incomingColor = (theme.chat.message.incoming.bubble.withoutWallpaper.fill, theme.chat.message.incoming.bubble.withoutWallpaper.gradientFill) + let outgoingColor = (theme.chat.message.outgoing.bubble.withoutWallpaper.fill, theme.chat.message.outgoing.bubble.withoutWallpaper.gradientFill) + let wallpaper = wallpaper ?? theme.chat.defaultWallpaper switch wallpaper { + case .builtin: + backgroundColor = (UIColor(rgb: 0xd6e2ee), nil, []) case let .color(color): - colors = [color] - topBackgroundColor = UIColor(rgb: color) - case let .gradient(_, colorsValue, settings): - colors = colorsValue - if colors.count >= 1 { - topBackgroundColor = UIColor(rgb: colors[0]) - } + backgroundColor = (UIColor(rgb: color), nil, []) + case let .gradient(_, colors, settings): if colors.count >= 2 { - bottomBackgroundColor = UIColor(rgb: colors[1]) + backgroundColor = (UIColor(rgb: colors[0]), UIColor(rgb: colors[1]), colors) + } else { + backgroundColor = (.white, nil, []) } rotation = settings.rotation - case let .file(file): - if file.isPattern, let intensity = file.settings.intensity, intensity < 0 { - colors = [0x000000] - topBackgroundColor = .black - bottomBackgroundColor = .black - } else { - colors = file.settings.colors - if !file.settings.colors.isEmpty { - topBackgroundColor = UIColor(rgb: file.settings.colors[0]) - if file.settings.colors.count >= 2 { - bottomBackgroundColor = UIColor(rgb: file.settings.colors[1]) - } - } - rotation = file.settings.rotation - } - default: - colors = [0xd6e2ee] - topBackgroundColor = UIColor(rgb: 0xd6e2ee) - } - } else { - colors = defaultBuiltinWallpaperGradientColors.map(\.rgb) - } - - colorsSignal = .single(((topBackgroundColor, bottomBackgroundColor, colors), (incomingColor, incomingColor), outgoingColor, nil, rotation)) - } else { - var reference: MediaResourceReference? - if case let .local(theme) = theme { - reference = .standalone(resource: theme.resource) - } else if case let .cloud(theme) = theme, let resource = theme.theme.file?.resource { - reference = .theme(theme: .slug(theme.theme.slug), resource: resource) - } - - let themeSignal: Signal - if case let .builtin(theme) = theme { - themeSignal = .single(makeDefaultPresentationTheme(reference: theme, serviceBackgroundColor: nil)) - } else if case let .cloud(theme) = theme, let settings = theme.theme.settings { - themeSignal = Signal { subscriber in - let theme = makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: .builtin(PresentationBuiltinThemeReference(baseTheme: settings.baseTheme)), accentColor: UIColor(argb: settings.accentColor), backgroundColors: [], bubbleColors: settings.messageColors.flatMap { (UIColor(argb: $0.top), UIColor(argb: $0.bottom)) }, wallpaper: settings.wallpaper, serviceBackgroundColor: nil, preview: false) - subscriber.putNext(theme) - subscriber.putCompletion() - - return EmptyDisposable - } - } else if let reference = reference { - themeSignal = telegramThemeData(account: account, accountManager: accountManager, reference: reference, synchronousLoad: false) - |> map { data -> PresentationTheme? in - if let data = data, let theme = makePresentationTheme(data: data) { - return theme - } else { - return nil - } - } - } else { - themeSignal = .never() - } - - colorsSignal = themeSignal - |> mapToSignal { theme -> Signal<((UIColor, UIColor?, [UInt32]), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError> in - if let theme = theme { - var wallpaperSignal: Signal<((UIColor, UIColor?, [UInt32]), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError> = .complete() - var rotation: Int32? - var backgroundColor: (UIColor, UIColor?, [UInt32]) - let incomingColor = (theme.chat.message.incoming.bubble.withoutWallpaper.fill, theme.chat.message.incoming.bubble.withoutWallpaper.gradientFill) - let outgoingColor = (theme.chat.message.outgoing.bubble.withoutWallpaper.fill, theme.chat.message.outgoing.bubble.withoutWallpaper.gradientFill) - let wallpaper = wallpaper ?? theme.chat.defaultWallpaper - switch wallpaper { - case .builtin: - backgroundColor = (UIColor(rgb: 0xd6e2ee), nil, []) - case let .color(color): - backgroundColor = (UIColor(rgb: color), nil, []) - case let .gradient(_, colors, settings): - if colors.count >= 2 { - backgroundColor = (UIColor(rgb: colors[0]), UIColor(rgb: colors[1]), colors) - } else { - backgroundColor = (.white, nil, []) - } - rotation = settings.rotation - case .image: + case .image: + backgroundColor = (.black, nil, []) + case let .file(_, _, _, _, isPattern, _, slug, _, settings): + rotation = settings.rotation + if isPattern, let intensity = settings.intensity, intensity < 0 { backgroundColor = (.black, nil, []) - case let .file(file): - rotation = file.settings.rotation - if file.isPattern, let intensity = file.settings.intensity, intensity < 0 { - backgroundColor = (.black, nil, []) - } else if !file.settings.colors.isEmpty { - var bottomColor: UIColor? - if file.settings.colors.count >= 2 { - bottomColor = UIColor(rgb: file.settings.colors[1]) - } - backgroundColor = (UIColor(rgb: file.settings.colors[0]), bottomColor, file.settings.colors) - } else { - backgroundColor = (theme.chatList.backgroundColor, nil, []) + } else if !settings.colors.isEmpty { + var bottomColor: UIColor? + if settings.colors.count >= 2 { + bottomColor = UIColor(rgb: settings.colors[1]) } - wallpaperSignal = cachedWallpaper(account: account, slug: file.slug, settings: file.settings) - |> mapToSignal { wallpaper in - if let wallpaper = wallpaper, case let .file(file) = wallpaper.wallpaper { - var effectiveBackgroundColor = backgroundColor - if !file.settings.colors.isEmpty { - var bottomColor: UIColor? - if file.settings.colors.count >= 2 { - bottomColor = UIColor(rgb: file.settings.colors[1]) - } - effectiveBackgroundColor = (UIColor(rgb: file.settings.colors[0]), bottomColor, file.settings.colors) + backgroundColor = (UIColor(rgb: settings.colors[0]), bottomColor, settings.colors) + } else { + backgroundColor = (theme.chatList.backgroundColor, nil, []) + } + wallpaperSignal = cachedWallpaper(account: account, slug: slug, settings: settings) + |> mapToSignal { wallpaper in + if let wallpaper = wallpaper, case let .file(_, _, _, _, _, _, _, file, settings) = wallpaper.wallpaper { + var effectiveBackgroundColor = backgroundColor + if !settings.colors.isEmpty { + var bottomColor: UIColor? + if settings.colors.count >= 2 { + bottomColor = UIColor(rgb: settings.colors[1]) } + effectiveBackgroundColor = (UIColor(rgb: settings.colors[0]), bottomColor, settings.colors) + } + + var convertedRepresentations: [ImageRepresentationWithReference] = [] + convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.resource, progressiveSizes: [], immediateThumbnailData: nil), reference: .wallpaper(wallpaper: .slug(slug), resource: file.resource))) + return wallpaperDatas(account: account, accountManager: accountManager, fileReference: .standalone(media: file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false) + |> mapToSignal { _, fullSizeData, complete -> Signal<((UIColor, UIColor?, [UInt32]), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError> in + guard complete, let fullSizeData = fullSizeData else { + return .complete() + } + accountManager.mediaBox.storeResourceData(file.resource.id, data: fullSizeData) + let _ = accountManager.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 720.0, height: 720.0), mode: .aspectFit), complete: true, fetch: true).start() - var convertedRepresentations: [ImageRepresentationWithReference] = [] - convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.file.resource, progressiveSizes: [], immediateThumbnailData: nil), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource))) - return wallpaperDatas(account: account, accountManager: accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false) - |> mapToSignal { _, fullSizeData, complete -> Signal<((UIColor, UIColor?, [UInt32]), (UIColor, UIColor), (UIColor, UIColor), UIImage?, Int32?), NoError> in - guard complete, let fullSizeData = fullSizeData else { - return .complete() - } - accountManager.mediaBox.storeResourceData(file.file.resource.id, data: fullSizeData) - let _ = accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 720.0, height: 720.0), mode: .aspectFit), complete: true, fetch: true).start() - - if wallpaper.wallpaper.isPattern { - if !file.settings.colors.isEmpty, let intensity = file.settings.intensity { - if intensity < 0 { - return .single(((.black, nil, []), incomingColor, outgoingColor, nil, rotation)) - } else { - return .single((effectiveBackgroundColor, incomingColor, outgoingColor, nil, rotation)) - } + if wallpaper.wallpaper.isPattern { + if !settings.colors.isEmpty, let intensity = settings.intensity { + if intensity < 0 { + return .single(((.black, nil, []), incomingColor, outgoingColor, nil, rotation)) } else { - return .complete() + return .single((effectiveBackgroundColor, incomingColor, outgoingColor, nil, rotation)) } - } else if file.settings.blur { - return accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true) - |> mapToSignal { _ in - if let image = UIImage(data: fullSizeData) { - return .single((backgroundColor, incomingColor, outgoingColor, image, rotation)) - } else { - return .complete() - } - } - } else if let image = UIImage(data: fullSizeData) { - return .single((backgroundColor, incomingColor, outgoingColor, image, rotation)) } else { return .complete() } + } else if settings.blur { + return accountManager.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true) + |> mapToSignal { _ in + if let image = UIImage(data: fullSizeData) { + return .single((backgroundColor, incomingColor, outgoingColor, image, rotation)) + } else { + return .complete() + } + } + } else if let image = UIImage(data: fullSizeData) { + return .single((backgroundColor, incomingColor, outgoingColor, image, rotation)) + } else { + return .complete() } - } else { - return .complete() } + } else { + return .complete() } - } - return .single((backgroundColor, incomingColor, outgoingColor, nil, rotation)) - |> then(wallpaperSignal) - } else { - return .complete() + } } + return .single((backgroundColor, incomingColor, outgoingColor, nil, rotation)) + |> then(wallpaperSignal) + } else { + return .complete() } } + return colorsSignal |> map { colors in return { arguments in diff --git a/submodules/WatchBridge/BUILD b/submodules/WatchBridge/BUILD index 515c17984f..5766afcb8f 100644 --- a/submodules/WatchBridge/BUILD +++ b/submodules/WatchBridge/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/SSignalKit/SSignalKit:SSignalKit", diff --git a/submodules/WatchBridge/Sources/WatchRequestHandlers.swift b/submodules/WatchBridge/Sources/WatchRequestHandlers.swift index e9b97e3698..dca4e0f729 100644 --- a/submodules/WatchBridge/Sources/WatchRequestHandlers.swift +++ b/submodules/WatchBridge/Sources/WatchRequestHandlers.swift @@ -60,7 +60,7 @@ final class WatchChatListHandler: WatchRequestHandler { users = users.merging(chatUsers, uniquingKeysWith: { (_, last) in last }) } } - subscriber?.putNext([ TGBridgeChatsArrayKey: chats, TGBridgeUsersDictionaryKey: users ]) + subscriber.putNext([ TGBridgeChatsArrayKey: chats, TGBridgeUsersDictionaryKey: users ]) }) return SBlockDisposable { @@ -108,7 +108,7 @@ final class WatchChatMessagesHandler: WatchRequestHandler { users = users.merging(messageUsers, uniquingKeysWith: { (_, last) in last }) } } - subscriber?.putNext([ TGBridgeMessagesArrayKey: messages, TGBridgeUsersDictionaryKey: users ]) + subscriber.putNext([ TGBridgeMessagesArrayKey: messages, TGBridgeUsersDictionaryKey: users ]) }) return SBlockDisposable { @@ -128,9 +128,9 @@ final class WatchChatMessagesHandler: WatchRequestHandler { } }) let disposable = signal.start(next: { _ in - subscriber?.putNext(true) + subscriber.putNext(true) }, completed: { - subscriber?.putCompletion() + subscriber.putCompletion() }) return SBlockDisposable { @@ -163,9 +163,9 @@ final class WatchChatMessagesHandler: WatchRequestHandler { if peerId.namespace != Namespaces.Peer.CloudUser { response[TGBridgeChatKey] = peers[makeBridgeIdentifier(peerId)] } - subscriber?.putNext(response) + subscriber.putNext(response) } - subscriber?.putCompletion() + subscriber.putCompletion() }) return SBlockDisposable { disposable.dispose() @@ -237,9 +237,9 @@ final class WatchSendMessageHandler: WatchRequestHandler { }) let disposable = signal.start(next: { _ in - subscriber?.putNext(true) + subscriber.putNext(true) }, completed: { - subscriber?.putCompletion() + subscriber.putCompletion() }) return SBlockDisposable { @@ -272,9 +272,9 @@ final class WatchPeerInfoHandler: WatchRequestHandler { }) let disposable = signal.start(next: { view in if let user = makeBridgeUser(peerViewMainPeer(view), presence: view.peerPresences[view.peerId], cachedData: view.cachedData) { - subscriber?.putNext([user.identifier: user]) + subscriber.putNext([user.identifier: user]) } else { - subscriber?.putCompletion() + subscriber.putCompletion() } }) @@ -295,7 +295,7 @@ final class WatchPeerInfoHandler: WatchRequestHandler { }) let disposable = signal.start(next: { view in let (chat, users) = makeBridgeChat(peerViewMainPeer(view), view: view) - subscriber?.putNext([ TGBridgeChatKey: chat, TGBridgeUsersDictionaryKey: users ]) + subscriber.putNext([ TGBridgeChatKey: chat, TGBridgeUsersDictionaryKey: users ]) }) return SBlockDisposable { @@ -430,9 +430,9 @@ final class WatchMediaHandler: WatchRequestHandler { if let image = image, let imageData = image.jpegData(compressionQuality: compressionRate) { sendData(manager: manager, data: imageData, key: key, ext: ".jpg", type: TGBridgeIncomingFileTypeImage, forceAsData: true) } - subscriber?.putNext(key) + subscriber.putNext(key) }, completed: { - subscriber?.putCompletion() + subscriber.putCompletion() }) return SBlockDisposable { @@ -497,9 +497,9 @@ final class WatchMediaHandler: WatchRequestHandler { if let image = image, let imageData = image.jpegData(compressionQuality: 0.2) { sendData(manager: manager, data: imageData, key: key, ext: ".jpg", type: TGBridgeIncomingFileTypeImage, forceAsData: args.notification) } - subscriber?.putNext(key) + subscriber.putNext(key) }, completed: { - subscriber?.putCompletion() + subscriber.putCompletion() }) return SBlockDisposable { @@ -582,9 +582,9 @@ final class WatchMediaHandler: WatchRequestHandler { if let image = image, let imageData = image.jpegData(compressionQuality: 0.5) { sendData(manager: manager, data: imageData, key: key, ext: ".jpg", type: TGBridgeIncomingFileTypeImage, forceAsData: args.notification) } - subscriber?.putNext(key) + subscriber.putNext(key) }, completed: { - subscriber?.putCompletion() + subscriber.putCompletion() }) return SBlockDisposable { @@ -634,7 +634,7 @@ final class WatchStickersHandler: WatchRequestHandler { } } } - subscriber?.putNext(stickers) + subscriber.putNext(stickers) }) return SBlockDisposable { @@ -693,9 +693,9 @@ final class WatchAudioHandler: WatchRequestHandler { let disposable = signal.start(next: { path in let _ = manager.sendFile(url: URL(fileURLWithPath: path), metadata: [TGBridgeIncomingFileTypeKey: TGBridgeIncomingFileTypeAudio, TGBridgeIncomingFileIdentifierKey: key]).start() - subscriber?.putNext(key) + subscriber.putNext(key) }, completed: { - subscriber?.putCompletion() + subscriber.putCompletion() }) return SBlockDisposable { @@ -780,7 +780,7 @@ final class WatchLocationHandler: WatchRequestHandler { venues.append(venue) } } - subscriber?.putNext(venues) + subscriber.putNext(venues) }) return SBlockDisposable { @@ -824,7 +824,7 @@ final class WatchPeerSettingsHandler: WatchRequestHandler { blocked = cachedData.isBlocked } - subscriber?.putNext([ "muted": muted, "blocked": blocked ]) + subscriber.putNext([ "muted": muted, "blocked": blocked ]) }) return SBlockDisposable { @@ -858,9 +858,9 @@ final class WatchPeerSettingsHandler: WatchRequestHandler { }) let disposable = signal.start(next: { _ in - subscriber?.putNext(true) + subscriber.putNext(true) }, completed: { - subscriber?.putCompletion() + subscriber.putCompletion() }) return SBlockDisposable { diff --git a/submodules/WatchBridgeAudio/BUILD b/submodules/WatchBridgeAudio/BUILD index 27b6b76576..e3994dc62d 100644 --- a/submodules/WatchBridgeAudio/BUILD +++ b/submodules/WatchBridgeAudio/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/WatchBridgeAudio/Impl:WatchBridgeAudioImpl", diff --git a/submodules/WebSearchUI/BUILD b/submodules/WebSearchUI/BUILD index d93aa711a6..358bc24569 100644 --- a/submodules/WebSearchUI/BUILD +++ b/submodules/WebSearchUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/WebSearchUI/Sources/LegacyWebSearchGallery.swift b/submodules/WebSearchUI/Sources/LegacyWebSearchGallery.swift index 47a58c964e..6c9362e217 100644 --- a/submodules/WebSearchUI/Sources/LegacyWebSearchGallery.swift +++ b/submodules/WebSearchUI/Sources/LegacyWebSearchGallery.swift @@ -55,8 +55,8 @@ class LegacyWebSearchItem: NSObject, TGMediaEditableItem, TGMediaSelectableItem func thumbnailImageSignal() -> SSignal! { return SSignal(generator: { subscriber -> SDisposable? in let disposable = self.thumbnailImage.start(next: { image in - subscriber?.putNext(image) - subscriber?.putCompletion() + subscriber.putNext(image) + subscriber.putCompletion() }) return SBlockDisposable(block: { @@ -69,17 +69,17 @@ class LegacyWebSearchItem: NSObject, TGMediaEditableItem, TGMediaSelectableItem return SSignal { subscriber in let imageDisposable = self.originalImage.start(next: { image in if !image.degraded() { - subscriber?.putNext(1.0) + subscriber.putNext(1.0) } - subscriber?.putNext(image) + subscriber.putNext(image) if !image.degraded() { - subscriber?.putCompletion() + subscriber.putCompletion() } }) let progressDisposable = (self.progress |> deliverOnMainQueue).start(next: { next in - subscriber?.putNext(next) + subscriber.putNext(next) }) return SBlockDisposable { @@ -96,9 +96,9 @@ class LegacyWebSearchItem: NSObject, TGMediaEditableItem, TGMediaSelectableItem func originalImageSignal(_ position: TimeInterval) -> SSignal! { return SSignal(generator: { subscriber -> SDisposable? in let disposable = self.originalImage.start(next: { image in - subscriber?.putNext(image) + subscriber.putNext(image) if !image.degraded() { - subscriber?.putCompletion() + subscriber.putCompletion() } }) @@ -149,7 +149,7 @@ private class LegacyWebSearchGalleryItem: TGModernGalleryImageItem, TGModernGall } private class LegacyWebSearchGalleryItemView: TGModernGalleryImageItemView, TGModernGalleryEditableItemView { - private let readyForTransition = SVariable()! + private let readyForTransition = SVariable() @objc func setHiddenAsBeingEdited(_ hidden: Bool) { self.imageView.isHidden = hidden @@ -162,7 +162,7 @@ private class LegacyWebSearchGalleryItemView: TGModernGalleryImageItemView, TGMo } override func readyForTransitionIn() -> SSignal! { - return self.readyForTransition.signal()!.take(1) + return self.readyForTransition.signal().take(1) } override func setItem(_ item: TGModernGalleryItem!, synchronously: Bool) { @@ -170,7 +170,7 @@ private class LegacyWebSearchGalleryItemView: TGModernGalleryImageItemView, TGMo self._setItem(item) self.imageSize = TGFitSize(item.editableMediaItem().originalSize!, CGSize(width: 1600, height: 1600)) - let signal = item.editingContext.imageSignal(for: item.editableMediaItem())?.map(toSignal: { result -> SSignal? in + let signal = item.editingContext.imageSignal(for: item.editableMediaItem())?.map(toSignal: { result -> SSignal in if let image = result as? UIImage { return SSignal.single(image) } else if result == nil, let mediaItem = item.editableMediaItem() as? LegacyWebSearchItem { @@ -180,7 +180,7 @@ private class LegacyWebSearchGalleryItemView: TGModernGalleryImageItemView, TGMo } }) - self.imageView.setSignal(signal?.deliver(on: SQueue.main())?.afterNext({ [weak self] next in + self.imageView.setSignal(signal?.deliver(on: SQueue.main()).afterNext({ [weak self] next in if let strongSelf = self, let image = next as? UIImage { strongSelf.imageSize = image.size strongSelf.reset() diff --git a/submodules/WebSearchUI/Sources/WebSearchController.swift b/submodules/WebSearchUI/Sources/WebSearchController.swift index ff2cfd27da..7652ef49a9 100644 --- a/submodules/WebSearchUI/Sources/WebSearchController.swift +++ b/submodules/WebSearchUI/Sources/WebSearchController.swift @@ -443,7 +443,6 @@ public final class WebSearchController: ViewController { return .single({ _ in return .contextRequestResult(nil, nil) }) } - let account = self.context.account let context = self.context let contextBot = self.context.engine.peers.resolvePeerByName(name: name) |> mapToSignal { peer -> Signal in diff --git a/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift b/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift index f913b8a3ad..16043dee6f 100644 --- a/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift +++ b/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift @@ -73,14 +73,6 @@ private func gridNodeLayoutForContainerLayout(size: CGSize) -> GridNodeLayoutTyp private struct WebSearchRecentQueryStableId: Hashable { let query: String - - var hashValue: Int { - return query.hashValue - } - - static func ==(lhs: WebSearchRecentQueryStableId, rhs: WebSearchRecentQueryStableId) -> Bool { - return lhs.query == rhs.query - } } private struct WebSearchRecentQueryEntry: Comparable, Identifiable { diff --git a/submodules/WebUI/BUILD b/submodules/WebUI/BUILD index a07198be9e..bbd1827908 100644 --- a/submodules/WebUI/BUILD +++ b/submodules/WebUI/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/AsyncDisplayKit:AsyncDisplayKit", "//submodules/Display:Display", diff --git a/submodules/WebsiteType/BUILD b/submodules/WebsiteType/BUILD index 6ede6cadc4..27297a6dcf 100644 --- a/submodules/WebsiteType/BUILD +++ b/submodules/WebsiteType/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/TelegramCore:TelegramCore", ], diff --git a/submodules/WidgetItems/BUILD b/submodules/WidgetItems/BUILD index 1a1cff6024..97cf2126e7 100644 --- a/submodules/WidgetItems/BUILD +++ b/submodules/WidgetItems/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], visibility = [ "//visibility:public", ], @@ -17,6 +20,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], visibility = [ "//visibility:public", ], diff --git a/submodules/WidgetItemsUtils/BUILD b/submodules/WidgetItemsUtils/BUILD index 3dca4cc574..95ae41d81e 100644 --- a/submodules/WidgetItemsUtils/BUILD +++ b/submodules/WidgetItemsUtils/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/Postbox:Postbox", diff --git a/submodules/WidgetSetupScreen/BUILD b/submodules/WidgetSetupScreen/BUILD index 29632e76f3..2eac6be9de 100644 --- a/submodules/WidgetSetupScreen/BUILD +++ b/submodules/WidgetSetupScreen/BUILD @@ -6,6 +6,9 @@ swift_library( srcs = glob([ "Sources/**/*.swift", ]), + copts = [ + "-warnings-as-errors", + ], deps = [ "//submodules/Display:Display", "//submodules/AsyncDisplayKit:AsyncDisplayKit", diff --git a/submodules/WidgetSetupScreen/Sources/WidgetSetupScreen.swift b/submodules/WidgetSetupScreen/Sources/WidgetSetupScreen.swift index e29cdbadb7..622b8cde32 100644 --- a/submodules/WidgetSetupScreen/Sources/WidgetSetupScreen.swift +++ b/submodules/WidgetSetupScreen/Sources/WidgetSetupScreen.swift @@ -206,8 +206,7 @@ public func widgetSetupScreen(context: AccountContext) -> ViewController { let updateState: ((WidgetSetupScreenControllerState) -> WidgetSetupScreenControllerState) -> Void = { f in statePromise.set(stateValue.modify { f($0) }) } - - var dismissImpl: (() -> Void)? + var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)? var pushControllerImpl: ((ViewController) -> Void)? @@ -372,11 +371,6 @@ public func widgetSetupScreen(context: AccountContext) -> ViewController { } let controller = ItemListController(context: context, state: signal) - dismissImpl = { [weak controller] in - if let controller = controller, let navigationController = controller.navigationController as? NavigationController { - navigationController.filterController(controller, animated: true) - } - } presentControllerImpl = { [weak controller] c, p in if let controller = controller { controller.present(c, in: .window(.root), with: p) diff --git a/versions.json b/versions.json index c2ec49706b..55a2b9955f 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,5 @@ { - "app": "7.9", + "app": "7.9.1", "bazel": "4.0.0", - "xcode": "12.4" + "xcode": "12.5.1" }