Various improvements

This commit is contained in:
Ilya Laktyushin 2022-04-15 19:48:29 +04:00
parent 92d53e6ca6
commit 6e4b9a82e9
11 changed files with 132 additions and 33 deletions

View File

@ -125,7 +125,7 @@ class InviteLinkHeaderItemNode: ListViewItemNode {
strongSelf.item = item strongSelf.item = item
strongSelf.accessibilityLabel = attributedText.string strongSelf.accessibilityLabel = attributedText.string
let iconSize = CGSize(width: 96.0, height: 96.0) let iconSize = CGSize(width: 128.0, height: 128.0)
strongSelf.animationNode.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - iconSize.width) / 2.0), y: -10.0), size: iconSize) strongSelf.animationNode.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - iconSize.width) / 2.0), y: -10.0), size: iconSize)
strongSelf.animationNode.updateLayout(size: iconSize) strongSelf.animationNode.updateLayout(size: iconSize)

View File

@ -83,6 +83,7 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
private let iconNode: ASImageNode? private let iconNode: ASImageNode?
private let animationNode: SimpleAnimationNode? private let animationNode: SimpleAnimationNode?
private var animationScale: CGFloat = 1.0
private var animationNodeOffset: CGFloat = 0.0 private var animationNodeOffset: CGFloat = 0.0
private var animationNodeFlip = false private var animationNodeFlip = false
var alignment: ItemListRevealOptionAlignment? var alignment: ItemListRevealOptionAlignment?
@ -102,7 +103,8 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
self.iconNode = iconNode self.iconNode = iconNode
self.animationNode = nil self.animationNode = nil
case let .animation(animation, _, offset, replaceColors, flip): case let .animation(animation, scale, offset, replaceColors, flip):
self.animationScale = scale
self.iconNode = nil self.iconNode = nil
var colors: [UInt32: UInt32] = [:] var colors: [UInt32: UInt32] = [:]
if let replaceColors = replaceColors { if let replaceColors = replaceColors {
@ -194,7 +196,7 @@ private final class ItemListRevealOptionNode: ASDisplayNode {
} }
if let animationNode = self.animationNode { if let animationNode = self.animationNode {
let imageSize = animationNode.size let imageSize = CGSize(width: animationNode.size.width * self.animationScale, height: animationNode.size.height * self.animationScale)
let iconOffset: CGFloat = -2.0 + self.animationNodeOffset let iconOffset: CGFloat = -2.0 + self.animationNodeOffset
let titleIconSpacing: CGFloat = 11.0 let titleIconSpacing: CGFloat = 11.0
let iconFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((baseSize.width - imageSize.width + sideInset) / 2.0), y: contentRect.midY - imageSize.height / 2.0 + iconOffset), size: imageSize) let iconFrame = CGRect(origin: CGPoint(x: contentRect.minX + floor((baseSize.width - imageSize.width + sideInset) / 2.0), y: contentRect.midY - imageSize.height / 2.0 + iconOffset), size: imageSize)

View File

@ -455,6 +455,7 @@ private enum ChannelVisibilityEntry: ItemListNodeEntry {
case let .joinToSendEveryone(_, text, selected): case let .joinToSendEveryone(_, text, selected):
return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: { return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: {
arguments.updateJoinToSend(.everyone) arguments.updateJoinToSend(.everyone)
arguments.toggleApproveMembers(false)
}) })
case let .joinToSendMembers(_, text, selected): case let .joinToSendMembers(_, text, selected):
return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: { return ItemListCheckboxItem(presentationData: presentationData, title: text, style: .left, checked: selected, zeroSeparatorInsets: false, sectionId: self.section, action: {
@ -816,13 +817,24 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo)) entries.append(.privateLinkManageInfo(presentationData.theme, presentationData.strings.InviteLink_CreateInfo))
} }
} }
if isGroup {
var isDiscussion = false
if let cachedData = view.cachedData as? CachedChannelData, case .known = cachedData.linkedDiscussionPeerId {
isDiscussion = true
}
if isDiscussion {
entries.append(.joinToSendHeader(presentationData.theme, presentationData.strings.Group_Setup_WhoCanSendMessages_Title.uppercased()))
entries.append(.joinToSendEveryone(presentationData.theme, presentationData.strings.Group_Setup_WhoCanSendMessages_Everyone, joinToSend == .everyone))
entries.append(.joinToSendMembers(presentationData.theme, presentationData.strings.Group_Setup_WhoCanSendMessages_OnlyMembers, joinToSend == .members))
}
entries.append(.joinToSendHeader(presentationData.theme, presentationData.strings.Group_Setup_WhoCanSendMessages_Title.uppercased())) if !isDiscussion || joinToSend == .members {
entries.append(.joinToSendEveryone(presentationData.theme, presentationData.strings.Group_Setup_WhoCanSendMessages_Everyone, joinToSend == .everyone)) entries.append(.approveMembers(presentationData.theme, presentationData.strings.Group_Setup_ApproveNewMembers, approveMembers))
entries.append(.joinToSendMembers(presentationData.theme, presentationData.strings.Group_Setup_WhoCanSendMessages_OnlyMembers, joinToSend == .members)) entries.append(.approveMembersInfo(presentationData.theme, presentationData.strings.Group_Setup_ApproveNewMembersInfo))
}
entries.append(.approveMembers(presentationData.theme, presentationData.strings.Group_Setup_ApproveNewMembers, approveMembers)) }
entries.append(.approveMembersInfo(presentationData.theme, presentationData.strings.Group_Setup_ApproveNewMembersInfo))
entries.append(.forwardingHeader(presentationData.theme, isGroup ? presentationData.strings.Group_Setup_ForwardingGroupTitle.uppercased() : presentationData.strings.Group_Setup_ForwardingChannelTitle.uppercased())) entries.append(.forwardingHeader(presentationData.theme, isGroup ? presentationData.strings.Group_Setup_ForwardingGroupTitle.uppercased() : presentationData.strings.Group_Setup_ForwardingChannelTitle.uppercased()))
entries.append(.forwardingDisabled(presentationData.theme, presentationData.strings.Group_Setup_ForwardingDisabled, !forwardingEnabled)) entries.append(.forwardingDisabled(presentationData.theme, presentationData.strings.Group_Setup_ForwardingDisabled, !forwardingEnabled))
@ -1074,6 +1086,12 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
let toggleCopyProtectionDisposable = MetaDisposable() let toggleCopyProtectionDisposable = MetaDisposable()
actionsDisposable.add(toggleCopyProtectionDisposable) actionsDisposable.add(toggleCopyProtectionDisposable)
let toggleJoinToSendDisposable = MetaDisposable()
actionsDisposable.add(toggleJoinToSendDisposable)
let toggleRequestToJoinDisposable = MetaDisposable()
actionsDisposable.add(toggleRequestToJoinDisposable)
let arguments = ChannelVisibilityControllerArguments(context: context, updateCurrentType: { type in let arguments = ChannelVisibilityControllerArguments(context: context, updateCurrentType: { type in
updateState { state in updateState { state in
return state.withUpdatedSelectedType(type) return state.withUpdatedSelectedType(type)
@ -1358,11 +1376,11 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
} }
if let updatedJoinToSend = state.joinToSend { if let updatedJoinToSend = state.joinToSend {
toggleCopyProtectionDisposable.set(context.engine.peers.toggleChannelJoinToSend(peerId: peerId, enabled: updatedJoinToSend == .members).start()) toggleJoinToSendDisposable.set(context.engine.peers.toggleChannelJoinToSend(peerId: peerId, enabled: updatedJoinToSend == .members).start())
} }
if let updatedApproveMembers = state.approveMembers { if let updatedApproveMembers = state.approveMembers {
toggleCopyProtectionDisposable.set(context.engine.peers.toggleChannelJoinRequest(peerId: peerId, enabled: updatedApproveMembers).start()) toggleRequestToJoinDisposable.set(context.engine.peers.toggleChannelJoinRequest(peerId: peerId, enabled: updatedApproveMembers).start())
} }
if let updatedAddressNameValue = updatedAddressNameValue { if let updatedAddressNameValue = updatedAddressNameValue {

View File

@ -16,7 +16,8 @@ extension BotMenuButton {
extension BotInfo { extension BotInfo {
convenience init(apiBotInfo: Api.BotInfo) { convenience init(apiBotInfo: Api.BotInfo) {
switch apiBotInfo { switch apiBotInfo {
case let .botInfo(_, _, description, _, apiCommands, apiMenuButton): case let .botInfo(_, _, description, descriptionPhoto, apiCommands, apiMenuButton):
let photo: TelegramMediaImage? = descriptionPhoto.flatMap(telegramMediaImageFromApiPhoto)
var commands: [BotCommand] = [] var commands: [BotCommand] = []
if let apiCommands = apiCommands { if let apiCommands = apiCommands {
commands = apiCommands.map { command in commands = apiCommands.map { command in
@ -30,7 +31,7 @@ extension BotInfo {
if let apiMenuButton = apiMenuButton { if let apiMenuButton = apiMenuButton {
menuButton = BotMenuButton(apiBotMenuButton: apiMenuButton) menuButton = BotMenuButton(apiBotMenuButton: apiMenuButton)
} }
self.init(description: description ?? "", commands: commands, menuButton: menuButton) self.init(description: description ?? "", photo: photo, commands: commands, menuButton: menuButton)
} }
} }
} }

View File

@ -1467,14 +1467,14 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
updatedState.updateCachedPeerData(peer.peerId, { current in updatedState.updateCachedPeerData(peer.peerId, { current in
if peer.peerId.namespace == Namespaces.Peer.CloudUser, let previous = current as? CachedUserData { if peer.peerId.namespace == Namespaces.Peer.CloudUser, let previous = current as? CachedUserData {
if let botInfo = previous.botInfo { if let botInfo = previous.botInfo {
return previous.withUpdatedBotInfo(BotInfo(description: botInfo.description, commands: commands, menuButton: botInfo.menuButton)) return previous.withUpdatedBotInfo(BotInfo(description: botInfo.description, photo: botInfo.photo, commands: commands, menuButton: botInfo.menuButton))
} }
} else if peer.peerId.namespace == Namespaces.Peer.CloudGroup, let previous = current as? CachedGroupData { } else if peer.peerId.namespace == Namespaces.Peer.CloudGroup, let previous = current as? CachedGroupData {
if let index = previous.botInfos.firstIndex(where: { $0.peerId == botPeerId }) { if let index = previous.botInfos.firstIndex(where: { $0.peerId == botPeerId }) {
var updatedBotInfos = previous.botInfos var updatedBotInfos = previous.botInfos
let previousBotInfo = updatedBotInfos[index] let previousBotInfo = updatedBotInfos[index]
updatedBotInfos.remove(at: index) updatedBotInfos.remove(at: index)
updatedBotInfos.insert(CachedPeerBotInfo(peerId: botPeerId, botInfo: BotInfo(description: previousBotInfo.botInfo.description, commands: commands, menuButton: previousBotInfo.botInfo.menuButton)), at: index) updatedBotInfos.insert(CachedPeerBotInfo(peerId: botPeerId, botInfo: BotInfo(description: previousBotInfo.botInfo.description, photo: previousBotInfo.botInfo.photo, commands: commands, menuButton: previousBotInfo.botInfo.menuButton)), at: index)
return previous.withUpdatedBotInfos(updatedBotInfos) return previous.withUpdatedBotInfos(updatedBotInfos)
} }
} else if peer.peerId.namespace == Namespaces.Peer.CloudChannel, let previous = current as? CachedChannelData { } else if peer.peerId.namespace == Namespaces.Peer.CloudChannel, let previous = current as? CachedChannelData {
@ -1482,7 +1482,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
var updatedBotInfos = previous.botInfos var updatedBotInfos = previous.botInfos
let previousBotInfo = updatedBotInfos[index] let previousBotInfo = updatedBotInfos[index]
updatedBotInfos.remove(at: index) updatedBotInfos.remove(at: index)
updatedBotInfos.insert(CachedPeerBotInfo(peerId: botPeerId, botInfo: BotInfo(description: previousBotInfo.botInfo.description, commands: commands, menuButton: previousBotInfo.botInfo.menuButton)), at: index) updatedBotInfos.insert(CachedPeerBotInfo(peerId: botPeerId, botInfo: BotInfo(description: previousBotInfo.botInfo.description, photo: previousBotInfo.botInfo.photo, commands: commands, menuButton: previousBotInfo.botInfo.menuButton)), at: index)
return previous.withUpdatedBotInfos(updatedBotInfos) return previous.withUpdatedBotInfos(updatedBotInfos)
} }
} }
@ -1494,7 +1494,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
updatedState.updateCachedPeerData(botPeerId, { current in updatedState.updateCachedPeerData(botPeerId, { current in
if let previous = current as? CachedUserData { if let previous = current as? CachedUserData {
if let botInfo = previous.botInfo { if let botInfo = previous.botInfo {
return previous.withUpdatedBotInfo(BotInfo(description: botInfo.description, commands: botInfo.commands, menuButton: menuButton)) return previous.withUpdatedBotInfo(BotInfo(description: botInfo.description, photo: botInfo.photo, commands: botInfo.commands, menuButton: menuButton))
} }
} }
return current return current

View File

@ -47,23 +47,35 @@ public enum BotMenuButton: PostboxCoding, Hashable {
public final class BotInfo: PostboxCoding, Equatable { public final class BotInfo: PostboxCoding, Equatable {
public let description: String public let description: String
public let photo: TelegramMediaImage?
public let commands: [BotCommand] public let commands: [BotCommand]
public let menuButton: BotMenuButton public let menuButton: BotMenuButton
public init(description: String, commands: [BotCommand], menuButton: BotMenuButton) { public init(description: String, photo: TelegramMediaImage?, commands: [BotCommand], menuButton: BotMenuButton) {
self.description = description self.description = description
self.photo = photo
self.commands = commands self.commands = commands
self.menuButton = menuButton self.menuButton = menuButton
} }
public init(decoder: PostboxDecoder) { public init(decoder: PostboxDecoder) {
self.description = decoder.decodeStringForKey("d", orElse: "") self.description = decoder.decodeStringForKey("d", orElse: "")
if let photo = decoder.decodeObjectForKey("ph", decoder: { TelegramMediaImage(decoder: $0) }) as? TelegramMediaImage {
self.photo = photo
} else {
self.photo = nil
}
self.commands = decoder.decodeObjectArrayWithDecoderForKey("c") self.commands = decoder.decodeObjectArrayWithDecoderForKey("c")
self.menuButton = (decoder.decodeObjectForKey("b", decoder: { BotMenuButton(decoder: $0) }) as? BotMenuButton) ?? .commands self.menuButton = (decoder.decodeObjectForKey("b", decoder: { BotMenuButton(decoder: $0) }) as? BotMenuButton) ?? .commands
} }
public func encode(_ encoder: PostboxEncoder) { public func encode(_ encoder: PostboxEncoder) {
encoder.encodeString(self.description, forKey: "d") encoder.encodeString(self.description, forKey: "d")
if let photo = self.photo {
encoder.encodeObject(photo, forKey: "ph")
} else {
encoder.encodeNil(forKey: "ph")
}
encoder.encodeObjectArray(self.commands, forKey: "c") encoder.encodeObjectArray(self.commands, forKey: "c")
encoder.encodeObject(self.menuButton, forKey: "b") encoder.encodeObject(self.menuButton, forKey: "b")
} }

View File

@ -8,6 +8,8 @@ import TelegramCore
import TelegramPresentationData import TelegramPresentationData
import TextFormat import TextFormat
import UrlEscaping import UrlEscaping
import PhotoResources
import AccountContext
private let messageFont = Font.regular(17.0) private let messageFont = Font.regular(17.0)
private let messageBoldFont = Font.semibold(17.0) private let messageBoldFont = Font.semibold(17.0)
@ -18,14 +20,18 @@ private let messageFixedFont = UIFont(name: "Menlo-Regular", size: 16.0) ?? UIFo
final class ChatBotInfoItem: ListViewItem { final class ChatBotInfoItem: ListViewItem {
fileprivate let title: String fileprivate let title: String
fileprivate let text: String fileprivate let text: String
fileprivate let photo: TelegramMediaImage?
fileprivate let controllerInteraction: ChatControllerInteraction fileprivate let controllerInteraction: ChatControllerInteraction
fileprivate let presentationData: ChatPresentationData fileprivate let presentationData: ChatPresentationData
fileprivate let context: AccountContext
init(title: String, text: String, controllerInteraction: ChatControllerInteraction, presentationData: ChatPresentationData) { init(title: String, text: String, photo: TelegramMediaImage?, controllerInteraction: ChatControllerInteraction, presentationData: ChatPresentationData, context: AccountContext) {
self.title = title self.title = title
self.text = text self.text = text
self.photo = photo
self.controllerInteraction = controllerInteraction self.controllerInteraction = controllerInteraction
self.presentationData = presentationData self.presentationData = presentationData
self.context = context
} }
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) { func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
@ -76,10 +82,13 @@ final class ChatBotInfoItemNode: ListViewItemNode {
let offsetContainer: ASDisplayNode let offsetContainer: ASDisplayNode
let backgroundNode: ASImageNode let backgroundNode: ASImageNode
let imageNode: TransformImageNode
let titleNode: TextNode let titleNode: TextNode
let textNode: TextNode let textNode: TextNode
private var linkHighlightingNode: LinkHighlightingNode? private var linkHighlightingNode: LinkHighlightingNode?
private let fetchDisposable = MetaDisposable()
var currentTextAndEntities: (String, [MessageTextEntity])? var currentTextAndEntities: (String, [MessageTextEntity])?
private var theme: ChatPresentationThemeData? private var theme: ChatPresentationThemeData?
@ -92,6 +101,7 @@ final class ChatBotInfoItemNode: ListViewItemNode {
self.backgroundNode = ASImageNode() self.backgroundNode = ASImageNode()
self.backgroundNode.displaysAsynchronously = false self.backgroundNode.displaysAsynchronously = false
self.backgroundNode.displayWithoutProcessing = true self.backgroundNode.displayWithoutProcessing = true
self.imageNode = TransformImageNode()
self.textNode = TextNode() self.textNode = TextNode()
self.titleNode = TextNode() self.titleNode = TextNode()
@ -101,11 +111,16 @@ final class ChatBotInfoItemNode: ListViewItemNode {
self.addSubnode(self.offsetContainer) self.addSubnode(self.offsetContainer)
self.offsetContainer.addSubnode(self.backgroundNode) self.offsetContainer.addSubnode(self.backgroundNode)
self.offsetContainer.addSubnode(self.imageNode)
self.offsetContainer.addSubnode(self.titleNode) self.offsetContainer.addSubnode(self.titleNode)
self.offsetContainer.addSubnode(self.textNode) self.offsetContainer.addSubnode(self.textNode)
self.wantsTrailingItemSpaceUpdates = true self.wantsTrailingItemSpaceUpdates = true
} }
deinit {
self.fetchDisposable.dispose()
}
override func didLoad() { override func didLoad() {
super.didLoad() super.didLoad()
@ -134,10 +149,14 @@ final class ChatBotInfoItemNode: ListViewItemNode {
} }
func asyncLayout() -> (_ item: ChatBotInfoItem, _ width: ListViewItemLayoutParams) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation) -> Void) { func asyncLayout() -> (_ item: ChatBotInfoItem, _ width: ListViewItemLayoutParams) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation) -> Void) {
let makeImageLayout = self.imageNode.asyncLayout()
let makeTitleLayout = TextNode.asyncLayout(self.titleNode) let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
let makeTextLayout = TextNode.asyncLayout(self.textNode) let makeTextLayout = TextNode.asyncLayout(self.textNode)
let currentTextAndEntities = self.currentTextAndEntities let currentTextAndEntities = self.currentTextAndEntities
let currentTheme = self.theme let currentTheme = self.theme
let currentItem = self.item
return { [weak self] item, params in return { [weak self] item, params in
self?.item = item self?.item = item
@ -145,7 +164,7 @@ final class ChatBotInfoItemNode: ListViewItemNode {
if currentTheme != item.presentationData.theme { if currentTheme != item.presentationData.theme {
updatedBackgroundImage = PresentationResourcesChat.chatInfoItemBackgroundImage(item.presentationData.theme.theme, wallpaper: !item.presentationData.theme.wallpaper.isEmpty) updatedBackgroundImage = PresentationResourcesChat.chatInfoItemBackgroundImage(item.presentationData.theme.theme, wallpaper: !item.presentationData.theme.wallpaper.isEmpty)
} }
var updatedTextAndEntities: (String, [MessageTextEntity]) var updatedTextAndEntities: (String, [MessageTextEntity])
if let (text, entities) = currentTextAndEntities { if let (text, entities) = currentTextAndEntities {
if text == item.text { if text == item.text {
@ -171,11 +190,40 @@ final class ChatBotInfoItemNode: ListViewItemNode {
let textSpacing: CGFloat = 1.0 let textSpacing: CGFloat = 1.0
let textSize = CGSize(width: max(titleLayout.size.width, textLayout.size.width), height: (titleLayout.size.height + (titleLayout.size.width.isZero ? 0.0 : textSpacing) + textLayout.size.height)) let textSize = CGSize(width: max(titleLayout.size.width, textLayout.size.width), height: (titleLayout.size.height + (titleLayout.size.width.isZero ? 0.0 : textSpacing) + textLayout.size.height))
let backgroundFrame = CGRect(origin: CGPoint(x: floor((params.width - textSize.width - horizontalContentInset * 2.0) / 2.0), y: verticalItemInset + 4.0), size: CGSize(width: textSize.width + horizontalContentInset * 2.0, height: textSize.height + verticalContentInset * 2.0)) var mediaUpdated = false
let titleFrame = CGRect(origin: CGPoint(x: backgroundFrame.origin.x + horizontalContentInset, y: backgroundFrame.origin.y + verticalContentInset), size: titleLayout.size) if let media = item.photo {
let textFrame = CGRect(origin: CGPoint(x: backgroundFrame.origin.x + horizontalContentInset, y: backgroundFrame.origin.y + verticalContentInset + titleLayout.size.height + (titleLayout.size.width.isZero ? 0.0 : textSpacing)), size: textLayout.size) if let currentMedia = currentItem?.photo {
mediaUpdated = !media.isSemanticallyEqual(to: currentMedia)
} else {
mediaUpdated = true
}
}
let itemLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: textLayout.size.height + verticalItemInset * 2.0 + verticalContentInset * 2.0 + titleLayout.size.height + (titleLayout.size.width.isZero ? 0.0 : textSpacing) - 3.0), insets: UIEdgeInsets()) var updatedImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?
var imageSize = CGSize()
var imageDimensions = CGSize()
var imageApply: (() -> Void)?
let imageInset: CGFloat = 1.0 + UIScreenPixel
if let image = item.photo, let dimensions = largestImageRepresentation(image.representations)?.dimensions {
imageDimensions = dimensions.cgSize.aspectFitted(CGSize(width: textSize.width + horizontalContentInset * 2.0 - imageInset * 2.0, height: CGFloat.greatestFiniteMagnitude))
imageSize = imageDimensions
imageSize.height += 4.0
let arguments = TransformImageArguments(corners: ImageCorners(topLeft: .Corner(17.0), topRight: .Corner(17.0), bottomLeft: .Corner(0.0), bottomRight: .Corner(0.0)), imageSize: dimensions.cgSize.aspectFilled(imageDimensions), boundingSize: imageDimensions, intrinsicInsets: UIEdgeInsets(), emptyColor: item.presentationData.theme.theme.list.mediaPlaceholderColor)
imageApply = makeImageLayout(arguments)
if mediaUpdated {
updatedImageSignal = chatMessagePhoto(postbox: item.context.account.postbox, photoReference: .standalone(media: image), synchronousLoad: true, highQuality: false)
}
}
let backgroundFrame = CGRect(origin: CGPoint(x: floor((params.width - textSize.width - horizontalContentInset * 2.0) / 2.0), y: verticalItemInset + 4.0), size: CGSize(width: textSize.width + horizontalContentInset * 2.0, height: imageSize.height + textSize.height + verticalContentInset * 2.0))
let titleFrame = CGRect(origin: CGPoint(x: backgroundFrame.origin.x + horizontalContentInset, y: backgroundFrame.origin.y + imageSize.height + verticalContentInset), size: titleLayout.size)
let textFrame = CGRect(origin: CGPoint(x: backgroundFrame.origin.x + horizontalContentInset, y: backgroundFrame.origin.y + imageSize.height + verticalContentInset + titleLayout.size.height + (titleLayout.size.width.isZero ? 0.0 : textSpacing)), size: textLayout.size)
let imageFrame = CGRect(origin: CGPoint(x: backgroundFrame.origin.x + imageInset, y: backgroundFrame.origin.y + imageInset), size: imageDimensions)
let itemLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: imageSize.height + textLayout.size.height + verticalItemInset * 2.0 + verticalContentInset * 2.0 + titleLayout.size.height + (titleLayout.size.width.isZero ? 0.0 : textSpacing) - 3.0), insets: UIEdgeInsets())
return (itemLayout, { _ in return (itemLayout, { _ in
if let strongSelf = self { if let strongSelf = self {
strongSelf.theme = item.presentationData.theme strongSelf.theme = item.presentationData.theme
@ -186,6 +234,22 @@ final class ChatBotInfoItemNode: ListViewItemNode {
strongSelf.controllerInteraction = item.controllerInteraction strongSelf.controllerInteraction = item.controllerInteraction
strongSelf.currentTextAndEntities = updatedTextAndEntities strongSelf.currentTextAndEntities = updatedTextAndEntities
if let imageApply = imageApply {
let _ = imageApply()
if let updatedImageSignal = updatedImageSignal {
strongSelf.imageNode.setSignal(updatedImageSignal)
if let image = item.photo {
strongSelf.fetchDisposable.set(chatMessagePhotoInteractiveFetched(context: item.context, photoReference: .standalone(media: image), displayAtSize: nil, storeToDownloadsPeerType: nil).start())
}
}
strongSelf.imageNode.isHidden = false
} else {
strongSelf.imageNode.isHidden = true
}
strongSelf.imageNode.frame = imageFrame
let _ = titleApply() let _ = titleApply()
let _ = textApply() let _ = textApply()
strongSelf.offsetContainer.frame = CGRect(origin: CGPoint(), size: itemLayout.contentSize) strongSelf.offsetContainer.frame = CGRect(origin: CGPoint(), size: itemLayout.contentSize)

View File

@ -255,9 +255,9 @@ func chatHistoryEntriesForView(
} }
} }
if case let .peer(peerId) = location, peerId.isReplies { if case let .peer(peerId) = location, peerId.isReplies {
entries.insert(.ChatInfoEntry("", presentationData.strings.RepliesChat_DescriptionText, presentationData), at: 0) entries.insert(.ChatInfoEntry("", presentationData.strings.RepliesChat_DescriptionText, nil, presentationData), at: 0)
} else if let cachedPeerData = cachedPeerData as? CachedUserData, let botInfo = cachedPeerData.botInfo, !botInfo.description.isEmpty { } else if let cachedPeerData = cachedPeerData as? CachedUserData, let botInfo = cachedPeerData.botInfo, !botInfo.description.isEmpty {
entries.insert(.ChatInfoEntry(presentationData.strings.Bot_DescriptionTitle, botInfo.description, presentationData), at: 0) entries.insert(.ChatInfoEntry(presentationData.strings.Bot_DescriptionTitle, botInfo.description, botInfo.photo, presentationData), at: 0)
} else { } else {
var isEmpty = true var isEmpty = true
if entries.count <= 3 { if entries.count <= 3 {

View File

@ -43,7 +43,7 @@ enum ChatHistoryEntry: Identifiable, Comparable {
case MessageGroupEntry(MessageGroupInfo, [(Message, Bool, ChatHistoryMessageSelection, ChatMessageEntryAttributes, MessageHistoryEntryLocation?)], ChatPresentationData) case MessageGroupEntry(MessageGroupInfo, [(Message, Bool, ChatHistoryMessageSelection, ChatMessageEntryAttributes, MessageHistoryEntryLocation?)], ChatPresentationData)
case UnreadEntry(MessageIndex, ChatPresentationData) case UnreadEntry(MessageIndex, ChatPresentationData)
case ReplyCountEntry(MessageIndex, Bool, Int, ChatPresentationData) case ReplyCountEntry(MessageIndex, Bool, Int, ChatPresentationData)
case ChatInfoEntry(String, String, ChatPresentationData) case ChatInfoEntry(String, String, TelegramMediaImage?, ChatPresentationData)
case SearchEntry(PresentationTheme, PresentationStrings) case SearchEntry(PresentationTheme, PresentationStrings)
var stableId: UInt64 { var stableId: UInt64 {
@ -201,8 +201,8 @@ enum ChatHistoryEntry: Identifiable, Comparable {
} else { } else {
return false return false
} }
case let .ChatInfoEntry(lhsTitle, lhsText, lhsPresentationData): case let .ChatInfoEntry(lhsTitle, lhsText, lhsPhoto, lhsPresentationData):
if case let .ChatInfoEntry(rhsTitle, rhsText, rhsPresentationData) = rhs, lhsTitle == rhsTitle, lhsText == rhsText, lhsPresentationData === rhsPresentationData { if case let .ChatInfoEntry(rhsTitle, rhsText, rhsPhoto, rhsPresentationData) = rhs, lhsTitle == rhsTitle, lhsText == rhsText, lhsPhoto == rhsPhoto, lhsPresentationData === rhsPresentationData {
return true return true
} else { } else {
return false return false

View File

@ -247,8 +247,8 @@ private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLoca
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index, presentationData: presentationData, context: context), directionHint: entry.directionHint) return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index, presentationData: presentationData, context: context), directionHint: entry.directionHint)
case let .ReplyCountEntry(_, isComments, count, presentationData): case let .ReplyCountEntry(_, isComments, count, presentationData):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatReplyCountItem(index: entry.entry.index, isComments: isComments, count: count, presentationData: presentationData, context: context, controllerInteraction: controllerInteraction), directionHint: entry.directionHint) return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatReplyCountItem(index: entry.entry.index, isComments: isComments, count: count, presentationData: presentationData, context: context, controllerInteraction: controllerInteraction), directionHint: entry.directionHint)
case let .ChatInfoEntry(title, text, presentationData): case let .ChatInfoEntry(title, text, photo, presentationData):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(title: title, text: text, controllerInteraction: controllerInteraction, presentationData: presentationData), directionHint: entry.directionHint) return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(title: title, text: text, photo: photo, controllerInteraction: controllerInteraction, presentationData: presentationData, context: context), directionHint: entry.directionHint)
case let .SearchEntry(theme, strings): case let .SearchEntry(theme, strings):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, placeholder: strings.Common_Search, activate: { return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, placeholder: strings.Common_Search, activate: {
controllerInteraction.openSearch() controllerInteraction.openSearch()
@ -292,8 +292,8 @@ private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLoca
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index, presentationData: presentationData, context: context), directionHint: entry.directionHint) return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index, presentationData: presentationData, context: context), directionHint: entry.directionHint)
case let .ReplyCountEntry(_, isComments, count, presentationData): case let .ReplyCountEntry(_, isComments, count, presentationData):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatReplyCountItem(index: entry.entry.index, isComments: isComments, count: count, presentationData: presentationData, context: context, controllerInteraction: controllerInteraction), directionHint: entry.directionHint) return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatReplyCountItem(index: entry.entry.index, isComments: isComments, count: count, presentationData: presentationData, context: context, controllerInteraction: controllerInteraction), directionHint: entry.directionHint)
case let .ChatInfoEntry(title, text, presentationData): case let .ChatInfoEntry(title, text, photo, presentationData):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(title: title, text: text, controllerInteraction: controllerInteraction, presentationData: presentationData), directionHint: entry.directionHint) return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(title: title, text: text, photo: photo, controllerInteraction: controllerInteraction, presentationData: presentationData, context: context), directionHint: entry.directionHint)
case let .SearchEntry(theme, strings): case let .SearchEntry(theme, strings):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, placeholder: strings.Common_Search, activate: { return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, placeholder: strings.Common_Search, activate: {
controllerInteraction.openSearch() controllerInteraction.openSearch()

View File

@ -38,6 +38,8 @@ public enum WebEmbedType {
public func webEmbedType(content: TelegramMediaWebpageLoadedContent, forcedTimestamp: Int? = nil) -> WebEmbedType { public func webEmbedType(content: TelegramMediaWebpageLoadedContent, forcedTimestamp: Int? = nil) -> WebEmbedType {
if let (videoId, timestamp) = extractYoutubeVideoIdAndTimestamp(url: content.url) { if let (videoId, timestamp) = extractYoutubeVideoIdAndTimestamp(url: content.url) {
return .youtube(videoId: videoId, timestamp: forcedTimestamp ?? timestamp) return .youtube(videoId: videoId, timestamp: forcedTimestamp ?? timestamp)
} else if let embedUrl = content.embedUrl, let (videoId, timestamp) = extractYoutubeVideoIdAndTimestamp(url: embedUrl) {
return .youtube(videoId: videoId, timestamp: forcedTimestamp ?? timestamp)
} else if let (videoId, timestamp) = extractVimeoVideoIdAndTimestamp(url: content.url) { } else if let (videoId, timestamp) = extractVimeoVideoIdAndTimestamp(url: content.url) {
return .vimeo(videoId: videoId, timestamp: forcedTimestamp ?? timestamp) return .vimeo(videoId: videoId, timestamp: forcedTimestamp ?? timestamp)
} else if let embedUrl = content.embedUrl, isTwitchVideoUrl(embedUrl) && false { } else if let embedUrl = content.embedUrl, isTwitchVideoUrl(embedUrl) && false {