This commit is contained in:
Isaac 2026-03-24 16:48:05 +08:00
commit 3920eb1507
22 changed files with 326 additions and 141 deletions

View file

@ -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

View 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

View file

@ -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] = []

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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
}

View file

@ -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)],

View file

@ -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))

View file

@ -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:

View file

@ -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?()
}
})

View file

@ -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,

View file

@ -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)

View file

@ -59,6 +59,7 @@ swift_library(
"//submodules/StickerPeekUI",
"//submodules/CounterControllerTitleView",
"//submodules/TelegramUI/Components/EdgeEffect",
"//submodules/ICloudResources",
],
visibility = [
"//visibility:public",

View file

@ -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 {

View file

@ -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)
}

View file

@ -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 {

View file

@ -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

View file

@ -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?

View file

@ -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

View 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
},