mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-17 03:40:18 +00:00
Various fixes
This commit is contained in:
parent
3ae64b399f
commit
90a1e9b688
@ -10776,3 +10776,4 @@ Sorry for the inconvenience.";
|
|||||||
|
|
||||||
"ChatList.PremiumGiftInSettingsInfo" = "You can gift **Telegram Premium** to a friend later in **Settings**.";
|
"ChatList.PremiumGiftInSettingsInfo" = "You can gift **Telegram Premium** to a friend later in **Settings**.";
|
||||||
|
|
||||||
|
"ChannelAppearance.BoostLevel" = "Level %@";
|
||||||
|
|||||||
@ -983,9 +983,9 @@ final class DrawingStickerEntititySelectionView: DrawingEntitySelectionView {
|
|||||||
var count = 12
|
var count = 12
|
||||||
if case .message = entity.content {
|
if case .message = entity.content {
|
||||||
cornerRadius *= 2.1
|
cornerRadius *= 2.1
|
||||||
count = 20
|
count = 24
|
||||||
} else if case .image = entity.content {
|
} else if case .image = entity.content {
|
||||||
count = 20
|
count = 24
|
||||||
}
|
}
|
||||||
|
|
||||||
let perimeter: CGFloat = 2.0 * (width + height - cornerRadius * (4.0 - .pi))
|
let perimeter: CGFloat = 2.0 * (width + height - cornerRadius * (4.0 - .pi))
|
||||||
|
|||||||
@ -491,9 +491,12 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
|||||||
if remainingCutoutHeight > 0.0 {
|
if remainingCutoutHeight > 0.0 {
|
||||||
cutout = TextNodeCutout(topRight: CGSize(width: cutoutWidth, height: remainingCutoutHeight))
|
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 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
|
textLayoutAndApply = textLayoutAndApplyValue
|
||||||
|
|
||||||
remainingCutoutHeight -= textLayoutAndApplyValue.0.size.height
|
remainingCutoutHeight -= textLayoutAndApplyValue.0.size.height
|
||||||
|
|||||||
@ -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)
|
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
|
var animated = animated
|
||||||
if let updatingMedia = attributes.updatingMedia, case .update = updatingMedia.media {
|
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)
|
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)
|
let formatting = DataSizeStringFormatting(strings: strings, decimalSeparator: decimalSeparator)
|
||||||
|
|
||||||
var media = self.media
|
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)
|
state = .play(messageTheme.mediaOverlayControlColors.foregroundColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2125,6 +2130,13 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
animated = true
|
animated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isPreview {
|
||||||
|
if case .play = state {
|
||||||
|
} else {
|
||||||
|
state = .none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
statusNode.transitionToState(state, animated: animated, completion: { [weak statusNode] in
|
statusNode.transitionToState(state, animated: animated, completion: { [weak statusNode] in
|
||||||
if removeStatusNode {
|
if removeStatusNode {
|
||||||
statusNode?.removeFromSupernode()
|
statusNode?.removeFromSupernode()
|
||||||
|
|||||||
@ -521,6 +521,8 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
} else {
|
} else {
|
||||||
maximumNumberOfLines = 6
|
maximumNumberOfLines = 6
|
||||||
}
|
}
|
||||||
|
} else if let _ = item.message.media.first(where: { $0 is TelegramMediaWebpage }) as? TelegramMediaWebpage {
|
||||||
|
maximumNumberOfLines = 9
|
||||||
} else {
|
} else {
|
||||||
maximumNumberOfLines = 12
|
maximumNumberOfLines = 12
|
||||||
}
|
}
|
||||||
|
|||||||
@ -334,8 +334,17 @@ public class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
var patternArguments: PatternWallpaperArguments?
|
var patternArguments: PatternWallpaperArguments?
|
||||||
|
|
||||||
var mediaContent = media.content
|
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 {
|
if case let .emoticon(emoticon) = mediaContent, let theme = item.associatedData.chatThemes.first(where: { $0.emoticon?.strippedEmoji == emoticon.strippedEmoji }) {
|
||||||
mediaContent = themeWallpaperContent
|
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 {
|
switch mediaContent {
|
||||||
|
|||||||
@ -77,180 +77,217 @@ public final class DrawingWallpaperRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class DrawingMessageRenderer {
|
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 context: AccountContext
|
||||||
private let messages: [Message]
|
private let messages: [Message]
|
||||||
|
|
||||||
private let containerNode: ASDisplayNode
|
private let dayContainerNode: ContainerNode
|
||||||
|
private let nightContainerNode: ContainerNode
|
||||||
private let messagesContainerNode: ASDisplayNode
|
|
||||||
private var avatarHeaderNode: ListViewItemHeaderNode?
|
|
||||||
private var messageNodes: [ListViewItemNode]?
|
|
||||||
|
|
||||||
public init(context: AccountContext, messages: [Message]) {
|
public init(context: AccountContext, messages: [Message]) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.messages = messages
|
self.messages = messages
|
||||||
|
|
||||||
self.containerNode = ASDisplayNode()
|
self.dayContainerNode = ContainerNode(context: context, messages: messages)
|
||||||
|
self.nightContainerNode = ContainerNode(context: context, messages: messages, isNight: true)
|
||||||
self.messagesContainerNode = ASDisplayNode()
|
|
||||||
self.messagesContainerNode.clipsToBounds = true
|
|
||||||
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
|
|
||||||
|
|
||||||
self.containerNode.addSubnode(self.messagesContainerNode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func render(completion: @escaping (CGSize, UIImage?, UIImage?) -> Void) {
|
public func render(completion: @escaping (CGSize, UIImage?, UIImage?) -> Void) {
|
||||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
var finalSize: CGSize = .zero
|
||||||
let defaultPresentationData = defaultPresentationData()
|
var dayImage: UIImage?
|
||||||
|
var nightImage: UIImage?
|
||||||
|
|
||||||
let mockPresentationData = PresentationData(
|
let completeIfReady = {
|
||||||
strings: presentationData.strings,
|
if let dayImage, let nightImage {
|
||||||
theme: defaultPresentationTheme,
|
completion(finalSize, dayImage, nightImage)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.dayContainerNode.render { size, image in
|
||||||
let avatarHeaderNode: ListViewItemHeaderNode
|
finalSize = size
|
||||||
if let currentAvatarHeaderNode = self.avatarHeaderNode {
|
dayImage = image
|
||||||
avatarHeaderNode = currentAvatarHeaderNode
|
completeIfReady()
|
||||||
avatarHeaderItem.updateNode(avatarHeaderNode, previous: nil, next: avatarHeaderItem)
|
}
|
||||||
} else {
|
self.nightContainerNode.render { size, image in
|
||||||
avatarHeaderNode = avatarHeaderItem.node(synchronousLoad: true)
|
finalSize = size
|
||||||
avatarHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
|
nightImage = image
|
||||||
self.messagesContainerNode.addSubnode(avatarHeaderNode)
|
completeIfReady()
|
||||||
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.containerNode.frame = CGRect(origin: CGPoint(), size: containerSize)
|
|
||||||
self.messagesContainerNode.frame = CGRect(origin: CGPoint(), size: containerSize)
|
|
||||||
|
|
||||||
return containerSize
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -258,8 +258,8 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
|||||||
if case let .peer(_, _, _, _, customLevel) = mode {
|
if case let .peer(_, _, _, _, customLevel) = mode {
|
||||||
requiredCustomWallpaperLevel = customLevel
|
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()
|
presentGallery()
|
||||||
})
|
})
|
||||||
self.galleryItemNode = ItemListPeerActionItemNode()
|
self.galleryItemNode = ItemListPeerActionItemNode()
|
||||||
@ -684,7 +684,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
|||||||
if case let .peer(_, _, _, _, customLevel) = mode {
|
if case let .peer(_, _, _, _, customLevel) = mode {
|
||||||
requiredCustomWallpaperLevel = customLevel
|
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?.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
|
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
|
var hasCustomWallpaper = false
|
||||||
if case let .peer(_, _, wallpaper, _, _) = self.mode {
|
if case let .peer(_, _, wallpaper, _, _) = self.mode {
|
||||||
isChannel = true
|
isChannel = true
|
||||||
if let wallpaper, !wallpaper.isPattern {
|
if let wallpaper, !wallpaper.isEmoticon {
|
||||||
hasCustomWallpaper = true
|
hasCustomWallpaper = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
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 {
|
if case .reaction = area {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
if let strongSelf = self, let messages = strongSelf.chatDisplayNode.historyNode.messageGroupInCurrentHistoryView(id), let message = messages.first {
|
||||||
let chatPresentationInterfaceState = strongSelf.presentationInterfaceState
|
let chatPresentationInterfaceState = strongSelf.presentationInterfaceState
|
||||||
var warnAboutPrivate = false
|
var warnAboutPrivate = false
|
||||||
|
var canShareToStory = false
|
||||||
if case .peer = chatPresentationInterfaceState.chatLocation, let channel = message.peers[message.id.peerId] as? TelegramChannel {
|
if case .peer = chatPresentationInterfaceState.chatLocation, let channel = message.peers[message.id.peerId] as? TelegramChannel {
|
||||||
|
canShareToStory = true
|
||||||
if channel.addressName == nil {
|
if channel.addressName == nil {
|
||||||
warnAboutPrivate = true
|
warnAboutPrivate = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let shareController = ShareController(context: strongSelf.context, subject: .messages(messages), updatedPresentationData: strongSelf.updatedPresentationData, shareAsLink: 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 let message = messages.first, message.media.contains(where: { media in
|
||||||
if media is TelegramMediaContact || media is TelegramMediaPoll {
|
if media is TelegramMediaContact || media is TelegramMediaPoll {
|
||||||
return true
|
return true
|
||||||
@ -6376,7 +6377,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
var useDarkAppearance = presentationData.theme.overallDarkAppearance
|
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 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
|
chatWallpaper = themeWallpaper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user