Fix file bubble layout

This commit is contained in:
Ilya Laktyushin 2020-10-07 17:34:55 +04:00
parent 77a7ac8882
commit 23df050f9b
11 changed files with 59 additions and 44 deletions

View File

@ -531,7 +531,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
var additionalImageBadgeContent: ChatMessageInteractiveMediaBadgeContent?
switch position {
case .linear(_, .None), .linear(_, .Neighbour(true, _)):
case .linear(_, .None), .linear(_, .Neighbour(true, _, _)):
let imageMode = !((refineContentImageLayout == nil && refineContentFileLayout == nil && contentInstantVideoSizeAndApply == nil) || preferMediaBeforeText)
statusInText = !imageMode

View File

@ -46,9 +46,14 @@ enum ChatMessageBubbleRelativePosition {
case freeform
}
enum NeighbourSpacing {
case `default`
case condensed
}
case None(ChatMessageBubbleMergeStatus)
case BubbleNeighbour
case Neighbour(Bool, NeighbourType)
case Neighbour(Bool, NeighbourType, NeighbourSpacing)
}
enum ChatMessageBubbleContentMosaicNeighbor {

View File

@ -32,6 +32,7 @@ enum InternalBubbleTapAction {
private struct BubbleItemAttributes {
var isAttachment: Bool
var neighborType: ChatMessageBubbleRelativePosition.NeighbourType
var neighborSpacing: ChatMessageBubbleRelativePosition.NeighbourSpacing
}
private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [(Message, AnyClass, ChatMessageEntryAttributes, BubbleItemAttributes)] {
@ -41,52 +42,61 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [(
var isUnsupportedMedia = false
var isAction = false
var previousItemIsMusic = false
outer: for (message, itemAttributes) in item.content {
for attribute in message.attributes {
if let attribute = attribute as? RestrictedContentMessageAttribute, attribute.platformText(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) != nil {
result.append((message, ChatMessageRestrictedBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform)))
result.append((message, ChatMessageRestrictedBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
break outer
}
}
inner: for media in message.media {
var isMusic = false
if let _ = media as? TelegramMediaImage {
result.append((message, ChatMessageMediaBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .media)))
result.append((message, ChatMessageMediaBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .media, neighborSpacing: .default)))
} else if let file = media as? TelegramMediaFile {
let isVideo = file.isVideo || (file.isAnimated && file.dimensions != nil)
if isVideo {
result.append((message, ChatMessageMediaBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .media)))
result.append((message, ChatMessageMediaBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .media, neighborSpacing: .default)))
} else {
result.append((message, ChatMessageFileBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform)))
var neighborSpacing: ChatMessageBubbleRelativePosition.NeighbourSpacing = .default
if previousItemIsMusic {
neighborSpacing = .condensed
}
isMusic = file.isMusic
result.append((message, ChatMessageFileBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: neighborSpacing)))
}
} else if let action = media as? TelegramMediaAction {
isAction = true
if case .phoneCall = action.action {
result.append((message, ChatMessageCallBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform)))
result.append((message, ChatMessageCallBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
} else {
result.append((message, ChatMessageActionBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform)))
result.append((message, ChatMessageActionBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
}
} else if let _ = media as? TelegramMediaMap {
result.append((message, ChatMessageMapBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform)))
result.append((message, ChatMessageMapBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
} else if let _ = media as? TelegramMediaGame {
skipText = true
result.append((message, ChatMessageGameBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform)))
result.append((message, ChatMessageGameBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
break inner
} else if let _ = media as? TelegramMediaInvoice {
skipText = true
result.append((message, ChatMessageInvoiceBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform)))
result.append((message, ChatMessageInvoiceBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
break inner
} else if let _ = media as? TelegramMediaContact {
result.append((message, ChatMessageContactBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform)))
result.append((message, ChatMessageContactBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
} else if let _ = media as? TelegramMediaExpiredContent {
result.removeAll()
result.append((message, ChatMessageActionBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform)))
result.append((message, ChatMessageActionBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
return result
} else if let _ = media as? TelegramMediaPoll {
result.append((message, ChatMessagePollBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform)))
result.append((message, ChatMessagePollBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
} else if let _ = media as? TelegramMediaUnsupported {
isUnsupportedMedia = true
}
previousItemIsMusic = isMusic
}
var messageText = message.text
@ -100,7 +110,7 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [(
messageWithCaptionToAdd = (message, itemAttributes)
skipText = true
} else {
result.append((message, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform)))
result.append((message, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
}
} else {
if case .group = item.content {
@ -112,29 +122,29 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [(
inner: for media in message.media {
if let webpage = media as? TelegramMediaWebpage {
if case .Loaded = webpage.content {
result.append((message, ChatMessageWebpageBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform)))
result.append((message, ChatMessageWebpageBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
}
break inner
}
}
if isUnsupportedMedia {
result.append((message, ChatMessageUnsupportedBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform)))
result.append((message, ChatMessageUnsupportedBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
}
}
if let (messageWithCaptionToAdd, itemAttributes) = messageWithCaptionToAdd {
result.append((messageWithCaptionToAdd, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform)))
result.append((messageWithCaptionToAdd, ChatMessageTextBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
}
if let additionalContent = item.additionalContent {
switch additionalContent {
case let .eventLogPreviousMessage(previousMessage):
result.append((previousMessage, ChatMessageEventLogPreviousMessageContentNode.self, ChatMessageEntryAttributes(), BubbleItemAttributes(isAttachment: false, neighborType: .freeform)))
result.append((previousMessage, ChatMessageEventLogPreviousMessageContentNode.self, ChatMessageEntryAttributes(), BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
case let .eventLogPreviousDescription(previousMessage):
result.append((previousMessage, ChatMessageEventLogPreviousDescriptionContentNode.self, ChatMessageEntryAttributes(), BubbleItemAttributes(isAttachment: false, neighborType: .freeform)))
result.append((previousMessage, ChatMessageEventLogPreviousDescriptionContentNode.self, ChatMessageEntryAttributes(), BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
case let .eventLogPreviousLink(previousMessage):
result.append((previousMessage, ChatMessageEventLogPreviousLinkContentNode.self, ChatMessageEntryAttributes(), BubbleItemAttributes(isAttachment: false, neighborType: .freeform)))
result.append((previousMessage, ChatMessageEventLogPreviousLinkContentNode.self, ChatMessageEntryAttributes(), BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
}
}
@ -167,10 +177,10 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [(
}
if canComment {
result.append((firstMessage, ChatMessageCommentFooterContentNode.self, ChatMessageEntryAttributes(), BubbleItemAttributes(isAttachment: true, neighborType: .freeform)))
result.append((firstMessage, ChatMessageCommentFooterContentNode.self, ChatMessageEntryAttributes(), BubbleItemAttributes(isAttachment: true, neighborType: .freeform, neighborSpacing: .default)))
}
} else if firstMessage.id.peerId.isReplies {
result.append((firstMessage, ChatMessageCommentFooterContentNode.self, ChatMessageEntryAttributes(), BubbleItemAttributes(isAttachment: true, neighborType: .freeform)))
result.append((firstMessage, ChatMessageCommentFooterContentNode.self, ChatMessageEntryAttributes(), BubbleItemAttributes(isAttachment: true, neighborType: .freeform, neighborSpacing: .default)))
}
}
@ -1118,8 +1128,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
let topPosition: ChatMessageBubbleRelativePosition
let bottomPosition: ChatMessageBubbleRelativePosition
var topBubbleAttributes = BubbleItemAttributes(isAttachment: false, neighborType: .freeform)
var bottomBubbleAttributes = BubbleItemAttributes(isAttachment: false, neighborType: .freeform)
var topBubbleAttributes = BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)
var bottomBubbleAttributes = BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)
if index != 0 {
topBubbleAttributes = contentPropertiesAndPrepareLayouts[index - 1].3
}
@ -1127,8 +1137,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
bottomBubbleAttributes = contentPropertiesAndPrepareLayouts[index + 1].3
}
topPosition = .Neighbour(topBubbleAttributes.isAttachment, topBubbleAttributes.neighborType)
bottomPosition = .Neighbour(bottomBubbleAttributes.isAttachment, bottomBubbleAttributes.neighborType)
topPosition = .Neighbour(topBubbleAttributes.isAttachment, topBubbleAttributes.neighborType, topBubbleAttributes.neighborSpacing)
bottomPosition = .Neighbour(bottomBubbleAttributes.isAttachment, bottomBubbleAttributes.neighborType, bottomBubbleAttributes.neighborSpacing)
let prepareContentPosition: ChatMessageBubblePreparePosition
if let mosaicRange = mosaicRange, mosaicRange.contains(index) {
@ -1262,7 +1272,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
let firstNodeTopPosition: ChatMessageBubbleRelativePosition
if displayHeader {
firstNodeTopPosition = .Neighbour(false, .freeform)
firstNodeTopPosition = .Neighbour(false, .freeform, .default)
} else {
firstNodeTopPosition = .None(topNodeMergeStatus)
}
@ -1556,7 +1566,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
if mosaicRange.upperBound - 1 == contentNodeCount - 1 {
lastMosaicBottomPosition = lastNodeTopPosition
} else {
lastMosaicBottomPosition = .Neighbour(false, .freeform)
lastMosaicBottomPosition = .Neighbour(false, .freeform, .default)
}
if position.contains(.bottom), case .Neighbour = lastMosaicBottomPosition {
@ -1619,8 +1629,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
let topPosition: ChatMessageBubbleRelativePosition
let bottomPosition: ChatMessageBubbleRelativePosition
var topBubbleAttributes = BubbleItemAttributes(isAttachment: false, neighborType: .freeform)
var bottomBubbleAttributes = BubbleItemAttributes(isAttachment: false, neighborType: .freeform)
var topBubbleAttributes = BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)
var bottomBubbleAttributes = BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)
if i != 0 {
topBubbleAttributes = contentPropertiesAndLayouts[i - 1].3
}
@ -1631,19 +1641,19 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
if i == 0 {
topPosition = firstNodeTopPosition
} else {
topPosition = .Neighbour(topBubbleAttributes.isAttachment, topBubbleAttributes.neighborType)
topPosition = .Neighbour(topBubbleAttributes.isAttachment, topBubbleAttributes.neighborType, topBubbleAttributes.neighborSpacing)
}
if i == contentNodeCount - 1 {
bottomPosition = lastNodeTopPosition
} else {
bottomPosition = .Neighbour(bottomBubbleAttributes.isAttachment, bottomBubbleAttributes.neighborType)
bottomPosition = .Neighbour(bottomBubbleAttributes.isAttachment, bottomBubbleAttributes.neighborType, bottomBubbleAttributes.neighborSpacing)
}
contentPosition = .linear(top: topPosition, bottom: bottomPosition)
case .mosaic:
assertionFailure()
contentPosition = .linear(top: .Neighbour(false, .freeform), bottom: .Neighbour(false, .freeform))
contentPosition = .linear(top: .Neighbour(false, .freeform, .default), bottom: .Neighbour(false, .freeform, .default))
}
let (contentNodeWidth, contentNodeFinalize) = contentNodeLayout(CGSize(width: maximumNodeWidth, height: CGFloat.greatestFiniteMagnitude), contentPosition)
#if DEBUG

View File

@ -106,7 +106,7 @@ final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContentNode {
let displaySeparator: Bool
let topOffset: CGFloat
if case let .linear(top, _) = preparePosition, case .Neighbour(_, .media) = top {
if case let .linear(top, _) = preparePosition, case .Neighbour(_, .media, _) = top {
displaySeparator = false
topOffset = 2.0
} else {

View File

@ -176,7 +176,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
let statusType: ChatMessageDateAndStatusType?
switch position {
case .linear(_, .None), .linear(_, .Neighbour(true, _)):
case .linear(_, .None), .linear(_, .Neighbour(true, _, _)):
if item.message.effectivelyIncoming(item.context.account.peerId) {
statusType = .BubbleIncoming
} else {

View File

@ -71,7 +71,7 @@ class ChatMessageFileBubbleContentNode: ChatMessageBubbleContentNode {
let incoming = item.message.effectivelyIncoming(item.context.account.peerId)
let statusType: ChatMessageDateAndStatusType?
switch preparePosition {
case .linear(_, .None), .linear(_, .Neighbour(true, _)):
case .linear(_, .None), .linear(_, .Neighbour(true, _, _)):
if incoming {
statusType = .BubbleIncoming
} else {
@ -102,7 +102,7 @@ class ChatMessageFileBubbleContentNode: ChatMessageBubbleContentNode {
var bottomInset = layoutConstants.file.bubbleInsets.bottom
if case let .linear(_, bottom) = position {
if case .Neighbour = bottom {
if case .Neighbour(_, _, .condensed) = bottom {
bottomInset -= 24.0
}
}

View File

@ -157,7 +157,7 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
if activeLiveBroadcastingTimeout != nil || selectedMedia?.venue != nil {
var relativePosition = position
if case let .linear(top, _) = position {
relativePosition = .linear(top: top, bottom: .Neighbour(false, .freeform))
relativePosition = .linear(top: top, bottom: .Neighbour(false, .freeform, .default))
}
imageCorners = chatMessageBubbleImageContentCorners(relativeContentPosition: relativePosition, normalRadius: layoutConstants.image.defaultCornerRadius, mergedRadius: layoutConstants.image.mergedCornerRadius, mergedWithAnotherContentRadius: layoutConstants.image.contentMergedCornerRadius, layoutConstants: layoutConstants, chatPresentationData: item.presentationData)
@ -212,7 +212,7 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
let statusType: ChatMessageDateAndStatusType?
switch position {
case .linear(_, .None), .linear(_, .Neighbour(true, _)):
case .linear(_, .None), .linear(_, .Neighbour(true, _, _)):
if selectedMedia?.venue != nil || activeLiveBroadcastingTimeout != nil {
if item.message.effectivelyIncoming(item.context.account.peerId) {
statusType = .BubbleIncoming

View File

@ -207,7 +207,7 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
let statusType: ChatMessageDateAndStatusType?
switch position {
case .linear(_, .None), .linear(_, .Neighbour(true, _)):
case .linear(_, .None), .linear(_, .Neighbour(true, _, _)):
if item.message.effectivelyIncoming(item.context.account.peerId) {
statusType = .ImageIncoming
} else {

View File

@ -1054,7 +1054,7 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
let statusType: ChatMessageDateAndStatusType?
switch position {
case .linear(_, .None), .linear(_, .Neighbour(true, _)):
case .linear(_, .None), .linear(_, .Neighbour(true, _, _)):
if incoming {
statusType = .BubbleIncoming
} else {

View File

@ -85,7 +85,7 @@ class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNode {
let statusType: ChatMessageDateAndStatusType?
switch position {
case .linear(_, .None), .linear(_, .Neighbour(true, _)):
case .linear(_, .None), .linear(_, .Neighbour(true, _, _)):
if incoming {
statusType = .BubbleIncoming
} else {

View File

@ -143,7 +143,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
case let .linear(_, neighbor):
if case .None = neighbor {
displayStatus = true
} else if case .Neighbour(true, _) = neighbor {
} else if case .Neighbour(true, _, _) = neighbor {
displayStatus = true
}
default: