diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index c9cac9a575..16740764ad 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -10776,3 +10776,4 @@ Sorry for the inconvenience."; "ChatList.PremiumGiftInSettingsInfo" = "You can gift **Telegram Premium** to a friend later in **Settings**."; +"ChannelAppearance.BoostLevel" = "Level %@"; diff --git a/submodules/DrawingUI/Sources/DrawingStickerEntityView.swift b/submodules/DrawingUI/Sources/DrawingStickerEntityView.swift index 71bd36285f..291ec87c6a 100644 --- a/submodules/DrawingUI/Sources/DrawingStickerEntityView.swift +++ b/submodules/DrawingUI/Sources/DrawingStickerEntityView.swift @@ -983,9 +983,9 @@ final class DrawingStickerEntititySelectionView: DrawingEntitySelectionView { var count = 12 if case .message = entity.content { cornerRadius *= 2.1 - count = 20 + count = 24 } else if case .image = entity.content { - count = 20 + count = 24 } let perimeter: CGFloat = 2.0 * (width + height - cornerRadius * (4.0 - .pi)) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift index 14c750a2bd..4bd98f7017 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift @@ -491,9 +491,12 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { if remainingCutoutHeight > 0.0 { cutout = TextNodeCutout(topRight: CGSize(width: cutoutWidth, height: remainingCutoutHeight)) } - + var maximumNumberOfLines: Int = 12 + if isPreview { + maximumNumberOfLines = mediaAndFlags != nil ? 4 : 6 + } let textString = stringWithAppliedEntities(text, entities: entities ?? [], baseColor: messageTheme.primaryTextColor, linkColor: incoming ? mainColor : messageTheme.linkTextColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont, blockQuoteFont: textBlockQuoteFont, message: nil, adjustQuoteFontSize: true) - let textLayoutAndApplyValue = makeTextLayout(TextNodeLayoutArguments(attributedString: textString, backgroundColor: nil, maximumNumberOfLines: 12, truncationType: .end, constrainedSize: CGSize(width: maxContentsWidth, height: 10000.0), alignment: .natural, lineSpacing: textLineSpacing, cutout: cutout, insets: UIEdgeInsets())) + let textLayoutAndApplyValue = makeTextLayout(TextNodeLayoutArguments(attributedString: textString, backgroundColor: nil, maximumNumberOfLines: maximumNumberOfLines, truncationType: .end, constrainedSize: CGSize(width: maxContentsWidth, height: 10000.0), alignment: .natural, lineSpacing: textLineSpacing, cutout: cutout, insets: UIEdgeInsets())) textLayoutAndApply = textLayoutAndApplyValue remainingCutoutHeight -= textLayoutAndApplyValue.0.size.height diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift index 6f87d78134..61c569893a 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode/Sources/ChatMessageInteractiveMediaNode.swift @@ -1862,6 +1862,9 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr badgeContent = .text(inset: 0.0, backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, text: string, iconName: nil) } } + + let gifTitle = game != nil ? strings.Message_Game.uppercased() : strings.Message_Animation.uppercased() + var animated = animated if let updatingMedia = attributes.updatingMedia, case .update = updatingMedia.media { state = .progress(color: messageTheme.mediaOverlayControlColors.foregroundColor, lineWidth: nil, value: CGFloat(updatingMedia.progress), cancelEnabled: true, animateRotation: true) @@ -1915,9 +1918,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr } } } - - let gifTitle = game != nil ? strings.Message_Game.uppercased() : strings.Message_Animation.uppercased() - + let formatting = DataSizeStringFormatting(strings: strings, decimalSeparator: decimalSeparator) var media = self.media @@ -2079,7 +2080,11 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr } } } - if isPreview, let _ = media as? TelegramMediaFile { + if isPreview, let file = media as? TelegramMediaFile { + if let duration = file.duration, !file.isVideoSticker { + let durationString = file.isAnimated ? gifTitle : stringForDuration(Int32(duration), position: nil) + badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: durationString, size: nil, muted: false, active: false) + } state = .play(messageTheme.mediaOverlayControlColors.foregroundColor) } @@ -2125,6 +2130,13 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr animated = true } + if isPreview { + if case .play = state { + } else { + state = .none + } + } + statusNode.transitionToState(state, animated: animated, completion: { [weak statusNode] in if removeStatusNode { statusNode?.removeFromSupernode() diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift index 2fc6aa4beb..e72960fde4 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift @@ -521,6 +521,8 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { } else { maximumNumberOfLines = 6 } + } else if let _ = item.message.media.first(where: { $0 is TelegramMediaWebpage }) as? TelegramMediaWebpage { + maximumNumberOfLines = 9 } else { maximumNumberOfLines = 12 } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageWallpaperBubbleContentNode/Sources/ChatMessageWallpaperBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageWallpaperBubbleContentNode/Sources/ChatMessageWallpaperBubbleContentNode.swift index eec6701260..a1ded02986 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageWallpaperBubbleContentNode/Sources/ChatMessageWallpaperBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageWallpaperBubbleContentNode/Sources/ChatMessageWallpaperBubbleContentNode.swift @@ -334,8 +334,17 @@ public class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode var patternArguments: PatternWallpaperArguments? var mediaContent = media.content - if case let .emoticon(emoticon) = mediaContent, let theme = item.associatedData.chatThemes.first(where: { $0.emoticon?.strippedEmoji == emoticon.strippedEmoji }), let themeWallpaper = theme.settings?.first?.wallpaper, let themeWallpaperContent = WallpaperPreviewMedia(wallpaper: themeWallpaper)?.content { - mediaContent = themeWallpaperContent + if case let .emoticon(emoticon) = mediaContent, let theme = item.associatedData.chatThemes.first(where: { $0.emoticon?.strippedEmoji == emoticon.strippedEmoji }) { + let themeSettings: TelegramThemeSettings? + if let matching = theme.settings?.first(where: { $0.baseTheme == item.presentationData.theme.theme.referenceTheme.baseTheme }) { + themeSettings = matching + } else { + themeSettings = theme.settings?.first + } + + if let themeWallpaper = themeSettings?.wallpaper, let themeWallpaperContent = WallpaperPreviewMedia(wallpaper: themeWallpaper)?.content { + mediaContent = themeWallpaperContent + } } switch mediaContent { diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/DrawingMessageRenderer.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/DrawingMessageRenderer.swift index cd3b903b46..c332316334 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/DrawingMessageRenderer.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/DrawingMessageRenderer.swift @@ -77,180 +77,217 @@ public final class DrawingWallpaperRenderer { } public final class DrawingMessageRenderer { + class ContainerNode: ASDisplayNode { + private let context: AccountContext + private let messages: [Message] + private let isNight: Bool + + private let messagesContainerNode: ASDisplayNode + private var avatarHeaderNode: ListViewItemHeaderNode? + private var messageNodes: [ListViewItemNode]? + + init(context: AccountContext, messages: [Message], isNight: Bool = false) { + self.context = context + self.messages = messages + self.isNight = isNight + + self.messagesContainerNode = ASDisplayNode() + self.messagesContainerNode.clipsToBounds = true + self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) + + super.init() + + self.addSubnode(self.messagesContainerNode) + } + + public func render(completion: @escaping (CGSize, UIImage?) -> Void) { + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } + let defaultPresentationData = defaultPresentationData() + + var mockPresentationData = PresentationData( + strings: presentationData.strings, + theme: defaultPresentationTheme, + autoNightModeTriggered: false, + chatWallpaper: presentationData.chatWallpaper, + chatFontSize: defaultPresentationData.chatFontSize, + chatBubbleCorners: defaultPresentationData.chatBubbleCorners, + listsFontSize: defaultPresentationData.listsFontSize, + dateTimeFormat: presentationData.dateTimeFormat, + nameDisplayOrder: presentationData.nameDisplayOrder, + nameSortOrder: presentationData.nameSortOrder, + reduceMotion: false, + largeEmoji: true + ) + + if self.isNight { + let darkTheme = defaultDarkColorPresentationTheme + mockPresentationData = mockPresentationData.withUpdated(theme: darkTheme).withUpdated(chatWallpaper: darkTheme.chat.defaultWallpaper) + } + + let layout = ContainerViewLayout(size: CGSize(width: 360.0, height: 640.0), metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact, orientation: .portrait), deviceMetrics: .iPhoneX, intrinsicInsets: .zero, safeInsets: .zero, additionalInsets: .zero, statusBarHeight: 0.0, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false) + let size = self.updateMessagesLayout(layout: layout, presentationData: mockPresentationData) + + Queue.mainQueue().after(0.03, { + self.generate(size: size) { image in + completion(size, image) + } + }) + } + + private func generate(size: CGSize, completion: @escaping (UIImage) -> Void) { + UIGraphicsBeginImageContextWithOptions(size, false, 3.0) + self.view.drawHierarchy(in: CGRect(origin: CGPoint(), size: size), afterScreenUpdates: true) + let img = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + let finalImage = generateImage(CGSize(width: size.width * 3.0, height: size.height * 3.0), contextGenerator: { size, context in + context.clear(CGRect(origin: .zero, size: size)) + if let cgImage = img?.cgImage { + context.draw(cgImage, in: CGRect(origin: .zero, size: size), byTiling: false) + } + }, opaque: false, scale: 1.0) + if let finalImage { + completion(finalImage) + } + } + + private func updateMessagesLayout(layout: ContainerViewLayout, presentationData: PresentationData) -> CGSize { + let size = layout.size + + let theme = presentationData.theme.withUpdated(preview: true) + + let avatarHeaderItem = self.context.sharedContext.makeChatMessageAvatarHeaderItem(context: self.context, timestamp: self.messages.first?.timestamp ?? 0, peer: self.messages.first!.peers[self.messages.first!.author!.id]!, message: self.messages.first!, theme: theme, strings: presentationData.strings, wallpaper: presentationData.chatWallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: presentationData.chatBubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder) + + let items: [ListViewItem] = [self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: self.messages, theme: theme, strings: presentationData.strings, wallpaper: presentationData.theme.chat.defaultWallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: presentationData.chatBubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: nil, availableReactions: nil, accountPeer: nil, isCentered: false)] + + let inset: CGFloat = 16.0 + let leftInset: CGFloat = 37.0 + let containerWidth = layout.size.width - inset * 2.0 + let params = ListViewItemLayoutParams(width: containerWidth, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height) + + var width: CGFloat = containerWidth + var height: CGFloat = size.height + if let messageNodes = self.messageNodes { + for i in 0 ..< items.count { + let itemNode = messageNodes[i] + items[i].updateNode(async: { $0() }, node: { + return itemNode + }, params: params, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], animation: .None, completion: { (layout, apply) in + let nodeFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: containerWidth, height: layout.size.height)) + + itemNode.contentSize = layout.contentSize + itemNode.insets = layout.insets + itemNode.frame = nodeFrame + itemNode.isUserInteractionEnabled = false + + apply(ListViewItemApply(isOnScreen: true)) + }) + } + } else { + var messageNodes: [ListViewItemNode] = [] + for i in 0 ..< items.count { + var itemNode: ListViewItemNode? + items[i].nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: true, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], completion: { node, apply in + itemNode = node + apply().1(ListViewItemApply(isOnScreen: true)) + }) + itemNode!.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0) + itemNode!.isUserInteractionEnabled = false + messageNodes.append(itemNode!) + self.messagesContainerNode.addSubnode(itemNode!) + } + self.messageNodes = messageNodes + } + + if let messageNodes = self.messageNodes { + var minX: CGFloat = .greatestFiniteMagnitude + var maxX: CGFloat = -.greatestFiniteMagnitude + var minY: CGFloat = .greatestFiniteMagnitude + var maxY: CGFloat = -.greatestFiniteMagnitude + for node in messageNodes { + if node.frame.minY < minY { + minY = node.frame.minY + } + if node.frame.maxY > maxY { + maxY = node.frame.maxY + } + if let areaNode = node.subnodes?.last { + if areaNode.frame.minX < minX { + minX = areaNode.frame.minX + } + if areaNode.frame.maxX > maxX { + maxX = areaNode.frame.maxX + } + } + } + width = abs(maxX - minX) + height = abs(maxY - minY) + } + + var bottomOffset: CGFloat = 0.0 + if let messageNodes = self.messageNodes { + for itemNode in messageNodes { + itemNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: itemNode.frame.size) + bottomOffset += itemNode.frame.maxY + itemNode.updateFrame(itemNode.frame, within: layout.size) + } + } + + let avatarHeaderNode: ListViewItemHeaderNode + if let currentAvatarHeaderNode = self.avatarHeaderNode { + avatarHeaderNode = currentAvatarHeaderNode + avatarHeaderItem.updateNode(avatarHeaderNode, previous: nil, next: avatarHeaderItem) + } else { + avatarHeaderNode = avatarHeaderItem.node(synchronousLoad: true) + avatarHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0) + self.messagesContainerNode.addSubnode(avatarHeaderNode) + self.avatarHeaderNode = avatarHeaderNode + } + + avatarHeaderNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 3.0), size: CGSize(width: layout.size.width, height: avatarHeaderItem.height)) + avatarHeaderNode.updateLayout(size: size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right) + + let containerSize = CGSize(width: width + leftInset + 6.0, height: height) + self.frame = CGRect(origin: CGPoint(), size: containerSize) + self.messagesContainerNode.frame = CGRect(origin: CGPoint(), size: containerSize) + + return containerSize + } + } + private let context: AccountContext private let messages: [Message] - private let containerNode: ASDisplayNode - - private let messagesContainerNode: ASDisplayNode - private var avatarHeaderNode: ListViewItemHeaderNode? - private var messageNodes: [ListViewItemNode]? + private let dayContainerNode: ContainerNode + private let nightContainerNode: ContainerNode public init(context: AccountContext, messages: [Message]) { self.context = context self.messages = messages - - self.containerNode = ASDisplayNode() - - self.messagesContainerNode = ASDisplayNode() - self.messagesContainerNode.clipsToBounds = true - self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) - - self.containerNode.addSubnode(self.messagesContainerNode) + + self.dayContainerNode = ContainerNode(context: context, messages: messages) + self.nightContainerNode = ContainerNode(context: context, messages: messages, isNight: true) } public func render(completion: @escaping (CGSize, UIImage?, UIImage?) -> Void) { - let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - let defaultPresentationData = defaultPresentationData() + var finalSize: CGSize = .zero + var dayImage: UIImage? + var nightImage: UIImage? - let mockPresentationData = PresentationData( - strings: presentationData.strings, - theme: defaultPresentationTheme, - autoNightModeTriggered: false, - chatWallpaper: presentationData.chatWallpaper, - chatFontSize: defaultPresentationData.chatFontSize, - chatBubbleCorners: defaultPresentationData.chatBubbleCorners, - listsFontSize: defaultPresentationData.listsFontSize, - dateTimeFormat: presentationData.dateTimeFormat, - nameDisplayOrder: presentationData.nameDisplayOrder, - nameSortOrder: presentationData.nameSortOrder, - reduceMotion: false, - largeEmoji: true - ) - - let layout = ContainerViewLayout(size: CGSize(width: 360.0, height: 640.0), metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact, orientation: .portrait), deviceMetrics: .iPhoneX, intrinsicInsets: .zero, safeInsets: .zero, additionalInsets: .zero, statusBarHeight: 0.0, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false) - let size = self.updateMessagesLayout(layout: layout, presentationData: mockPresentationData) - - Queue.mainQueue().after(0.01, { - self.generate(size: size) { dayImage in - let darkTheme = defaultDarkColorPresentationTheme - let darkPresentationData = mockPresentationData.withUpdated(theme: darkTheme) - - let _ = self.updateMessagesLayout(layout: layout, presentationData: darkPresentationData) - self.generate(size: size) { nightImage in - completion(size, dayImage, nightImage) - } - } - }) - } - - private func generate(size: CGSize, completion: @escaping (UIImage) -> Void) { - UIGraphicsBeginImageContextWithOptions(size, false, 3.0) - self.containerNode.view.drawHierarchy(in: CGRect(origin: CGPoint(), size: size), afterScreenUpdates: true) - let img = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - - let finalImage = generateImage(CGSize(width: size.width * 3.0, height: size.height * 3.0), contextGenerator: { size, context in - context.clear(CGRect(origin: .zero, size: size)) - if let cgImage = img?.cgImage { - context.draw(cgImage, in: CGRect(origin: .zero, size: size), byTiling: false) - } - }, opaque: false, scale: 1.0) - if let finalImage { - completion(finalImage) - } - } - - private func updateMessagesLayout(layout: ContainerViewLayout, presentationData: PresentationData) -> CGSize { - let size = layout.size - - let theme = presentationData.theme.withUpdated(preview: true) - - let avatarHeaderItem = self.context.sharedContext.makeChatMessageAvatarHeaderItem(context: self.context, timestamp: self.messages.first?.timestamp ?? 0, peer: self.messages.first!.peers[self.messages.first!.author!.id]!, message: self.messages.first!, theme: theme, strings: presentationData.strings, wallpaper: presentationData.chatWallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: presentationData.chatBubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder) - - let items: [ListViewItem] = [self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: self.messages, theme: theme, strings: presentationData.strings, wallpaper: presentationData.theme.chat.defaultWallpaper, fontSize: presentationData.chatFontSize, chatBubbleCorners: presentationData.chatBubbleCorners, dateTimeFormat: presentationData.dateTimeFormat, nameOrder: presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: nil, availableReactions: nil, accountPeer: nil, isCentered: false)] - - let inset: CGFloat = 16.0 - let leftInset: CGFloat = 37.0 - let containerWidth = layout.size.width - inset * 2.0 - let params = ListViewItemLayoutParams(width: containerWidth, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height) - - var width: CGFloat = containerWidth - var height: CGFloat = size.height - if let messageNodes = self.messageNodes { - for i in 0 ..< items.count { - let itemNode = messageNodes[i] - items[i].updateNode(async: { $0() }, node: { - return itemNode - }, params: params, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], animation: .None, completion: { (layout, apply) in - let nodeFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: containerWidth, height: layout.size.height)) - - itemNode.contentSize = layout.contentSize - itemNode.insets = layout.insets - itemNode.frame = nodeFrame - itemNode.isUserInteractionEnabled = false - - apply(ListViewItemApply(isOnScreen: true)) - }) - } - } else { - var messageNodes: [ListViewItemNode] = [] - for i in 0 ..< items.count { - var itemNode: ListViewItemNode? - items[i].nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: true, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], completion: { node, apply in - itemNode = node - apply().1(ListViewItemApply(isOnScreen: true)) - }) - itemNode!.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0) - itemNode!.isUserInteractionEnabled = false - messageNodes.append(itemNode!) - self.messagesContainerNode.addSubnode(itemNode!) - } - self.messageNodes = messageNodes - } - - if let messageNodes = self.messageNodes { - var minX: CGFloat = .greatestFiniteMagnitude - var maxX: CGFloat = -.greatestFiniteMagnitude - var minY: CGFloat = .greatestFiniteMagnitude - var maxY: CGFloat = -.greatestFiniteMagnitude - for node in messageNodes { - if node.frame.minY < minY { - minY = node.frame.minY - } - if node.frame.maxY > maxY { - maxY = node.frame.maxY - } - if let areaNode = node.subnodes?.last { - if areaNode.frame.minX < minX { - minX = areaNode.frame.minX - } - if areaNode.frame.maxX > maxX { - maxX = areaNode.frame.maxX - } - } - } - width = abs(maxX - minX) - height = abs(maxY - minY) - } - - var bottomOffset: CGFloat = 0.0 - if let messageNodes = self.messageNodes { - for itemNode in messageNodes { - itemNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: itemNode.frame.size) - bottomOffset += itemNode.frame.maxY - itemNode.updateFrame(itemNode.frame, within: layout.size) + let completeIfReady = { + if let dayImage, let nightImage { + completion(finalSize, dayImage, nightImage) } } - - let avatarHeaderNode: ListViewItemHeaderNode - if let currentAvatarHeaderNode = self.avatarHeaderNode { - avatarHeaderNode = currentAvatarHeaderNode - avatarHeaderItem.updateNode(avatarHeaderNode, previous: nil, next: avatarHeaderItem) - } else { - avatarHeaderNode = avatarHeaderItem.node(synchronousLoad: true) - avatarHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0) - self.messagesContainerNode.addSubnode(avatarHeaderNode) - self.avatarHeaderNode = avatarHeaderNode + self.dayContainerNode.render { size, image in + finalSize = size + dayImage = image + completeIfReady() + } + self.nightContainerNode.render { size, image in + finalSize = size + nightImage = image + completeIfReady() } - - avatarHeaderNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 3.0), size: CGSize(width: layout.size.width, height: avatarHeaderItem.height)) - avatarHeaderNode.updateLayout(size: size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right) - - let containerSize = CGSize(width: width + leftInset + 6.0, height: height) - self.containerNode.frame = CGRect(origin: CGPoint(), size: containerSize) - self.messagesContainerNode.frame = CGRect(origin: CGPoint(), size: containerSize) - - return containerSize } } diff --git a/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridControllerNode.swift b/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridControllerNode.swift index 65d5b031c0..cad651ce11 100644 --- a/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridControllerNode.swift +++ b/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridControllerNode.swift @@ -258,8 +258,8 @@ final class ThemeGridControllerNode: ASDisplayNode { if case let .peer(_, _, _, _, customLevel) = mode { requiredCustomWallpaperLevel = customLevel } - //TODO:localize - self.galleryItem = ItemListPeerActionItem(presentationData: ItemListPresentationData(presentationData), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat/Attach Menu/Image"), color: presentationData.theme.list.itemAccentColor), title: presentationData.strings.Wallpaper_SetCustomBackground, additionalBadgeIcon: requiredCustomWallpaperLevel.flatMap { generateDisclosureActionBoostLevelBadgeImage(text: "Level \($0)") }, alwaysPlain: false, hasSeparator: true, sectionId: 0, height: .generic, color: .accent, editing: false, action: { + + self.galleryItem = ItemListPeerActionItem(presentationData: ItemListPresentationData(presentationData), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat/Attach Menu/Image"), color: presentationData.theme.list.itemAccentColor), title: presentationData.strings.Wallpaper_SetCustomBackground, additionalBadgeIcon: requiredCustomWallpaperLevel.flatMap { generateDisclosureActionBoostLevelBadgeImage(text: presentationData.strings.ChannelAppearance_BoostLevel("\($0)").string) }, alwaysPlain: false, hasSeparator: true, sectionId: 0, height: .generic, color: .accent, editing: false, action: { presentGallery() }) self.galleryItemNode = ItemListPeerActionItemNode() @@ -684,7 +684,7 @@ final class ThemeGridControllerNode: ASDisplayNode { if case let .peer(_, _, _, _, customLevel) = mode { requiredCustomWallpaperLevel = customLevel } - self.galleryItem = ItemListPeerActionItem(presentationData: ItemListPresentationData(presentationData), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat/Attach Menu/Image"), color: presentationData.theme.list.itemAccentColor), title: presentationData.strings.Wallpaper_SetCustomBackground, additionalBadgeIcon: requiredCustomWallpaperLevel.flatMap { generateDisclosureActionBoostLevelBadgeImage(text: "Level \($0)") }, alwaysPlain: false, hasSeparator: true, sectionId: 0, height: .generic, color: .accent, editing: false, action: { [weak self] in + self.galleryItem = ItemListPeerActionItem(presentationData: ItemListPresentationData(presentationData), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat/Attach Menu/Image"), color: presentationData.theme.list.itemAccentColor), title: presentationData.strings.Wallpaper_SetCustomBackground, additionalBadgeIcon: requiredCustomWallpaperLevel.flatMap { generateDisclosureActionBoostLevelBadgeImage(text: presentationData.strings.ChannelAppearance_BoostLevel("\($0)").string) }, alwaysPlain: false, hasSeparator: true, sectionId: 0, height: .generic, color: .accent, editing: false, action: { [weak self] in self?.presentGallery() }) self.removeItem = ItemListPeerActionItem(presentationData: ItemListPresentationData(presentationData), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: presentationData.theme.list.itemDestructiveColor), title: presentationData.strings.Wallpaper_ChannelRemoveBackground, alwaysPlain: false, hasSeparator: true, sectionId: 0, height: .generic, color: .destructive, editing: false, action: { [weak self] in @@ -805,7 +805,7 @@ final class ThemeGridControllerNode: ASDisplayNode { var hasCustomWallpaper = false if case let .peer(_, _, wallpaper, _, _) = self.mode { isChannel = true - if let wallpaper, !wallpaper.isPattern { + if let wallpaper, !wallpaper.isEmoticon { hasCustomWallpaper = true } } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index ad8c16d3fb..88a5109fbc 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -979,7 +979,7 @@ public final class StoryItemSetContainerComponent: Component { return abs(rotatedX) <= area.coordinates.width / 100.0 * referenceSize.width / 2.0 * 1.1 && abs(rotatedY) <= area.coordinates.height / 100.0 * referenceSize.height / 2.0 * 1.1 } - for area in component.slice.item.storyItem.mediaAreas { + for area in component.slice.item.storyItem.mediaAreas.reversed() { if case .reaction = area { continue } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index bc026afad1..98ae78ce9d 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -2415,14 +2415,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let strongSelf = self, let messages = strongSelf.chatDisplayNode.historyNode.messageGroupInCurrentHistoryView(id), let message = messages.first { let chatPresentationInterfaceState = strongSelf.presentationInterfaceState var warnAboutPrivate = false + var canShareToStory = false if case .peer = chatPresentationInterfaceState.chatLocation, let channel = message.peers[message.id.peerId] as? TelegramChannel { + canShareToStory = true if channel.addressName == nil { warnAboutPrivate = true } } let shareController = ShareController(context: strongSelf.context, subject: .messages(messages), updatedPresentationData: strongSelf.updatedPresentationData, shareAsLink: true) - var canShareToStory = true if let message = messages.first, message.media.contains(where: { media in if media is TelegramMediaContact || media is TelegramMediaPoll { return true @@ -6376,7 +6377,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var useDarkAppearance = presentationData.theme.overallDarkAppearance if let wallpaper = chatWallpaper, case let .emoticon(wallpaperEmoticon) = wallpaper, let theme = chatThemes.first(where: { $0.emoticon?.strippedEmoji == wallpaperEmoticon.strippedEmoji }) { - if let themeWallpaper = theme.settings?.first?.wallpaper { + let themeSettings: TelegramThemeSettings? + if let matching = theme.settings?.first(where: { $0.baseTheme == presentationData.theme.referenceTheme.baseTheme }) { + themeSettings = matching + } else { + themeSettings = theme.settings?.first + } + if let themeWallpaper = themeSettings?.wallpaper { chatWallpaper = themeWallpaper } }