mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
c27ebb4787
@ -811,7 +811,7 @@ public func createPollController(context: AccountContext, peer: Peer, isQuiz: Bo
|
||||
if state.isQuiz {
|
||||
kind = .quiz
|
||||
if !state.solutionText.value.string.isEmpty {
|
||||
let entities = generateTextEntities(state.solutionText.value.string, enabledTypes: .url, currentEntities: generateChatInputTextEntities(state.solutionText.value))
|
||||
let entities = generateTextEntities(state.solutionText.value.string, enabledTypes: .allUrl, currentEntities: generateChatInputTextEntities(state.solutionText.value))
|
||||
resolvedSolution = TelegramMediaPollResults.Solution(text: state.solutionText.value.string, entities: entities)
|
||||
}
|
||||
} else {
|
||||
|
@ -57,7 +57,7 @@ final class InstantPageArticleNode: ASDisplayNode, InstantPageNode {
|
||||
|
||||
let imageReference = ImageMediaReference.webPage(webPage: WebpageReference(webPage), media: image)
|
||||
imageNode.setSignal(chatMessagePhoto(postbox: context.account.postbox, photoReference: imageReference))
|
||||
self.fetchedDisposable.set(chatMessagePhotoInteractiveFetched(context: context, photoReference: imageReference, storeToDownloadsPeerType: nil).start())
|
||||
self.fetchedDisposable.set(chatMessagePhotoInteractiveFetched(context: context, photoReference: imageReference, displayAtSize: nil, storeToDownloadsPeerType: nil).start())
|
||||
|
||||
self.imageNode = imageNode
|
||||
self.addSubnode(imageNode)
|
||||
|
@ -74,12 +74,12 @@ final class InstantPageImageNode: ASDisplayNode, InstantPageNode {
|
||||
self.imageNode.setSignal(chatMessagePhoto(postbox: context.account.postbox, photoReference: imageReference))
|
||||
|
||||
if !interactive || shouldDownloadMediaAutomatically(settings: context.sharedContext.currentAutomaticMediaDownloadSettings.with { $0 }, peerType: sourcePeerType, networkType: MediaAutoDownloadNetworkType(context.account.immediateNetworkType), authorPeerId: nil, contactsPeerIds: Set(), media: image) {
|
||||
self.fetchedDisposable.set(chatMessagePhotoInteractiveFetched(context: context, photoReference: imageReference, storeToDownloadsPeerType: nil).start())
|
||||
self.fetchedDisposable.set(chatMessagePhotoInteractiveFetched(context: context, photoReference: imageReference, displayAtSize: nil, storeToDownloadsPeerType: nil).start())
|
||||
}
|
||||
|
||||
self.fetchControls = FetchControls(fetch: { [weak self] manual in
|
||||
if let strongSelf = self {
|
||||
strongSelf.fetchedDisposable.set(chatMessagePhotoInteractiveFetched(context: context, photoReference: imageReference, storeToDownloadsPeerType: nil).start())
|
||||
strongSelf.fetchedDisposable.set(chatMessagePhotoInteractiveFetched(context: context, photoReference: imageReference, displayAtSize: nil, storeToDownloadsPeerType: nil).start())
|
||||
}
|
||||
}, cancel: {
|
||||
chatMessagePhotoCancelInteractiveFetch(account: context.account, photoReference: imageReference)
|
||||
@ -133,7 +133,7 @@ final class InstantPageImageNode: ASDisplayNode, InstantPageNode {
|
||||
} else if let webPage = media.media as? TelegramMediaWebpage, case let .Loaded(content) = webPage.content, let image = content.image {
|
||||
let imageReference = ImageMediaReference.webPage(webPage: WebpageReference(webPage), media: image)
|
||||
self.imageNode.setSignal(chatMessagePhoto(postbox: context.account.postbox, photoReference: imageReference))
|
||||
self.fetchedDisposable.set(chatMessagePhotoInteractiveFetched(context: context, photoReference: imageReference, storeToDownloadsPeerType: nil).start())
|
||||
self.fetchedDisposable.set(chatMessagePhotoInteractiveFetched(context: context, photoReference: imageReference, displayAtSize: nil, storeToDownloadsPeerType: nil).start())
|
||||
self.statusNode.transitionToState(.play(.white), animated: false, completion: {})
|
||||
self.addSubnode(self.statusNode)
|
||||
}
|
||||
|
@ -334,7 +334,7 @@ private enum ChannelInfoEntry: ItemListNodeEntry {
|
||||
arguments.tapAvatarAction()
|
||||
}, context: arguments.avatarAndNameInfoContext, updatingImage: updatingAvatar)
|
||||
case let .about(theme, text, value):
|
||||
return ItemListTextWithLabelItem(presentationData: presentationData, label: text, text: foldMultipleLineBreaks(value), enabledEntityTypes: [.url, .mention, .hashtag], multiline: true, sectionId: self.section, action: nil, longTapAction: {
|
||||
return ItemListTextWithLabelItem(presentationData: presentationData, label: text, text: foldMultipleLineBreaks(value), enabledEntityTypes: [.allUrl, .mention, .hashtag], multiline: true, sectionId: self.section, action: nil, longTapAction: {
|
||||
arguments.displayContextMenu(ChannelInfoEntryTag.about, value)
|
||||
}, linkItemAction: { action, itemLink in
|
||||
arguments.aboutLinkAction(action, itemLink)
|
||||
|
@ -507,7 +507,7 @@ private enum GroupInfoEntry: ItemListNodeEntry {
|
||||
arguments.changeProfilePhoto()
|
||||
})
|
||||
case let .about(theme, text):
|
||||
return ItemListMultilineTextItem(presentationData: presentationData, text: foldMultipleLineBreaks(text), enabledEntityTypes: [.url, .mention, .hashtag], sectionId: self.section, style: .blocks, longTapAction: {
|
||||
return ItemListMultilineTextItem(presentationData: presentationData, text: foldMultipleLineBreaks(text), enabledEntityTypes: [.allUrl, .mention, .hashtag], sectionId: self.section, style: .blocks, longTapAction: {
|
||||
arguments.displayAboutContextMenu(text)
|
||||
}, linkItemAction: { action, itemLink in
|
||||
arguments.aboutLinkAction(action, itemLink)
|
||||
|
@ -410,7 +410,7 @@ private enum UserInfoEntry: ItemListNodeEntry {
|
||||
case let .about(theme, peer, text, value):
|
||||
var enabledEntityTypes: EnabledEntityTypes = []
|
||||
if let peer = peer as? TelegramUser, let _ = peer.botInfo {
|
||||
enabledEntityTypes = [.url, .mention, .hashtag]
|
||||
enabledEntityTypes = [.allUrl, .mention, .hashtag]
|
||||
}
|
||||
return ItemListTextWithLabelItem(presentationData: presentationData, label: text, text: foldMultipleLineBreaks(value), enabledEntityTypes: enabledEntityTypes, multiline: true, sectionId: self.section, action: nil, longTapAction: {
|
||||
arguments.displayAboutContextMenu(value)
|
||||
|
@ -37,8 +37,8 @@ private let progressiveRangeMap: [(Int, [Int])] = [
|
||||
(Int(Int32.max), [2, 3, 4])
|
||||
]
|
||||
|
||||
public func representationFetchRangeForDisplayAtSize(representation: TelegramMediaImageRepresentation, dimension: Int) -> Range<Int>? {
|
||||
if representation.progressiveSizes.count > 1 {
|
||||
public func representationFetchRangeForDisplayAtSize(representation: TelegramMediaImageRepresentation, dimension: Int?) -> Range<Int>? {
|
||||
if representation.progressiveSizes.count > 1, let dimension = dimension {
|
||||
var largestByteSize = Int(representation.progressiveSizes[0])
|
||||
for (maxDimension, byteSizes) in progressiveRangeMap {
|
||||
largestByteSize = Int(representation.progressiveSizes[byteSizes.last!])
|
||||
@ -1684,10 +1684,10 @@ public func standaloneChatMessagePhotoInteractiveFetched(account: Account, photo
|
||||
}
|
||||
}
|
||||
|
||||
public func chatMessagePhotoInteractiveFetched(context: AccountContext, photoReference: ImageMediaReference, displayAtSize: Int = 1000, storeToDownloadsPeerType: MediaAutoDownloadPeerType?) -> Signal<FetchResourceSourceType, FetchResourceError> {
|
||||
public func chatMessagePhotoInteractiveFetched(context: AccountContext, photoReference: ImageMediaReference, displayAtSize: Int?, storeToDownloadsPeerType: MediaAutoDownloadPeerType?) -> Signal<FetchResourceSourceType, FetchResourceError> {
|
||||
if let largestRepresentation = largestRepresentationForPhoto(photoReference.media) {
|
||||
var fetchRange: (Range<Int>, MediaBoxFetchPriority)?
|
||||
if let range = representationFetchRangeForDisplayAtSize(representation: largestRepresentation, dimension: displayAtSize) {
|
||||
if let displayAtSize = displayAtSize, let range = representationFetchRangeForDisplayAtSize(representation: largestRepresentation, dimension: displayAtSize) {
|
||||
fetchRange = (range, .default)
|
||||
}
|
||||
|
||||
|
@ -5323,10 +5323,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
case .commit:
|
||||
let _ = (requestUpdatePinnedMessage(account: strongSelf.context.account, peerId: peer.id, update: .clear(id: id))
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.chatDisplayNode.historyNode.pendingRemovedMessages.remove(id)
|
||||
Queue.mainQueue().after(1.0, {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.chatDisplayNode.historyNode.pendingRemovedMessages.remove(id)
|
||||
})
|
||||
})
|
||||
case .undo:
|
||||
strongSelf.chatDisplayNode.historyNode.pendingRemovedMessages.remove(id)
|
||||
@ -8144,7 +8146,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
public func displayPromoAnnouncement(text: String) {
|
||||
let psaText: String = text
|
||||
let psaEntities: [MessageTextEntity] = generateTextEntities(psaText, enabledTypes: .url)
|
||||
let psaEntities: [MessageTextEntity] = generateTextEntities(psaText, enabledTypes: .allUrl)
|
||||
|
||||
var found = false
|
||||
self.forEachController({ controller in
|
||||
@ -8237,7 +8239,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
psaText = string
|
||||
}
|
||||
|
||||
let psaEntities: [MessageTextEntity] = generateTextEntities(psaText, enabledTypes: .url)
|
||||
let psaEntities: [MessageTextEntity] = generateTextEntities(psaText, enabledTypes: .allUrl)
|
||||
|
||||
let messageId = item.message.id
|
||||
|
||||
|
@ -1159,6 +1159,15 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
}
|
||||
|
||||
let selectionRecognizer = ChatHistoryListSelectionRecognizer(target: self, action: #selector(self.selectionPanGesture(_:)))
|
||||
selectionRecognizer.shouldBegin = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return false
|
||||
}
|
||||
if case .pinnedMessages = strongSelf.subject {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
self.view.addGestureRecognizer(selectionRecognizer)
|
||||
}
|
||||
|
||||
|
@ -201,7 +201,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
strongSelf.insertSubnode(imageNode, at: 0)
|
||||
strongSelf.insertSubnode(strongSelf.mediaBackgroundNode, at: 0)
|
||||
}
|
||||
strongSelf.fetchDisposable.set(chatMessagePhotoInteractiveFetched(context: item.context, photoReference: .message(message: MessageReference(item.message), media: image), storeToDownloadsPeerType: nil).start())
|
||||
strongSelf.fetchDisposable.set(chatMessagePhotoInteractiveFetched(context: item.context, photoReference: .message(message: MessageReference(item.message), media: image), displayAtSize: nil, storeToDownloadsPeerType: nil).start())
|
||||
let updateImageSignal = chatMessagePhoto(postbox: item.context.account.postbox, photoReference: .message(message: MessageReference(item.message), media: image), synchronousLoad: synchronousLoads)
|
||||
|
||||
imageNode.setSignal(updateImageSignal, attemptSynchronously: synchronousLoads)
|
||||
|
@ -722,7 +722,12 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .minimal, reactionCount: dateReactionCount)
|
||||
|
||||
let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode)
|
||||
var isReplyThread = false
|
||||
if case .replyThread = item.chatLocation {
|
||||
isReplyThread = true
|
||||
}
|
||||
|
||||
let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread)
|
||||
|
||||
var viaBotApply: (TextNodeLayout, () -> TextNode)?
|
||||
var replyInfoApply: (CGSize, () -> ChatMessageReplyInfoNode)?
|
||||
|
@ -416,6 +416,11 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
|
||||
var skipStandardStatus = false
|
||||
|
||||
var isReplyThread = false
|
||||
if case .replyThread = chatLocation {
|
||||
isReplyThread = true
|
||||
}
|
||||
|
||||
if let (media, flags) = mediaAndFlags {
|
||||
if let file = media as? TelegramMediaFile {
|
||||
if file.mimeType == "application/x-tgtheme-ios", let size = file.size, size < 16 * 1024 {
|
||||
@ -424,7 +429,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
refineContentImageLayout = refineLayout
|
||||
} else if file.isInstantVideo {
|
||||
let automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, authorPeerId: message.author?.id, contactsPeerIds: associatedData.contactsPeerIds, media: file)
|
||||
let (videoLayout, apply) = contentInstantVideoLayout(ChatMessageBubbleContentItem(context: context, controllerInteraction: controllerInteraction, message: message, read: messageRead, chatLocation: chatLocation, presentationData: presentationData, associatedData: associatedData, attributes: attributes, isItemPinned: message.tags.contains(.pinned), isItemEdited: false), constrainedSize.width - horizontalInsets.left - horizontalInsets.right, CGSize(width: 212.0, height: 212.0), .bubble, automaticDownload)
|
||||
let (videoLayout, apply) = contentInstantVideoLayout(ChatMessageBubbleContentItem(context: context, controllerInteraction: controllerInteraction, message: message, read: messageRead, chatLocation: chatLocation, presentationData: presentationData, associatedData: associatedData, attributes: attributes, isItemPinned: message.tags.contains(.pinned) && !isReplyThread, isItemEdited: false), constrainedSize.width - horizontalInsets.left - horizontalInsets.right, CGSize(width: 212.0, height: 212.0), .bubble, automaticDownload)
|
||||
initialWidth = videoLayout.contentSize.width + videoLayout.overflowLeft + videoLayout.overflowRight
|
||||
contentInstantVideoSizeAndApply = (videoLayout, apply)
|
||||
} else if file.isVideo {
|
||||
@ -474,7 +479,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
let (_, refineLayout) = contentFileLayout(context, presentationData, message, associatedData, chatLocation, attributes, message.tags.contains(.pinned) && !associatedData.isInPinnedListMode, false, file, automaticDownload, message.effectivelyIncoming(context.account.peerId), false, associatedData.forcedResourceStatus, statusType, nil, CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height))
|
||||
let (_, refineLayout) = contentFileLayout(context, presentationData, message, associatedData, chatLocation, attributes, message.tags.contains(.pinned) && !associatedData.isInPinnedListMode && !isReplyThread, false, file, automaticDownload, message.effectivelyIncoming(context.account.peerId), false, associatedData.forcedResourceStatus, statusType, nil, CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height))
|
||||
refineContentFileLayout = refineLayout
|
||||
}
|
||||
} else if let image = media as? TelegramMediaImage {
|
||||
@ -572,7 +577,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
statusSizeAndApply = statusLayout(context, presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies, message.tags.contains(.pinned) && !associatedData.isInPinnedListMode)
|
||||
statusSizeAndApply = statusLayout(context, presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies, message.tags.contains(.pinned) && !associatedData.isInPinnedListMode && !isReplyThread)
|
||||
}
|
||||
default:
|
||||
break
|
||||
|
@ -1088,6 +1088,12 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
var needShareButton = false
|
||||
if case .pinnedMessages = item.associatedData.subject {
|
||||
needShareButton = true
|
||||
for media in item.message.media {
|
||||
if let _ = media as? TelegramMediaExpiredContent {
|
||||
needShareButton = false
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.effectiveTopId == item.message.id {
|
||||
needShareButton = false
|
||||
allowFullWidth = true
|
||||
@ -1287,6 +1293,10 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
}
|
||||
}
|
||||
|
||||
if case .replyThread = item.chatLocation {
|
||||
isItemPinned = false
|
||||
}
|
||||
|
||||
var mosaicStartIndex: Int?
|
||||
var mosaicRange: Range<Int>?
|
||||
for i in 0 ..< contentPropertiesAndPrepareLayouts.count {
|
||||
@ -1526,7 +1536,12 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
}
|
||||
}
|
||||
|
||||
mosaicStatusSizeAndApply = mosaicStatusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: 200.0, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode)
|
||||
var isReplyThread = false
|
||||
if case .replyThread = item.chatLocation {
|
||||
isReplyThread = true
|
||||
}
|
||||
|
||||
mosaicStatusSizeAndApply = mosaicStatusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: 200.0, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,7 +196,12 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
var statusApply: ((Bool) -> Void)?
|
||||
|
||||
if let statusType = statusType {
|
||||
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: constrainedSize.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode)
|
||||
var isReplyThread = false
|
||||
if case .replyThread = item.chatLocation {
|
||||
isReplyThread = true
|
||||
}
|
||||
|
||||
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: constrainedSize.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread)
|
||||
statusSize = size
|
||||
statusApply = apply
|
||||
}
|
||||
|
@ -278,7 +278,12 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
}
|
||||
}
|
||||
|
||||
let (videoLayout, videoApply) = makeVideoLayout(ChatMessageBubbleContentItem(context: item.context, controllerInteraction: item.controllerInteraction, message: item.message, read: item.read, chatLocation: item.chatLocation, presentationData: item.presentationData, associatedData: item.associatedData, attributes: item.content.firstMessageAttributes, isItemPinned: item.message.tags.contains(.pinned), isItemEdited: false), params.width - params.leftInset - params.rightInset - avatarInset, displaySize, .free, automaticDownload)
|
||||
var isReplyThread = false
|
||||
if case .replyThread = item.chatLocation {
|
||||
isReplyThread = true
|
||||
}
|
||||
|
||||
let (videoLayout, videoApply) = makeVideoLayout(ChatMessageBubbleContentItem(context: item.context, controllerInteraction: item.controllerInteraction, message: item.message, read: item.read, chatLocation: item.chatLocation, presentationData: item.presentationData, associatedData: item.associatedData, attributes: item.content.firstMessageAttributes, isItemPinned: item.message.tags.contains(.pinned) && !isReplyThread, isItemEdited: false), params.width - params.leftInset - params.rightInset - avatarInset, displaySize, .free, automaticDownload)
|
||||
|
||||
let videoFrame = CGRect(origin: CGPoint(x: (incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset + layoutConstants.bubble.contentInsets.left) : (params.width - params.rightInset - videoLayout.contentSize.width - layoutConstants.bubble.edgeInset - layoutConstants.bubble.contentInsets.left - deliveryFailedInset)), y: 0.0), size: videoLayout.contentSize)
|
||||
|
||||
|
@ -285,7 +285,13 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
} else {
|
||||
maxDateAndStatusWidth = width - videoFrame.midX - 85.0
|
||||
}
|
||||
let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.context, item.presentationData, edited && !sentViaBot, viewCount, dateText, statusType, CGSize(width: max(1.0, maxDateAndStatusWidth), height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode)
|
||||
|
||||
var isReplyThread = false
|
||||
if case .replyThread = item.chatLocation {
|
||||
isReplyThread = true
|
||||
}
|
||||
|
||||
let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.context, item.presentationData, edited && !sentViaBot, viewCount, dateText, statusType, CGSize(width: max(1.0, maxDateAndStatusWidth), height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread)
|
||||
|
||||
var contentSize = imageSize
|
||||
var dateAndStatusOverflow = false
|
||||
|
@ -486,9 +486,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
updatedFetchControls = FetchControls(fetch: { manual in
|
||||
if let strongSelf = self {
|
||||
if !manual {
|
||||
strongSelf.fetchDisposable.set(chatMessagePhotoInteractiveFetched(context: context, photoReference: .message(message: MessageReference(message), media: image), displayAtSize: 600, storeToDownloadsPeerType: storeToDownloadsPeerType).start())
|
||||
strongSelf.fetchDisposable.set(chatMessagePhotoInteractiveFetched(context: context, photoReference: .message(message: MessageReference(message), media: image), displayAtSize: isSecretMedia ? nil : 600, storeToDownloadsPeerType: storeToDownloadsPeerType).start())
|
||||
} else if let representation = largestRepresentationForPhoto(image) {
|
||||
strongSelf.fetchDisposable.set(messageMediaImageInteractiveFetched(context: context, message: message, image: image, resource: representation.resource, range: representationFetchRangeForDisplayAtSize(representation: representation, dimension: 600), storeToDownloadsPeerType: storeToDownloadsPeerType).start())
|
||||
strongSelf.fetchDisposable.set(messageMediaImageInteractiveFetched(context: context, message: message, image: image, resource: representation.resource, range: representationFetchRangeForDisplayAtSize(representation: representation, dimension: isSecretMedia ? nil : 600), storeToDownloadsPeerType: storeToDownloadsPeerType).start())
|
||||
}
|
||||
}
|
||||
}, cancel: {
|
||||
|
@ -246,7 +246,12 @@ class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
var statusApply: ((Bool) -> Void)?
|
||||
|
||||
if let statusType = statusType {
|
||||
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: constrainedSize.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode)
|
||||
var isReplyThread = false
|
||||
if case .replyThread = item.chatLocation {
|
||||
isReplyThread = true
|
||||
}
|
||||
|
||||
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: constrainedSize.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread)
|
||||
statusSize = size
|
||||
statusApply = apply
|
||||
}
|
||||
|
@ -231,7 +231,12 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
var statusApply: ((Bool) -> Void)?
|
||||
|
||||
if let statusType = statusType {
|
||||
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: imageSize.width - 30.0, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode)
|
||||
var isReplyThread = false
|
||||
if case .replyThread = item.chatLocation {
|
||||
isReplyThread = true
|
||||
}
|
||||
|
||||
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: imageSize.width - 30.0, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread)
|
||||
statusSize = size
|
||||
statusApply = apply
|
||||
}
|
||||
|
@ -1074,7 +1074,12 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
var statusApply: ((Bool) -> Void)?
|
||||
|
||||
if let statusType = statusType {
|
||||
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode)
|
||||
var isReplyThread = false
|
||||
if case .replyThread = item.chatLocation {
|
||||
isReplyThread = true
|
||||
}
|
||||
|
||||
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread)
|
||||
statusSize = size
|
||||
statusApply = apply
|
||||
}
|
||||
|
@ -105,7 +105,12 @@ class ChatMessageRestrictedBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
var statusApply: ((Bool) -> Void)?
|
||||
|
||||
if let statusType = statusType {
|
||||
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode)
|
||||
var isReplyThread = false
|
||||
if case .replyThread = item.chatLocation {
|
||||
isReplyThread = true
|
||||
}
|
||||
|
||||
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && isReplyThread)
|
||||
statusSize = size
|
||||
statusApply = apply
|
||||
}
|
||||
|
@ -384,7 +384,12 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
|
||||
let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .regular, reactionCount: dateReactionCount)
|
||||
|
||||
let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode)
|
||||
var isReplyThread = false
|
||||
if case .replyThread = item.chatLocation {
|
||||
isReplyThread = true
|
||||
}
|
||||
|
||||
let (dateAndStatusSize, dateAndStatusApply) = makeDateAndStatusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, CGSize(width: params.width, height: CGFloat.greatestFiniteMagnitude), dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread)
|
||||
|
||||
var viaBotApply: (TextNodeLayout, () -> TextNode)?
|
||||
var replyInfoApply: (CGSize, () -> ChatMessageReplyInfoNode)?
|
||||
|
@ -169,7 +169,12 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
var statusApply: ((Bool) -> Void)?
|
||||
|
||||
if let statusType = statusType {
|
||||
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode)
|
||||
var isReplyThread = false
|
||||
if case .replyThread = item.chatLocation {
|
||||
isReplyThread = true
|
||||
}
|
||||
|
||||
let (size, apply) = statusLayout(item.context, item.presentationData, edited, viewCount, dateText, statusType, textConstrainedSize, dateReactions, dateReplies, item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread)
|
||||
statusSize = size
|
||||
statusApply = apply
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
|
||||
if let textValue = webpage.text, !textValue.isEmpty {
|
||||
text = textValue
|
||||
var entityTypes: EnabledEntityTypes = [.url]
|
||||
var entityTypes: EnabledEntityTypes = [.allUrl]
|
||||
switch type {
|
||||
case .twitter, .instagram:
|
||||
entityTypes.insert(.mention)
|
||||
|
@ -325,27 +325,22 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor)))
|
||||
}
|
||||
|
||||
for media in message.media {
|
||||
if let image = media as? TelegramMediaImage {
|
||||
updatedMediaReference = .message(message: MessageReference(message), media: image)
|
||||
if let representation = largestRepresentationForPhoto(image) {
|
||||
imageDimensions = representation.dimensions.cgSize
|
||||
if !message.containsSecretMedia {
|
||||
for media in message.media {
|
||||
if let image = media as? TelegramMediaImage {
|
||||
updatedMediaReference = .message(message: MessageReference(message), media: image)
|
||||
if let representation = largestRepresentationForPhoto(image) {
|
||||
imageDimensions = representation.dimensions.cgSize
|
||||
}
|
||||
break
|
||||
} else if let file = media as? TelegramMediaFile {
|
||||
updatedMediaReference = .message(message: MessageReference(message), media: file)
|
||||
if !file.isInstantVideo, let representation = largestImageRepresentation(file.previewRepresentations), !file.isSticker {
|
||||
imageDimensions = representation.dimensions.cgSize
|
||||
}
|
||||
break
|
||||
}
|
||||
break
|
||||
} else if let file = media as? TelegramMediaFile {
|
||||
updatedMediaReference = .message(message: MessageReference(message), media: file)
|
||||
if !file.isInstantVideo, let representation = largestImageRepresentation(file.previewRepresentations), !file.isSticker {
|
||||
imageDimensions = representation.dimensions.cgSize
|
||||
}
|
||||
break
|
||||
}/* else if let poll = media as? TelegramMediaPoll {
|
||||
switch poll.kind {
|
||||
case .poll:
|
||||
titleString = strings.Conversation_PinnedPoll
|
||||
case .quiz:
|
||||
titleString = strings.Conversation_PinnedQuiz
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
if isReplyThread {
|
||||
|
@ -621,7 +621,8 @@ private final class PeerInfoInteraction {
|
||||
}
|
||||
}
|
||||
|
||||
private let enabledBioEntities: EnabledEntityTypes = [.url, .mention, .hashtag]
|
||||
private let enabledPublicBioEntities: EnabledEntityTypes = [.allUrl, .mention, .hashtag]
|
||||
private let enabledPrivateBioEntities: EnabledEntityTypes = [.internalUrl, .mention, .hashtag]
|
||||
|
||||
private enum SettingsSection: Int, CaseIterable {
|
||||
case edit
|
||||
@ -927,11 +928,11 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
}
|
||||
if let cachedData = data.cachedData as? CachedUserData {
|
||||
if user.isScam {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: user.botInfo != nil ? presentationData.strings.UserInfo_ScamBotWarning : presentationData.strings.UserInfo_ScamUserWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.botInfo != nil ? enabledBioEntities : []), action: nil, requestLayout: {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: user.botInfo != nil ? presentationData.strings.UserInfo_ScamBotWarning : presentationData.strings.UserInfo_ScamUserWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.botInfo != nil ? enabledPrivateBioEntities : []), action: nil, requestLayout: {
|
||||
interaction.requestLayout()
|
||||
}))
|
||||
} else if let about = cachedData.about, !about.isEmpty {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: about, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: about, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPrivateBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
||||
interaction.requestLayout()
|
||||
}))
|
||||
}
|
||||
@ -1015,11 +1016,11 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
}
|
||||
if let cachedData = data.cachedData as? CachedChannelData {
|
||||
if channel.isScam {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: presentationData.strings.Channel_AboutItem, text: presentationData.strings.GroupInfo_ScamGroupWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledBioEntities), action: nil, requestLayout: {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: presentationData.strings.Channel_AboutItem, text: presentationData.strings.GroupInfo_ScamGroupWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPublicBioEntities), action: nil, requestLayout: {
|
||||
interaction.requestLayout()
|
||||
}))
|
||||
} else if let about = cachedData.about, !about.isEmpty {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: presentationData.strings.Channel_AboutItem, text: about, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: presentationData.strings.Channel_AboutItem, text: about, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPublicBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
||||
interaction.requestLayout()
|
||||
}))
|
||||
}
|
||||
@ -1051,11 +1052,11 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
} else if let group = data.peer as? TelegramGroup {
|
||||
if let cachedData = data.cachedData as? CachedGroupData {
|
||||
if group.isScam {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: presentationData.strings.PeerInfo_GroupAboutItem, text: presentationData.strings.GroupInfo_ScamGroupWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledBioEntities), action: nil, requestLayout: {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: presentationData.strings.PeerInfo_GroupAboutItem, text: presentationData.strings.GroupInfo_ScamGroupWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPublicBioEntities), action: nil, requestLayout: {
|
||||
interaction.requestLayout()
|
||||
}))
|
||||
} else if let about = cachedData.about, !about.isEmpty {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: presentationData.strings.PeerInfo_GroupAboutItem, text: about, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
||||
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: presentationData.strings.PeerInfo_GroupAboutItem, text: about, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPublicBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
|
||||
interaction.requestLayout()
|
||||
}))
|
||||
}
|
||||
|
@ -85,12 +85,13 @@ public struct EnabledEntityTypes: OptionSet {
|
||||
public static let command = EnabledEntityTypes(rawValue: 1 << 0)
|
||||
public static let mention = EnabledEntityTypes(rawValue: 1 << 1)
|
||||
public static let hashtag = EnabledEntityTypes(rawValue: 1 << 2)
|
||||
public static let url = EnabledEntityTypes(rawValue: 1 << 3)
|
||||
public static let allUrl = EnabledEntityTypes(rawValue: 1 << 3)
|
||||
public static let phoneNumber = EnabledEntityTypes(rawValue: 1 << 4)
|
||||
public static let timecode = EnabledEntityTypes(rawValue: 1 << 5)
|
||||
public static let external = EnabledEntityTypes(rawValue: 1 << 6)
|
||||
public static let internalUrl = EnabledEntityTypes(rawValue: 1 << 7)
|
||||
|
||||
public static let all: EnabledEntityTypes = [.command, .mention, .hashtag, .url, .phoneNumber]
|
||||
public static let all: EnabledEntityTypes = [.command, .mention, .hashtag, .allUrl, .phoneNumber]
|
||||
}
|
||||
|
||||
private func commitEntity(_ utf16: String.UTF16View, _ type: CurrentEntityType, _ range: Range<String.UTF16View.Index>, _ enabledTypes: EnabledEntityTypes, _ entities: inout [MessageTextEntity], mediaDuration: Double? = nil) {
|
||||
@ -160,11 +161,11 @@ public func generateTextEntities(_ text: String, enabledTypes: EnabledEntityType
|
||||
let utf16 = text.utf16
|
||||
|
||||
var detector: NSDataDetector?
|
||||
if enabledTypes.contains(.phoneNumber) && enabledTypes.contains(.url) {
|
||||
if enabledTypes.contains(.phoneNumber) && (enabledTypes.contains(.allUrl) || enabledTypes.contains(.internalUrl)) {
|
||||
detector = dataAndPhoneNumberDetector
|
||||
} else if enabledTypes.contains(.phoneNumber) {
|
||||
detector = phoneNumberDetector
|
||||
} else if enabledTypes.contains(.url) {
|
||||
} else if enabledTypes.contains(.allUrl) || enabledTypes.contains(.internalUrl) {
|
||||
detector = dataDetector
|
||||
}
|
||||
|
||||
@ -179,6 +180,21 @@ public func generateTextEntities(_ text: String, enabledTypes: EnabledEntityType
|
||||
if let lowerBound = lowerBound, let upperBound = upperBound {
|
||||
let type: MessageTextEntityType
|
||||
if result.resultType == NSTextCheckingResult.CheckingType.link {
|
||||
if !enabledTypes.contains(.allUrl) && enabledTypes.contains(.internalUrl) {
|
||||
guard let url = result.url else {
|
||||
return
|
||||
}
|
||||
if url.scheme != "tg" {
|
||||
guard let host = url.host?.lowercased() else {
|
||||
return
|
||||
}
|
||||
if host == "telegram.org" || host == "t.me" {
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type = .Url
|
||||
} else {
|
||||
type = .PhoneNumber
|
||||
|
Loading…
x
Reference in New Issue
Block a user