mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Merge commit '010945d371'
This commit is contained in:
commit
3920eb1507
22 changed files with 326 additions and 141 deletions
|
|
@ -155,6 +155,9 @@ public func peerMessageMediaPlayerType(_ message: EngineMessage) -> MediaManager
|
|||
} else if let media = media as? TelegramMediaWebpage, case let .Loaded(content) = media.content, let f = content.file {
|
||||
file = f
|
||||
break
|
||||
} else if let media = media as? TelegramMediaPoll, let f = media.attachedMedia as? TelegramMediaFile {
|
||||
file = f
|
||||
break
|
||||
}
|
||||
}
|
||||
return file
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ public func chatMessageGalleryControllerData(context: AccountContext, chatLocati
|
|||
if let poll = media as? TelegramMediaPoll, let attachedMedia = poll.attachedMedia {
|
||||
if attachedMedia is TelegramMediaImage {
|
||||
galleryMedia = attachedMedia
|
||||
} else if let file = attachedMedia as? TelegramMediaFile, file.isVideo {
|
||||
} else if let file = attachedMedia as? TelegramMediaFile, file.isVideo || file.isMusic {
|
||||
galleryMedia = attachedMedia
|
||||
} else if attachedMedia is TelegramMediaMap {
|
||||
galleryMedia = attachedMedia
|
||||
|
|
|
|||
|
|
@ -409,7 +409,12 @@ public struct LegacyAssetPickerEnqueueMessage {
|
|||
public var isFile: Bool
|
||||
}
|
||||
|
||||
public func legacyAssetPickerEnqueueMessages(context: AccountContext, account: Account, signals: [Any], originalMediaReference: AnyMediaReference? = nil) -> Signal<[LegacyAssetPickerEnqueueMessage], Void> {
|
||||
public func legacyAssetPickerEnqueueMessages(
|
||||
context: AccountContext,
|
||||
account: Account,
|
||||
signals: [Any],
|
||||
originalMediaReference: AnyMediaReference? = nil
|
||||
) -> Signal<[LegacyAssetPickerEnqueueMessage], Void> {
|
||||
return Signal { subscriber in
|
||||
let disposable = SSignal.combineSignals(signals).start(next: { anyValues in
|
||||
var messages: [LegacyAssetPickerEnqueueMessage] = []
|
||||
|
|
|
|||
|
|
@ -395,6 +395,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
|||
private var layoutParams: ListViewItemLayoutParams?
|
||||
private var contentSizeValue: CGSize?
|
||||
private var currentLeftOffset: CGFloat = 0.0
|
||||
private var currentRightOffset: CGFloat = 0.0
|
||||
|
||||
var reorderControlNode: ItemListEditableReorderControlNode?
|
||||
|
||||
|
|
@ -637,11 +638,17 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
|||
let rightInset: CGFloat = 8.0 + params.rightInset
|
||||
|
||||
var leftOffset: CGFloat = 0.0
|
||||
var rightOffset: CGFloat = 0.0
|
||||
var selectionNodeWidthAndApply: (CGFloat, (CGSize, Bool) -> ItemListSelectableControlNode)?
|
||||
if case let .selectable(selected, num) = item.selection {
|
||||
let (selectionWidth, selectionApply) = selectionNodeLayout(item.presentationData.theme.theme.list.itemCheckColors.strokeColor, item.presentationData.theme.theme.list.itemCheckColors.fillColor, item.presentationData.theme.theme.list.itemCheckColors.foregroundColor, selected, .regular, num.flatMap { $0 + 1 })
|
||||
selectionNodeWidthAndApply = (selectionWidth, selectionApply)
|
||||
leftOffset += selectionWidth
|
||||
switch item.selectionSide {
|
||||
case .left:
|
||||
leftOffset += selectionWidth
|
||||
case .right:
|
||||
rightOffset += selectionWidth
|
||||
}
|
||||
}
|
||||
|
||||
var extensionIconImage: UIImage?
|
||||
|
|
@ -1023,17 +1030,19 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
|||
reorderInset = sizeAndApply.0
|
||||
}
|
||||
|
||||
let contentRightInset = rightInset + rightOffset + reorderInset
|
||||
|
||||
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||
let dateText = stringForRelativeTimestamp(strings: item.presentationData.strings, relativeTimestamp: item.message?.timestamp ?? 0, relativeTo: timestamp, dateTimeFormat: item.presentationData.dateTimeFormat)
|
||||
let dateAttributedString = NSAttributedString(string: dateText, font: dateFont, textColor: item.presentationData.theme.theme.list.itemSecondaryTextColor)
|
||||
|
||||
let (dateNodeLayout, dateNodeApply) = dateNodeMakeLayout(TextNodeLayoutArguments(attributedString: dateAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 12.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (dateNodeLayout, dateNodeApply) = dateNodeMakeLayout(TextNodeLayoutArguments(attributedString: dateAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - leftOffset - contentRightInset - 12.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (titleNodeLayout, titleNodeApply) = titleNodeMakeLayout(item.context, params.width - leftInset - leftOffset - rightInset - dateNodeLayout.size.width - 4.0 - reorderInset, item.presentationData.theme.theme, titleText, titleExtraData)
|
||||
let (titleNodeLayout, titleNodeApply) = titleNodeMakeLayout(item.context, params.width - leftInset - leftOffset - contentRightInset - dateNodeLayout.size.width - 4.0, item.presentationData.theme.theme, titleText, titleExtraData)
|
||||
|
||||
let (textNodeLayout, textNodeApply) = textNodeMakeLayout(TextNodeLayoutArguments(attributedString: captionText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 30.0 - reorderInset, height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (textNodeLayout, textNodeApply) = textNodeMakeLayout(TextNodeLayoutArguments(attributedString: captionText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - leftOffset - contentRightInset - 30.0, height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (descriptionNodeLayout, descriptionNodeApply) = descriptionNodeMakeLayout(item.context, params.width - leftInset - rightInset - 30.0 - reorderInset, item.presentationData.theme.theme, descriptionText, descriptionExtraData)
|
||||
let (descriptionNodeLayout, descriptionNodeApply) = descriptionNodeMakeLayout(item.context, params.width - leftInset - leftOffset - contentRightInset - 30.0, item.presentationData.theme.theme, descriptionText, descriptionExtraData)
|
||||
|
||||
var (extensionTextLayout, extensionTextApply) = extensionIconTextMakeLayout(TextNodeLayoutArguments(attributedString: extensionText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 38.0, height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
if extensionTextLayout.truncated, let text = extensionText?.string {
|
||||
|
|
@ -1141,6 +1150,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
|||
strongSelf.layoutParams = params
|
||||
strongSelf.contentSizeValue = nodeLayout.contentSize
|
||||
strongSelf.currentLeftOffset = leftOffset
|
||||
strongSelf.currentRightOffset = rightOffset + reorderInset
|
||||
|
||||
if let _ = updatedTheme {
|
||||
if item.displayBackground || item.canReorder {
|
||||
|
|
@ -1165,21 +1175,37 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
|||
}
|
||||
|
||||
if let (selectionWidth, selectionApply) = selectionNodeWidthAndApply {
|
||||
let selectionFrame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: selectionWidth, height: nodeLayout.contentSize.height))
|
||||
let selectionFrame: CGRect
|
||||
let sidePosition: CGPoint
|
||||
switch item.selectionSide {
|
||||
case .left:
|
||||
selectionFrame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: selectionWidth, height: nodeLayout.contentSize.height))
|
||||
sidePosition = CGPoint(x: -selectionFrame.size.width / 2.0, y: selectionFrame.midY)
|
||||
case .right:
|
||||
selectionFrame = CGRect(origin: CGPoint(x: params.width - params.rightInset - reorderInset - selectionWidth - 7.0, y: 0.0), size: CGSize(width: selectionWidth, height: nodeLayout.contentSize.height))
|
||||
sidePosition = CGPoint(x: params.width + selectionFrame.size.width / 2.0, y: selectionFrame.midY)
|
||||
}
|
||||
let selectionNode = selectionApply(selectionFrame.size, transition.isAnimated)
|
||||
if selectionNode !== strongSelf.selectionNode {
|
||||
strongSelf.selectionNode?.removeFromSupernode()
|
||||
strongSelf.selectionNode = selectionNode
|
||||
strongSelf.contextSourceNode.contentNode.addSubnode(selectionNode)
|
||||
selectionNode.frame = selectionFrame
|
||||
transition.animatePosition(node: selectionNode, from: CGPoint(x: -selectionFrame.size.width / 2.0, y: selectionFrame.midY))
|
||||
transition.animatePosition(node: selectionNode, from: sidePosition)
|
||||
} else {
|
||||
transition.updateFrame(node: selectionNode, frame: selectionFrame)
|
||||
}
|
||||
} else if let selectionNode = strongSelf.selectionNode {
|
||||
strongSelf.selectionNode = nil
|
||||
let selectionFrame = selectionNode.frame
|
||||
transition.updatePosition(node: selectionNode, position: CGPoint(x: -selectionFrame.size.width / 2.0, y: selectionFrame.midY), completion: { [weak selectionNode] _ in
|
||||
let sidePosition: CGPoint
|
||||
switch item.selectionSide {
|
||||
case .left:
|
||||
sidePosition = CGPoint(x: -selectionFrame.size.width / 2.0, y: selectionFrame.midY)
|
||||
case .right:
|
||||
sidePosition = CGPoint(x: params.width + selectionFrame.size.width / 2.0, y: selectionFrame.midY)
|
||||
}
|
||||
transition.updatePosition(node: selectionNode, position: sidePosition, completion: { [weak selectionNode] _ in
|
||||
selectionNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
|
|
@ -1202,7 +1228,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
|||
})
|
||||
}
|
||||
|
||||
transition.updateFrame(node: strongSelf.separatorNode, frame: CGRect(origin: CGPoint(x: leftInset + leftOffset, y: nodeLayout.contentSize.height - UIScreenPixel), size: CGSize(width: params.width - leftInset - leftOffset - separatorRightInset - params.rightInset, height: UIScreenPixel)))
|
||||
transition.updateFrame(node: strongSelf.separatorNode, frame: CGRect(origin: CGPoint(x: leftInset + leftOffset, y: nodeLayout.contentSize.height - UIScreenPixel), size: CGSize(width: params.width - leftInset - leftOffset - separatorRightInset - params.rightInset - rightOffset, height: UIScreenPixel)))
|
||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -nodeLayout.insets.top - UIScreenPixel), size: CGSize(width: params.width, height: nodeLayout.size.height + UIScreenPixel - nodeLayout.insets.bottom))
|
||||
|
||||
if let backgroundNode = strongSelf.backgroundNode {
|
||||
|
|
@ -1264,7 +1290,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
|||
let _ = descriptionNodeApply()
|
||||
|
||||
let _ = dateNodeApply()
|
||||
transition.updateFrame(node: strongSelf.dateNode, frame: CGRect(origin: CGPoint(x: params.width - rightInset - dateNodeLayout.size.width, y: 11.0), size: dateNodeLayout.size))
|
||||
transition.updateFrame(node: strongSelf.dateNode, frame: CGRect(origin: CGPoint(x: params.width - contentRightInset - dateNodeLayout.size.width, y: 11.0), size: dateNodeLayout.size))
|
||||
strongSelf.dateNode.isHidden = !item.isGlobalSearchResult
|
||||
|
||||
let iconSize = CGSize(width: 40.0, height: 40.0)
|
||||
|
|
@ -1545,7 +1571,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
|||
|
||||
switch maybeFetchStatus {
|
||||
case .Fetching(_, let progress), .Paused(let progress):
|
||||
let progressFrame = CGRect(x: self.currentLeftOffset + leftInset + 65.0, y: size.height - 3.0, width: floorToScreenPixels((size.width - 65.0 - leftInset - rightInset)), height: 3.0)
|
||||
let progressFrame = CGRect(x: self.currentLeftOffset + leftInset + 65.0, y: size.height - 3.0, width: floorToScreenPixels((size.width - 65.0 - leftInset - rightInset - self.currentLeftOffset - self.currentRightOffset)), height: 3.0)
|
||||
let linearProgressNode: LinearProgressNode
|
||||
if let current = self.linearProgressNode {
|
||||
linearProgressNode = current
|
||||
|
|
|
|||
|
|
@ -56,6 +56,11 @@ public final class ListMessageItemInteraction {
|
|||
})
|
||||
}
|
||||
|
||||
public enum ListMessageItemSelectionSide {
|
||||
case left
|
||||
case right
|
||||
}
|
||||
|
||||
public final class ListMessageItem: ListViewItem, ItemListItem {
|
||||
let presentationData: ChatPresentationData
|
||||
let systemStyle: ItemListSystemStyle
|
||||
|
|
@ -65,6 +70,7 @@ public final class ListMessageItem: ListViewItem, ItemListItem {
|
|||
let message: Message?
|
||||
let translateToLanguage: String?
|
||||
public let selection: ChatHistoryMessageSelection
|
||||
public let selectionSide: ListMessageItemSelectionSide
|
||||
let hintIsLink: Bool
|
||||
let isGlobalSearchResult: Bool
|
||||
let isDownloadList: Bool
|
||||
|
|
@ -91,6 +97,7 @@ public final class ListMessageItem: ListViewItem, ItemListItem {
|
|||
message: Message?,
|
||||
translateToLanguage: String? = nil,
|
||||
selection: ChatHistoryMessageSelection,
|
||||
selectionSide: ListMessageItemSelectionSide = .right,
|
||||
displayHeader: Bool,
|
||||
customHeader: ListViewItemHeader? = nil,
|
||||
hintIsLink: Bool = false,
|
||||
|
|
@ -120,6 +127,7 @@ public final class ListMessageItem: ListViewItem, ItemListItem {
|
|||
self.header = nil
|
||||
}
|
||||
self.selection = selection
|
||||
self.selectionSide = selectionSide
|
||||
self.hintIsLink = hintIsLink
|
||||
self.isGlobalSearchResult = isGlobalSearchResult
|
||||
self.isDownloadList = isDownloadList
|
||||
|
|
|
|||
|
|
@ -266,12 +266,19 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
|
|||
let leftInset: CGFloat = 65.0 + params.leftInset
|
||||
|
||||
var leftOffset: CGFloat = 0.0
|
||||
var rightOffset: CGFloat = 0.0
|
||||
var selectionNodeWidthAndApply: (CGFloat, (CGSize, Bool) -> ItemListSelectableControlNode)?
|
||||
if case let .selectable(selected, num) = item.selection {
|
||||
let (selectionWidth, selectionApply) = selectionNodeLayout(item.presentationData.theme.theme.list.itemCheckColors.strokeColor, item.presentationData.theme.theme.list.itemCheckColors.fillColor, item.presentationData.theme.theme.list.itemCheckColors.foregroundColor, selected, .regular, num.flatMap { $0 + 1 })
|
||||
selectionNodeWidthAndApply = (selectionWidth, selectionApply)
|
||||
leftOffset += selectionWidth
|
||||
switch item.selectionSide {
|
||||
case .left:
|
||||
leftOffset += selectionWidth
|
||||
case .right:
|
||||
rightOffset += selectionWidth
|
||||
}
|
||||
}
|
||||
let contentRightInset = params.rightInset + rightOffset
|
||||
|
||||
var title: NSAttributedString?
|
||||
var descriptionText: NSAttributedString?
|
||||
|
|
@ -628,13 +635,13 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
|
|||
let dateText = stringForRelativeTimestamp(strings: item.presentationData.strings, relativeTimestamp: item.message?.timestamp ?? 0, relativeTo: timestamp, dateTimeFormat: item.presentationData.dateTimeFormat)
|
||||
let dateAttributedString = NSAttributedString(string: dateText, font: dateFont, textColor: item.presentationData.theme.theme.list.itemSecondaryTextColor)
|
||||
|
||||
let (dateNodeLayout, dateNodeApply) = dateNodeMakeLayout(TextNodeLayoutArguments(attributedString: dateAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - params.rightInset - 12.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (dateNodeLayout, dateNodeApply) = dateNodeMakeLayout(TextNodeLayoutArguments(attributedString: dateAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - leftOffset - contentRightInset - 12.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (titleNodeLayout, titleNodeApply) = titleNodeMakeLayout(TextNodeLayoutArguments(attributedString: title, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .middle, constrainedSize: CGSize(width: params.width - leftInset - leftOffset - 8.0 - params.rightInset - 16.0 - dateNodeLayout.size.width, height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (titleNodeLayout, titleNodeApply) = titleNodeMakeLayout(TextNodeLayoutArguments(attributedString: title, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .middle, constrainedSize: CGSize(width: params.width - leftInset - leftOffset - 8.0 - contentRightInset - 16.0 - dateNodeLayout.size.width, height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (descriptionNodeLayout, descriptionNodeApply) = descriptionNodeMakeLayout(TextNodeLayoutArguments(attributedString: descriptionText, backgroundColor: nil, maximumNumberOfLines: descriptionMaxNumberOfLines, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - params.rightInset - 16.0 - 8.0, height: CGFloat.infinity), alignment: .natural, lineSpacing: 0.3, cutout: nil, insets: UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0)))
|
||||
let (descriptionNodeLayout, descriptionNodeApply) = descriptionNodeMakeLayout(TextNodeLayoutArguments(attributedString: descriptionText, backgroundColor: nil, maximumNumberOfLines: descriptionMaxNumberOfLines, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - leftOffset - 8.0 - contentRightInset - 16.0 - 8.0, height: CGFloat.infinity), alignment: .natural, lineSpacing: 0.3, cutout: nil, insets: UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0)))
|
||||
|
||||
let (linkNodeLayout, linkNodeApply) = linkNodeMakeLayout(TextNodeLayoutArguments(attributedString: linkText, backgroundColor: nil, maximumNumberOfLines: 4, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 8.0 - params.rightInset - 16.0 - 8.0, height: CGFloat.infinity), alignment: .natural, lineSpacing: 0.3, cutout: isInstantView ? TextNodeCutout(topLeft: CGSize(width: 14.0, height: 8.0)) : nil, insets: UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0)))
|
||||
let (linkNodeLayout, linkNodeApply) = linkNodeMakeLayout(TextNodeLayoutArguments(attributedString: linkText, backgroundColor: nil, maximumNumberOfLines: 4, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - leftOffset - 8.0 - contentRightInset - 16.0 - 8.0, height: CGFloat.infinity), alignment: .natural, lineSpacing: 0.3, cutout: isInstantView ? TextNodeCutout(topLeft: CGSize(width: 14.0, height: 8.0)) : nil, insets: UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0)))
|
||||
var instantViewImage: UIImage?
|
||||
if isInstantView {
|
||||
instantViewImage = PresentationResourcesChat.sharedMediaInstantViewIcon(item.presentationData.theme.theme)
|
||||
|
|
@ -665,7 +672,7 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
|
|||
}
|
||||
|
||||
let authorText = NSAttributedString(string: authorString, font: authorFont, textColor: item.presentationData.theme.theme.list.itemSecondaryTextColor)
|
||||
let (authorNodeLayout, authorNodeApply) = authorNodeMakeLayout(item.context, params.width - leftInset - params.rightInset - 30.0, item.presentationData.theme.theme, authorText, forumThreadTitle)
|
||||
let (authorNodeLayout, authorNodeApply) = authorNodeMakeLayout(item.context, params.width - leftInset - leftOffset - contentRightInset - 30.0, item.presentationData.theme.theme, authorText, forumThreadTitle)
|
||||
|
||||
var contentHeight = 9.0 + titleNodeLayout.size.height + 10.0 + descriptionNodeLayout.size.height + linkNodeLayout.size.height
|
||||
if !authorString.isEmpty {
|
||||
|
|
@ -715,26 +722,42 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
|
|||
}
|
||||
|
||||
if let (selectionWidth, selectionApply) = selectionNodeWidthAndApply {
|
||||
let selectionFrame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: selectionWidth, height: contentHeight))
|
||||
let selectionFrame: CGRect
|
||||
let sidePosition: CGPoint
|
||||
switch item.selectionSide {
|
||||
case .left:
|
||||
selectionFrame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: selectionWidth, height: contentHeight))
|
||||
sidePosition = CGPoint(x: -selectionFrame.size.width / 2.0, y: selectionFrame.midY)
|
||||
case .right:
|
||||
selectionFrame = CGRect(origin: CGPoint(x: params.width - params.rightInset - selectionWidth - 7.0, y: 0.0), size: CGSize(width: selectionWidth, height: contentHeight))
|
||||
sidePosition = CGPoint(x: params.width + selectionFrame.size.width / 2.0, y: selectionFrame.midY)
|
||||
}
|
||||
let selectionNode = selectionApply(selectionFrame.size, transition.isAnimated)
|
||||
if selectionNode !== strongSelf.selectionNode {
|
||||
strongSelf.selectionNode?.removeFromSupernode()
|
||||
strongSelf.selectionNode = selectionNode
|
||||
strongSelf.addSubnode(selectionNode)
|
||||
selectionNode.frame = selectionFrame
|
||||
transition.animatePosition(node: selectionNode, from: CGPoint(x: -selectionFrame.size.width / 2.0, y: selectionFrame.midY))
|
||||
transition.animatePosition(node: selectionNode, from: sidePosition)
|
||||
} else {
|
||||
transition.updateFrame(node: selectionNode, frame: selectionFrame)
|
||||
}
|
||||
} else if let selectionNode = strongSelf.selectionNode {
|
||||
strongSelf.selectionNode = nil
|
||||
let selectionFrame = selectionNode.frame
|
||||
transition.updatePosition(node: selectionNode, position: CGPoint(x: -selectionFrame.size.width / 2.0, y: selectionFrame.midY), completion: { [weak selectionNode] _ in
|
||||
let sidePosition: CGPoint
|
||||
switch item.selectionSide {
|
||||
case .left:
|
||||
sidePosition = CGPoint(x: -selectionFrame.size.width / 2.0, y: selectionFrame.midY)
|
||||
case .right:
|
||||
sidePosition = CGPoint(x: params.width + selectionFrame.size.width / 2.0, y: selectionFrame.midY)
|
||||
}
|
||||
transition.updatePosition(node: selectionNode, position: sidePosition, completion: { [weak selectionNode] _ in
|
||||
selectionNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
|
||||
transition.updateFrame(node: strongSelf.separatorNode, frame: CGRect(origin: CGPoint(x: leftOffset + leftInset, y: contentHeight - UIScreenPixel), size: CGSize(width: params.width - leftInset - leftOffset, height: UIScreenPixel)))
|
||||
transition.updateFrame(node: strongSelf.separatorNode, frame: CGRect(origin: CGPoint(x: leftOffset + leftInset, y: contentHeight - UIScreenPixel), size: CGSize(width: params.width - leftInset - leftOffset - rightOffset, height: UIScreenPixel)))
|
||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: contentHeight + UIScreenPixel))
|
||||
|
||||
transition.updateFrame(node: strongSelf.titleNode, frame: CGRect(origin: CGPoint(x: leftOffset + leftInset, y: 9.0), size: titleNodeLayout.size))
|
||||
|
|
@ -745,7 +768,7 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
|
|||
let _ = descriptionNodeApply()
|
||||
|
||||
let _ = dateNodeApply()
|
||||
transition.updateFrame(node: strongSelf.dateNode, frame: CGRect(origin: CGPoint(x: params.width - params.rightInset - dateNodeLayout.size.width - 8.0, y: 11.0), size: dateNodeLayout.size))
|
||||
transition.updateFrame(node: strongSelf.dateNode, frame: CGRect(origin: CGPoint(x: params.width - contentRightInset - dateNodeLayout.size.width - 8.0, y: 11.0), size: dateNodeLayout.size))
|
||||
strongSelf.dateNode.isHidden = !item.isGlobalSearchResult
|
||||
|
||||
let linkFrame = CGRect(origin: CGPoint(x: leftOffset + leftInset - 1.0, y: descriptionFrame.maxY), size: linkNodeLayout.size)
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
|||
case cover
|
||||
case createSticker
|
||||
case createAvatar
|
||||
case poll(PollMode)
|
||||
case poll(mode: PollMode, asFile: Bool)
|
||||
}
|
||||
|
||||
case assets(PHAssetCollection?, AssetsMode)
|
||||
|
|
@ -2049,7 +2049,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
|||
self.titleView.title = presentationData.strings.MediaPicker_AddImage
|
||||
case .cover:
|
||||
self.titleView.title = presentationData.strings.MediaPicker_ChooseCover
|
||||
case let .poll(pollMode):
|
||||
case let .poll(pollMode, _):
|
||||
self.titleView.title = presentationData.strings.MediaPicker_Recents
|
||||
//TODO:localize
|
||||
switch pollMode {
|
||||
|
|
@ -2088,6 +2088,9 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
|
|||
|
||||
super.init(navigationBarPresentationData: navigationBarPresentationData)
|
||||
|
||||
if case .glass = style {
|
||||
self._hasGlassStyle = true
|
||||
}
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
|
||||
selectionContext.attemptSelectingItem = { [weak self] item in
|
||||
|
|
|
|||
|
|
@ -238,6 +238,11 @@ public final class TelegramMediaPoll: Media, Equatable {
|
|||
peerIds.append(contentsOf: voter.recentVoters)
|
||||
}
|
||||
}
|
||||
for option in options {
|
||||
if let addedBy = option.addedBy {
|
||||
peerIds.append(addedBy)
|
||||
}
|
||||
}
|
||||
return peerIds
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -267,7 +267,7 @@ public func customizeDefaultDarkPresentationTheme(theme: PresentationTheme, edit
|
|||
fileTitleColor: outgoingPrimaryTextColor,
|
||||
fileDescriptionColor: outgoingSecondaryTextColor,
|
||||
fileDurationColor: outgoingSecondaryTextColor,
|
||||
polls: chat.message.outgoing.polls.withUpdated(radioButton: outgoingPrimaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor?.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor)
|
||||
polls: chat.message.outgoing.polls.withUpdated(radioButton: outgoingSecondaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor?.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor)
|
||||
),
|
||||
freeform: chat.message.freeform.withUpdated(
|
||||
withWallpaper: chat.message.freeform.withWallpaper.withUpdated(
|
||||
|
|
@ -592,8 +592,8 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
|
|||
reactionInactiveMediaPlaceholder: UIColor(rgb: 0x000000, alpha: 0.1),
|
||||
reactionActiveMediaPlaceholder: UIColor(rgb: 0x000000, alpha: 0.1)
|
||||
)
|
||||
), primaryTextColor: UIColor(rgb: 0xffffff), secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), linkTextColor: UIColor(rgb: 0xffffff), linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.5), scamColor: UIColor(rgb: 0xeb5545), textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: UIColor(rgb: 0xffffff), accentControlColor: UIColor(rgb: 0xffffff), accentControlDisabledColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaActiveControlColor: UIColor(rgb: 0xffffff), mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaControlInnerBackgroundColor: UIColor(rgb: 0x313131), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: UIColor(rgb: 0xffffff), fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0xffffff, alpha: 0.2), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xffffff), radioProgress: UIColor(rgb: 0xffffff), highlight: UIColor(rgb: 0xffffff).withAlphaComponent(0.12), separator: UIColor(rgb: 0xffffff, alpha: 0.5), bar: UIColor(rgb: 0xffffff), barIconForeground: .clear, barPositive: UIColor(rgb: 0xffffff), barNegative: UIColor(rgb: 0xffffff)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: UIColor(rgb: 0xffffff, alpha: 0.2), textSelectionKnobColor: UIColor(rgb: 0xffffff)
|
||||
),
|
||||
), primaryTextColor: UIColor(rgb: 0xffffff), secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), linkTextColor: UIColor(rgb: 0xffffff), linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.5), scamColor: UIColor(rgb: 0xeb5545), textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: UIColor(rgb: 0xffffff), accentControlColor: UIColor(rgb: 0xffffff), accentControlDisabledColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaActiveControlColor: UIColor(rgb: 0xffffff), mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaControlInnerBackgroundColor: UIColor(rgb: 0x313131), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: UIColor(rgb: 0xffffff), fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0xffffff, alpha: 0.2), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xffffff, alpha: 0.5), radioProgress: UIColor(rgb: 0xffffff), highlight: UIColor(rgb: 0xffffff).withAlphaComponent(0.12), separator: UIColor(rgb: 0xffffff, alpha: 0.5), bar: UIColor(rgb: 0xffffff), barIconForeground: .clear, barPositive: UIColor(rgb: 0xffffff), barNegative: UIColor(rgb: 0xffffff)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: UIColor(rgb: 0xffffff, alpha: 0.2), textSelectionKnobColor: UIColor(rgb: 0xffffff)
|
||||
),
|
||||
freeform: PresentationThemeBubbleColor(
|
||||
withWallpaper: PresentationThemeBubbleColorComponents(
|
||||
fill: [UIColor(rgb: 0x1f1f1f)],
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ public struct PresentationResourcesItemList {
|
|||
return theme.image(PresentationResourceKey.itemListReorderIndicatorIcon.rawValue, { theme in
|
||||
return generateImage(CGSize(width: 17.0, height: 14.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(theme.list.itemBlocksSeparatorColor.cgColor)
|
||||
context.setFillColor(theme.list.itemPlaceholderTextColor.cgColor)
|
||||
|
||||
let lineHeight = 1.0 + UIScreenPixel
|
||||
context.addPath(CGPath(roundedRect: CGRect(x: 0.0, y: UIScreenPixel, width: 17.0, height: lineHeight), cornerWidth: lineHeight / 2.0, cornerHeight: lineHeight / 2.0, transform: nil))
|
||||
|
|
@ -150,7 +150,7 @@ public struct PresentationResourcesItemList {
|
|||
return theme.image(PresentationResourceKey.itemListAddIndicatorIcon.rawValue, { theme in
|
||||
return generateImage(CGSize(width: 17.0, height: 15.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(theme.list.itemBlocksSeparatorColor.cgColor)
|
||||
context.setFillColor(theme.list.itemPlaceholderTextColor.cgColor)
|
||||
|
||||
let lineHeight = 1.0 + UIScreenPixel
|
||||
context.addPath(CGPath(roundedRect: CGRect(x: 1.0, y: 7.0, width: 15.0, height: lineHeight), cornerWidth: lineHeight / 2.0, cornerHeight: lineHeight / 2.0, transform: nil))
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import TextFormat
|
|||
public enum MessageContentKindKey {
|
||||
case text
|
||||
case image
|
||||
case livePhoto
|
||||
case video
|
||||
case videoMessage
|
||||
case audioMessage
|
||||
|
|
@ -36,6 +37,7 @@ public enum MessageContentKindKey {
|
|||
public enum MessageContentKind: Equatable {
|
||||
case text(NSAttributedString)
|
||||
case image
|
||||
case livePhoto
|
||||
case video
|
||||
case videoMessage
|
||||
case audioMessage
|
||||
|
|
@ -72,6 +74,12 @@ public enum MessageContentKind: Equatable {
|
|||
} else {
|
||||
return false
|
||||
}
|
||||
case .livePhoto:
|
||||
if case .livePhoto = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .video:
|
||||
if case .video = other {
|
||||
return true
|
||||
|
|
@ -207,6 +215,8 @@ public enum MessageContentKind: Equatable {
|
|||
return .text
|
||||
case .image:
|
||||
return .image
|
||||
case .livePhoto:
|
||||
return .livePhoto
|
||||
case .video:
|
||||
return .video
|
||||
case .videoMessage:
|
||||
|
|
@ -316,8 +326,12 @@ public func mediaContentKind(_ media: EngineMedia, message: EngineMessage? = nil
|
|||
case .videoMessage:
|
||||
return .expiredVideoMessage
|
||||
}
|
||||
case .image:
|
||||
return .image
|
||||
case let .image(image):
|
||||
if let _ = image.video {
|
||||
return .livePhoto
|
||||
} else {
|
||||
return .image
|
||||
}
|
||||
case let .file(file):
|
||||
var fileName: String = ""
|
||||
|
||||
|
|
@ -429,6 +443,9 @@ public func stringForMediaKind(_ kind: MessageContentKind, strings: Presentation
|
|||
return (foldLineBreaks(text), false)
|
||||
case .image:
|
||||
return (NSAttributedString(string: strings.Message_Photo), true)
|
||||
case .livePhoto:
|
||||
//TODO:localize
|
||||
return (NSAttributedString(string: "Live Photo"), true)
|
||||
case .video:
|
||||
return (NSAttributedString(string: strings.Message_Video), true)
|
||||
case .videoMessage:
|
||||
|
|
|
|||
|
|
@ -599,6 +599,7 @@ public func makeAttachmentFileControllerImpl(
|
|||
updatedState.savedMusicExpanded = true
|
||||
return updatedState
|
||||
}
|
||||
expandImpl?()
|
||||
},
|
||||
expandRecentMusic: {
|
||||
updateState { state in
|
||||
|
|
@ -610,7 +611,13 @@ public func makeAttachmentFileControllerImpl(
|
|||
send: { message in
|
||||
if message.id.namespace == Namespaces.Message.Local {
|
||||
if let file = message.media.first(where: { $0 is TelegramMediaFile }) as? TelegramMediaFile {
|
||||
send([.standalone(media: file)], false, nil, nil)
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
guard let peer, let peerReference = PeerReference(peer._asPeer()) else {
|
||||
return
|
||||
}
|
||||
send([.savedMusic(peer: peerReference, media: file)], false, nil, nil)
|
||||
})
|
||||
dismissImpl?()
|
||||
}
|
||||
} else {
|
||||
|
|
@ -938,13 +945,54 @@ public func makeAttachmentFileControllerImpl(
|
|||
controller.mulitpleCompletion = { sendMode, _, _, caption in
|
||||
let _ = stateValue.with({ state in
|
||||
if let selectedMessageIds = state.selectedMessageIds {
|
||||
var mediaReferences: [AnyMediaReference] = []
|
||||
var remoteMessageIds: [MessageId] = []
|
||||
for id in selectedMessageIds {
|
||||
if let message = state.messageMap[id]?._asMessage(), let file = message.media.first(where: { $0 is TelegramMediaFile}) as? TelegramMediaFile {
|
||||
mediaReferences.append(.standalone(media: file))
|
||||
if let message = state.messageMap[id]?._asMessage() {
|
||||
if message.id.namespace == Namespaces.Message.Cloud {
|
||||
remoteMessageIds.append(message.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
send(mediaReferences, sendMode == .silently, nil, caption)
|
||||
let _ = combineLatest(queue: Queue.mainQueue(),
|
||||
context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)),
|
||||
context.engine.messages.getMessagesLoadIfNecessary(remoteMessageIds, strategy: .cloud(skipLocal: true))
|
||||
|> `catch` { _ in
|
||||
return .single(.result([]))
|
||||
}
|
||||
|> mapToSignal { result -> Signal<[Message], NoError> in
|
||||
guard case let .result(result) = result else {
|
||||
return .complete()
|
||||
}
|
||||
return .single(result)
|
||||
}
|
||||
).start(next: { peer, remoteMessages in
|
||||
guard let peer, let peerReference = PeerReference(peer._asPeer()) else {
|
||||
return
|
||||
}
|
||||
var messageMap: [MessageId: Message] = [:]
|
||||
for message in remoteMessages {
|
||||
messageMap[message.id] = message
|
||||
}
|
||||
|
||||
var mediaReferences: [AnyMediaReference] = []
|
||||
|
||||
for id in selectedMessageIds {
|
||||
if var message = state.messageMap[id]?._asMessage() {
|
||||
if message.id.namespace == Namespaces.Message.Cloud, let remoteMessage = messageMap[message.id] {
|
||||
message = remoteMessage
|
||||
}
|
||||
if let file = message.media.first(where: { $0 is TelegramMediaFile}) as? TelegramMediaFile {
|
||||
if message.id.namespace == Namespaces.Message.Cloud {
|
||||
mediaReferences.append(.message(message: MessageReference(message), media: file))
|
||||
} else {
|
||||
mediaReferences.append(.savedMusic(peer: peerReference, media: file))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
send(mediaReferences, sendMode == .silently, nil, caption)
|
||||
})
|
||||
dismissImpl?()
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -105,7 +105,6 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
|||
|
||||
private var inlineMediaValue: InlineMedia?
|
||||
|
||||
//private var additionalImageBadgeNode: ChatMessageInteractiveMediaBadge?
|
||||
private var linkHighlightingNode: LinkHighlightingNode?
|
||||
|
||||
private var context: AccountContext?
|
||||
|
|
@ -1558,7 +1557,9 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
|||
|
||||
if displayLine {
|
||||
var pattern: MessageInlineBlockBackgroundView.Pattern?
|
||||
if let backgroundEmojiId = author?.backgroundEmojiId {
|
||||
if let _ = message.media.first(where: { $0 is TelegramMediaPoll }) {
|
||||
|
||||
} else if let backgroundEmojiId = author?.backgroundEmojiId {
|
||||
pattern = MessageInlineBlockBackgroundView.Pattern(
|
||||
context: context,
|
||||
fileId: backgroundEmojiId,
|
||||
|
|
|
|||
|
|
@ -495,6 +495,7 @@ private final class ChatMessagePollOptionNode: ASDisplayNode {
|
|||
private(set) var currentSelection: ChatMessagePollOptionSelection?
|
||||
var pressed: (() -> Void)?
|
||||
var selectionUpdated: (() -> Void)?
|
||||
var resultPressed: (() -> Void)?
|
||||
var longTapped: (() -> Void)?
|
||||
var context: AccountContext?
|
||||
var message: Message?
|
||||
|
|
@ -728,7 +729,12 @@ private final class ChatMessagePollOptionNode: ASDisplayNode {
|
|||
return
|
||||
}
|
||||
|
||||
guard self.forceSelected == nil && self.currentResult == nil else {
|
||||
guard self.forceSelected == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
if let _ = self.currentResult {
|
||||
self.resultPressed?()
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -1018,7 +1024,15 @@ private final class ChatMessagePollOptionNode: ASDisplayNode {
|
|||
}
|
||||
let radioSize: CGFloat = 22.0
|
||||
radioNode.frame = CGRect(origin: CGPoint(x: 12.0, y: 15.0), size: CGSize(width: radioSize, height: radioSize))
|
||||
radioNode.update(isRectangle: poll.kind.multipleAnswers, staticColor: incoming ? presentationData.theme.theme.chat.message.incoming.polls.radioButton : presentationData.theme.theme.chat.message.outgoing.polls.radioButton, animatedColor: incoming ? presentationData.theme.theme.chat.message.incoming.polls.radioProgress : presentationData.theme.theme.chat.message.outgoing.polls.radioProgress, fillColor: incoming ? presentationData.theme.theme.chat.message.incoming.polls.bar : presentationData.theme.theme.chat.message.outgoing.polls.bar, foregroundColor: incoming ? presentationData.theme.theme.chat.message.incoming.polls.barIconForeground : presentationData.theme.theme.chat.message.outgoing.polls.barIconForeground, isSelectable: isSelectable || forceSelected != nil, isAnimating: inProgress)
|
||||
radioNode.update(
|
||||
isRectangle: poll.kind.multipleAnswers,
|
||||
staticColor: incoming ? presentationData.theme.theme.chat.message.incoming.polls.radioButton : presentationData.theme.theme.chat.message.outgoing.polls.radioButton,
|
||||
animatedColor: incoming ? presentationData.theme.theme.chat.message.incoming.polls.radioProgress : presentationData.theme.theme.chat.message.outgoing.polls.radioProgress,
|
||||
fillColor: incoming ? presentationData.theme.theme.chat.message.incoming.polls.bar : presentationData.theme.theme.chat.message.outgoing.polls.bar,
|
||||
foregroundColor: incoming ? presentationData.theme.theme.chat.message.incoming.polls.barIconForeground : presentationData.theme.theme.chat.message.outgoing.polls.barIconForeground,
|
||||
isSelectable: isSelectable || forceSelected != nil,
|
||||
isAnimating: inProgress
|
||||
)
|
||||
|
||||
if let forceSelected {
|
||||
radioNode.updateIsChecked(forceSelected, animated: false)
|
||||
|
|
@ -2214,7 +2228,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
|||
updatedPresentationData: item.controllerInteraction.updatedPresentationData,
|
||||
subject: .option,
|
||||
availableButtons: [.gallery, .sticker, .location],
|
||||
present: { [weak item] controller in
|
||||
present: { [weak item] controller, _ in
|
||||
item?.controllerInteraction.navigationController()?.pushViewController(controller)
|
||||
},
|
||||
completion: { [weak self] media in
|
||||
|
|
@ -2877,12 +2891,19 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
|||
if optionNode.supernode !== strongSelf {
|
||||
strongSelf.addSubnode(optionNode)
|
||||
let option = optionNode.option
|
||||
|
||||
optionNode.pressed = { [weak self] in
|
||||
guard let self, let item = self.item, let option else {
|
||||
return
|
||||
}
|
||||
item.controllerInteraction.requestSelectMessagePollOptions(item.message.id, [option.opaqueIdentifier])
|
||||
}
|
||||
optionNode.resultPressed = { [weak self] in
|
||||
guard let self, let item = self.item, let option else {
|
||||
return
|
||||
}
|
||||
item.controllerInteraction.openMessagePollResults(item.message.id, option.opaqueIdentifier)
|
||||
}
|
||||
optionNode.selectionUpdated = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
|
|
@ -3195,15 +3216,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
|||
strongSelf.updateSelection()
|
||||
strongSelf.updatePollTooltipMessageState(animated: false)
|
||||
|
||||
var buttonWidth: CGFloat = 0.0
|
||||
if !strongSelf.buttonSaveTextNode.isHidden {
|
||||
buttonWidth = strongSelf.buttonSaveTextNode.frame.width
|
||||
} else if !strongSelf.buttonViewResultsTextNode.isHidden {
|
||||
buttonWidth = strongSelf.buttonViewResultsTextNode.frame.width
|
||||
} else if !strongSelf.buttonSubmitActiveTextNode.isHidden || !strongSelf.buttonSubmitInactiveTextNode.isHidden {
|
||||
buttonWidth = max(strongSelf.buttonSubmitActiveTextNode.frame.width, strongSelf.buttonSubmitInactiveTextNode.frame.width)
|
||||
}
|
||||
buttonWidth = floor(buttonWidth * 1.1)
|
||||
let buttonWidth: CGFloat = floor(max(strongSelf.buttonSaveTextNode.frame.width, max(strongSelf.buttonViewResultsTextNode.frame.width, strongSelf.buttonSubmitActiveTextNode.frame.width)) * 1.1)
|
||||
strongSelf.buttonNode.frame = CGRect(origin: CGPoint(x: floor((resultSize.width - buttonWidth) / 2.0), y: verticalOffset), size: CGSize(width: buttonWidth, height: 44.0))
|
||||
|
||||
strongSelf.updateIsTranslating(isTranslating)
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ swift_library(
|
|||
"//submodules/StickerPeekUI",
|
||||
"//submodules/CounterControllerTitleView",
|
||||
"//submodules/TelegramUI/Components/EdgeEffect",
|
||||
"//submodules/ICloudResources",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -879,8 +879,15 @@ final class ComposePollScreenComponent: Component {
|
|||
subject: pollAttachmentSubject,
|
||||
availableButtons: availableButtons,
|
||||
inputMediaNodeData: self.inputMediaNodeDataPromise.get() |> map(Optional.init),
|
||||
present: { [weak self] c in
|
||||
(self?.environment?.controller() as? ComposePollScreen)?.parentController()?.push(c)
|
||||
present: { [weak self] c, push in
|
||||
guard let parentController = (self?.environment?.controller() as? ComposePollScreen)?.parentController() else {
|
||||
return
|
||||
}
|
||||
if push {
|
||||
parentController.push(c)
|
||||
} else {
|
||||
parentController.present(c, in: .window(.root))
|
||||
}
|
||||
},
|
||||
completion: { [weak self] media in
|
||||
guard let self else {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import LegacyMediaPickerUI
|
|||
import LocationUI
|
||||
import AttachmentFileController
|
||||
import ChatEntityKeyboardInputNode
|
||||
import ICloudResources
|
||||
|
||||
public enum PollAttachmentSubject {
|
||||
case description
|
||||
|
|
@ -28,7 +29,7 @@ public func presentPollAttachmentScreen(
|
|||
subject: PollAttachmentSubject,
|
||||
availableButtons: [AttachmentButtonType],
|
||||
inputMediaNodeData: Signal<ChatEntityKeyboardInputNode.InputData?, NoError> = .single(nil),
|
||||
present: @escaping (ViewController) -> Void,
|
||||
present: @escaping (ViewController, Bool) -> Void,
|
||||
completion: @escaping (AnyMediaReference) -> Void
|
||||
) {
|
||||
let attachmentController = AttachmentController(
|
||||
|
|
@ -44,17 +45,30 @@ public func presentPollAttachmentScreen(
|
|||
}
|
||||
)
|
||||
attachmentController.requestController = { [weak attachmentController] type, controllerCompletion in
|
||||
let mediaPickerPollSubject: MediaPickerScreenImpl.Subject.AssetsMode.PollMode
|
||||
let filePickerPollSubject: AttachmentFileControllerSource.PollMode
|
||||
let locationPickerPollSubject: LocationPickerController.Source.PollMode
|
||||
let stickerPickerPollSubject: StickerAttachmentScreen.Source.PollMode
|
||||
switch subject {
|
||||
case .description:
|
||||
mediaPickerPollSubject = .description
|
||||
filePickerPollSubject = .description
|
||||
locationPickerPollSubject = .description
|
||||
stickerPickerPollSubject = .description
|
||||
case .quizAnswer:
|
||||
mediaPickerPollSubject = .quizAnswer
|
||||
filePickerPollSubject = .quizAnswer
|
||||
locationPickerPollSubject = .quizAnswer
|
||||
stickerPickerPollSubject = .quizAnswer
|
||||
default:
|
||||
mediaPickerPollSubject = .option
|
||||
filePickerPollSubject = .description
|
||||
locationPickerPollSubject = .option
|
||||
stickerPickerPollSubject = .option
|
||||
}
|
||||
|
||||
switch type {
|
||||
case .gallery:
|
||||
let mediaPickerPollSubject: MediaPickerScreenImpl.Subject.AssetsMode.PollMode
|
||||
switch subject {
|
||||
case .description:
|
||||
mediaPickerPollSubject = .description
|
||||
case .quizAnswer:
|
||||
mediaPickerPollSubject = .quizAnswer
|
||||
case .option:
|
||||
mediaPickerPollSubject = .option
|
||||
}
|
||||
let controller = MediaPickerScreenImpl(
|
||||
context: context,
|
||||
updatedPresentationData: updatedPresentationData,
|
||||
|
|
@ -63,12 +77,12 @@ public func presentPollAttachmentScreen(
|
|||
threadTitle: nil,
|
||||
chatLocation: nil,
|
||||
enableMultiselection: false,
|
||||
subject: .assets(nil, .poll(mediaPickerPollSubject))
|
||||
subject: .assets(nil, .poll(mode: mediaPickerPollSubject, asFile: false))
|
||||
)
|
||||
controller.getCaptionPanelView = {
|
||||
return nil
|
||||
}
|
||||
controller.legacyCompletion = { fromGallery, signals, silently, scheduleTime, parameters, getAnimatedTransitionSource, sendCompletion in
|
||||
controller.legacyCompletion = { _, signals, _, _, _, _, sendCompletion in
|
||||
let _ = (legacyAssetPickerEnqueueMessages(context: context, account: context.account, signals: signals)
|
||||
|> deliverOnMainQueue).start(next: { items in
|
||||
if let item = items.first, case let .message(_, _, _, mediaReference, _, _, _, _, _, _) = item.message, let mediaReference {
|
||||
|
|
@ -80,24 +94,69 @@ public func presentPollAttachmentScreen(
|
|||
controllerCompletion(controller, controller.mediaPickerContext)
|
||||
return true
|
||||
case .file:
|
||||
let filePickerPollSubject: AttachmentFileControllerSource.PollMode
|
||||
switch subject {
|
||||
case .description:
|
||||
filePickerPollSubject = .description
|
||||
case .quizAnswer:
|
||||
filePickerPollSubject = .quizAnswer
|
||||
default:
|
||||
filePickerPollSubject = .description
|
||||
}
|
||||
let controller = makeAttachmentFileControllerImpl(
|
||||
context: context,
|
||||
updatedPresentationData: updatedPresentationData,
|
||||
source: .poll(filePickerPollSubject),
|
||||
bannedSendMedia: nil,
|
||||
presentGallery: {},
|
||||
presentGallery: { [weak attachmentController] in
|
||||
attachmentController?.dismiss(animated: true)
|
||||
|
||||
let controller = MediaPickerScreenImpl(
|
||||
context: context,
|
||||
updatedPresentationData: updatedPresentationData,
|
||||
style: .glass,
|
||||
peer: nil,
|
||||
threadTitle: nil,
|
||||
chatLocation: nil,
|
||||
enableMultiselection: false,
|
||||
subject: .assets(nil, .poll(mode: mediaPickerPollSubject, asFile: true))
|
||||
)
|
||||
controller.getCaptionPanelView = {
|
||||
return nil
|
||||
}
|
||||
controller.legacyCompletion = { _, signals, _, _, _, _, sendCompletion in
|
||||
let _ = (legacyAssetPickerEnqueueMessages(context: context, account: context.account, signals: signals)
|
||||
|> deliverOnMainQueue).start(next: { items in
|
||||
if let item = items.first, case let .message(_, _, _, mediaReference, _, _, _, _, _, _) = item.message, let mediaReference {
|
||||
completion(mediaReference)
|
||||
sendCompletion()
|
||||
}
|
||||
})
|
||||
}
|
||||
controller.navigationPresentation = .modal
|
||||
present(controller, true)
|
||||
},
|
||||
presentFiles: { [weak attachmentController] in
|
||||
attachmentController?.dismiss(animated: true)
|
||||
//TODO
|
||||
|
||||
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = legacyICloudFilePicker(theme: presentationData.theme, mode: .import, documentTypes: ["public.item"], completion: { urls in
|
||||
guard let url = urls.first else {
|
||||
return
|
||||
}
|
||||
let _ = (iCloudFileDescription(url)
|
||||
|> deliverOnMainQueue).start(next: { item in
|
||||
guard let item else {
|
||||
return
|
||||
}
|
||||
let fileId = Int64.random(in: Int64.min ... Int64.max)
|
||||
let mimeType = guessMimeTypeByFileExtension((item.fileName as NSString).pathExtension)
|
||||
var previewRepresentations: [TelegramMediaImageRepresentation] = []
|
||||
if mimeType.hasPrefix("image/") || mimeType == "application/pdf" {
|
||||
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 320, height: 320), resource: ICloudFileResource(urlData: item.urlData, thumbnail: true), progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
||||
}
|
||||
var attributes: [TelegramMediaFileAttribute] = []
|
||||
attributes.append(.FileName(fileName: item.fileName))
|
||||
if let audioMetadata = item.audioMetadata {
|
||||
attributes.append(.Audio(isVoice: false, duration: audioMetadata.duration, title: audioMetadata.title, performer: audioMetadata.performer, waveform: nil))
|
||||
}
|
||||
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: fileId), partialReference: nil, resource: ICloudFileResource(urlData: item.urlData, thumbnail: false), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int64(item.fileSize), attributes: attributes, alternativeRepresentations: [])
|
||||
completion(.standalone(media: file))
|
||||
})
|
||||
})
|
||||
present(controller, false)
|
||||
},
|
||||
presentDocumentScanner: nil,
|
||||
send: { mediaReferences, _, _, _ in
|
||||
|
|
@ -107,15 +166,6 @@ public func presentPollAttachmentScreen(
|
|||
controllerCompletion(controller, controller.mediaPickerContext)
|
||||
return true
|
||||
case .location:
|
||||
let locationPickerPollSubject: LocationPickerController.Source.PollMode
|
||||
switch subject {
|
||||
case .description:
|
||||
locationPickerPollSubject = .description
|
||||
case .quizAnswer:
|
||||
locationPickerPollSubject = .quizAnswer
|
||||
case .option:
|
||||
locationPickerPollSubject = .option
|
||||
}
|
||||
let controller = LocationPickerController(
|
||||
context: context,
|
||||
style: .glass,
|
||||
|
|
@ -134,15 +184,6 @@ public func presentPollAttachmentScreen(
|
|||
guard let content = content?.stickers else {
|
||||
return
|
||||
}
|
||||
let stickerPickerPollSubject: StickerAttachmentScreen.Source.PollMode
|
||||
switch subject {
|
||||
case .description:
|
||||
stickerPickerPollSubject = .description
|
||||
case .quizAnswer:
|
||||
stickerPickerPollSubject = .quizAnswer
|
||||
case .option:
|
||||
stickerPickerPollSubject = .option
|
||||
}
|
||||
let controller = StickerAttachmentScreen(
|
||||
context: context,
|
||||
mode: .stickers(content),
|
||||
|
|
@ -161,15 +202,6 @@ public func presentPollAttachmentScreen(
|
|||
guard let content = content?.emoji else {
|
||||
return
|
||||
}
|
||||
let stickerPickerPollSubject: StickerAttachmentScreen.Source.PollMode
|
||||
switch subject {
|
||||
case .description:
|
||||
stickerPickerPollSubject = .description
|
||||
case .quizAnswer:
|
||||
stickerPickerPollSubject = .quizAnswer
|
||||
case .option:
|
||||
stickerPickerPollSubject = .option
|
||||
}
|
||||
let controller = StickerAttachmentScreen(
|
||||
context: context,
|
||||
mode: .emoji(content),
|
||||
|
|
@ -186,5 +218,5 @@ public func presentPollAttachmentScreen(
|
|||
}
|
||||
}
|
||||
attachmentController.navigationPresentation = .flatModal
|
||||
present(attachmentController)
|
||||
present(attachmentController, true)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -615,6 +615,13 @@ final class ContextSourceContainer: ASDisplayNode {
|
|||
self.presentationData = presentationData
|
||||
self.validLayout = layout
|
||||
|
||||
var layout = layout
|
||||
if self.sources.count > 1 {
|
||||
var intrinsicInsets = layout.intrinsicInsets
|
||||
intrinsicInsets.bottom += 30.0
|
||||
layout = layout.withUpdatedIntrinsicInsets(intrinsicInsets)
|
||||
}
|
||||
|
||||
var childLayout = layout
|
||||
|
||||
if let activeSource = self.activeSource {
|
||||
|
|
|
|||
|
|
@ -774,7 +774,7 @@ public final class ListActionItemComponent: Component {
|
|||
|
||||
if case let .toggle(toggle) = component.accessory {
|
||||
switch toggle.style {
|
||||
case .regular:
|
||||
case .regular, .lock:
|
||||
let switchNode: SwitchNode
|
||||
var switchTransition = transition
|
||||
var updateSwitchTheme = themeUpdated
|
||||
|
|
@ -827,7 +827,7 @@ public final class ListActionItemComponent: Component {
|
|||
}
|
||||
let switchFrame = CGRect(origin: CGPoint(x: availableSize.width - 16.0 - switchSize.width, y: switchY), size: switchSize)
|
||||
switchTransition.setFrame(view: switchNode.view, frame: switchFrame)
|
||||
case .icons, .lock:
|
||||
case .icons:
|
||||
let switchNode: IconSwitchNode
|
||||
var switchTransition = transition
|
||||
var updateSwitchTheme = themeUpdated
|
||||
|
|
|
|||
|
|
@ -1065,15 +1065,15 @@ public final class ListComposePollOptionComponent: Component {
|
|||
if let dimensions = file.dimensions {
|
||||
imageSize = dimensions.cgSize.aspectFilled(imageNodeSize)
|
||||
}
|
||||
if file.mimeType.hasPrefix("image/") {
|
||||
if updateMedia {
|
||||
imageNode.setSignal(instantPageImageFile(account: component.context.account, userLocation: .other, fileReference: fileReference, fetched: true))
|
||||
}
|
||||
} else if file.isVideo {
|
||||
if file.isVideo {
|
||||
if updateMedia {
|
||||
imageNode.setSignal(chatMessageVideo(postbox: component.context.account.postbox, userLocation: .other, videoReference: fileReference))
|
||||
}
|
||||
isVideo = true
|
||||
} else if file.mimeType.hasPrefix("image/") || !file.previewRepresentations.isEmpty {
|
||||
if updateMedia {
|
||||
imageNode.setSignal(chatMessageImageFile(account: component.context.account, userLocation: .other, fileReference: fileReference, thumbnail: true))
|
||||
}
|
||||
} else {
|
||||
let fileName: String = file.fileName ?? "File"
|
||||
var fileExtension: String?
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@ private func extractFileMedia(_ message: Message) -> TelegramMediaFile? {
|
|||
} else if let media = media as? TelegramMediaWebpage, case let .Loaded(content) = media.content, let f = content.file {
|
||||
file = f
|
||||
break
|
||||
} else if let media = media as? TelegramMediaPoll, let f = media.attachedMedia as? TelegramMediaFile {
|
||||
file = f
|
||||
break
|
||||
}
|
||||
}
|
||||
return file
|
||||
|
|
|
|||
|
|
@ -147,23 +147,23 @@ extension ChatControllerImpl {
|
|||
}
|
||||
},
|
||||
transitionNode: { [weak self] messageId, media, adjustRect in
|
||||
guard let self else {
|
||||
return nil
|
||||
var selectedNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
||||
if let self {
|
||||
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView {
|
||||
if let result = itemNode.transitionNode(id: messageId, media: media, adjustRect: adjustRect) {
|
||||
selectedNode = result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = self
|
||||
return nil
|
||||
//return self.transitionNode(messageId: messageId, media: media, adjustRect: adjustRect)
|
||||
return selectedNode
|
||||
},
|
||||
addToTransitionSurface: { [weak self] view in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = self
|
||||
// if let superview = self.itemNode?.view.superview?.superview?.superview {
|
||||
// superview.addSubview(view)
|
||||
// } else {
|
||||
// self.view.addSubview(view)
|
||||
// }
|
||||
self.chatDisplayNode.historyNode.view.superview?.insertSubview(view, aboveSubview: self.chatDisplayNode.historyNode.view)
|
||||
},
|
||||
openUrl: { [weak self] url in
|
||||
guard let self else {
|
||||
|
|
@ -203,24 +203,7 @@ extension ChatControllerImpl {
|
|||
}
|
||||
self.controllerInteraction?.sendEmoji(text, attribute, false)
|
||||
},
|
||||
setupTemporaryHiddenMedia: { [weak self] signal, _, galleryMedia in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = self
|
||||
// self.temporaryHiddenMediaDisposable.set((signal |> deliverOnMainQueue).startStrict(next: { [weak self] entry in
|
||||
// guard let self, let item = self.item else {
|
||||
// return
|
||||
// }
|
||||
// var hiddenMedia = item.controllerInteraction.hiddenMedia
|
||||
// if entry != nil {
|
||||
// hiddenMedia[item.message.id] = [galleryMedia]
|
||||
// } else {
|
||||
// hiddenMedia.removeValue(forKey: item.message.id)
|
||||
// }
|
||||
// item.controllerInteraction.hiddenMedia = hiddenMedia
|
||||
// self.itemNode?.updateHiddenMedia()
|
||||
// }))
|
||||
setupTemporaryHiddenMedia: { _, _, _ in
|
||||
},
|
||||
chatAvatarHiddenMedia: { _, _ in
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue