diff --git a/Images.xcassets/Chat/EmptyChatIcon.imageset/Contents.json b/Images.xcassets/Chat/EmptyChatIcon.imageset/Contents.json new file mode 100644 index 0000000000..c84a6db8ab --- /dev/null +++ b/Images.xcassets/Chat/EmptyChatIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ModernConversationEmptyListLogo@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Images.xcassets/Chat/EmptyChatIcon.imageset/ModernConversationEmptyListLogo@2x.png b/Images.xcassets/Chat/EmptyChatIcon.imageset/ModernConversationEmptyListLogo@2x.png new file mode 100644 index 0000000000..f12d8d290d Binary files /dev/null and b/Images.xcassets/Chat/EmptyChatIcon.imageset/ModernConversationEmptyListLogo@2x.png differ diff --git a/TelegramUI.xcodeproj/project.pbxproj b/TelegramUI.xcodeproj/project.pbxproj index fd206a9252..5e6d2a5240 100644 --- a/TelegramUI.xcodeproj/project.pbxproj +++ b/TelegramUI.xcodeproj/project.pbxproj @@ -287,6 +287,7 @@ D0BC387F1E40F1CF0044D6FE /* ContactSelectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC387E1E40F1CF0044D6FE /* ContactSelectionController.swift */; }; D0BC38811E40F1D80044D6FE /* ContactSelectionControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC38801E40F1D80044D6FE /* ContactSelectionControllerNode.swift */; }; D0BE383C1E7C3E51000079AF /* StickerPackGalleryController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BE383B1E7C3E51000079AF /* StickerPackGalleryController.swift */; }; + D0C48F441E81D5110075317D /* ChatEmptyItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C48F431E81D5110075317D /* ChatEmptyItem.swift */; }; D0C932361E0988C60074F044 /* ChatButtonKeyboardInputNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C932351E0988C60074F044 /* ChatButtonKeyboardInputNode.swift */; }; D0C932381E09E0EA0074F044 /* ChatBotInfoItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C932371E09E0EA0074F044 /* ChatBotInfoItem.swift */; }; D0C9323C1E0B4AE90074F044 /* DataAndStorageSettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C9323B1E0B4AE90074F044 /* DataAndStorageSettingsController.swift */; }; @@ -369,6 +370,10 @@ D0EF40DD1E72F00E000DFCD4 /* SelectivePrivacySettingsPeersController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EF40DC1E72F00E000DFCD4 /* SelectivePrivacySettingsPeersController.swift */; }; D0EF40DF1E73100D000DFCD4 /* ChatHistoryNavigationStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EF40DE1E73100D000DFCD4 /* ChatHistoryNavigationStack.swift */; }; D0EFD8961DDE8249009E508A /* LegacyLocationPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EFD8951DDE8249009E508A /* LegacyLocationPicker.swift */; }; + D0F3A8AB1E82D83E00B4C64C /* TelegramAccountAuxiliaryMethods.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3A8AA1E82D83E00B4C64C /* TelegramAccountAuxiliaryMethods.swift */; }; + D0F3A8B61E83120A00B4C64C /* FetchResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3A8B51E83120A00B4C64C /* FetchResource.swift */; }; + D0F3A8B81E83125C00B4C64C /* MediaResources.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3A8B71E83125C00B4C64C /* MediaResources.swift */; }; + D0F3A8BA1E831E6300B4C64C /* FetchVideoMediaResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3A8B91E831E6300B4C64C /* FetchVideoMediaResource.swift */; }; D0F53BEC1E784DA900117362 /* ChangePhoneNumberCodeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F53BEB1E784DA900117362 /* ChangePhoneNumberCodeController.swift */; }; D0F53BF71E79593500117362 /* AuthorizationSequenceSignUpController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F53BF61E79593500117362 /* AuthorizationSequenceSignUpController.swift */; }; D0F53BF91E79593F00117362 /* AuthorizationSequenceSignUpControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F53BF81E79593F00117362 /* AuthorizationSequenceSignUpControllerNode.swift */; }; @@ -795,6 +800,7 @@ D0BC387E1E40F1CF0044D6FE /* ContactSelectionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactSelectionController.swift; sourceTree = ""; }; D0BC38801E40F1D80044D6FE /* ContactSelectionControllerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactSelectionControllerNode.swift; sourceTree = ""; }; D0BE383B1E7C3E51000079AF /* StickerPackGalleryController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StickerPackGalleryController.swift; sourceTree = ""; }; + D0C48F431E81D5110075317D /* ChatEmptyItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatEmptyItem.swift; sourceTree = ""; }; D0C932351E0988C60074F044 /* ChatButtonKeyboardInputNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatButtonKeyboardInputNode.swift; sourceTree = ""; }; D0C932371E09E0EA0074F044 /* ChatBotInfoItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatBotInfoItem.swift; sourceTree = ""; }; D0C9323B1E0B4AE90074F044 /* DataAndStorageSettingsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataAndStorageSettingsController.swift; sourceTree = ""; }; @@ -877,6 +883,10 @@ D0EF40DC1E72F00E000DFCD4 /* SelectivePrivacySettingsPeersController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectivePrivacySettingsPeersController.swift; sourceTree = ""; }; D0EF40DE1E73100D000DFCD4 /* ChatHistoryNavigationStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatHistoryNavigationStack.swift; sourceTree = ""; }; D0EFD8951DDE8249009E508A /* LegacyLocationPicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyLocationPicker.swift; sourceTree = ""; }; + D0F3A8AA1E82D83E00B4C64C /* TelegramAccountAuxiliaryMethods.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TelegramAccountAuxiliaryMethods.swift; sourceTree = ""; }; + D0F3A8B51E83120A00B4C64C /* FetchResource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchResource.swift; sourceTree = ""; }; + D0F3A8B71E83125C00B4C64C /* MediaResources.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaResources.swift; sourceTree = ""; }; + D0F3A8B91E831E6300B4C64C /* FetchVideoMediaResource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchVideoMediaResource.swift; sourceTree = ""; }; D0F53BEB1E784DA900117362 /* ChangePhoneNumberCodeController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChangePhoneNumberCodeController.swift; sourceTree = ""; }; D0F53BF61E79593500117362 /* AuthorizationSequenceSignUpController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthorizationSequenceSignUpController.swift; sourceTree = ""; }; D0F53BF81E79593F00117362 /* AuthorizationSequenceSignUpControllerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthorizationSequenceSignUpControllerNode.swift; sourceTree = ""; }; @@ -2117,6 +2127,7 @@ D0F7AB381DCFF87B009AD9A1 /* ChatMessageDateHeader.swift */, D01AC9171DD5033100E8160F /* ChatMessageActionButtonsNode.swift */, D0C932371E09E0EA0074F044 /* ChatBotInfoItem.swift */, + D0C48F431E81D5110075317D /* ChatEmptyItem.swift */, D02298361E0C34E900707F91 /* ChatMessageBackground.swift */, ); name = Items; @@ -2265,6 +2276,7 @@ D05A32DB1E6EFCC2002760B4 /* NumericFormat.swift */, D05B724F1E720597000BD3AD /* PresentationData.swift */, D01C2AAC1E768404001F6F9A /* Markdown.swift */, + D0F3A8AA1E82D83E00B4C64C /* TelegramAccountAuxiliaryMethods.swift */, ); name = Utils; sourceTree = ""; @@ -2278,6 +2290,9 @@ D06879541DB8F1FC00424BBD /* CachedResourceRepresentations.swift */, D06879561DB8F22200424BBD /* FetchCachedRepresentations.swift */, D04662801E68BA64006FAFC4 /* TransformOutgoingMessageMedia.swift */, + D0F3A8B51E83120A00B4C64C /* FetchResource.swift */, + D0F3A8B71E83125C00B4C64C /* MediaResources.swift */, + D0F3A8B91E831E6300B4C64C /* FetchVideoMediaResource.swift */, ); name = Resources; sourceTree = ""; @@ -2552,6 +2567,7 @@ D0DF0C9A1D81FF3F008AEB01 /* ChatInputContextPanelNode.swift in Sources */, D0D03B1B1DECB0FE00220C46 /* info.c in Sources */, D0215D4A1E041CAF001A0B1E /* InstantPageMediaItem.swift in Sources */, + D0C48F441E81D5110075317D /* ChatEmptyItem.swift in Sources */, D087751E1E3F579300A97350 /* CounterContollerTitleView.swift in Sources */, D050F2131E48B61500988324 /* PhoneInputNode.swift in Sources */, D08775141E3F4A7700A97350 /* ContactListNameIndexHeader.swift in Sources */, @@ -2632,6 +2648,7 @@ D0D03B1C1DECB0FE00220C46 /* internal.c in Sources */, D0F69D231D6B87D30046BCD6 /* FFMpegMediaFrameSourceContext.swift in Sources */, D0F69E8D1D6B8C850046BCD6 /* Localizable.swift in Sources */, + D0F3A8BA1E831E6300B4C64C /* FetchVideoMediaResource.swift in Sources */, D049EAE41E44949F00A2CD3A /* HorizontalStickerGridItem.swift in Sources */, D0736F231DF496D000F2C02A /* PeerMediaAudioPlaylist.swift in Sources */, D02958021D6F0D5F00360E5E /* TapLongTapOrDoubleTapGestureRecognizer.swift in Sources */, @@ -2691,6 +2708,7 @@ D01C2AA11E758F90001F6F9A /* NavigateToChatController.swift in Sources */, D0F69E391D6B8B030046BCD6 /* ChatMessageMediaBubbleContentNode.swift in Sources */, D0F69D351D6B87D30046BCD6 /* MediaFrameSource.swift in Sources */, + D0F3A8AB1E82D83E00B4C64C /* TelegramAccountAuxiliaryMethods.swift in Sources */, D0736F2A1DF4D5FF00F2C02A /* MediaNavigationAccessoryPanel.swift in Sources */, D087751C1E3F542500A97350 /* ContactMultiselectionControllerNode.swift in Sources */, D0E7A1BD1D8C246D00C37A6F /* ChatHistoryListNode.swift in Sources */, @@ -2742,6 +2760,7 @@ D0D748061E7AF63800F4B1F6 /* StickerPackPreviewController.swift in Sources */, D0F69D671D6B87D30046BCD6 /* FFMpegPacket.swift in Sources */, D0F69E321D6B8B030046BCD6 /* ChatMessageDateAndStatusNode.swift in Sources */, + D0F3A8B61E83120A00B4C64C /* FetchResource.swift in Sources */, D0F69E041D6B8A880046BCD6 /* ChatListSearchItem.swift in Sources */, D0F69E611D6B8BF90046BCD6 /* ChatDocumentGalleryItem.swift in Sources */, D0D748081E7AF64400F4B1F6 /* StickerPackPreviewControllerNode.swift in Sources */, @@ -2754,6 +2773,7 @@ D0F69D771D6B87DF0046BCD6 /* FFMpegMediaPassthroughVideoFrameDecoder.swift in Sources */, D0F69DFE1D6B8A880046BCD6 /* AvatarNode.swift in Sources */, D0F69E9B1D6B8D200046BCD6 /* UIImage+WebP.m in Sources */, + D0F3A8B81E83125C00B4C64C /* MediaResources.swift in Sources */, D02383701DDF0462004018B6 /* UrlHandling.swift in Sources */, D05811941DD5F9380057C769 /* TelegramApplicationContext.swift in Sources */, D02383751DDF0E5E004018B6 /* ChatInterfaceTitlePanelNodes.swift in Sources */, diff --git a/TelegramUI/AuthorizationSequenceController.swift b/TelegramUI/AuthorizationSequenceController.swift index 3ef8785b77..d5f3dd0575 100644 --- a/TelegramUI/AuthorizationSequenceController.swift +++ b/TelegramUI/AuthorizationSequenceController.swift @@ -257,7 +257,7 @@ public final class AuthorizationSequenceController: NavigationController { self.setViewControllers([self.splashController(), self.signUpController(firstName: firstName, lastName: lastName)], animated: !self.viewControllers.isEmpty) } } else if let _ = state as? AuthorizedAccountState { - self._authorizedAccount.set(accountWithId(apiId: self.account.apiId, id: self.account.id, appGroupPath: self.account.appGroupPath, testingEnvironment: self.account.testingEnvironment) |> mapToSignal { account -> Signal in + self._authorizedAccount.set(accountWithId(apiId: self.account.apiId, id: self.account.id, appGroupPath: self.account.appGroupPath, testingEnvironment: self.account.testingEnvironment, auxiliaryMethods: telegramAccountAuxiliaryMethods) |> mapToSignal { account -> Signal in if case let .right(authorizedAccount) = account { return .single(authorizedAccount) } else { diff --git a/TelegramUI/ChatBotInfoItem.swift b/TelegramUI/ChatBotInfoItem.swift index d76a6f0094..03e6e68e47 100644 --- a/TelegramUI/ChatBotInfoItem.swift +++ b/TelegramUI/ChatBotInfoItem.swift @@ -81,7 +81,7 @@ final class ChatBotInfoItemNode: ListViewItemNode { super.init(layerBacked: false, dynamicBounce: true, rotated: true) - self.transform = CATransform3DMakeRotation(CGFloat(M_PI), 0.0, 0.0, 1.0) + self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) self.addSubnode(self.offsetContainer) self.offsetContainer.addSubnode(self.backgroundNode) @@ -117,7 +117,7 @@ final class ChatBotInfoItemNode: ListViewItemNode { let verticalItemInset: CGFloat = 10.0 let verticalContentInset: CGFloat = 8.0 - let (textLayout, textApply) = makeTextLayout(attributedText, nil, 0, .end, CGSize(width: width - horizontalEdgeInset * 2.0 - horizontalContentInset * 2.0, height: CGFloat.greatestFiniteMagnitude), nil) + let (textLayout, textApply) = makeTextLayout(attributedText, nil, 0, .end, CGSize(width: width - horizontalEdgeInset * 2.0 - horizontalContentInset * 2.0, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let backgroundFrame = CGRect(origin: CGPoint(x: floor((width - textLayout.size.width - horizontalContentInset * 2.0) / 2.0), y: verticalItemInset + 4.0), size: CGSize(width: textLayout.size.width + horizontalContentInset * 2.0, height: textLayout.size.height + verticalContentInset * 2.0)) let textFrame = CGRect(origin: CGPoint(x: backgroundFrame.origin.x + horizontalContentInset, y: backgroundFrame.origin.y + verticalContentInset), size: textLayout.size) @@ -144,6 +144,10 @@ final class ChatBotInfoItemNode: ListViewItemNode { } } + override func animateAdded(_ currentTimestamp: Double, duration: Double) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration * 0.5) + } + override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration * 0.5) } diff --git a/TelegramUI/ChatController.swift b/TelegramUI/ChatController.swift index fd0a5f9cdc..9ed54d0a97 100644 --- a/TelegramUI/ChatController.swift +++ b/TelegramUI/ChatController.swift @@ -602,7 +602,7 @@ public class ChatController: TelegramController { } else if let cachedData = combinedInitialData.cachedData as? CachedGroupData { canReport = cachedData.reportStatus == .canReport } - strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ _ in return interfaceState }).updatedKeyboardButtonsMessage(combinedInitialData.buttonKeyboardMessage).updatedPinnedMessageId(pinnedMessageId).updatedPeerIsBlocked(peerIsBlocked).updatedCanReportPeer(canReport).updatedTitlePanelContext({ context in + strongSelf.updateChatPresentationInterfaceState(animated: false, interactive: false, { $0.updatedInterfaceState({ _ in return interfaceState }).updatedKeyboardButtonsMessage(combinedInitialData.buttonKeyboardMessage).updatedPinnedMessageId(pinnedMessageId).updatedPeerIsBlocked(peerIsBlocked).updatedCanReportPeer(canReport).updatedTitlePanelContext({ context in if pinnedMessageId != nil { if !context.contains(where: { switch $0 { @@ -1293,14 +1293,9 @@ public class ChatController: TelegramController { super.viewWillDisappear(animated) self.chatDisplayNode.historyNode.canReadHistory.set(.single(false)) - let peerId = self.peerId let timestamp = Int32(Date().timeIntervalSince1970) let interfaceState = self.presentationInterfaceState.interfaceState.withUpdatedTimestamp(timestamp) - let _ = self.account.postbox.modify({ modifier -> Void in - modifier.updatePeerChatInterfaceState(peerId, update: { _ in - return interfaceState - }) - }).start() + let _ = updatePeerChatInterfaceState(account: account, peerId: self.peerId, state: interfaceState).start() } override public func viewDidDisappear(_ animated: Bool) { diff --git a/TelegramUI/ChatEmptyItem.swift b/TelegramUI/ChatEmptyItem.swift new file mode 100644 index 0000000000..c9dd00fa9a --- /dev/null +++ b/TelegramUI/ChatEmptyItem.swift @@ -0,0 +1,141 @@ +import Foundation +import Display +import AsyncDisplayKit +import SwiftSignalKit +import Postbox +import TelegramCore + +private let messageFont = Font.medium(14.0) +private let iconImage = UIImage(bundleImageName: "Chat/EmptyChatIcon")?.precomposed() + +final class ChatEmptyItem: ListViewItem { + func nodeConfiguredForWidth(async: @escaping (@escaping () -> Void) -> Void, width: CGFloat, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, () -> Void)) -> Void) { + let configure = { + let node = ChatEmptyItemNode() + + let nodeLayout = node.asyncLayout() + let (layout, apply) = nodeLayout(self, width) + + node.contentSize = layout.contentSize + node.insets = layout.insets + + completion(node, { + return (nil, { apply(.None) }) + }) + } + if Thread.isMainThread { + async { + configure() + } + } else { + configure() + } + } + + func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: ListViewItemNode, width: CGFloat, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping () -> Void) -> Void) { + if let node = node as? ChatEmptyItemNode { + Queue.mainQueue().async { + let nodeLayout = node.asyncLayout() + + async { + let (layout, apply) = nodeLayout(self, width) + Queue.mainQueue().async { + completion(layout, { + apply(animation) + }) + } + } + } + } + } +} + +private let backgroundImage = generateStretchableFilledCircleImage(radius: 14.0, color: UIColor(0x748391, 0.45)) + +final class ChatEmptyItemNode: ListViewItemNode { + var controllerInteraction: ChatControllerInteraction? + + let offsetContainer: ASDisplayNode + let backgroundNode: ASImageNode + let iconNode: ASImageNode + let textNode: TextNode + + init() { + self.offsetContainer = ASDisplayNode() + + self.backgroundNode = ASImageNode() + self.backgroundNode.displaysAsynchronously = false + self.backgroundNode.displayWithoutProcessing = true + self.backgroundNode.image = backgroundImage + self.iconNode = ASImageNode() + self.iconNode.image = iconImage + self.textNode = TextNode() + + super.init(layerBacked: false, dynamicBounce: true, rotated: true) + + self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) + + self.addSubnode(self.offsetContainer) + self.offsetContainer.addSubnode(self.backgroundNode) + self.offsetContainer.addSubnode(self.iconNode) + self.offsetContainer.addSubnode(self.textNode) + self.wantsTrailingItemSpaceUpdates = true + } + + func asyncLayout() -> (_ item: ChatEmptyItem, _ width: CGFloat) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation) -> Void) { + let makeTextLayout = TextNode.asyncLayout(self.textNode) + return { [weak self] item, width in + let attributedText = NSAttributedString(string: "No messages\nhere yet...", font: messageFont, textColor: .white, paragraphAlignment: .center) + + let horizontalEdgeInset: CGFloat = 10.0 + let horizontalContentInset: CGFloat = 12.0 + let verticalItemInset: CGFloat = 10.0 + let verticalContentInset: CGFloat = 14.0 + + var imageSize = CGSize(width: 80.0, height: 80.0) + if let iconImage = iconImage { + imageSize = iconImage.size + } + let imageSpacing: CGFloat = 10.0 + + let (textLayout, textApply) = makeTextLayout(attributedText, nil, 0, .end, CGSize(width: width - horizontalEdgeInset * 2.0 - horizontalContentInset * 2.0, height: CGFloat.greatestFiniteMagnitude), .center, nil) + + let contentWidth = max(textLayout.size.width, 120.0) + + let backgroundFrame = CGRect(origin: CGPoint(x: floor((width - contentWidth - horizontalContentInset * 2.0) / 2.0), y: verticalItemInset + 4.0), size: CGSize(width: contentWidth + horizontalContentInset * 2.0, height: textLayout.size.height + imageSize.height + imageSpacing + verticalContentInset * 2.0)) + let textFrame = CGRect(origin: CGPoint(x: backgroundFrame.origin.x + horizontalContentInset + floor((contentWidth - textLayout.size.width) / 2.0), y: backgroundFrame.origin.y + verticalContentInset + imageSize.height + imageSpacing), size: textLayout.size) + let iconFrame = CGRect(origin: CGPoint(x: backgroundFrame.origin.x + horizontalContentInset + floor((contentWidth - imageSize.width) / 2.0), y: backgroundFrame.origin.y + verticalContentInset), size: imageSize) + + let itemLayout = ListViewItemNodeLayout(contentSize: CGSize(width: width, height: imageSize.height + imageSpacing + textLayout.size.height + verticalItemInset * 2.0 + verticalContentInset * 2.0 + 4.0), insets: UIEdgeInsets()) + return (itemLayout, { _ in + if let strongSelf = self { + let _ = textApply() + strongSelf.offsetContainer.frame = CGRect(origin: CGPoint(), size: itemLayout.contentSize) + strongSelf.backgroundNode.frame = backgroundFrame + strongSelf.textNode.frame = textFrame + strongSelf.iconNode.frame = iconFrame + } + }) + } + } + + override func updateTrailingItemSpace(_ height: CGFloat, transition: ContainedViewLayoutTransition) { + if height.isLessThanOrEqualTo(0.0) { + transition.updateBounds(node: self.offsetContainer, bounds: CGRect(origin: CGPoint(), size: self.offsetContainer.bounds.size)) + } else { + transition.updateBounds(node: self.offsetContainer, bounds: CGRect(origin: CGPoint(x: 0.0, y: floor(height) / 2.0), size: self.offsetContainer.bounds.size)) + } + } + + override func animateAdded(_ currentTimestamp: Double, duration: Double) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration * 0.5) + } + + override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration * 0.5) + } + + override func animateRemoved(_ currentTimestamp: Double, duration: Double) { + self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration * 0.5, removeOnCompletion: false) + } +} diff --git a/TelegramUI/ChatHistoryEntriesForView.swift b/TelegramUI/ChatHistoryEntriesForView.swift index 5b7afeb7e4..0c85eef3f4 100644 --- a/TelegramUI/ChatHistoryEntriesForView.swift +++ b/TelegramUI/ChatHistoryEntriesForView.swift @@ -32,16 +32,20 @@ func chatHistoryEntriesForView(_ view: MessageHistoryView, includeUnreadEntry: B } } - if includeChatInfoEntry && view.earlierId == nil { - var cachedPeerData: CachedPeerData? - for entry in view.additionalData { - if case let .cachedPeerData(_, data) = entry { - cachedPeerData = data - break + if includeChatInfoEntry { + if view.earlierId == nil { + var cachedPeerData: CachedPeerData? + for entry in view.additionalData { + if case let .cachedPeerData(_, data) = entry { + cachedPeerData = data + break + } + } + if let cachedPeerData = cachedPeerData as? CachedUserData, let botInfo = cachedPeerData.botInfo, !botInfo.description.isEmpty { + entries.insert(.ChatInfoEntry(botInfo.description), at: 0) + } else if view.entries.isEmpty { + entries.insert(.EmptyChatInfoEntry, at: 0) } - } - if let cachedPeerData = cachedPeerData as? CachedUserData, let botInfo = cachedPeerData.botInfo, !botInfo.description.isEmpty { - entries.insert(.ChatInfoEntry(botInfo.description), at: 0) } } diff --git a/TelegramUI/ChatHistoryEntry.swift b/TelegramUI/ChatHistoryEntry.swift index 22d73c4681..7ef4a178ea 100644 --- a/TelegramUI/ChatHistoryEntry.swift +++ b/TelegramUI/ChatHistoryEntry.swift @@ -1,11 +1,12 @@ import Postbox import TelegramCore -enum ChatHistoryEntry: Identifiable, Comparable, CustomStringConvertible { +enum ChatHistoryEntry: Identifiable, Comparable { case HoleEntry(MessageHistoryHole) case MessageEntry(Message, Bool) case UnreadEntry(MessageIndex) case ChatInfoEntry(String) + case EmptyChatInfoEntry var stableId: UInt64 { switch self { @@ -17,6 +18,8 @@ enum ChatHistoryEntry: Identifiable, Comparable, CustomStringConvertible { return UInt64(3) << 40 case .ChatInfoEntry: return UInt64(4) << 40 + case .EmptyChatInfoEntry: + return UInt64(5) << 40 } } @@ -30,19 +33,8 @@ enum ChatHistoryEntry: Identifiable, Comparable, CustomStringConvertible { return index case .ChatInfoEntry: return MessageIndex.absoluteLowerBound() - } - } - - var description: String { - switch self { - case let .HoleEntry(hole): - return "HoleEntry(\(hole))" - case let .MessageEntry(message, read): - return "MessageEntry(\(message), \(read))" - case let .UnreadEntry(index): - return "UnreadEntry(\(index))" - case .ChatInfoEntry: - return "ChatInfoEntry" + case .EmptyChatInfoEntry: + return MessageIndex.absoluteLowerBound() } } } @@ -99,6 +91,12 @@ func ==(lhs: ChatHistoryEntry, rhs: ChatHistoryEntry) -> Bool { } else { return false } + case .EmptyChatInfoEntry: + if case .EmptyChatInfoEntry = rhs { + return true + } else { + return false + } } } diff --git a/TelegramUI/ChatHistoryGridNode.swift b/TelegramUI/ChatHistoryGridNode.swift index 39da22008c..e66abd444b 100644 --- a/TelegramUI/ChatHistoryGridNode.swift +++ b/TelegramUI/ChatHistoryGridNode.swift @@ -24,7 +24,7 @@ private func mappedInsertEntries(account: Account, peerId: PeerId, controllerInt case .UnreadEntry: assertionFailure() return GridNodeInsertItem(index: entry.index, item: GridHoleItem(), previousIndex: entry.previousIndex) - case .ChatInfoEntry: + case .ChatInfoEntry, .EmptyChatInfoEntry: assertionFailure() return GridNodeInsertItem(index: entry.index, item: GridHoleItem(), previousIndex: entry.previousIndex) } @@ -41,7 +41,7 @@ private func mappedUpdateEntries(account: Account, peerId: PeerId, controllerInt case .UnreadEntry: assertionFailure() return GridNodeUpdateItem(index: entry.index, item: GridHoleItem()) - case .ChatInfoEntry: + case .ChatInfoEntry, .EmptyChatInfoEntry: assertionFailure() return GridNodeUpdateItem(index: entry.index, item: GridHoleItem()) } diff --git a/TelegramUI/ChatHistoryListNode.swift b/TelegramUI/ChatHistoryListNode.swift index 4bff04ef17..b4580c95cb 100644 --- a/TelegramUI/ChatHistoryListNode.swift +++ b/TelegramUI/ChatHistoryListNode.swift @@ -123,6 +123,8 @@ private func mappedInsertEntries(account: Account, peerId: PeerId, controllerInt return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index), directionHint: entry.directionHint) case let .ChatInfoEntry(text): return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(text: text, controllerInteraction: controllerInteraction), directionHint: entry.directionHint) + case .EmptyChatInfoEntry: + return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatEmptyItem(), directionHint: entry.directionHint) } } } @@ -152,6 +154,8 @@ private func mappedUpdateEntries(account: Account, peerId: PeerId, controllerInt return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index), directionHint: entry.directionHint) case let .ChatInfoEntry(text): return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(text: text, controllerInteraction: controllerInteraction), directionHint: entry.directionHint) + case .EmptyChatInfoEntry: + return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatEmptyItem(), directionHint: entry.directionHint) } } } @@ -462,6 +466,11 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { preconditionFailure() } + if !strongSelf.didSetInitialData { + strongSelf.didSetInitialData = true + strongSelf._initialData.set(.single(ChatHistoryCombinedInitialData(initialData: transition.initialData, buttonKeyboardMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData))) + } + strongSelf.enqueuedHistoryViewTransition = (transition, { subscriber.putCompletion() }) @@ -469,10 +478,10 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { if strongSelf.isNodeLoaded { strongSelf.dequeueHistoryViewTransition() } else { - if !strongSelf.didSetInitialData { + /*if !strongSelf.didSetInitialData { strongSelf.didSetInitialData = true strongSelf._initialData.set(.single(ChatHistoryCombinedInitialData(initialData: transition.initialData, buttonKeyboardMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData))) - } + }*/ strongSelf._cachedPeerData.set(.single(transition.cachedData)) let historyState: ChatHistoryNodeHistoryState = .loaded(isEmpty: transition.historyView.originalView.entries.isEmpty) if strongSelf.currentHistoryState != historyState { diff --git a/TelegramUI/ChatHoleItem.swift b/TelegramUI/ChatHoleItem.swift index 4c134b451f..330fcca9fa 100644 --- a/TelegramUI/ChatHoleItem.swift +++ b/TelegramUI/ChatHoleItem.swift @@ -80,7 +80,7 @@ class ChatHoleItemNode: ListViewItemNode { let labelLayout = TextNode.asyncLayout(self.labelNode) let layoutConstants = self.layoutConstants return { item, width, dateAtBottom in - let (size, apply) = labelLayout(NSAttributedString(string: "Loading", font: titleFont, textColor: UIColor.white), nil, 1, .end, CGSize(width: width, height: CGFloat.greatestFiniteMagnitude), nil) + let (size, apply) = labelLayout(NSAttributedString(string: "Loading", font: titleFont, textColor: UIColor.white), nil, 1, .end, CGSize(width: width, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let backgroundSize = CGSize(width: size.size.width + 8.0 + 8.0, height: 20.0) diff --git a/TelegramUI/ChatInterfaceState.swift b/TelegramUI/ChatInterfaceState.swift index b2e4f6f467..bb1c938fc6 100644 --- a/TelegramUI/ChatInterfaceState.swift +++ b/TelegramUI/ChatInterfaceState.swift @@ -1,5 +1,6 @@ import Foundation import Postbox +import TelegramCore struct ChatInterfaceSelectionState: Coding, Equatable { let selectedIds: Set @@ -215,7 +216,7 @@ struct ChatInterfaceMessageActionsState: Coding, Equatable { } } -final class ChatInterfaceState: PeerChatInterfaceState, Equatable { +final class ChatInterfaceState: SynchronizeableChatInterfaceState, Equatable { let timestamp: Int32 let composeInputState: ChatTextInputState let composeDisableUrlPreview: String? @@ -233,6 +234,18 @@ final class ChatInterfaceState: PeerChatInterfaceState, Equatable { } } + var synchronizeableInputState: SynchronizeableChatInputState? { + if self.composeInputState.inputText.isEmpty { + return nil + } else { + return SynchronizeableChatInputState(replyToMessageId: self.replyMessageId, text: self.composeInputState.inputText, timestamp: self.timestamp) + } + } + + func withUpdatedSynchronizeableInputState(_ state: SynchronizeableChatInputState?) -> SynchronizeableChatInterfaceState { + return self.withUpdatedComposeInputState(ChatTextInputState(inputText: state?.text ?? "")).withUpdatedReplyMessageId(state?.replyToMessageId) + } + var effectiveInputState: ChatTextInputState { if let editMessage = self.editMessage { return editMessage.inputState diff --git a/TelegramUI/ChatListHoleItem.swift b/TelegramUI/ChatListHoleItem.swift index 664e75e77c..87b3c7ad33 100644 --- a/TelegramUI/ChatListHoleItem.swift +++ b/TelegramUI/ChatListHoleItem.swift @@ -78,7 +78,7 @@ class ChatListHoleItemNode: ListViewItemNode { let labelNodeLayout = TextNode.asyncLayout(self.labelNode) return { width, first, last in - let (labelLayout, labelApply) = labelNodeLayout(NSAttributedString(string: "Loading", font: titleFont, textColor: UIColor(0xc8c7cc)), nil, 1, .end, CGSize(width: width, height: CGFloat.greatestFiniteMagnitude), nil) + let (labelLayout, labelApply) = labelNodeLayout(NSAttributedString(string: "Loading", font: titleFont, textColor: UIColor(0xc8c7cc)), nil, 1, .end, CGSize(width: width, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let insets = ChatListItemNode.insets(first: first, last: last, firstWithHeader: false) let layout = ListViewItemNodeLayout(contentSize: CGSize(width: width, height: 68.0), insets: insets) diff --git a/TelegramUI/ChatListItem.swift b/TelegramUI/ChatListItem.swift index 6879da0dad..4b97f97db1 100644 --- a/TelegramUI/ChatListItem.swift +++ b/TelegramUI/ChatListItem.swift @@ -524,9 +524,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { let rawContentRect = CGRect(origin: CGPoint(x: 2.0, y: 12.0), size: CGSize(width: width - 78.0 - 10.0 - 1.0 - editingOffset, height: 68.0 - 12.0 - 9.0)) - let (dateLayout, dateApply) = dateLayout(dateAttributedString, nil, 1, .end, CGSize(width: rawContentRect.width, height: CGFloat.greatestFiniteMagnitude), nil) + let (dateLayout, dateApply) = dateLayout(dateAttributedString, nil, 1, .end, CGSize(width: rawContentRect.width, height: CGFloat.greatestFiniteMagnitude), .natural, nil) - let (badgeLayout, badgeApply) = badgeTextLayout(badgeAttributedString, nil, 1, .end, CGSize(width: 50.0, height: CGFloat.greatestFiniteMagnitude), nil) + let (badgeLayout, badgeApply) = badgeTextLayout(badgeAttributedString, nil, 1, .end, CGSize(width: 50.0, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let badgeSize: CGFloat if let currentBadgeBackgroundImage = currentBadgeBackgroundImage { @@ -535,10 +535,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { badgeSize = 0.0 } - let (textLayout, textApply) = textLayout(textAttributedString, nil, 1, .end, CGSize(width: rawContentRect.width - badgeSize, height: CGFloat.greatestFiniteMagnitude), nil) + let (textLayout, textApply) = textLayout(textAttributedString, nil, 1, .end, CGSize(width: rawContentRect.width - badgeSize, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let titleRect = CGRect(origin: rawContentRect.origin, size: CGSize(width: rawContentRect.width - dateLayout.size.width - 10.0 - statusWidth - muteWidth, height: rawContentRect.height)) - let (titleLayout, titleApply) = titleLayout(titleAttributedString, nil, 1, .end, CGSize(width: titleRect.width, height: CGFloat.greatestFiniteMagnitude), nil) + let (titleLayout, titleApply) = titleLayout(titleAttributedString, nil, 1, .end, CGSize(width: titleRect.width, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let insets = ChatListItemNode.insets(first: first, last: last, firstWithHeader: firstWithHeader) let layout = ListViewItemNodeLayout(contentSize: CGSize(width: width, height: 68.0), insets: insets) diff --git a/TelegramUI/ChatMessageActionButtonsNode.swift b/TelegramUI/ChatMessageActionButtonsNode.swift index 203f64f6a6..ce8691168e 100644 --- a/TelegramUI/ChatMessageActionButtonsNode.swift +++ b/TelegramUI/ChatMessageActionButtonsNode.swift @@ -62,7 +62,7 @@ private final class ChatMessageActionButtonNode: ASDisplayNode { return { button, constrainedWidth, position in let sideInset: CGFloat = 8.0 let minimumSideInset: CGFloat = 4.0 - let (titleSize, titleApply) = titleLayout(NSAttributedString(string: button.title, font: titleFont, textColor: .white), nil, 1, .end, CGSize(width: max(1.0, constrainedWidth - minimumSideInset - minimumSideInset), height: CGFloat.greatestFiniteMagnitude), nil) + let (titleSize, titleApply) = titleLayout(NSAttributedString(string: button.title, font: titleFont, textColor: .white), nil, 1, .end, CGSize(width: max(1.0, constrainedWidth - minimumSideInset - minimumSideInset), height: CGFloat.greatestFiniteMagnitude), .natural, nil) let backgroundImage: UIImage switch position { diff --git a/TelegramUI/ChatMessageActionItemNode.swift b/TelegramUI/ChatMessageActionItemNode.swift index e0441b7984..7249014e86 100644 --- a/TelegramUI/ChatMessageActionItemNode.swift +++ b/TelegramUI/ChatMessageActionItemNode.swift @@ -169,7 +169,7 @@ class ChatMessageActionItemNode: ChatMessageItemView { } } - let (size, apply) = labelLayout(attributedString, nil, 1, .end, CGSize(width: width - 32.0, height: CGFloat.greatestFiniteMagnitude), nil) + let (size, apply) = labelLayout(attributedString, nil, 1, .end, CGSize(width: width - 32.0, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let backgroundSize = CGSize(width: size.size.width + 8.0 + 8.0, height: 20.0) var layoutInsets = UIEdgeInsets(top: 4.0, left: 0.0, bottom: 4.0, right: 0.0) diff --git a/TelegramUI/ChatMessageBubbleItemNode.swift b/TelegramUI/ChatMessageBubbleItemNode.swift index 447c8a42c1..b80325d3e1 100644 --- a/TelegramUI/ChatMessageBubbleItemNode.swift +++ b/TelegramUI/ChatMessageBubbleItemNode.swift @@ -316,7 +316,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { attributedString = NSAttributedString(string: "", font: nameFont, textColor: UIColor.black) } - let sizeAndApply = authorNameLayout(attributedString, nil, 1, .end, CGSize(width: maximumNodeWidth, height: CGFloat.greatestFiniteMagnitude), nil) + let sizeAndApply = authorNameLayout(attributedString, nil, 1, .end, CGSize(width: maximumNodeWidth, height: CGFloat.greatestFiniteMagnitude), .natural, nil) nameNodeSizeApply = (sizeAndApply.0.size, { return sizeAndApply.1() }) diff --git a/TelegramUI/ChatMessageDateAndStatusNode.swift b/TelegramUI/ChatMessageDateAndStatusNode.swift index 35ecf49d50..d174d320bd 100644 --- a/TelegramUI/ChatMessageDateAndStatusNode.swift +++ b/TelegramUI/ChatMessageDateAndStatusNode.swift @@ -192,7 +192,7 @@ class ChatMessageDateAndStatusNode: ASTransformLayerNode { updatedDateText = "edited " + updatedDateText } - let (date, dateApply) = dateLayout(NSAttributedString(string: updatedDateText, font: dateFont, textColor: dateColor), nil, 1, .end, constrainedSize, nil) + let (date, dateApply) = dateLayout(NSAttributedString(string: updatedDateText, font: dateFont, textColor: dateColor), nil, 1, .end, constrainedSize, .natural, nil) let statusWidth: CGFloat diff --git a/TelegramUI/ChatMessageDateHeader.swift b/TelegramUI/ChatMessageDateHeader.swift index 44442892d7..4cfebac37a 100644 --- a/TelegramUI/ChatMessageDateHeader.swift +++ b/TelegramUI/ChatMessageDateHeader.swift @@ -122,7 +122,7 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode { let attributedString = NSAttributedString(string: text, font: titleFont, textColor: UIColor.white) let labelLayout = TextNode.asyncLayout(self.labelNode) - let (size, apply) = labelLayout(attributedString, nil, 1, .end, CGSize(width: 320.0, height: CGFloat.greatestFiniteMagnitude), nil) + let (size, apply) = labelLayout(attributedString, nil, 1, .end, CGSize(width: 320.0, height: CGFloat.greatestFiniteMagnitude), .natural, nil) apply() self.labelNode.frame = CGRect(origin: CGPoint(), size: size.size) } diff --git a/TelegramUI/ChatMessageForwardInfoNode.swift b/TelegramUI/ChatMessageForwardInfoNode.swift index fba362ea88..718db12d34 100644 --- a/TelegramUI/ChatMessageForwardInfoNode.swift +++ b/TelegramUI/ChatMessageForwardInfoNode.swift @@ -28,7 +28,7 @@ class ChatMessageForwardInfoNode: ASTransformLayerNode { let color = incoming ? UIColor(0x007bff) : UIColor(0x00a516) let string = NSMutableAttributedString(string: completeString as String, attributes: [NSForegroundColorAttributeName: color, NSFontAttributeName: prefixFont]) string.addAttributes([NSFontAttributeName: peerFont], range: NSMakeRange(prefix.length, completeString.length - prefix.length)) - let (textLayout, textApply) = textNodeLayout(string, nil, 2, .end, constrainedSize, nil) + let (textLayout, textApply) = textNodeLayout(string, nil, 2, .end, constrainedSize, .natural, nil) return (textLayout.size, { let node: ChatMessageForwardInfoNode diff --git a/TelegramUI/ChatMessageInteractiveFileNode.swift b/TelegramUI/ChatMessageInteractiveFileNode.swift index 5245e67680..0dc98731a3 100644 --- a/TelegramUI/ChatMessageInteractiveFileNode.swift +++ b/TelegramUI/ChatMessageInteractiveFileNode.swift @@ -251,8 +251,8 @@ final class ChatMessageInteractiveFileNode: ASTransformNode { let textConstrainedSize = CGSize(width: constrainedSize.width - 44.0 - 8.0, height: constrainedSize.height) - let (titleLayout, titleApply) = titleAsyncLayout(titleString, nil, 1, .middle, textConstrainedSize, nil) - let (descriptionLayout, descriptionApply) = descriptionAsyncLayout(descriptionString, nil, 1, .middle, textConstrainedSize, nil) + let (titleLayout, titleApply) = titleAsyncLayout(titleString, nil, 1, .middle, textConstrainedSize, .natural, nil) + let (descriptionLayout, descriptionApply) = descriptionAsyncLayout(descriptionString, nil, 1, .middle, textConstrainedSize, .natural, nil) var voiceWidth: CGFloat = 0.0 let minVoiceWidth: CGFloat = 120.0 diff --git a/TelegramUI/ChatMessageReplyInfoNode.swift b/TelegramUI/ChatMessageReplyInfoNode.swift index 0eb4ce4718..b0b1c5b180 100644 --- a/TelegramUI/ChatMessageReplyInfoNode.swift +++ b/TelegramUI/ChatMessageReplyInfoNode.swift @@ -14,7 +14,7 @@ private let titleFont: UIFont = { }() private let textFont = Font.regular(14.0) -private func textStringForMessage(_ message: Message) -> (String, Bool) { +func textStringForReplyMessage(_ message: Message) -> (String, Bool) { if !message.text.isEmpty { return (message.text, false) } else { @@ -110,7 +110,7 @@ class ChatMessageReplyInfoNode: ASTransformLayerNode { return { account, type, message, constrainedSize in let titleString = message.author?.displayTitle ?? "" - let (textString, textMedia) = textStringForMessage(message) + let (textString, textMedia) = textStringForReplyMessage(message) let titleColor: UIColor let lineColor: UIColor @@ -174,8 +174,8 @@ class ChatMessageReplyInfoNode: ASTransformLayerNode { let contrainedTextSize = CGSize(width: maximumTextWidth, height: constrainedSize.height) - let (titleLayout, titleApply) = titleNodeLayout(NSAttributedString(string: titleString, font: titleFont, textColor: titleColor), nil, 1, .end, contrainedTextSize, nil) - let (textLayout, textApply) = textNodeLayout(NSAttributedString(string: textString, font: textFont, textColor: textMedia ? titleColor : textColor), nil, 1, .end, contrainedTextSize, nil) + let (titleLayout, titleApply) = titleNodeLayout(NSAttributedString(string: titleString, font: titleFont, textColor: titleColor), nil, 1, .end, contrainedTextSize, .natural, nil) + let (textLayout, textApply) = textNodeLayout(NSAttributedString(string: textString, font: textFont, textColor: textMedia ? titleColor : textColor), nil, 1, .end, contrainedTextSize, .natural, nil) let size = CGSize(width: max(titleLayout.size.width, textLayout.size.width) + leftInset, height: titleLayout.size.height + textLayout.size.height) diff --git a/TelegramUI/ChatMessageTextBubbleContentNode.swift b/TelegramUI/ChatMessageTextBubbleContentNode.swift index 13fc18b566..f8f6e8da45 100644 --- a/TelegramUI/ChatMessageTextBubbleContentNode.swift +++ b/TelegramUI/ChatMessageTextBubbleContentNode.swift @@ -118,7 +118,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { attributedText = NSAttributedString(string: message.text, font: messageFont, textColor: UIColor.black) } - let (textLayout, textApply) = textLayout(attributedText, nil, 0, .end, textConstrainedSize, nil) + let (textLayout, textApply) = textLayout(attributedText, nil, 0, .end, textConstrainedSize, .natural, nil) var textFrame = CGRect(origin: CGPoint(), size: textLayout.size) let textSize = textLayout.size diff --git a/TelegramUI/ChatMessageWebpageBubbleContentNode.swift b/TelegramUI/ChatMessageWebpageBubbleContentNode.swift index 115e353806..7922f322bf 100644 --- a/TelegramUI/ChatMessageWebpageBubbleContentNode.swift +++ b/TelegramUI/ChatMessageWebpageBubbleContentNode.swift @@ -200,7 +200,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode { statusSizeAndApply = statusLayout(edited && !sentViaBot, viewCount, dateText, statusType, textConstrainedSize) } - let (textLayout, textApply) = textAsyncLayout(textString, nil, 12, .end, textConstrainedSize, textCutout) + let (textLayout, textApply) = textAsyncLayout(textString, nil, 12, .end, textConstrainedSize, .natural, textCutout) var textFrame = CGRect(origin: CGPoint(), size: textLayout.size) diff --git a/TelegramUI/ChatPinnedMessageTitlePanelNode.swift b/TelegramUI/ChatPinnedMessageTitlePanelNode.swift index 972cd1ff92..4be8a2ba2d 100644 --- a/TelegramUI/ChatPinnedMessageTitlePanelNode.swift +++ b/TelegramUI/ChatPinnedMessageTitlePanelNode.swift @@ -154,9 +154,9 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { let rightInset: CGFloat = 18.0 let textRightInset: CGFloat = 25.0 - let (titleLayout, titleApply) = makeTitleLayout(NSAttributedString(string: "Pinned message", font: Font.medium(15.0), textColor: UIColor(0x007ee5)), nil, 1, .end, CGSize(width: width - leftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), nil) + let (titleLayout, titleApply) = makeTitleLayout(NSAttributedString(string: "Pinned message", font: Font.medium(15.0), textColor: UIColor(0x007ee5)), nil, 1, .end, CGSize(width: width - leftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), .natural, nil) - let (textLayout, textApply) = makeTextLayout(NSAttributedString(string: message.text, font: Font.regular(15.0), textColor: .black), nil, 1, .end, CGSize(width: width - leftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), nil) + let (textLayout, textApply) = makeTextLayout(NSAttributedString(string: message.text, font: Font.regular(15.0), textColor: .black), nil, 1, .end, CGSize(width: width - leftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), .natural, nil) Queue.mainQueue().async { if let strongSelf = self { diff --git a/TelegramUI/ChatTextInputAudioRecordingCancelIndicator.swift b/TelegramUI/ChatTextInputAudioRecordingCancelIndicator.swift index 6b3ea86e92..2225426c9d 100644 --- a/TelegramUI/ChatTextInputAudioRecordingCancelIndicator.swift +++ b/TelegramUI/ChatTextInputAudioRecordingCancelIndicator.swift @@ -24,7 +24,7 @@ final class ChatTextInputAudioRecordingCancelIndicator: ASDisplayNode { self.addSubnode(self.labelNode) let makeLayout = TextNode.asyncLayout(self.labelNode) - let (labelLayout, labelApply) = makeLayout(NSAttributedString(string: "Slide to cancel", font: Font.regular(14.0), textColor: UIColor(0xaaaab2)), nil, 1, .end, CGSize(width: 200.0, height: 100.0), nil) + let (labelLayout, labelApply) = makeLayout(NSAttributedString(string: "Slide to cancel", font: Font.regular(14.0), textColor: UIColor(0xaaaab2)), nil, 1, .end, CGSize(width: 200.0, height: 100.0), .natural, nil) labelApply() let arrowSize = arrowImage?.size ?? CGSize() diff --git a/TelegramUI/ChatTextInputAudioRecordingTimeNode.swift b/TelegramUI/ChatTextInputAudioRecordingTimeNode.swift index e5903f9dee..dd16a9a22a 100644 --- a/TelegramUI/ChatTextInputAudioRecordingTimeNode.swift +++ b/TelegramUI/ChatTextInputAudioRecordingTimeNode.swift @@ -57,7 +57,7 @@ final class ChatTextInputAudioRecordingTimeNode: ASDisplayNode { override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { let makeLayout = TextNode.asyncLayout(self.textNode) - let (size, apply) = makeLayout(NSAttributedString(string: "00:00,00", font: Font.regular(15.0), textColor: .black), nil, 1, .end, CGSize(width: 200.0, height: 100.0), nil) + let (size, apply) = makeLayout(NSAttributedString(string: "00:00,00", font: Font.regular(15.0), textColor: .black), nil, 1, .end, CGSize(width: 200.0, height: 100.0), .natural, nil) apply() self.textNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 1.0 + UIScreenPixel), size: size.size) return size.size diff --git a/TelegramUI/ChatTextInputPanelNode.swift b/TelegramUI/ChatTextInputPanelNode.swift index 869b1618e7..5948b9330e 100644 --- a/TelegramUI/ChatTextInputPanelNode.swift +++ b/TelegramUI/ChatTextInputPanelNode.swift @@ -334,7 +334,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { self.view.addSubview(self.textInputBackgroundView) let placeholderLayout = TextNode.asyncLayout(self.textPlaceholderNode) - let (placeholderSize, placeholderApply) = placeholderLayout(NSAttributedString(string: "Message", font: Font.regular(17.0), textColor: UIColor(0xC8C8CE)), nil, 1, .end, CGSize(width: 320.0, height: CGFloat.greatestFiniteMagnitude), nil) + let (placeholderSize, placeholderApply) = placeholderLayout(NSAttributedString(string: "Message", font: Font.regular(17.0), textColor: UIColor(0xC8C8CE)), nil, 1, .end, CGSize(width: 320.0, height: CGFloat.greatestFiniteMagnitude), .natural, nil) self.textPlaceholderNode.frame = CGRect(origin: CGPoint(), size: placeholderSize.size) let _ = placeholderApply() self.addSubnode(self.textPlaceholderNode) @@ -424,7 +424,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { if self.currentPlaceholder != placeholder { self.currentPlaceholder = placeholder let placeholderLayout = TextNode.asyncLayout(self.textPlaceholderNode) - let (placeholderSize, placeholderApply) = placeholderLayout(NSAttributedString(string: placeholder, font: Font.regular(17.0), textColor: UIColor(0xbebec0)), nil, 1, .end, CGSize(width: 320.0, height: CGFloat.greatestFiniteMagnitude), nil) + let (placeholderSize, placeholderApply) = placeholderLayout(NSAttributedString(string: placeholder, font: Font.regular(17.0), textColor: UIColor(0xbebec0)), nil, 1, .end, CGSize(width: 320.0, height: CGFloat.greatestFiniteMagnitude), .natural, nil) self.textPlaceholderNode.frame = CGRect(origin: self.textPlaceholderNode.frame.origin, size: placeholderSize.size) let _ = placeholderApply() } @@ -641,7 +641,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { if let contextPlaceholder = self.presentationInterfaceState.inputTextPanelState.contextPlaceholder { let placeholderLayout = TextNode.asyncLayout(self.contextPlaceholderNode) - let (placeholderSize, placeholderApply) = placeholderLayout(contextPlaceholder, nil, 1, .end, CGSize(width: width - self.textFieldInsets.left - self.textFieldInsets.right - self.textInputViewInternalInsets.left - self.textInputViewInternalInsets.right - accessoryButtonsWidth, height: CGFloat.greatestFiniteMagnitude), nil) + let (placeholderSize, placeholderApply) = placeholderLayout(contextPlaceholder, nil, 1, .end, CGSize(width: width - self.textFieldInsets.left - self.textFieldInsets.right - self.textInputViewInternalInsets.left - self.textInputViewInternalInsets.right - accessoryButtonsWidth, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let contextPlaceholderNode = placeholderApply() if let currentContextPlaceholderNode = self.contextPlaceholderNode, currentContextPlaceholderNode !== contextPlaceholderNode { self.contextPlaceholderNode = nil diff --git a/TelegramUI/ChatUnreadItem.swift b/TelegramUI/ChatUnreadItem.swift index cbee4c22d9..33c5c4cee1 100644 --- a/TelegramUI/ChatUnreadItem.swift +++ b/TelegramUI/ChatUnreadItem.swift @@ -100,7 +100,7 @@ class ChatUnreadItemNode: ListViewItemNode { let labelLayout = TextNode.asyncLayout(self.labelNode) let layoutConstants = self.layoutConstants return { item, width, dateAtBottom in - let (size, apply) = labelLayout(NSAttributedString(string: "Unread", font: titleFont, textColor: UIColor(0x86868d)), nil, 1, .end, CGSize(width: width, height: CGFloat.greatestFiniteMagnitude), nil) + let (size, apply) = labelLayout(NSAttributedString(string: "Unread", font: titleFont, textColor: UIColor(0x86868d)), nil, 1, .end, CGSize(width: width, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let backgroundSize = CGSize(width: width, height: 25.0) diff --git a/TelegramUI/CommandChatInputPanelItem.swift b/TelegramUI/CommandChatInputPanelItem.swift index 0555c3c2ba..ad678d6a51 100644 --- a/TelegramUI/CommandChatInputPanelItem.swift +++ b/TelegramUI/CommandChatInputPanelItem.swift @@ -164,7 +164,7 @@ final class CommandChatInputPanelItemNode: ListViewItemNode { commandString.append(NSAttributedString(string: " " + item.command.command.description, font: descriptionFont, textColor: descriptionColor)) } - let (textLayout, textApply) = makeTextLayout(commandString, nil, 1, .end, CGSize(width: width - leftInset - rightInset, height: 100.0), nil) + let (textLayout, textApply) = makeTextLayout(commandString, nil, 1, .end, CGSize(width: width - leftInset - rightInset, height: 100.0), .natural, nil) let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: width, height: HashtagChatInputPanelItemNode.itemHeight), insets: UIEdgeInsets()) diff --git a/TelegramUI/ContactListActionItem.swift b/TelegramUI/ContactListActionItem.swift index 7d2bf8c3e4..d6c6e1d6ae 100644 --- a/TelegramUI/ContactListActionItem.swift +++ b/TelegramUI/ContactListActionItem.swift @@ -103,7 +103,7 @@ class ContactListActionItemNode: ListViewItemNode { return { item, width in let leftInset: CGFloat = 65.0 - let (titleLayout, titleApply) = makeTitleLayout(NSAttributedString(string: item.title, font: titleFont, textColor: UIColor(0x007ee5)), nil, 1, .end, CGSize(width: width - 10.0 - leftInset, height: CGFloat.greatestFiniteMagnitude), nil) + let (titleLayout, titleApply) = makeTitleLayout(NSAttributedString(string: item.title, font: titleFont, textColor: UIColor(0x007ee5)), nil, 1, .end, CGSize(width: width - 10.0 - leftInset, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let contentSize = CGSize(width: width, height: 48.0) let insets = UIEdgeInsets() diff --git a/TelegramUI/ContactsPeerItem.swift b/TelegramUI/ContactsPeerItem.swift index 41965fbd58..efb3f13868 100644 --- a/TelegramUI/ContactsPeerItem.swift +++ b/TelegramUI/ContactsPeerItem.swift @@ -323,9 +323,9 @@ class ContactsPeerItemNode: ListViewItemNode { } } - let (titleLayout, titleApply) = makeTitleLayout(titleAttributedString, nil, 1, .end, CGSize(width: max(0.0, width - leftInset - rightInset), height: CGFloat.infinity), nil) + let (titleLayout, titleApply) = makeTitleLayout(titleAttributedString, nil, 1, .end, CGSize(width: max(0.0, width - leftInset - rightInset), height: CGFloat.infinity), .natural, nil) - let (statusLayout, statusApply) = makeStatusLayout(statusAttributedString, nil, 1, .end, CGSize(width: max(0.0, width - leftInset - rightInset), height: CGFloat.infinity), nil) + let (statusLayout, statusApply) = makeStatusLayout(statusAttributedString, nil, 1, .end, CGSize(width: max(0.0, width - leftInset - rightInset), height: CGFloat.infinity), .natural, nil) let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: width, height: 48.0), insets: UIEdgeInsets(top: firstWithHeader ? 29.0 : 0.0, left: 0.0, bottom: 0.0, right: 0.0)) diff --git a/TelegramUI/ContactsVCardItem.swift b/TelegramUI/ContactsVCardItem.swift index 91639cc739..c25a74a6de 100644 --- a/TelegramUI/ContactsVCardItem.swift +++ b/TelegramUI/ContactsVCardItem.swift @@ -172,9 +172,9 @@ class ContactsVCardItemNode: ListViewItemNode { } } - let (titleLayout, titleApply) = makeTitleLayout(titleAttributedString, nil, 1, .end, CGSize(width: max(0.0, width - leftInset - rightInset), height: CGFloat.infinity), nil) + let (titleLayout, titleApply) = makeTitleLayout(titleAttributedString, nil, 1, .end, CGSize(width: max(0.0, width - leftInset - rightInset), height: CGFloat.infinity), .natural, nil) - let (statusLayout, statusApply) = makeStatusLayout(statusAttributedString, nil, 1, .end, CGSize(width: max(0.0, width - leftInset - rightInset), height: CGFloat.infinity), nil) + let (statusLayout, statusApply) = makeStatusLayout(statusAttributedString, nil, 1, .end, CGSize(width: max(0.0, width - leftInset - rightInset), height: CGFloat.infinity), .natural, nil) let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: width, height: 78.0), insets: UIEdgeInsets()) diff --git a/TelegramUI/DeclareEncodables.swift b/TelegramUI/DeclareEncodables.swift index da8319cb27..5d4ce2e5bc 100644 --- a/TelegramUI/DeclareEncodables.swift +++ b/TelegramUI/DeclareEncodables.swift @@ -3,6 +3,8 @@ import Postbox private var telegramUIDeclaredEncodables: Void = { 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) }) return }() diff --git a/TelegramUI/FetchResource.swift b/TelegramUI/FetchResource.swift new file mode 100644 index 0000000000..ca189acdb8 --- /dev/null +++ b/TelegramUI/FetchResource.swift @@ -0,0 +1,9 @@ +import Foundation +import Postbox +import TelegramCore +import SwiftSignalKit + +func fetchResource(account: Account, resource: MediaResource, range: Range) -> Signal? { + return nil +} + diff --git a/TelegramUI/FetchVideoMediaResource.swift b/TelegramUI/FetchVideoMediaResource.swift new file mode 100644 index 0000000000..36749766cd --- /dev/null +++ b/TelegramUI/FetchVideoMediaResource.swift @@ -0,0 +1,136 @@ +import Foundation +import Postbox +import SwiftSignalKit +import TelegramLegacyComponents + +private final class VideoConversionWatcher: TGMediaVideoFileWatcher { + private let update: (String, Int) -> Void + private var path: String? + + init(update: @escaping (String, Int) -> Void) { + self.update = update + + super.init() + } + + override func setup(withFileURL fileURL: URL!) { + self.path = fileURL?.path + super.setup(withFileURL: fileURL) + } + + override func fileUpdated(_ completed: Bool) -> Any! { + if let path = self.path { + var value = stat() + if stat(path, &value) == 0 { + self.update(path, Int(value.st_size)) + } + } + + return super.fileUpdated(completed) + } +} + +func fetchVideoLibraryMediaResource(resource: VideoLibraryMediaResource) -> Signal { + return Signal { subscriber in + subscriber.putNext(.reset) + print("request video") + + let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [resource.localIdentifier], options: nil) + var requestId: PHImageRequestID? + let disposable = MetaDisposable() + if fetchResult.count != 0 { + let asset = fetchResult.object(at: 0) + let option = PHVideoRequestOptions() + option.deliveryMode = .highQualityFormat + + let alreadyReceivedAsset = Atomic(value: false) + requestId = PHImageManager.default().requestAVAsset(forVideo: asset, options: option, resultHandler: { avAsset, _, _ in + if alreadyReceivedAsset.swap(true) { + return + } + + var adjustments: TGVideoEditAdjustments? + if let videoAdjustments = resource.adjustments { + if let dict = NSKeyedUnarchiver.unarchiveObject(with: videoAdjustments.data.makeData()) as? [AnyHashable : Any] { + adjustments = TGVideoEditAdjustments(dictionary: dict) + } + } + let updatedSize = Atomic(value: 0) + let signal = TGMediaVideoConverter.convert(avAsset, adjustments: adjustments, watcher: VideoConversionWatcher(update: { path, size in + var value = stat() + if stat(path, &value) == 0 { + if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: [.mappedRead]) { + var range: Range? + let _ = updatedSize.modify { updatedSize in + range = updatedSize ..< Int(value.st_size) + return Int(value.st_size) + } + //print("size = \(Int(value.st_size)), range: \(range!)") + subscriber.putNext(.dataPart(data: data, range: range!, complete: false)) + } + } + }))! + let signalDisposable = signal.start(next: { next in + if let result = next as? TGMediaVideoConversionResult { + var value = stat() + if stat(result.fileURL.path, &value) == 0 { + if let data = try? Data(contentsOf: result.fileURL, options: [.mappedRead]) { + var range: Range? + let _ = updatedSize.modify { updatedSize in + range = updatedSize ..< Int(value.st_size) + return Int(value.st_size) + } + //print("finish size = \(Int(value.st_size)), range: \(range!)") + subscriber.putNext(.dataPart(data: data, range: range!, complete: false)) + subscriber.putNext(.replaceHeader(data: data, range: 0 ..< 1024)) + subscriber.putNext(.dataPart(data: Data(), range: 0 ..< 0, complete: true)) + } + } + subscriber.putCompletion() + } + }, error: { _ in + }, completed: nil) + disposable.set(ActionDisposable { + signalDisposable?.dispose() + }) + }) + } + + return ActionDisposable { + if let requestId = requestId { + PHImageManager.default().cancelImageRequest(requestId) + } + disposable.dispose() + } + } +} + +func fetchLocalFileVideoMediaResource(resource: LocalFileVideoMediaResource) -> Signal { + return Signal { subscriber in + subscriber.putNext(.reset) + + let avAsset = AVURLAsset(url: URL(fileURLWithPath: resource.path)) + var adjustments: TGVideoEditAdjustments? + if let videoAdjustments = resource.adjustments { + if let dict = NSKeyedUnarchiver.unarchiveObject(with: videoAdjustments.data.makeData()) as? [AnyHashable : Any] { + adjustments = TGVideoEditAdjustments(dictionary: dict) + } + } + let signal = TGMediaVideoConverter.convert(avAsset, adjustments: adjustments, watcher: nil)! + let signalDisposable = signal.start(next: { next in + if let result = next as? TGMediaVideoConversionResult { + subscriber.putNext(.moveLocalFile(path: result.fileURL.path)) + subscriber.putCompletion() + } + }, error: { _ in + }, completed: nil) + + let disposable = ActionDisposable { + signalDisposable?.dispose() + } + + return ActionDisposable { + disposable.dispose() + } + } +} diff --git a/TelegramUI/HashtagChatInputPanelItem.swift b/TelegramUI/HashtagChatInputPanelItem.swift index 5a7cd99d02..fa75561a26 100644 --- a/TelegramUI/HashtagChatInputPanelItem.swift +++ b/TelegramUI/HashtagChatInputPanelItem.swift @@ -115,7 +115,7 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode { let leftInset: CGFloat = 15.0 let rightInset: CGFloat = 10.0 - let (textLayout, textApply) = makeTextLayout(NSAttributedString(string: item.text, font: textFont, textColor: .black), nil, 1, .end, CGSize(width: width - leftInset - rightInset, height: 100.0), nil) + let (textLayout, textApply) = makeTextLayout(NSAttributedString(string: item.text, font: textFont, textColor: .black), nil, 1, .end, CGSize(width: width - leftInset - rightInset, height: 100.0), .natural, nil) let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: width, height: HashtagChatInputPanelItemNode.itemHeight), insets: UIEdgeInsets()) diff --git a/TelegramUI/ItemListActionItem.swift b/TelegramUI/ItemListActionItem.swift index e5c5999079..e7adfefb98 100644 --- a/TelegramUI/ItemListActionItem.swift +++ b/TelegramUI/ItemListActionItem.swift @@ -132,7 +132,7 @@ class ItemListActionItemNode: ListViewItemNode { textColor = UIColor(0x8e8e93) } - let (titleLayout, titleApply) = makeTitleLayout(NSAttributedString(string: item.title, font: titleFont, textColor: textColor), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), nil) + let (titleLayout, titleApply) = makeTitleLayout(NSAttributedString(string: item.title, font: titleFont, textColor: textColor), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let contentSize: CGSize let insets: UIEdgeInsets diff --git a/TelegramUI/ItemListActivityTextItem.swift b/TelegramUI/ItemListActivityTextItem.swift index cf97e1bb51..d19df24094 100644 --- a/TelegramUI/ItemListActivityTextItem.swift +++ b/TelegramUI/ItemListActivityTextItem.swift @@ -104,7 +104,7 @@ class ItemListActivityTextItemNode: ListViewItemNode { titleString.removeAttribute(NSFontAttributeName, range: NSMakeRange(0, titleString.length)) titleString.addAttributes([NSFontAttributeName: titleFont], range: NSMakeRange(0, titleString.length)) - let (titleLayout, titleApply) = makeTitleLayout(titleString, nil, 0, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), TextNodeCutout(position: .TopLeft, size: CGSize(width: activityWidth, height: 4.0))) + let (titleLayout, titleApply) = makeTitleLayout(titleString, nil, 0, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), .natural, TextNodeCutout(position: .TopLeft, size: CGSize(width: activityWidth, height: 4.0))) let contentSize: CGSize let insets: UIEdgeInsets diff --git a/TelegramUI/ItemListAvatarAndNameItem.swift b/TelegramUI/ItemListAvatarAndNameItem.swift index d941bb0757..01e9738d2e 100644 --- a/TelegramUI/ItemListAvatarAndNameItem.swift +++ b/TelegramUI/ItemListAvatarAndNameItem.swift @@ -205,7 +205,7 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode { displayTitle = .title(title: "") } - let (nameNodeLayout, nameNodeApply) = layoutNameNode(NSAttributedString(string: displayTitle.composedTitle, font: nameFont, textColor: UIColor.black), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), nil) + let (nameNodeLayout, nameNodeApply) = layoutNameNode(NSAttributedString(string: displayTitle.composedTitle, font: nameFont, textColor: UIColor.black), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let statusText: String let statusColor: UIColor @@ -240,7 +240,7 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode { statusColor = UIColor.black } - let (statusNodeLayout, statusNodeApply) = layoutStatusNode(NSAttributedString(string: statusText, font: statusFont, textColor: statusColor), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), nil) + let (statusNodeLayout, statusNodeApply) = layoutStatusNode(NSAttributedString(string: statusText, font: statusFont, textColor: statusColor), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let separatorHeight = UIScreenPixel diff --git a/TelegramUI/ItemListCheckboxItem.swift b/TelegramUI/ItemListCheckboxItem.swift index 28371ba08e..2a9cf794e8 100644 --- a/TelegramUI/ItemListCheckboxItem.swift +++ b/TelegramUI/ItemListCheckboxItem.swift @@ -117,7 +117,7 @@ class ItemListCheckboxItemNode: ListViewItemNode { return { item, width, neighbors in let leftInset: CGFloat = 44.0 - let (titleLayout, titleApply) = makeTitleLayout(NSAttributedString(string: item.title, font: titleFont, textColor: UIColor.black), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), nil) + let (titleLayout, titleApply) = makeTitleLayout(NSAttributedString(string: item.title, font: titleFont, textColor: UIColor.black), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let separatorHeight = UIScreenPixel diff --git a/TelegramUI/ItemListDisclosureItem.swift b/TelegramUI/ItemListDisclosureItem.swift index 0e53375112..68ce1d8130 100644 --- a/TelegramUI/ItemListDisclosureItem.swift +++ b/TelegramUI/ItemListDisclosureItem.swift @@ -126,8 +126,8 @@ class ItemListDisclosureItemNode: ListViewItemNode { insets = itemListNeighborsGroupedInsets(neighbors) } - let (titleLayout, titleApply) = makeTitleLayout(NSAttributedString(string: item.title, font: titleFont, textColor: UIColor.black), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), nil) - let (labelLayout, labelApply) = makeLabelLayout(NSAttributedString(string: item.label, font: titleFont, textColor: UIColor(0x8e8e93)), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), nil) + let (titleLayout, titleApply) = makeTitleLayout(NSAttributedString(string: item.title, font: titleFont, textColor: UIColor.black), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), .natural, nil) + let (labelLayout, labelApply) = makeLabelLayout(NSAttributedString(string: item.label, font: titleFont, textColor: UIColor(0x8e8e93)), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) let layoutSize = layout.size diff --git a/TelegramUI/ItemListMultilineInputItem.swift b/TelegramUI/ItemListMultilineInputItem.swift index 820bd33ea7..50d9ac7280 100644 --- a/TelegramUI/ItemListMultilineInputItem.swift +++ b/TelegramUI/ItemListMultilineInputItem.swift @@ -117,7 +117,7 @@ class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNodeDelega } let attributedMeasureText = NSAttributedString(string: measureText, font: Font.regular(17.0), textColor: .black) let attributedText = NSAttributedString(string: item.text, font: Font.regular(17.0), textColor: .black) - let (textLayout, textApply) = makeTextLayout(attributedMeasureText, nil, 0, .end, CGSize(width: width - 8 - leftInset, height: CGFloat.greatestFiniteMagnitude), nil) + let (textLayout, textApply) = makeTextLayout(attributedMeasureText, nil, 0, .end, CGSize(width: width - 8 - leftInset, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let separatorHeight = UIScreenPixel diff --git a/TelegramUI/ItemListMultilineTextItem.swift b/TelegramUI/ItemListMultilineTextItem.swift index 00d2412bd9..19e0486473 100644 --- a/TelegramUI/ItemListMultilineTextItem.swift +++ b/TelegramUI/ItemListMultilineTextItem.swift @@ -98,7 +98,7 @@ class ItemListMultilineTextItemNode: ListViewItemNode { leftInset = 16.0 } - let (titleLayout, titleApply) = makeTextLayout(NSAttributedString(string: item.text, font: titleFont, textColor: textColor), nil, 0, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), nil) + let (titleLayout, titleApply) = makeTextLayout(NSAttributedString(string: item.text, font: titleFont, textColor: textColor), nil, 0, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let contentSize: CGSize let insets: UIEdgeInsets diff --git a/TelegramUI/ItemListPeerActionItem.swift b/TelegramUI/ItemListPeerActionItem.swift index 089439cb6c..3f5ad1bcc8 100644 --- a/TelegramUI/ItemListPeerActionItem.swift +++ b/TelegramUI/ItemListPeerActionItem.swift @@ -114,7 +114,7 @@ class ItemListPeerActionItemNode: ListViewItemNode { let editingOffset: CGFloat = (item.editing ? 38.0 : 0.0) - let (titleLayout, titleApply) = makeTitleLayout(NSAttributedString(string: item.title, font: titleFont, textColor: UIColor(0x007ee5)), nil, 1, .end, CGSize(width: width - leftInset - editingOffset, height: CGFloat.greatestFiniteMagnitude), nil) + let (titleLayout, titleApply) = makeTitleLayout(NSAttributedString(string: item.title, font: titleFont, textColor: UIColor(0x007ee5)), nil, 1, .end, CGSize(width: width - leftInset - editingOffset, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let separatorHeight = UIScreenPixel diff --git a/TelegramUI/ItemListPeerItem.swift b/TelegramUI/ItemListPeerItem.swift index 1db9cacc1d..05843fafb2 100644 --- a/TelegramUI/ItemListPeerItem.swift +++ b/TelegramUI/ItemListPeerItem.swift @@ -276,10 +276,10 @@ class ItemListPeerItemNode: ItemListRevealOptionsItemNode { editingOffset = 0.0 } - let (labelLayout, labelApply) = makeLabelLayout(labelAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), nil) + let (labelLayout, labelApply) = makeLabelLayout(labelAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), .natural, nil) - let (titleLayout, titleApply) = makeTitleLayout(titleAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - labelLayout.size.width - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), nil) - let (statusLayout, statusApply) = makeStatusLayout(statusAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - (labelLayout.size.width > 0.0 ? (labelLayout.size.width) + 15.0 : 0.0) - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), nil) + let (titleLayout, titleApply) = makeTitleLayout(titleAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - labelLayout.size.width - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), .natural, nil) + let (statusLayout, statusApply) = makeStatusLayout(statusAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - (labelLayout.size.width > 0.0 ? (labelLayout.size.width) + 15.0 : 0.0) - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let insets = itemListNeighborsGroupedInsets(neighbors) let contentSize = CGSize(width: width, height: 48.0) diff --git a/TelegramUI/ItemListRecentSessionItem.swift b/TelegramUI/ItemListRecentSessionItem.swift index 9f1ba32157..3bb70ac698 100644 --- a/TelegramUI/ItemListRecentSessionItem.swift +++ b/TelegramUI/ItemListRecentSessionItem.swift @@ -220,10 +220,10 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode { editingOffset = 0.0 } - let (labelLayout, labelApply) = makeLabelLayout(labelAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), nil) - let (titleLayout, titleApply) = makeTitleLayout(titleAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - editingOffset - rightInset - labelLayout.size.width - 5.0, height: CGFloat.greatestFiniteMagnitude), nil) - let (appLayout, appApply) = makeAppLayout(appAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), nil) - let (locationLayout, locationApply) = makeLocationLayout(locationAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), nil) + let (labelLayout, labelApply) = makeLabelLayout(labelAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), .natural, nil) + let (titleLayout, titleApply) = makeTitleLayout(titleAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - editingOffset - rightInset - labelLayout.size.width - 5.0, height: CGFloat.greatestFiniteMagnitude), .natural, nil) + let (appLayout, appApply) = makeAppLayout(appAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), .natural, nil) + let (locationLayout, locationApply) = makeLocationLayout(locationAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let insets = itemListNeighborsGroupedInsets(neighbors) let contentSize = CGSize(width: width, height: 75.0) diff --git a/TelegramUI/ItemListSectionHeaderItem.swift b/TelegramUI/ItemListSectionHeaderItem.swift index bd4b901b10..78c9e6a7b9 100644 --- a/TelegramUI/ItemListSectionHeaderItem.swift +++ b/TelegramUI/ItemListSectionHeaderItem.swift @@ -71,7 +71,7 @@ class ItemListSectionHeaderItemNode: ListViewItemNode { return { item, width, neighbors in let leftInset: CGFloat = 15.0 - let (titleLayout, titleApply) = makeTitleLayout(NSAttributedString(string: item.text, font: titleFont, textColor: UIColor(0x6d6d72)), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), nil) + let (titleLayout, titleApply) = makeTitleLayout(NSAttributedString(string: item.text, font: titleFont, textColor: UIColor(0x6d6d72)), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let contentSize: CGSize var insets = UIEdgeInsets() diff --git a/TelegramUI/ItemListSingleLineInputItem.swift b/TelegramUI/ItemListSingleLineInputItem.swift index bf16d0630e..afdfba316b 100644 --- a/TelegramUI/ItemListSingleLineInputItem.swift +++ b/TelegramUI/ItemListSingleLineInputItem.swift @@ -125,7 +125,7 @@ class ItemListSingleLineInputItemNode: ListViewItemNode, UITextFieldDelegate, It titleString.removeAttribute(NSFontAttributeName, range: NSMakeRange(0, titleString.length)) titleString.addAttributes([NSFontAttributeName: Font.regular(17.0)], range: NSMakeRange(0, titleString.length)) - let (titleLayout, titleApply) = makeTitleLayout(titleString, nil, 0, .end, CGSize(width: width - 32 - leftInset, height: CGFloat.greatestFiniteMagnitude), nil) + let (titleLayout, titleApply) = makeTitleLayout(titleString, nil, 0, .end, CGSize(width: width - 32 - leftInset, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let separatorHeight = UIScreenPixel diff --git a/TelegramUI/ItemListStickerPackItem.swift b/TelegramUI/ItemListStickerPackItem.swift index 4cd0b0f9b1..d992cd0b91 100644 --- a/TelegramUI/ItemListStickerPackItem.swift +++ b/TelegramUI/ItemListStickerPackItem.swift @@ -296,8 +296,8 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode { editingOffset = 0.0 } - let (titleLayout, titleApply) = makeTitleLayout(titleAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - editingOffset - rightInset - 10.0, height: CGFloat.greatestFiniteMagnitude), nil) - let (statusLayout, statusApply) = makeStatusLayout(statusAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), nil) + let (titleLayout, titleApply) = makeTitleLayout(titleAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - editingOffset - rightInset - 10.0, height: CGFloat.greatestFiniteMagnitude), .natural, nil) + let (statusLayout, statusApply) = makeStatusLayout(statusAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let insets = itemListNeighborsGroupedInsets(neighbors) let contentSize = CGSize(width: width, height: 59.0) @@ -416,9 +416,9 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode { strongSelf.installationActionImageNode.isHidden = false strongSelf.installationActionNode.isHidden = false strongSelf.installationActionNode.isUserInteractionEnabled = !installed - strongSelf.installationActionNode.setImage(installationActionImage, for: []) if let image = installationActionImage { let imageSize = image.size + strongSelf.installationActionImageNode.image = image strongSelf.installationActionImageNode.frame = CGRect(origin: CGPoint(x: installationActionFrame.minX + floor((installationActionFrame.size.width - imageSize.width) / 2.0), y: installationActionFrame.minY + floor((installationActionFrame.size.height - imageSize.height) / 2.0)), size: imageSize) } } diff --git a/TelegramUI/ItemListSwitchItem.swift b/TelegramUI/ItemListSwitchItem.swift index a1cc35730b..4ef42cf5b8 100644 --- a/TelegramUI/ItemListSwitchItem.swift +++ b/TelegramUI/ItemListSwitchItem.swift @@ -115,7 +115,7 @@ class ItemListSwitchItemNode: ListViewItemNode { insets = itemListNeighborsGroupedInsets(neighbors) } - let (titleLayout, titleApply) = makeTitleLayout(NSAttributedString(string: item.title, font: titleFont, textColor: UIColor.black), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), nil) + let (titleLayout, titleApply) = makeTitleLayout(NSAttributedString(string: item.title, font: titleFont, textColor: UIColor.black), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) let layoutSize = layout.size diff --git a/TelegramUI/ItemListTextItem.swift b/TelegramUI/ItemListTextItem.swift index 2caa54618a..3bf2ddc218 100644 --- a/TelegramUI/ItemListTextItem.swift +++ b/TelegramUI/ItemListTextItem.swift @@ -104,7 +104,7 @@ class ItemListTextItemNode: ListViewItemNode { return (TextNode.UrlAttribute, contents) })) } - let (titleLayout, titleApply) = makeTitleLayout(attributedText, nil, 0, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), nil) + let (titleLayout, titleApply) = makeTitleLayout(attributedText, nil, 0, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let contentSize: CGSize diff --git a/TelegramUI/ItemListTextWithLabelItem.swift b/TelegramUI/ItemListTextWithLabelItem.swift index 777ffc46a3..cf6fa51b68 100644 --- a/TelegramUI/ItemListTextWithLabelItem.swift +++ b/TelegramUI/ItemListTextWithLabelItem.swift @@ -93,8 +93,8 @@ class ItemListTextWithLabelItemNode: ListViewItemNode { let insets = itemListNeighborsPlainInsets(neighbors) let leftInset: CGFloat = 35.0 - let (labelLayout, labelApply) = makeLabelLayout(NSAttributedString(string: item.label, font: labelFont, textColor: UIColor(0x007ee5)), nil, 1, .end, CGSize(width: width - leftInset - 8.0, height: CGFloat.greatestFiniteMagnitude), nil) - let (textLayout, textApply) = makeTextLayout(NSAttributedString(string: item.text, font: textFont, textColor: UIColor.black), nil, item.multiline ? 0 : 1, .end, CGSize(width: width - leftInset - 8.0, height: CGFloat.greatestFiniteMagnitude), nil) + let (labelLayout, labelApply) = makeLabelLayout(NSAttributedString(string: item.label, font: labelFont, textColor: UIColor(0x007ee5)), nil, 1, .end, CGSize(width: width - leftInset - 8.0, height: CGFloat.greatestFiniteMagnitude), .natural, nil) + let (textLayout, textApply) = makeTextLayout(NSAttributedString(string: item.text, font: textFont, textColor: UIColor.black), nil, item.multiline ? 0 : 1, .end, CGSize(width: width - leftInset - 8.0, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let contentSize = CGSize(width: width, height: textLayout.size.height + 39.0) return (ListViewItemNodeLayout(contentSize: contentSize, insets: insets), { [weak self] in if let strongSelf = self { diff --git a/TelegramUI/LegacyCamera.swift b/TelegramUI/LegacyCamera.swift index 645265c093..48b8e0dda3 100644 --- a/TelegramUI/LegacyCamera.swift +++ b/TelegramUI/LegacyCamera.swift @@ -80,6 +80,22 @@ func presentedLegacyCamera(cameraView: TGAttachmentCameraView?, menuController: } controller.finishedWithVideo = { [weak menuController] videoURL, previewImage, duration, dimensions, adjustments, caption, stickers in + if let videoURL = videoURL { + let description = NSMutableDictionary() + description["type"] = "video" + description["url"] = videoURL.path + if let previewImage = previewImage { + description["previewImage"] = previewImage + } + if let adjustments = adjustments { + description["adjustments"] = adjustments + } + description["duration"] = duration as NSNumber + description["dimensions"] = NSValue(cgSize: dimensions) + if let item = legacyAssetPickerItemGenerator()(description, caption, nil) { + sendMessagesWithSignals([SSignal.single(item)]) + } + } menuController?.dismiss(animated: false) } diff --git a/TelegramUI/LegacyMediaPickers.swift b/TelegramUI/LegacyMediaPickers.swift index ea12e85c98..cba222ac4f 100644 --- a/TelegramUI/LegacyMediaPickers.swift +++ b/TelegramUI/LegacyMediaPickers.swift @@ -55,15 +55,21 @@ func legacyAssetPicker(fileMode: Bool) -> Signal<(@escaping (UIViewController) - } } -private enum LegacyAssetData { +private enum LegacyAssetImageData { case image(UIImage) case asset(PHAsset) case tempFile(String) } +private enum LegacyAssetVideoData { + case asset(TGMediaAsset) + case tempFile(path: String, dimensions: CGSize, duration: Double) +} + private enum LegacyAssetItem { - case image(LegacyAssetData) - case file(LegacyAssetData, mimeType: String, name: String) + case image(LegacyAssetImageData) + case file(LegacyAssetImageData, mimeType: String, name: String) + case video(LegacyAssetVideoData, UIImage?, TGVideoEditAdjustments?) } private final class LegacyAssetItemWrapper: NSObject { @@ -90,7 +96,7 @@ func legacyAssetPickerItemGenerator() -> ((Any?, String?, String?) -> [AnyHashab if let document = dict["document"] as? NSNumber, document.boolValue { asFile = true } - var result: [AnyHashable : Any] = [:] + var result: [AnyHashable: Any] = [:] if asFile { //result["item" as NSString] = LegacyAssetItemWrapper(item: .file(.asset(asset.backingAsset))) return nil @@ -109,10 +115,22 @@ func legacyAssetPickerItemGenerator() -> ((Any?, String?, String?) -> [AnyHashab name = customName } - var result: [AnyHashable : Any] = [:] + var result: [AnyHashable: Any] = [:] result["item" as NSString] = LegacyAssetItemWrapper(item: .file(.tempFile(tempFileUrl.path), mimeType: mimeType, name: name)) return result } + } else if (dict["type"] as! NSString) == "video" { + if let asset = dict["asset"] as? TGMediaAsset { + var result: [AnyHashable: Any] = [:] + result["item" as NSString] = LegacyAssetItemWrapper(item: .video(.asset(asset), dict["previewImage"] as? UIImage, dict["adjustments"] as? TGVideoEditAdjustments)) + return result + } else if let url = dict["url"] as? String { + let dimensions = (dict["dimensions"]! as AnyObject).cgSizeValue! + let duration = (dict["duration"]! as AnyObject).doubleValue! + var result: [AnyHashable: Any] = [:] + result["item" as NSString] = LegacyAssetItemWrapper(item: .video(.tempFile(path: url, dimensions: dimensions, duration: duration), dict["previewImage"] as? UIImage, dict["adjustments"] as? TGVideoEditAdjustments)) + return result + } } return nil } @@ -136,7 +154,7 @@ func legacyAssetPickerEnqueueMessages(account: Account, peerId: PeerId, signals: if let scaledImage = generateImage(scaledSize, contextGenerator: { size, context in context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size)) }, opaque: true) { - if let scaledImageData = UIImageJPEGRepresentation(image, 0.52) { + if let scaledImageData = UIImageJPEGRepresentation(scaledImage, 0.52) { let _ = try? scaledImageData.write(to: URL(fileURLWithPath: tempFilePath)) let resource = LocalFileReferenceMediaResource(localFilePath: tempFilePath, randomId: randomId) let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: [TelegramMediaImageRepresentation(dimensions: scaledSize, resource: resource)]) @@ -166,6 +184,54 @@ func legacyAssetPickerEnqueueMessages(account: Account, peerId: PeerId, signals: default: break } + case let .video(data, previewImage, adjustments): + var finalDimensions: CGSize + var finalDuration: Double + switch data { + case let .asset(asset): + finalDimensions = asset.dimensions + finalDuration = asset.videoDuration + case let .tempFile(_, dimensions, duration): + finalDimensions = dimensions + finalDuration = duration + } + + finalDimensions = TGFitSize(finalDimensions, CGSize(width: 848.0, height: 848.0)) + + var previewRepresentations: [TelegramMediaImageRepresentation] = [] + if let previewImage = previewImage { + let resource = LocalFileMediaResource(fileId: arc4random64()) + let thumbnailSize = finalDimensions.aspectFitted(CGSize(width: 90.0, height: 90.0)) + let thumbnailImage = TGScaleImageToPixelSize(previewImage, thumbnailSize)! + if let thumbnailData = UIImageJPEGRepresentation(thumbnailImage, 0.4) { + account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnailData) + previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: thumbnailSize, resource: resource)) + } + } + + finalDimensions = TGMediaVideoConverter.dimensions(for: finalDimensions, adjustments: adjustments, preset: TGMediaVideoConversionPresetCompressedMedium) + + var resourceAdjustments: VideoMediaResourceAdjustments? + if let adjustments = adjustments { + if adjustments.trimApplied() { + finalDuration = adjustments.trimEndValue - adjustments.trimStartValue + } + + let adjustmentsData = MemoryBuffer(data: NSKeyedArchiver.archivedData(withRootObject: adjustments.dictionary())) + let digest = MemoryBuffer(data: adjustmentsData.md5Digest()) + resourceAdjustments = VideoMediaResourceAdjustments(data: adjustmentsData, digest: digest) + } + + let resource: TelegramMediaResource + switch data { + case let .asset(asset): + resource = VideoLibraryMediaResource(localIdentifier: asset.backingAsset.localIdentifier, adjustments: resourceAdjustments) + case let .tempFile(path, _, _): + resource = LocalFileVideoMediaResource(randomId: arc4random64(), path: path, adjustments: resourceAdjustments) + } + + let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), resource: resource, previewRepresentations: previewRepresentations, mimeType: "video/mp4", size: nil, attributes: [.FileName(fileName: "video.mp4"), .Video(duration: Int(finalDuration), size: finalDimensions)]) + messages.append(.message(text: "", attributes: [], media: media, replyToMessageId: nil)) } } } diff --git a/TelegramUI/ListControllerButtonItem.swift b/TelegramUI/ListControllerButtonItem.swift index 387ed84b00..19c8c8a0d3 100644 --- a/TelegramUI/ListControllerButtonItem.swift +++ b/TelegramUI/ListControllerButtonItem.swift @@ -43,7 +43,7 @@ class ListControllerButtonItemNode: ListControllerGroupableItemNode { let layoutLabel = TextNode.asyncLayout(self.label) return { item, width in if let item = item as? ListControllerButtonItem { - let (labelLayout, labelApply) = layoutLabel(NSAttributedString(string: item.title, font: titleFont, textColor: item.color), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), nil) + let (labelLayout, labelApply) = layoutLabel(NSAttributedString(string: item.title, font: titleFont, textColor: item.color), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), .natural, nil) return (CGSize(width: width, height: 44.0), { [weak self] in if let strongSelf = self { let _ = labelApply() diff --git a/TelegramUI/ListControllerDisclosureActionItem.swift b/TelegramUI/ListControllerDisclosureActionItem.swift index f5f5cb56e4..ea9752303c 100644 --- a/TelegramUI/ListControllerDisclosureActionItem.swift +++ b/TelegramUI/ListControllerDisclosureActionItem.swift @@ -61,7 +61,7 @@ class ListControllerDisclosureActionItemNode: ListControllerGroupableItemNode { let layoutLabel = TextNode.asyncLayout(self.label) return { item, width in if let item = item as? ListControllerDisclosureActionItem { - let (labelLayout, labelApply) = layoutLabel(NSAttributedString(string: item.title, font: titleFont, textColor: UIColor.black), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), nil) + let (labelLayout, labelApply) = layoutLabel(NSAttributedString(string: item.title, font: titleFont, textColor: UIColor.black), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), .natural, nil) return (CGSize(width: width, height: 44.0), { [weak self] in if let strongSelf = self { let _ = labelApply() diff --git a/TelegramUI/ListMessageFileItemNode.swift b/TelegramUI/ListMessageFileItemNode.swift index 746cda15c1..05ece3214e 100644 --- a/TelegramUI/ListMessageFileItemNode.swift +++ b/TelegramUI/ListMessageFileItemNode.swift @@ -360,11 +360,11 @@ final class ListMessageFileItemNode: ListMessageNode { } } - let (titleNodeLayout, titleNodeApply) = titleNodeMakeLayout(titleText, nil, 1, .middle, CGSize(width: width - leftInset - 8.0, height: CGFloat.infinity), nil) + let (titleNodeLayout, titleNodeApply) = titleNodeMakeLayout(titleText, nil, 1, .middle, CGSize(width: width - leftInset - 8.0, height: CGFloat.infinity), .natural, nil) - let (descriptionNodeLayout, descriptionNodeApply) = descriptionNodeMakeLayout(descriptionText, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - 12.0, height: CGFloat.infinity), nil) + let (descriptionNodeLayout, descriptionNodeApply) = descriptionNodeMakeLayout(descriptionText, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - 12.0, height: CGFloat.infinity), .natural, nil) - let (extensionTextLayout, extensionTextApply) = extensionIconTextMakeLayout(extensionText, nil, 1, .end, CGSize(width: 38.0, height: CGFloat.infinity), nil) + let (extensionTextLayout, extensionTextApply) = extensionIconTextMakeLayout(extensionText, nil, 1, .end, CGSize(width: 38.0, height: CGFloat.infinity), .natural, nil) var iconImageApply: (() -> Void)? if let iconImageRepresentation = iconImageRepresentation { diff --git a/TelegramUI/ListMessageSnippetItemNode.swift b/TelegramUI/ListMessageSnippetItemNode.swift index 921e3f2d1c..0084a4507c 100644 --- a/TelegramUI/ListMessageSnippetItemNode.swift +++ b/TelegramUI/ListMessageSnippetItemNode.swift @@ -147,11 +147,11 @@ final class ListMessageSnippetItemNode: ListMessageNode { } } - let (titleNodeLayout, titleNodeApply) = titleNodeMakeLayout(title, nil, 1, .middle, CGSize(width: width - leftInset - 8.0, height: CGFloat.infinity), nil) + let (titleNodeLayout, titleNodeApply) = titleNodeMakeLayout(title, nil, 1, .middle, CGSize(width: width - leftInset - 8.0, height: CGFloat.infinity), .natural, nil) - let (descriptionNodeLayout, descriptionNodeApply) = descriptionNodeMakeLayout(descriptionText, nil, 0, .end, CGSize(width: width - leftInset - 8.0 - 12.0, height: CGFloat.infinity), nil) + let (descriptionNodeLayout, descriptionNodeApply) = descriptionNodeMakeLayout(descriptionText, nil, 0, .end, CGSize(width: width - leftInset - 8.0 - 12.0, height: CGFloat.infinity), .natural, nil) - let (iconTextLayout, iconTextApply) = iconTextMakeLayout(iconText, nil, 1, .end, CGSize(width: 38.0, height: CGFloat.infinity), nil) + let (iconTextLayout, iconTextApply) = iconTextMakeLayout(iconText, nil, 1, .end, CGSize(width: 38.0, height: CGFloat.infinity), .natural, nil) var iconImageApply: (() -> Void)? if let iconImageRepresentation = iconImageRepresentation { diff --git a/TelegramUI/ListSectionHeaderNode.swift b/TelegramUI/ListSectionHeaderNode.swift index 1f33bed101..0f0870940e 100644 --- a/TelegramUI/ListSectionHeaderNode.swift +++ b/TelegramUI/ListSectionHeaderNode.swift @@ -28,7 +28,7 @@ final class ListSectionHeaderNode: ASDisplayNode { let size = self.bounds.size let makeLayout = TextNode.asyncLayout(self.label) - let (labelLayout, labelApply) = makeLayout(NSAttributedString(string: self.title ?? "", font: Font.medium(12.0), textColor: UIColor(0x8e8e93)), self.backgroundColor, 1, .end, CGSize(width: max(0.0, size.width - 18.0), height: size.height), nil) + let (labelLayout, labelApply) = makeLayout(NSAttributedString(string: self.title ?? "", font: Font.medium(12.0), textColor: UIColor(0x8e8e93)), self.backgroundColor, 1, .end, CGSize(width: max(0.0, size.width - 18.0), height: size.height), .natural, nil) let _ = labelApply() self.label.frame = CGRect(origin: CGPoint(x: 9.0, y: 6.0), size: labelLayout.size) } diff --git a/TelegramUI/MediaNavigationAccessoryHeaderNode.swift b/TelegramUI/MediaNavigationAccessoryHeaderNode.swift index e73893443f..3660196f69 100644 --- a/TelegramUI/MediaNavigationAccessoryHeaderNode.swift +++ b/TelegramUI/MediaNavigationAccessoryHeaderNode.swift @@ -292,11 +292,11 @@ final class MediaNavigationAccessoryHeaderNode: ASDisplayNode { let makeMaximizedTitleLayout = TextNode.asyncLayout(self.maximizedTitleNode) let makeMaximizedSubtitleLayout = TextNode.asyncLayout(self.maximizedSubtitleNode) - let (titleLayout, titleApply) = makeTitleLayout(titleString, nil, 1, .middle, CGSize(width: size.width - 80.0, height: 100.0), nil) - let (subtitleLayout, subtitleApply) = makeSubtitleLayout(subtitleString, nil, 1, .middle, CGSize(width: size.width - 80.0, height: 100.0), nil) + let (titleLayout, titleApply) = makeTitleLayout(titleString, nil, 1, .middle, CGSize(width: size.width - 80.0, height: 100.0), .natural, nil) + let (subtitleLayout, subtitleApply) = makeSubtitleLayout(subtitleString, nil, 1, .middle, CGSize(width: size.width - 80.0, height: 100.0), .natural, nil) - let (maximizedTitleLayout, maximizedTitleApply) = makeMaximizedTitleLayout(maximizedTitleString, nil, 1, .middle, CGSize(width: size.width - 80.0, height: 100.0), nil) - let (maximizedSubtitleLayout, maximizedSubtitleApply) = makeMaximizedSubtitleLayout(maximizedSubtitleString, nil, 1, .middle, CGSize(width: size.width - 80.0, height: 100.0), nil) + let (maximizedTitleLayout, maximizedTitleApply) = makeMaximizedTitleLayout(maximizedTitleString, nil, 1, .middle, CGSize(width: size.width - 80.0, height: 100.0), .natural, nil) + let (maximizedSubtitleLayout, maximizedSubtitleApply) = makeMaximizedSubtitleLayout(maximizedSubtitleString, nil, 1, .middle, CGSize(width: size.width - 80.0, height: 100.0), .natural, nil) titleApply() subtitleApply() diff --git a/TelegramUI/MediaResources.swift b/TelegramUI/MediaResources.swift new file mode 100644 index 0000000000..d2763ada66 --- /dev/null +++ b/TelegramUI/MediaResources.swift @@ -0,0 +1,156 @@ +import Foundation +import Postbox +import TelegramCore + +public final class VideoMediaResourceAdjustments: Coding, Equatable { + let data: MemoryBuffer + let digest: MemoryBuffer + + init(data: MemoryBuffer, digest: MemoryBuffer) { + self.data = data + self.digest = digest + } + + public init(decoder: Decoder) { + self.data = decoder.decodeBytesForKey("d")! + self.digest = decoder.decodeBytesForKey("h")! + } + + public func encode(_ encoder: Encoder) { + encoder.encodeBytes(self.data, forKey: "d") + encoder.encodeBytes(self.digest, forKey: "h") + } + + public static func ==(lhs: VideoMediaResourceAdjustments, rhs: VideoMediaResourceAdjustments) -> Bool { + return lhs.data == rhs.data && lhs.digest == rhs.digest + } +} + +public struct VideoLibraryMediaResourceId: MediaResourceId { + public let localIdentifier: String + public let adjustmentsDigest: MemoryBuffer? + + public var uniqueId: String { + if let adjustmentsDigest = self.adjustmentsDigest { + return "vi-\(self.localIdentifier.replacingOccurrences(of: "/", with: "_"))-\(adjustmentsDigest.description)" + } else { + return "vi-\(self.localIdentifier.replacingOccurrences(of: "/", with: "_"))" + } + } + + public var hashValue: Int { + return self.localIdentifier.hashValue + } + + public func isEqual(to: MediaResourceId) -> Bool { + if let to = to as? VideoLibraryMediaResourceId { + return self.localIdentifier == to.localIdentifier + } else { + return false + } + } +} + +public final class VideoLibraryMediaResource: TelegramMediaResource { + public let localIdentifier: String + public let adjustments: VideoMediaResourceAdjustments? + + public var headerSize: Int32 { + return 32 * 1024 + } + + public init(localIdentifier: String, adjustments: VideoMediaResourceAdjustments?) { + self.localIdentifier = localIdentifier + self.adjustments = adjustments + } + + public required init(decoder: Decoder) { + self.localIdentifier = decoder.decodeStringForKey("i") + self.adjustments = decoder.decodeObjectForKey("a", decoder: { VideoMediaResourceAdjustments(decoder: $0) }) as? VideoMediaResourceAdjustments + } + + public func encode(_ encoder: Encoder) { + encoder.encodeString(self.localIdentifier, forKey: "i") + if let adjustments = self.adjustments { + encoder.encodeObject(adjustments, forKey: "a") + } else { + encoder.encodeNil(forKey: "a") + } + } + + public var id: MediaResourceId { + return VideoLibraryMediaResourceId(localIdentifier: self.localIdentifier, adjustmentsDigest: self.adjustments?.digest) + } + + public func isEqual(to: TelegramMediaResource) -> Bool { + if let to = to as? VideoLibraryMediaResource { + return self.localIdentifier == to.localIdentifier && self.adjustments == to.adjustments + } else { + return false + } + } +} + +public struct LocalFileVideoMediaResourceId: MediaResourceId { + public let randomId: Int64 + + public var uniqueId: String { + return "lvi-\(self.randomId)" + } + + public var hashValue: Int { + return self.randomId.hashValue + } + + public func isEqual(to: MediaResourceId) -> Bool { + if let to = to as? LocalFileVideoMediaResourceId { + return self.randomId == to.randomId + } else { + return false + } + } +} + +public final class LocalFileVideoMediaResource: TelegramMediaResource { + public let randomId: Int64 + public let path: String + public let adjustments: VideoMediaResourceAdjustments? + + public var headerSize: Int32 { + return 32 * 1024 + } + + public init(randomId: Int64, path: String, adjustments: VideoMediaResourceAdjustments?) { + self.randomId = randomId + self.path = path + self.adjustments = adjustments + } + + public required init(decoder: Decoder) { + self.randomId = decoder.decodeInt64ForKey("i") + self.path = decoder.decodeStringForKey("p") + self.adjustments = decoder.decodeObjectForKey("a", decoder: { VideoMediaResourceAdjustments(decoder: $0) }) as? VideoMediaResourceAdjustments + } + + public func encode(_ encoder: Encoder) { + encoder.encodeInt64(self.randomId, forKey: "i") + encoder.encodeString(self.path, forKey: "p") + if let adjustments = self.adjustments { + encoder.encodeObject(adjustments, forKey: "a") + } else { + encoder.encodeNil(forKey: "a") + } + } + + public var id: MediaResourceId { + return LocalFileVideoMediaResourceId(randomId: self.randomId) + } + + public func isEqual(to: TelegramMediaResource) -> Bool { + if let to = to as? LocalFileVideoMediaResource { + return self.randomId == to.randomId && self.path == to.path && self.adjustments == to.adjustments + } else { + return false + } + } +} diff --git a/TelegramUI/MentionChatInputPanelItem.swift b/TelegramUI/MentionChatInputPanelItem.swift index 16217d32ad..2b76d20c0f 100644 --- a/TelegramUI/MentionChatInputPanelItem.swift +++ b/TelegramUI/MentionChatInputPanelItem.swift @@ -124,7 +124,7 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { let leftInset: CGFloat = 55.0 let rightInset: CGFloat = 10.0 - let (textLayout, textApply) = makeTextLayout(NSAttributedString(string: item.peer.displayTitle, font: textFont, textColor: .black), nil, 1, .end, CGSize(width: width - leftInset - rightInset, height: 100.0), nil) + let (textLayout, textApply) = makeTextLayout(NSAttributedString(string: item.peer.displayTitle, font: textFont, textColor: .black), nil, 1, .end, CGSize(width: width - leftInset - rightInset, height: 100.0), .natural, nil) let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: width, height: HashtagChatInputPanelItemNode.itemHeight), insets: UIEdgeInsets()) diff --git a/TelegramUI/ReplyAccessoryPanelNode.swift b/TelegramUI/ReplyAccessoryPanelNode.swift index c84412b91d..78bc5f68e8 100644 --- a/TelegramUI/ReplyAccessoryPanelNode.swift +++ b/TelegramUI/ReplyAccessoryPanelNode.swift @@ -23,10 +23,13 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { private let messageDisposable = MetaDisposable() let messageId: MessageId + private var previousMedia: Media? + let closeButton: ASButtonNode let lineNode: ASImageNode let titleNode: ASTextNode let textNode: ASTextNode + let imageNode: TransformImageNode init(account: Account, messageId: MessageId) { self.messageId = messageId @@ -51,6 +54,9 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { self.textNode.maximumNumberOfLines = 1 self.textNode.displaysAsynchronously = false + self.imageNode = TransformImageNode() + self.imageNode.isHidden = true + super.init() self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: [.touchUpInside]) @@ -59,6 +65,7 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { self.addSubnode(self.lineNode) self.addSubnode(self.titleNode) self.addSubnode(self.textNode) + self.addSubnode(self.imageNode) self.messageDisposable.set((account.postbox.messageAtId(messageId) |> deliverOnMainQueue).start(next: { [weak self] message in @@ -68,13 +75,72 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { if let author = message?.author { authorName = author.displayTitle } - if let messageText = message?.text { - text = messageText + if let message = message { + let (string, _) = textStringForReplyMessage(message) + text = string + } + + var updatedMedia: Media? + var imageDimensions: CGSize? + if let message = message { + for media in message.media { + if let image = media as? TelegramMediaImage { + updatedMedia = image + if let representation = largestRepresentationForPhoto(image) { + imageDimensions = representation.dimensions + } + break + } else if let file = media as? TelegramMediaFile { + updatedMedia = file + if let representation = largestImageRepresentation(file.previewRepresentations), !file.isSticker { + imageDimensions = representation.dimensions + } + break + } + } + } + + let imageNodeLayout = strongSelf.imageNode.asyncLayout() + var applyImage: (() -> Void)? + if let imageDimensions = imageDimensions { + let boundingSize = CGSize(width: 35.0, height: 35.0) + applyImage = imageNodeLayout(TransformImageArguments(corners: ImageCorners(radius: 2.0), imageSize: imageDimensions.aspectFilled(boundingSize), boundingSize: boundingSize, intrinsicInsets: UIEdgeInsets())) + } + + var mediaUpdated = false + if let updatedMedia = updatedMedia, let previousMedia = strongSelf.previousMedia { + mediaUpdated = !updatedMedia.isEqual(previousMedia) + } else if (updatedMedia != nil) != (strongSelf.previousMedia != nil) { + mediaUpdated = true + } + + var updateImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? + if mediaUpdated { + if let updatedMedia = updatedMedia, imageDimensions != nil { + if let image = updatedMedia as? TelegramMediaImage { + updateImageSignal = chatMessagePhotoThumbnail(account: account, photo: image) + } else if let file = updatedMedia as? TelegramMediaFile { + + } + } else { + updateImageSignal = .single({ _ in return nil }) + } } strongSelf.titleNode.attributedText = NSAttributedString(string: authorName, font: Font.medium(15.0), textColor: UIColor(0x007ee5)) strongSelf.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(15.0), textColor: UIColor.black) + if let applyImage = applyImage { + applyImage() + strongSelf.imageNode.isHidden = false + } else { + strongSelf.imageNode.isHidden = true + } + + if let updateImageSignal = updateImageSignal { + strongSelf.imageNode.setSignal(account: account, signal: updateImageSignal) + } + strongSelf.setNeedsLayout() } })) @@ -102,11 +168,17 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { self.lineNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 8.0), size: CGSize(width: 2.0, height: bounds.size.height - 10.0)) - let titleSize = self.titleNode.measure(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset, height: bounds.size.height)) - self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset, y: 7.0), size: titleSize) + var imageTextInset: CGFloat = 0.0 + if !self.imageNode.isHidden { + imageTextInset = 9.0 + 35.0 + } + self.imageNode.frame = CGRect(origin: CGPoint(x: leftInset + 9.0, y: 8.0), size: CGSize(width: 35.0, height: 35.0)) - let textSize = self.textNode.measure(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset, height: bounds.size.height)) - self.textNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset, y: 25.0), size: textSize) + let titleSize = self.titleNode.measure(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset - imageTextInset, height: bounds.size.height)) + self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset, y: 7.0), size: titleSize) + + let textSize = self.textNode.measure(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset - imageTextInset, height: bounds.size.height)) + self.textNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset, y: 25.0), size: textSize) } @objc func closePressed() { diff --git a/TelegramUI/SearchBarPlaceholderNode.swift b/TelegramUI/SearchBarPlaceholderNode.swift index c072a4fa7c..d021b26cc0 100644 --- a/TelegramUI/SearchBarPlaceholderNode.swift +++ b/TelegramUI/SearchBarPlaceholderNode.swift @@ -69,7 +69,7 @@ class SearchBarPlaceholderNode: ASDisplayNode, ASEditableTextNodeDelegate { let currentForegroundColor = self.foregroundColor return { placeholderString, constrainedSize, foregroundColor in - let (labelLayoutResult, labelApply) = labelLayout(placeholderString, foregroundColor, 1, .end, constrainedSize, nil) + let (labelLayoutResult, labelApply) = labelLayout(placeholderString, foregroundColor, 1, .end, constrainedSize, .natural, nil) var updatedBackgroundImage: UIImage? if !currentForegroundColor.isEqual(foregroundColor) { diff --git a/TelegramUI/SettingsAccountInfoItem.swift b/TelegramUI/SettingsAccountInfoItem.swift index 1ce75a8aa3..1d5a90ee5f 100644 --- a/TelegramUI/SettingsAccountInfoItem.swift +++ b/TelegramUI/SettingsAccountInfoItem.swift @@ -62,7 +62,7 @@ class SettingsAccountInfoItemNode: ListControllerGroupableItemNode { return { item, width in if let item = item as? SettingsAccountInfoItem { - let (nameNodeLayout, nameNodeApply) = layoutNameNode(NSAttributedString(string: item.peer?.displayTitle ?? "", font: nameFont, textColor: UIColor.black), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), nil) + let (nameNodeLayout, nameNodeApply) = layoutNameNode(NSAttributedString(string: item.peer?.displayTitle ?? "", font: nameFont, textColor: UIColor.black), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), .natural, nil) let statusText: String let statusColor: UIColor @@ -81,7 +81,7 @@ class SettingsAccountInfoItemNode: ListControllerGroupableItemNode { statusColor = UIColor(0x007ee5) } - let (statusNodeLayout, statusNodeApply) = layoutStatusNode(NSAttributedString(string: statusText, font: statusFont, textColor: statusColor), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), nil) + let (statusNodeLayout, statusNodeApply) = layoutStatusNode(NSAttributedString(string: statusText, font: statusFont, textColor: statusColor), nil, 1, .end, CGSize(width: width - 20, height: CGFloat.greatestFiniteMagnitude), .natural, nil) return (CGSize(width: width, height: 97.0), { [weak self] in if let strongSelf = self { diff --git a/TelegramUI/StickerPackPreviewControllerNode.swift b/TelegramUI/StickerPackPreviewControllerNode.swift index 055d5cb4c5..b89e5f7d2a 100644 --- a/TelegramUI/StickerPackPreviewControllerNode.swift +++ b/TelegramUI/StickerPackPreviewControllerNode.swift @@ -424,13 +424,6 @@ final class StickerPackPreviewControllerNode: ASDisplayNode, UIScrollViewDelegat return super.hitTest(point, with: event) } - /*func scrollViewDidScroll(_ scrollView: UIScrollView) { - let contentOffset = scrollView.contentOffset - let additionalTopHeight = -contentOffset.y - - scrollView.alwaysBounceVertical = additionalTopHeight >= 0.0 - }*/ - func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { let contentOffset = scrollView.contentOffset let additionalTopHeight = max(0.0, -contentOffset.y) diff --git a/TelegramUI/TelegramAccountAuxiliaryMethods.swift b/TelegramUI/TelegramAccountAuxiliaryMethods.swift new file mode 100644 index 0000000000..b413d84128 --- /dev/null +++ b/TelegramUI/TelegramAccountAuxiliaryMethods.swift @@ -0,0 +1,20 @@ +import Foundation +import TelegramCore +import Postbox + +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, range in + if let resource = resource as? VideoLibraryMediaResource { + return fetchVideoLibraryMediaResource(resource: resource) + } else if let resource = resource as? LocalFileVideoMediaResource { + return fetchLocalFileVideoMediaResource(resource: resource) + } + return nil +}) diff --git a/TelegramUI/TextNode.swift b/TelegramUI/TextNode.swift index 53d3de457b..5b578ba607 100644 --- a/TelegramUI/TextNode.swift +++ b/TelegramUI/TextNode.swift @@ -36,15 +36,17 @@ final class TextNodeLayout: NSObject { fileprivate let truncationType: CTLineTruncationType fileprivate let backgroundColor: UIColor? fileprivate let constrainedSize: CGSize + fileprivate let alignment: NSTextAlignment fileprivate let cutout: TextNodeCutout? let size: CGSize fileprivate let lines: [TextNodeLine] - fileprivate init(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, cutout: TextNodeCutout?, size: CGSize, lines: [TextNodeLine], backgroundColor: UIColor?) { + fileprivate init(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment, cutout: TextNodeCutout?, size: CGSize, lines: [TextNodeLine], backgroundColor: UIColor?) { self.attributedString = attributedString self.maximumNumberOfLines = maximumNumberOfLines self.truncationType = truncationType self.constrainedSize = constrainedSize + self.alignment = alignment self.cutout = cutout self.size = size self.lines = lines @@ -125,7 +127,7 @@ final class TextNode: ASDisplayNode { } } - private class func calculateLayout(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, backgroundColor: UIColor?, constrainedSize: CGSize, cutout: TextNodeCutout?) -> TextNodeLayout { + private class func calculateLayout(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, backgroundColor: UIColor?, constrainedSize: CGSize, alignment: NSTextAlignment, cutout: TextNodeCutout?) -> TextNodeLayout { if let attributedString = attributedString { let stringLength = attributedString.length @@ -150,7 +152,7 @@ final class TextNode: ASDisplayNode { var maybeTypesetter: CTTypesetter? maybeTypesetter = CTTypesetterCreateWithAttributedString(attributedString as CFAttributedString) if maybeTypesetter == nil { - return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, cutout: cutout, size: CGSize(), lines: [], backgroundColor: backgroundColor) + return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, cutout: cutout, size: CGSize(), lines: [], backgroundColor: backgroundColor) } let typesetter = maybeTypesetter! @@ -253,9 +255,9 @@ final class TextNode: ASDisplayNode { } } - return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, cutout: cutout, size: CGSize(width: ceil(layoutSize.width), height: ceil(layoutSize.height)), lines: lines, backgroundColor: backgroundColor) + return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, cutout: cutout, size: CGSize(width: ceil(layoutSize.width), height: ceil(layoutSize.height)), lines: lines, backgroundColor: backgroundColor) } else { - return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, cutout: cutout, size: CGSize(), lines: [], backgroundColor: backgroundColor) + return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, cutout: cutout, size: CGSize(), lines: [], backgroundColor: backgroundColor) } } @@ -292,9 +294,17 @@ final class TextNode: ASDisplayNode { //let clipRect = CGContextGetClipBoundingBox(context) + let alignment = layout.alignment + for i in 0 ..< layout.lines.count { let line = layout.lines[i] - context.textPosition = CGPoint(x: line.frame.origin.x, y: line.frame.origin.y) + let lineOffset: CGFloat + if alignment == .center { + lineOffset = floor((bounds.size.width - line.frame.size.width) / 2.0) + } else { + lineOffset = 0.0 + } + context.textPosition = CGPoint(x: line.frame.origin.x + lineOffset, y: line.frame.origin.y) CTLineDraw(line.line, context) } @@ -306,14 +316,14 @@ final class TextNode: ASDisplayNode { context.setBlendMode(.normal) } - class func asyncLayout(_ maybeNode: TextNode?) -> (_ attributedString: NSAttributedString?, _ backgroundColor: UIColor?, _ maximumNumberOfLines: Int, _ truncationType: CTLineTruncationType, _ constrainedSize: CGSize, _ cutout: TextNodeCutout?) -> (TextNodeLayout, () -> TextNode) { + class func asyncLayout(_ maybeNode: TextNode?) -> (_ attributedString: NSAttributedString?, _ backgroundColor: UIColor?, _ maximumNumberOfLines: Int, _ truncationType: CTLineTruncationType, _ constrainedSize: CGSize, _ alignment: NSTextAlignment, _ cutout: TextNodeCutout?) -> (TextNodeLayout, () -> TextNode) { let existingLayout: TextNodeLayout? = maybeNode?.cachedLayout - return { attributedString, backgroundColor, maximumNumberOfLines, truncationType, constrainedSize, cutout in + return { attributedString, backgroundColor, maximumNumberOfLines, truncationType, constrainedSize, alignment, cutout in let layout: TextNodeLayout var updated = false - if let existingLayout = existingLayout, existingLayout.constrainedSize == constrainedSize && existingLayout.maximumNumberOfLines == maximumNumberOfLines && existingLayout.truncationType == truncationType && existingLayout.cutout == cutout { + if let existingLayout = existingLayout, existingLayout.constrainedSize == constrainedSize && existingLayout.maximumNumberOfLines == maximumNumberOfLines && existingLayout.truncationType == truncationType && existingLayout.cutout == cutout && existingLayout.alignment == alignment { let stringMatch: Bool var colorMatch: Bool = true @@ -338,11 +348,11 @@ final class TextNode: ASDisplayNode { if stringMatch { layout = existingLayout } else { - layout = TextNode.calculateLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, backgroundColor: backgroundColor, constrainedSize: constrainedSize, cutout: cutout) + layout = TextNode.calculateLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, backgroundColor: backgroundColor, constrainedSize: constrainedSize, alignment: alignment, cutout: cutout) updated = true } } else { - layout = TextNode.calculateLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, backgroundColor: backgroundColor, constrainedSize: constrainedSize, cutout: cutout) + layout = TextNode.calculateLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, backgroundColor: backgroundColor, constrainedSize: constrainedSize, alignment: alignment, cutout: cutout) updated = true } diff --git a/TelegramUI/UserInfoController.swift b/TelegramUI/UserInfoController.swift index 852ea0e747..b1285f3318 100644 --- a/TelegramUI/UserInfoController.swift +++ b/TelegramUI/UserInfoController.swift @@ -396,7 +396,7 @@ private func userInfoEntries(account: Account, view: PeerView, state: UserInfoSt entries.append(UserInfoEntry.sharedMedia) } entries.append(UserInfoEntry.notifications(settings: view.notificationSettings)) - if let groupsInCommon = (view.cachedData as? CachedUserData)?.commonGroupCount, !isEditing { + if let groupsInCommon = (view.cachedData as? CachedUserData)?.commonGroupCount, groupsInCommon != 0 && !isEditing { entries.append(UserInfoEntry.groupsInCommon(groupsInCommon)) } diff --git a/TelegramUI/VerticalListContextResultsChatInputPanelButtonItem.swift b/TelegramUI/VerticalListContextResultsChatInputPanelButtonItem.swift index e2bd789149..2c390bde1d 100644 --- a/TelegramUI/VerticalListContextResultsChatInputPanelButtonItem.swift +++ b/TelegramUI/VerticalListContextResultsChatInputPanelButtonItem.swift @@ -126,7 +126,7 @@ final class VerticalListContextResultsChatInputPanelButtonItemNode: ListViewItem return { [weak self] item, width, mergedTop, mergedBottom in let titleString = NSAttributedString(string: item.title, font: titleFont, textColor: UIColor(0x007ee5)) - let (titleLayout, titleApply) = makeTitleLayout(titleString, nil, 1, .end, CGSize(width: width - 16.0, height: 100.0), nil) + let (titleLayout, titleApply) = makeTitleLayout(titleString, nil, 1, .end, CGSize(width: width - 16.0, height: 100.0), .natural, nil) let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: width, height: VerticalListContextResultsChatInputPanelButtonItemNode.itemHeight), insets: UIEdgeInsets()) diff --git a/TelegramUI/VerticalListContextResultsChatInputPanelItem.swift b/TelegramUI/VerticalListContextResultsChatInputPanelItem.swift index 8d95b0480b..45f74a2397 100644 --- a/TelegramUI/VerticalListContextResultsChatInputPanelItem.swift +++ b/TelegramUI/VerticalListContextResultsChatInputPanelItem.swift @@ -225,11 +225,11 @@ final class VerticalListContextResultsChatInputPanelItemNode: ListViewItemNode { } } - let (titleLayout, titleApply) = makeTitleLayout(titleString, nil, 1, .end, CGSize(width: width - leftInset - rightInset, height: 100.0), nil) + let (titleLayout, titleApply) = makeTitleLayout(titleString, nil, 1, .end, CGSize(width: width - leftInset - rightInset, height: 100.0), .natural, nil) - let (textLayout, textApply) = makeTextLayout(textString, nil, 2, .end, CGSize(width: width - leftInset - rightInset, height: 100.0), nil) + let (textLayout, textApply) = makeTextLayout(textString, nil, 2, .end, CGSize(width: width - leftInset - rightInset, height: 100.0), .natural, nil) - let (iconTextLayout, iconTextApply) = iconTextMakeLayout(iconText, nil, 1, .end, CGSize(width: 38.0, height: CGFloat.infinity), nil) + let (iconTextLayout, iconTextApply) = iconTextMakeLayout(iconText, nil, 1, .end, CGSize(width: 38.0, height: CGFloat.infinity), .natural, nil) var titleFrame: CGRect? if let _ = titleString {