mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Various improvements
This commit is contained in:
parent
f3c18e1436
commit
21adc7d061
14 changed files with 388 additions and 41 deletions
|
|
@ -524,7 +524,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
|||
}
|
||||
|
||||
if isExtracted {
|
||||
strongSelf.extractedBackgroundImageNode.image = generateStretchableFilledCircleImage(diameter: 28.0, color: item.presentationData.theme.theme.list.plainBackgroundColor)
|
||||
strongSelf.extractedBackgroundImageNode.image = generateStretchableFilledCircleImage(diameter: 28.0, color: item.presentationData.theme.theme.list.itemModalBlocksBackgroundColor)
|
||||
}
|
||||
|
||||
if let extractedRect = strongSelf.extractedRect, let nonExtractedRect = strongSelf.nonExtractedRect {
|
||||
|
|
@ -1036,7 +1036,10 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
|||
reorderInset = sizeAndApply.0
|
||||
}
|
||||
|
||||
let contentRightInset = rightInset + rightOffset + reorderInset
|
||||
var contentRightInset = rightInset + rightOffset + reorderInset
|
||||
if !item.isGlobalSearchResult {
|
||||
contentRightInset += 16.0
|
||||
}
|
||||
|
||||
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
|
||||
let dateText = stringForRelativeTimestamp(strings: item.presentationData.strings, relativeTimestamp: item.message?.timestamp ?? 0, relativeTo: timestamp, dateTimeFormat: item.presentationData.dateTimeFormat)
|
||||
|
|
|
|||
|
|
@ -279,12 +279,10 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, ASScrollVi
|
|||
self.scrollNode.addSubnode(self.previousItemNode)
|
||||
self.scrollNode.addSubnode(self.nextItemNode)
|
||||
|
||||
self.addSubnode(self.closeButton)
|
||||
self.addSubnode(self.rateButton)
|
||||
self.addSubnode(self.accessibilityAreaNode)
|
||||
|
||||
self.actionButton.addSubnode(self.playPauseIconNode)
|
||||
self.addSubnode(self.actionButton)
|
||||
|
||||
self.closeButton.addTarget(self, action: #selector(self.closeButtonPressed), forControlEvents: .touchUpInside)
|
||||
self.actionButton.addTarget(self, action: #selector(self.actionButtonPressed), forControlEvents: .touchUpInside)
|
||||
|
|
@ -296,6 +294,9 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, ASScrollVi
|
|||
|
||||
self.addSubnode(self.scrubbingNode)
|
||||
|
||||
self.addSubnode(self.actionButton)
|
||||
self.addSubnode(self.closeButton)
|
||||
|
||||
self.addSubnode(self.separatorNode)
|
||||
|
||||
self.actionButton.highligthedChanged = { [weak self] highlighted in
|
||||
|
|
|
|||
|
|
@ -175,6 +175,11 @@ private func findMediaResource(media: Media, previousMedia: Media?, resource: Me
|
|||
return representation.resource
|
||||
}
|
||||
}
|
||||
if let video = image.video {
|
||||
if let resource = findMediaResource(media: video, previousMedia: previousMedia, resource: resource) {
|
||||
return resource
|
||||
}
|
||||
}
|
||||
} else if let file = media as? TelegramMediaFile {
|
||||
if areResourcesEqual(file.resource, resource) {
|
||||
return file.resource
|
||||
|
|
|
|||
|
|
@ -201,8 +201,11 @@ public extension Stories {
|
|||
|
||||
self.folders = try container.decodeIfPresent([Int64].self, forKey: .folders) ?? []
|
||||
|
||||
let musicData = try container.decode(Data.self, forKey: .music)
|
||||
self.music = PostboxDecoder(buffer: MemoryBuffer(data: musicData)).decodeRootObject() as? TelegramMediaFile
|
||||
if let musicData = try container.decodeIfPresent(Data.self, forKey: .music) {
|
||||
self.music = PostboxDecoder(buffer: MemoryBuffer(data: musicData)).decodeRootObject() as? TelegramMediaFile
|
||||
} else {
|
||||
self.music = nil
|
||||
}
|
||||
|
||||
self.uploadInfo = try container.decodeIfPresent(StoryUploadInfo.self, forKey: .uploadInfo)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,23 +66,27 @@ public struct MediaAudioTrack: Codable, Equatable {
|
|||
case artist
|
||||
case title
|
||||
case duration
|
||||
case file
|
||||
}
|
||||
|
||||
public let path: String
|
||||
public let artist: String?
|
||||
public let title: String?
|
||||
public let duration: Double
|
||||
public let file: TelegramMediaFile?
|
||||
|
||||
public init(
|
||||
path: String,
|
||||
artist: String?,
|
||||
title: String?,
|
||||
duration: Double
|
||||
duration: Double,
|
||||
file: TelegramMediaFile? = nil
|
||||
) {
|
||||
self.path = path
|
||||
self.artist = artist
|
||||
self.title = title
|
||||
self.duration = duration
|
||||
self.file = file
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5163,7 +5163,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
return
|
||||
}
|
||||
|
||||
self.insertAudio(path: copyPath, fileName: fileName)
|
||||
self.insertAudio(path: copyPath, fileName: fileName, file: file.media as? TelegramMediaFile)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -5224,7 +5224,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
}), in: .window(.root))
|
||||
}
|
||||
|
||||
private func insertAudio(path: String, fileName: String, dispose: (() -> Void)? = nil) {
|
||||
private func insertAudio(path: String, fileName: String, file: TelegramMediaFile? = nil, dispose: (() -> Void)? = nil) {
|
||||
guard let mediaEditor = self.mediaEditor else {
|
||||
return
|
||||
}
|
||||
|
|
@ -5300,7 +5300,15 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
audioTrimRange = 0 ..< min(15, audioDuration)
|
||||
}
|
||||
|
||||
mediaEditor.setAudioTrack(MediaAudioTrack(path: fileName, artist: artist, title: title, duration: audioDuration), trimRange: audioTrimRange, offset: audioOffset)
|
||||
var passFile = false
|
||||
if let file {
|
||||
for attribute in file.attributes {
|
||||
if case let .Audio(_, _, title, _, _) = attribute, let title, !title.isEmpty {
|
||||
passFile = true
|
||||
}
|
||||
}
|
||||
}
|
||||
mediaEditor.setAudioTrack(MediaAudioTrack(path: fileName, artist: artist, title: title, duration: audioDuration, file: passFile ? file : nil), trimRange: audioTrimRange, offset: audioOffset)
|
||||
|
||||
mediaEditor.seek(mediaEditor.values.videoTrimRange?.lowerBound ?? 0.0, andPlay: true)
|
||||
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ extension MediaEditorScreenImpl {
|
|||
if self.isEmbeddedEditor && !(hasAnyChanges || hasEntityChanges) {
|
||||
self.saveDraft(id: randomId, isEdit: true)
|
||||
|
||||
self.completion([MediaEditorScreenImpl.Result(media: nil, mediaAreas: [], caption: caption, coverTimestamp: mediaEditor.values.coverImageTimestamp, options: self.state.privacy, stickers: stickers, music: nil, randomId: randomId)], { [weak self] finished in
|
||||
self.completion([MediaEditorScreenImpl.Result(media: nil, mediaAreas: [], caption: caption, coverTimestamp: mediaEditor.values.coverImageTimestamp, options: self.state.privacy, stickers: stickers, music: mediaEditor.values.audioTrack?.file, randomId: randomId)], { [weak self] finished in
|
||||
self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in
|
||||
self?.dismiss()
|
||||
Queue.mainQueue().justDispatch {
|
||||
|
|
@ -464,7 +464,7 @@ extension MediaEditorScreenImpl {
|
|||
return
|
||||
}
|
||||
Logger.shared.log("MediaEditor", "Completed with video \(videoResult)")
|
||||
self.completion([MediaEditorScreenImpl.Result(media: .video(video: videoResult, coverImage: coverImage, values: values, duration: duration, dimensions: values.resultDimensions), mediaAreas: mediaAreas, caption: caption, coverTimestamp: values.coverImageTimestamp, options: self.state.privacy, stickers: stickers, music: nil, randomId: randomId)], { [weak self] finished in
|
||||
self.completion([MediaEditorScreenImpl.Result(media: .video(video: videoResult, coverImage: coverImage, values: values, duration: duration, dimensions: values.resultDimensions), mediaAreas: mediaAreas, caption: caption, coverTimestamp: values.coverImageTimestamp, options: self.state.privacy, stickers: stickers, music: values.audioTrack?.file, randomId: randomId)], { [weak self] finished in
|
||||
self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in
|
||||
self?.dismiss()
|
||||
Queue.mainQueue().justDispatch {
|
||||
|
|
@ -506,7 +506,7 @@ extension MediaEditorScreenImpl {
|
|||
return
|
||||
}
|
||||
Logger.shared.log("MediaEditor", "Completed with image \(resultImage)")
|
||||
self.completion([MediaEditorScreenImpl.Result(media: .image(image: resultImage, dimensions: PixelDimensions(resultImage.size)), mediaAreas: mediaAreas, caption: caption, coverTimestamp: nil, options: self.state.privacy, stickers: stickers, music: nil, randomId: randomId)], { [weak self] finished in
|
||||
self.completion([MediaEditorScreenImpl.Result(media: .image(image: resultImage, dimensions: PixelDimensions(resultImage.size)), mediaAreas: mediaAreas, caption: caption, coverTimestamp: nil, options: self.state.privacy, stickers: stickers, music: values.audioTrack?.file, randomId: randomId)], { [weak self] finished in
|
||||
self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in
|
||||
self?.dismiss()
|
||||
Queue.mainQueue().justDispatch {
|
||||
|
|
@ -636,7 +636,7 @@ extension MediaEditorScreenImpl {
|
|||
}
|
||||
guard let avAsset else {
|
||||
Queue.mainQueue().async {
|
||||
completion(self.createEmptyResult(randomId: randomId))
|
||||
completion(self.createEmptyResult(randomId: randomId, music: itemMediaEditor.values.audioTrack?.file))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -683,19 +683,19 @@ extension MediaEditorScreenImpl {
|
|||
coverTimestamp: itemMediaEditor.values.coverImageTimestamp,
|
||||
options: self.state.privacy,
|
||||
stickers: stickers,
|
||||
music: nil,
|
||||
music: itemMediaEditor.values.audioTrack?.file,
|
||||
randomId: randomId
|
||||
)
|
||||
completion(result)
|
||||
} else {
|
||||
completion(self.createEmptyResult(randomId: randomId))
|
||||
completion(self.createEmptyResult(randomId: randomId, music: itemMediaEditor.values.audioTrack?.file))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
completion(self.createEmptyResult(randomId: randomId))
|
||||
completion(self.createEmptyResult(randomId: randomId, music: itemMediaEditor.values.audioTrack?.file))
|
||||
}
|
||||
} else {
|
||||
completion(self.createEmptyResult(randomId: randomId))
|
||||
completion(self.createEmptyResult(randomId: randomId, music: itemMediaEditor.values.audioTrack?.file))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -737,7 +737,7 @@ extension MediaEditorScreenImpl {
|
|||
return
|
||||
}
|
||||
guard let image else {
|
||||
completion(self.createEmptyResult(randomId: randomId))
|
||||
completion(self.createEmptyResult(randomId: randomId, music: itemMediaEditor.values.audioTrack?.file))
|
||||
return
|
||||
}
|
||||
itemMediaEditor.replaceSource(image, additionalImage: nil, time: .zero, mirror: false)
|
||||
|
|
@ -766,16 +766,16 @@ extension MediaEditorScreenImpl {
|
|||
coverTimestamp: nil,
|
||||
options: self.state.privacy,
|
||||
stickers: stickers,
|
||||
music: nil,
|
||||
music: itemMediaEditor.values.audioTrack?.file,
|
||||
randomId: randomId
|
||||
)
|
||||
completion(result)
|
||||
} else {
|
||||
completion(self.createEmptyResult(randomId: randomId))
|
||||
completion(self.createEmptyResult(randomId: randomId, music: itemMediaEditor.values.audioTrack?.file))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
completion(self.createEmptyResult(randomId: randomId))
|
||||
completion(self.createEmptyResult(randomId: randomId, music: itemMediaEditor.values.audioTrack?.file))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -818,7 +818,6 @@ extension MediaEditorScreenImpl {
|
|||
mode: .default,
|
||||
subject: editorSubject,
|
||||
values: values,
|
||||
hasHistogram: false,
|
||||
isStandalone: true
|
||||
)
|
||||
}
|
||||
|
|
@ -842,7 +841,7 @@ extension MediaEditorScreenImpl {
|
|||
}
|
||||
}
|
||||
|
||||
private func createEmptyResult(randomId: Int64) -> MediaEditorScreenImpl.Result {
|
||||
private func createEmptyResult(randomId: Int64, music: TelegramMediaFile? = nil) -> MediaEditorScreenImpl.Result {
|
||||
let emptyImage = UIImage()
|
||||
return MediaEditorScreenImpl.Result(
|
||||
media: .image(
|
||||
|
|
@ -854,7 +853,7 @@ extension MediaEditorScreenImpl {
|
|||
coverTimestamp: nil,
|
||||
options: self.state.privacy,
|
||||
stickers: [],
|
||||
music: nil,
|
||||
music: music,
|
||||
randomId: randomId
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -280,12 +280,10 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, ASScrollVi
|
|||
self.scrollNode.addSubnode(self.previousItemNode)
|
||||
self.scrollNode.addSubnode(self.nextItemNode)
|
||||
|
||||
self.addSubnode(self.closeButton)
|
||||
self.addSubnode(self.rateButton)
|
||||
self.addSubnode(self.accessibilityAreaNode)
|
||||
|
||||
self.actionButton.addSubnode(self.playPauseIconNode)
|
||||
self.addSubnode(self.actionButton)
|
||||
|
||||
self.closeButton.addTarget(self, action: #selector(self.closeButtonPressed), forControlEvents: .touchUpInside)
|
||||
self.actionButton.addTarget(self, action: #selector(self.actionButtonPressed), forControlEvents: .touchUpInside)
|
||||
|
|
@ -297,6 +295,9 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, ASScrollVi
|
|||
|
||||
self.addSubnode(self.scrubbingNode)
|
||||
|
||||
self.addSubnode(self.actionButton)
|
||||
self.addSubnode(self.closeButton)
|
||||
|
||||
self.actionButton.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ public final class PlainButtonComponent: Component {
|
|||
public let animateAlpha: Bool
|
||||
public let animateScale: Bool
|
||||
public let animateContents: Bool
|
||||
public let rasterizeOnScaleAnimation: Bool
|
||||
public let tag: AnyObject?
|
||||
|
||||
public init(
|
||||
|
|
@ -33,6 +34,7 @@ public final class PlainButtonComponent: Component {
|
|||
animateAlpha: Bool = true,
|
||||
animateScale: Bool = true,
|
||||
animateContents: Bool = true,
|
||||
rasterizeOnScaleAnimation: Bool = true,
|
||||
tag: AnyObject? = nil
|
||||
) {
|
||||
self.content = content
|
||||
|
|
@ -45,6 +47,7 @@ public final class PlainButtonComponent: Component {
|
|||
self.animateAlpha = animateAlpha
|
||||
self.animateScale = animateScale
|
||||
self.animateContents = animateContents
|
||||
self.rasterizeOnScaleAnimation = rasterizeOnScaleAnimation
|
||||
self.tag = tag
|
||||
}
|
||||
|
||||
|
|
@ -76,6 +79,9 @@ public final class PlainButtonComponent: Component {
|
|||
if lhs.animateContents != rhs.animateContents {
|
||||
return false
|
||||
}
|
||||
if lhs.rasterizeOnScaleAnimation != rhs.rasterizeOnScaleAnimation {
|
||||
return false
|
||||
}
|
||||
if lhs.tag !== rhs.tag {
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,3 +206,109 @@ public final class ForwardInfoPanelComponent: Component {
|
|||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
public final class MusicPanelComponent: Component {
|
||||
public let context: AccountContext
|
||||
public let file: TelegramMediaFile
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
file: TelegramMediaFile
|
||||
) {
|
||||
self.context = context
|
||||
self.file = file
|
||||
}
|
||||
|
||||
public static func ==(lhs: MusicPanelComponent, rhs: MusicPanelComponent) -> Bool {
|
||||
if lhs.file != rhs.file {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: UIView {
|
||||
public let backgroundView: UIImageView
|
||||
private let blurBackgroundView: UIVisualEffectView
|
||||
private var iconView = UIImageView()
|
||||
private var title = ComponentView<Empty>()
|
||||
private var text = ComponentView<Empty>()
|
||||
|
||||
private var component: MusicPanelComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
if #available(iOS 13.0, *) {
|
||||
self.blurBackgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterialDark))
|
||||
} else {
|
||||
self.blurBackgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
|
||||
}
|
||||
self.blurBackgroundView.clipsToBounds = true
|
||||
|
||||
self.backgroundView = UIImageView()
|
||||
self.backgroundView.image = generateStretchableFilledCircleImage(radius: 4.0, color: UIColor(white: 0.0, alpha: 0.4))
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.iconView.image = UIImage(bundleImageName: "Media Editor/SmallAudio")
|
||||
self.addSubview(self.iconView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(component: MusicPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
var titleOffset: CGFloat = 0.0
|
||||
if let image = self.iconView.image {
|
||||
self.iconView.frame = CGRect(origin: CGPoint(x: 10.0, y: 5.0), size: image.size)
|
||||
}
|
||||
titleOffset += 29.0
|
||||
|
||||
let text = NSMutableAttributedString()
|
||||
for attribute in component.file.attributes {
|
||||
if case let .Audio(_, _, title, performer, _) = attribute, let title, !title.isEmpty {
|
||||
text.append(NSAttributedString(string: title, font: Font.semibold(13.0), textColor: .white))
|
||||
if let performer, !performer.isEmpty {
|
||||
text.append(NSAttributedString(string: " • ", font: Font.semibold(13.0), textColor: .white.withAlphaComponent(0.28)))
|
||||
text.append(NSAttributedString(string: performer, font: Font.regular(13.0), textColor: .white))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let titleSize = self.title.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(text),
|
||||
maximumNumberOfLines: 1
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - titleOffset - 20.0, height: availableSize.height)
|
||||
)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: titleOffset, y: 5.0 - UIScreenPixel), size: titleSize)
|
||||
if let view = self.title.view {
|
||||
if view.superview == nil {
|
||||
self.addSubview(view)
|
||||
}
|
||||
view.frame = titleFrame
|
||||
}
|
||||
|
||||
let size = CGSize(width: titleSize.width + 39.0, height: 26.0)
|
||||
self.blurBackgroundView.frame = CGRect(origin: .zero, size: size)
|
||||
self.insertSubview(self.blurBackgroundView, at: 0)
|
||||
self.blurBackgroundView.layer.cornerRadius = size.height / 2.0
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
public func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ final class StoryContentCaptionComponent: Component {
|
|||
let textSelectionAction: (NSAttributedString, TextSelectionAction) -> Void
|
||||
let controller: () -> ViewController?
|
||||
let openStory: (EnginePeer, EngineStoryItem?) -> Void
|
||||
let openMusic: (FileMediaReference, UIView) -> Void
|
||||
|
||||
init(
|
||||
externalState: ExternalState,
|
||||
|
|
@ -87,7 +88,8 @@ final class StoryContentCaptionComponent: Component {
|
|||
longTapAction: @escaping (Action) -> Void,
|
||||
textSelectionAction: @escaping (NSAttributedString, TextSelectionAction) -> Void,
|
||||
controller: @escaping () -> ViewController?,
|
||||
openStory: @escaping (EnginePeer, EngineStoryItem?) -> Void
|
||||
openStory: @escaping (EnginePeer, EngineStoryItem?) -> Void,
|
||||
openMusic: @escaping (FileMediaReference, UIView) -> Void
|
||||
) {
|
||||
self.externalState = externalState
|
||||
self.context = context
|
||||
|
|
@ -105,6 +107,7 @@ final class StoryContentCaptionComponent: Component {
|
|||
self.textSelectionAction = textSelectionAction
|
||||
self.controller = controller
|
||||
self.openStory = openStory
|
||||
self.openMusic = openMusic
|
||||
}
|
||||
|
||||
static func ==(lhs: StoryContentCaptionComponent, rhs: StoryContentCaptionComponent) -> Bool {
|
||||
|
|
@ -187,6 +190,8 @@ final class StoryContentCaptionComponent: Component {
|
|||
private let scrollBottomFullMaskView: UIView
|
||||
private let scrollTopMaskView: UIImageView
|
||||
|
||||
private var musicPanel: ComponentView<Empty>?
|
||||
|
||||
private var forwardInfoPanel: ComponentView<Empty>?
|
||||
private var forwardInfoDisposable: Disposable?
|
||||
private var forwardInfoStory: EngineStoryItem?
|
||||
|
|
@ -316,10 +321,10 @@ final class StoryContentCaptionComponent: Component {
|
|||
|
||||
let contentItem = self.isExpanded ? self.expandedText : self.collapsedText
|
||||
|
||||
if let textView = contentItem.textNode?.textNode.view {
|
||||
let textLocalPoint = self.convert(point, to: textView)
|
||||
if textLocalPoint.y >= -7.0 {
|
||||
return self.textSelectionNode?.view ?? textView
|
||||
if let musicView = self.musicPanel?.view {
|
||||
let musicLocalPoint = self.convert(point, to: musicView)
|
||||
if let result = musicView.hitTest(musicLocalPoint, with: nil) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -330,6 +335,13 @@ final class StoryContentCaptionComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
if let textView = contentItem.textNode?.textNode.view {
|
||||
let textLocalPoint = self.convert(point, to: textView)
|
||||
if textLocalPoint.y >= -7.0 {
|
||||
return self.textSelectionNode?.view ?? textView
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -617,7 +629,8 @@ final class StoryContentCaptionComponent: Component {
|
|||
self.state = state
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
let verticalInset: CGFloat = 7.0
|
||||
let verticalInset: CGFloat = 8.0
|
||||
let bottomPanelSpacing: CGFloat = 5.0
|
||||
let textContainerSize = CGSize(width: availableSize.width - sideInset * 2.0, height: availableSize.height - verticalInset * 2.0)
|
||||
|
||||
var baseQuoteSecondaryTintColor: UIColor?
|
||||
|
|
@ -716,6 +729,57 @@ final class StoryContentCaptionComponent: Component {
|
|||
let visibleTextHeight = collapsedTextLayout.0.size.height - textInsets.top - textInsets.bottom
|
||||
let textOverflowHeight: CGFloat = expandedTextLayout.0.size.height - textInsets.top - textInsets.bottom - visibleTextHeight
|
||||
let scrollContentSize = CGSize(width: availableSize.width, height: availableSize.height + textOverflowHeight)
|
||||
let hasBottomStackContent = !component.text.isEmpty || component.forwardInfo != nil
|
||||
var bottomContentOffset: CGFloat = 0.0
|
||||
|
||||
if let music = component.music?._asMedia() as? TelegramMediaFile {
|
||||
let musicPanel: ComponentView<Empty>
|
||||
if let current = self.musicPanel {
|
||||
musicPanel = current
|
||||
} else {
|
||||
musicPanel = ComponentView<Empty>()
|
||||
self.musicPanel = musicPanel
|
||||
}
|
||||
|
||||
let musicPanelSize = musicPanel.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
PlainButtonComponent(
|
||||
content: AnyComponent(
|
||||
MusicPanelComponent(
|
||||
context: component.context,
|
||||
file: music
|
||||
)
|
||||
),
|
||||
action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let sourceView = self.musicPanel?.view {
|
||||
self.component?.openMusic(.standalone(media: music), sourceView)
|
||||
}
|
||||
},
|
||||
animateScale: false
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: availableSize.height)
|
||||
)
|
||||
bottomContentOffset = musicPanelSize.height + (hasBottomStackContent ? bottomPanelSpacing : 0.0)
|
||||
let musicPanelFrame = CGRect(origin: CGPoint(x: sideInset - 8.0, y: availableSize.height - verticalInset - musicPanelSize.height), size: musicPanelSize)
|
||||
if let view = musicPanel.view {
|
||||
if view.superview == nil {
|
||||
self.scrollView.addSubview(view)
|
||||
transition.animateAlpha(view: view, from: 0.0, to: 1.0)
|
||||
}
|
||||
view.frame = musicPanelFrame
|
||||
}
|
||||
} else if let musicPanel = self.musicPanel {
|
||||
self.musicPanel = nil
|
||||
musicPanel.view?.removeFromSuperview()
|
||||
}
|
||||
|
||||
let contentBottomY = availableSize.height - verticalInset - bottomContentOffset
|
||||
|
||||
if let forwardInfo = component.forwardInfo {
|
||||
let authorName: String
|
||||
|
|
@ -778,8 +842,6 @@ final class StoryContentCaptionComponent: Component {
|
|||
fillsWidth: false
|
||||
)
|
||||
),
|
||||
effectAlignment: .center,
|
||||
minSize: nil,
|
||||
action: { [weak self] in
|
||||
if let self, case let .known(peer, _, _) = forwardInfo {
|
||||
self.component?.openStory(peer, self.forwardInfoStory)
|
||||
|
|
@ -792,13 +854,14 @@ final class StoryContentCaptionComponent: Component {
|
|||
return nil
|
||||
}))
|
||||
}
|
||||
}
|
||||
},
|
||||
animateScale: false
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: availableSize.height)
|
||||
)
|
||||
let forwardInfoPanelFrame = CGRect(origin: CGPoint(x: sideInset, y: availableSize.height - visibleTextHeight - verticalInset - forwardInfoPanelSize.height - 10.0), size: forwardInfoPanelSize)
|
||||
let forwardInfoPanelFrame = CGRect(origin: CGPoint(x: sideInset, y: contentBottomY - visibleTextHeight - forwardInfoPanelSize.height - bottomPanelSpacing), size: forwardInfoPanelSize)
|
||||
if let view = forwardInfoPanel.view {
|
||||
if view.superview == nil {
|
||||
self.scrollView.addSubview(view)
|
||||
|
|
@ -813,8 +876,8 @@ final class StoryContentCaptionComponent: Component {
|
|||
}
|
||||
|
||||
|
||||
let collapsedTextFrame = CGRect(origin: CGPoint(x: sideInset - textInsets.left, y: availableSize.height - visibleTextHeight - verticalInset - textInsets.top), size: collapsedTextLayout.0.size)
|
||||
let expandedTextFrame = CGRect(origin: CGPoint(x: sideInset - textInsets.left, y: availableSize.height - visibleTextHeight - verticalInset - textInsets.top), size: expandedTextLayout.0.size)
|
||||
let collapsedTextFrame = CGRect(origin: CGPoint(x: sideInset - textInsets.left, y: contentBottomY - visibleTextHeight - textInsets.top), size: collapsedTextLayout.0.size)
|
||||
let expandedTextFrame = CGRect(origin: CGPoint(x: sideInset - textInsets.left, y: contentBottomY - visibleTextHeight - textInsets.top), size: expandedTextLayout.0.size)
|
||||
|
||||
var spoilerExpandRect: CGRect?
|
||||
if let location = self.displayContentsUnderSpoilers.location {
|
||||
|
|
|
|||
|
|
@ -4523,7 +4523,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
if !isUnsupported, !component.slice.item.storyItem.text.isEmpty || component.slice.item.storyItem.forwardInfo != nil {
|
||||
if !isUnsupported, !component.slice.item.storyItem.text.isEmpty || component.slice.item.storyItem.forwardInfo != nil || component.slice.item.storyItem.music != nil {
|
||||
var captionItemTransition = transition
|
||||
let captionItem: CaptionItem
|
||||
if let current = self.captionItem {
|
||||
|
|
@ -4709,6 +4709,12 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
openMusic: { [weak self] file, sourceView in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.performMusicAction(file: file, sourceView: sourceView, gesture: nil)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
|
|
@ -7541,6 +7547,144 @@ public final class StoryItemSetContainerComponent: Component {
|
|||
})
|
||||
}
|
||||
|
||||
private func performMusicAction(file: FileMediaReference, sourceView: UIView, gesture: ContextGesture?) {
|
||||
guard let component = self.component, let controller = component.controller() else {
|
||||
return
|
||||
}
|
||||
|
||||
self.dismissAllTooltips()
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme)
|
||||
|
||||
let items = component.context.engine.peers.savedMusicIds()
|
||||
|> take(1)
|
||||
|> map { [weak self] savedIds -> ContextController.Items in
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
items.append(
|
||||
.action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_SaveTo, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/DownloadTone"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||
if let self {
|
||||
var subActions: [ContextMenuItem] = []
|
||||
// subActions.append(
|
||||
// .action(ContextMenuActionItem(text: presentationData.strings.Common_Back, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) }, iconPosition: .left, action: { c, _ in
|
||||
// c?.popItems()
|
||||
// }))
|
||||
// )
|
||||
// subActions.append(.separator)
|
||||
|
||||
subActions.append(
|
||||
.action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_SaveTo_Profile, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/User"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = component.context.engine.peers.addSavedMusic(file: file).start()
|
||||
|
||||
guard let controller = component.controller() as? StoryContainerScreen else {
|
||||
return
|
||||
}
|
||||
|
||||
let overlayController = UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .universalImage(
|
||||
image: generateTintedImage(image: UIImage(bundleImageName: "Peer Info/SavedMusic"), color: .white)!,
|
||||
size: nil,
|
||||
title: nil,
|
||||
text: presentationData.strings.MediaPlayer_SavedMusic_AddedToProfile,
|
||||
customUndoText: presentationData.strings.MediaPlayer_SavedMusic_AddedToProfile_View,
|
||||
timeout: 3.0
|
||||
),
|
||||
action: { [weak self] action in
|
||||
guard let self, let component = self.component, case .undo = action else {
|
||||
return false
|
||||
}
|
||||
let _ = (component.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: component.context.account.peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let self, let component = self.component, let peer else {
|
||||
return
|
||||
}
|
||||
guard let controller = component.controller() as? StoryContainerScreen else {
|
||||
return
|
||||
}
|
||||
guard let navigationController = controller.navigationController as? NavigationController else {
|
||||
return
|
||||
}
|
||||
if let controller = component.context.sharedContext.makePeerInfoController(
|
||||
context: component.context,
|
||||
updatedPresentationData: nil,
|
||||
peer: peer._asPeer(),
|
||||
mode: .myProfile,
|
||||
avatarInitiallyExpanded: false,
|
||||
fromChat: false,
|
||||
requestsContext: nil
|
||||
) {
|
||||
navigationController.pushViewController(controller)
|
||||
}
|
||||
})
|
||||
return true
|
||||
}
|
||||
)
|
||||
controller.present(overlayController, in: .current)
|
||||
}))
|
||||
)
|
||||
|
||||
subActions.append(
|
||||
.action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_SaveTo_SavedMessages, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Fave"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = component.context.engine.messages.enqueueOutgoingMessage(to: component.context.account.peerId, replyTo: nil, content: .file(file)).start()
|
||||
|
||||
guard let controller = component.controller() as? StoryContainerScreen else {
|
||||
return
|
||||
}
|
||||
|
||||
let overlayController = UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .forward(savedMessages: true, text: presentationData.strings.MediaPlayer_AudioForwardedToSavedMesagesTooltip),
|
||||
action: { _ in
|
||||
return true
|
||||
}
|
||||
)
|
||||
controller.present(overlayController, in: .current)
|
||||
}))
|
||||
)
|
||||
|
||||
let noAction: ((ContextMenuActionItem.Action) -> Void)? = nil
|
||||
subActions.append(
|
||||
.action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_SaveTo_Info, textLayout: .multiline, textFont: .small, icon: { _ in return nil }, action: noAction))
|
||||
)
|
||||
|
||||
c?.pushItems(items: .single(ContextController.Items(content: .list(subActions))))
|
||||
}
|
||||
}))
|
||||
)
|
||||
return ContextController.Items(content: .list(items))
|
||||
}
|
||||
|
||||
let contextController = makeContextController(
|
||||
presentationData: presentationData,
|
||||
source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView, position: .top)),
|
||||
items: items,
|
||||
gesture: gesture
|
||||
)
|
||||
contextController.dismissed = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.contextController = nil
|
||||
self.updateIsProgressPaused()
|
||||
}
|
||||
self.contextController = contextController
|
||||
self.updateIsProgressPaused()
|
||||
controller.present(contextController, in: .window(.root))
|
||||
}
|
||||
|
||||
private func beginPictureInPicture() {
|
||||
guard let component = self.component, let visibleItem = self.visibleItems[component.slice.item.id] else {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -963,7 +963,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||
self.updateHistoryContentOffset(self.historyNode.visibleContentOffset(), transition: transition)
|
||||
|
||||
var layout = layout
|
||||
layout.intrinsicInsets.bottom = controlsHeight + 8.0
|
||||
layout.intrinsicInsets.bottom = controlsHeight + (self.historyNode.hasAnyMessages ? 0.0 : 8.0)
|
||||
self.getParentController()?.presentationContext.containerLayoutUpdated(layout, transition: transition)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -464,6 +464,10 @@ public func pollResultsController(context: AccountContext, messageId: EngineMess
|
|||
resultsContext.state
|
||||
)
|
||||
|> map { presentationData, state, resultsState -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
var presentationData = presentationData
|
||||
let updatedTheme = presentationData.theme.withModalBlocksBackground()
|
||||
presentationData = presentationData.withUpdated(theme: updatedTheme)
|
||||
|
||||
var isEmpty = false
|
||||
for (_, optionState) in resultsState.options {
|
||||
if !optionState.hasLoadedOnce {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue