mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Various improvements
This commit is contained in:
parent
650ec2296e
commit
e6d4e8ff23
@ -61,6 +61,7 @@ public final class ContextReferenceButtonComponent: Component {
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.buttonView.allowsGroupOpacity = true
|
||||
self.addSubview(self.buttonView.view)
|
||||
self.buttonView.addSubnode(self.sourceView)
|
||||
self.sourceView.addSubnode(self.contextContentView)
|
||||
|
@ -913,7 +913,8 @@ public final class MediaEditor {
|
||||
}
|
||||
|
||||
if let audioTrack {
|
||||
let audioAsset = AVURLAsset(url: URL(fileURLWithPath: audioTrack.path))
|
||||
let path = fullDraftPath(engine: self.context.engine, path: audioTrack.path)
|
||||
let audioAsset = AVURLAsset(url: URL(fileURLWithPath: path))
|
||||
let playerItem = AVPlayerItem(asset: audioAsset)
|
||||
let player = AVPlayer(playerItem: playerItem)
|
||||
player.automaticallyWaitsToMinimizeStalling = false
|
||||
|
@ -274,6 +274,6 @@ public extension MediaEditorDraft {
|
||||
}
|
||||
}
|
||||
|
||||
private func fullDraftPath(engine: TelegramEngine, path: String) -> String {
|
||||
func fullDraftPath(engine: TelegramEngine, path: String) -> String {
|
||||
return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/storyDrafts_\(engine.account.peerId.toInt64())/" + path
|
||||
}
|
||||
|
@ -3070,7 +3070,11 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
}
|
||||
|
||||
let path = url.path
|
||||
let audioAsset = AVURLAsset(url: URL(fileURLWithPath: path))
|
||||
let fileName = "audio_\(url.lastPathComponent)"
|
||||
let copyPath = fullDraftPath(engine: self.context.engine, path: fileName)
|
||||
try? FileManager.default.copyItem(atPath: path, toPath: copyPath)
|
||||
|
||||
let audioAsset = AVURLAsset(url: URL(fileURLWithPath: copyPath))
|
||||
var artist: String?
|
||||
var title: String?
|
||||
for data in audioAsset.commonMetadata {
|
||||
@ -3083,7 +3087,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
}
|
||||
|
||||
let duration = audioAsset.duration.seconds
|
||||
mediaEditor.setAudioTrack(MediaAudioTrack(path: path, artist: artist, title: title, duration: duration))
|
||||
mediaEditor.setAudioTrack(MediaAudioTrack(path: fileName, artist: artist, title: title, duration: duration))
|
||||
if !mediaEditor.sourceIsVideo {
|
||||
mediaEditor.setAudioTrackTrimRange(0 ..< min(15, duration), apply: true)
|
||||
}
|
||||
@ -3093,8 +3097,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
}
|
||||
|
||||
func presentAudioOptions(sourceView: UIView) {
|
||||
let value = self.mediaEditor?.values.audioTrackVolume ?? 1.0
|
||||
let items: [ContextMenuItem] = [
|
||||
.custom(VolumeSliderContextItem(minValue: 0.0, value: 0.75, valueChanged: { [weak self] value, _ in
|
||||
.custom(VolumeSliderContextItem(minValue: 0.0, value: value, valueChanged: { [weak self] value, _ in
|
||||
if let self {
|
||||
self.mediaEditor?.setAudioTrackVolume(value)
|
||||
}
|
||||
@ -4262,6 +4267,19 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
guard let mediaEditor = self.node.mediaEditor, let subject = self.node.subject, !self.didComplete else {
|
||||
return
|
||||
}
|
||||
|
||||
if "".isEmpty { // let sendAsPeerId = self.state.privacy.sendAsPeerId, sendAsPeerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
let controller = self.context.sharedContext.makePremiumLimitController(context: self.context, subject: .storiesChannelBoost(level: 0, link: "t.me/channel?boost"), count: 5, forceDark: true, cancel: {}, action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
})
|
||||
self.push(controller)
|
||||
return
|
||||
}
|
||||
|
||||
self.didComplete = true
|
||||
|
||||
self.dismissAllTooltips()
|
||||
@ -5107,6 +5125,10 @@ func draftPath(engine: TelegramEngine) -> String {
|
||||
return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/storyDrafts_\(engine.account.peerId.toInt64())"
|
||||
}
|
||||
|
||||
private func fullDraftPath(engine: TelegramEngine, path: String) -> String {
|
||||
return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/storyDrafts_\(engine.account.peerId.toInt64())/" + path
|
||||
}
|
||||
|
||||
func hasFirstResponder(_ view: UIView) -> Bool {
|
||||
if view.isFirstResponder {
|
||||
return true
|
||||
|
@ -165,6 +165,8 @@ final class VideoScrubberComponent: Component {
|
||||
private let audioWaveform = ComponentView<Empty>()
|
||||
|
||||
private let trimView = TrimView(frame: .zero)
|
||||
private let ghostTrimView = TrimView(frame: .zero)
|
||||
|
||||
private let cursorView = HandleView()
|
||||
|
||||
private let transparentFramesContainer = UIView()
|
||||
@ -251,6 +253,7 @@ final class VideoScrubberComponent: Component {
|
||||
|
||||
self.addSubview(self.transparentFramesContainer)
|
||||
self.addSubview(self.opaqueFramesContainer)
|
||||
self.addSubview(self.ghostTrimView)
|
||||
self.addSubview(self.trimView)
|
||||
|
||||
self.addSubview(self.audioButton)
|
||||
@ -278,6 +281,12 @@ final class VideoScrubberComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
self.ghostTrimView.trimUpdated = { [weak self] startValue, endValue, updatedEnd, done in
|
||||
if let self, let component = self.component {
|
||||
component.videoTrimUpdated(startValue, endValue, updatedEnd, done)
|
||||
}
|
||||
}
|
||||
|
||||
self.audioButton.addTarget(self, action: #selector(self.audioButtonPressed), for: .touchUpInside)
|
||||
self.videoButton.addTarget(self, action: #selector(self.videoButtonPressed), for: .touchUpInside)
|
||||
|
||||
@ -552,7 +561,20 @@ final class VideoScrubberComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
if let audioData = component.audioData, let samples = audioData.samples {
|
||||
if let audioData = component.audioData {
|
||||
let samples = audioData.samples ?? Data()
|
||||
|
||||
if let view = self.audioWaveform.view, previousComponent?.audioData?.samples == nil && audioData.samples != nil, let snapshotView = view.snapshotView(afterScreenUpdates: false) {
|
||||
snapshotView.frame = view.frame
|
||||
self.audioVibrancyContainer.addSubview(snapshotView)
|
||||
|
||||
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||
snapshotView.removeFromSuperview()
|
||||
})
|
||||
|
||||
view.layer.animateScaleY(from: 0.01, to: 1.0, duration: 0.2)
|
||||
view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
let audioWaveformSize = self.audioWaveform.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
@ -574,9 +596,6 @@ final class VideoScrubberComponent: Component {
|
||||
if let view = self.audioWaveform.view {
|
||||
if view.superview == nil {
|
||||
self.audioVibrancyContainer.addSubview(view)
|
||||
|
||||
view.layer.animateScaleY(from: 0.01, to: 1.0, duration: 0.2)
|
||||
view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
audioTransition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: 0.0, y: self.isAudioSelected || component.audioOnly ? 0.0 : 6.0), size: audioWaveformSize))
|
||||
}
|
||||
@ -637,11 +656,23 @@ final class VideoScrubberComponent: Component {
|
||||
transition: transition
|
||||
)
|
||||
|
||||
let (ghostLeftHandleFrame, ghostRightHandleFrame) = self.ghostTrimView.update(
|
||||
totalWidth: totalWidth,
|
||||
scrubberSize: CGSize(width: scrubberSize.width, height: collapsedScrubberHeight),
|
||||
duration: component.duration,
|
||||
startPosition: component.startPosition,
|
||||
endPosition: component.endPosition,
|
||||
position: component.position,
|
||||
minDuration: component.minDuration,
|
||||
maxDuration: component.maxDuration,
|
||||
transition: transition
|
||||
)
|
||||
|
||||
var containerLeftEdge = leftHandleFrame.maxX
|
||||
var containerRightEdge = rightHandleFrame.minX
|
||||
if self.isAudioSelected && component.duration > 0.0 {
|
||||
containerLeftEdge = floorToScreenPixels(component.startPosition / component.duration * scrubberSize.width)
|
||||
containerRightEdge = floorToScreenPixels(component.endPosition / component.duration * scrubberSize.width)
|
||||
containerLeftEdge = ghostLeftHandleFrame.maxX
|
||||
containerRightEdge = ghostRightHandleFrame.minX
|
||||
}
|
||||
|
||||
if self.isPanningPositionHandle || !component.isPlaying {
|
||||
@ -664,6 +695,10 @@ final class VideoScrubberComponent: Component {
|
||||
// transition.setAlpha(view: self.cursorView, alpha: self.isPanningTrimHandle ? 0.0 : 1.0)
|
||||
|
||||
videoTransition.setFrame(view: self.trimView, frame: bounds.offsetBy(dx: 0.0, dy: self.isAudioSelected ? 0.0 : originY))
|
||||
|
||||
videoTransition.setFrame(view: self.ghostTrimView, frame: bounds.offsetBy(dx: 0.0, dy: originY))
|
||||
videoTransition.setAlpha(view: self.ghostTrimView, alpha: self.isAudioSelected ? 0.75 : 0.0)
|
||||
|
||||
let handleInset: CGFloat = 7.0
|
||||
videoTransition.setFrame(view: self.transparentFramesContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: originY), size: CGSize(width: scrubberSize.width, height: videoScrubberHeight)))
|
||||
videoTransition.setFrame(view: self.opaqueFramesContainer, frame: CGRect(origin: CGPoint(x: containerLeftEdge - handleInset, y: originY), size: CGSize(width: containerRightEdge - containerLeftEdge + handleInset * 2.0, height: videoScrubberHeight)))
|
||||
@ -718,15 +753,19 @@ private class TrimView: UIView {
|
||||
private let borderView = UIImageView()
|
||||
private let zoneView = HandleView()
|
||||
|
||||
private let leftCapsuleView = UIView()
|
||||
private let rightCapsuleView = UIView()
|
||||
|
||||
private var isPanningTrimHandle = false
|
||||
|
||||
var trimUpdated: (Double, Double, Bool, Bool) -> Void = { _, _, _, _ in }
|
||||
var updated: (Transition) -> Void = { _ in }
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
super.init(frame: .zero)
|
||||
|
||||
let handleImage = generateImage(CGSize(width: handleWidth, height: scrubberHeight), rotatedContext: { size, context in
|
||||
let height = scrubberHeight
|
||||
let handleImage = generateImage(CGSize(width: handleWidth, height: height), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: .zero, size: size))
|
||||
context.setFillColor(UIColor.white.cgColor)
|
||||
|
||||
@ -739,12 +778,14 @@ private class TrimView: UIView {
|
||||
context.addPath(innerPath.cgPath)
|
||||
context.fillPath()
|
||||
|
||||
context.setBlendMode(.clear)
|
||||
let holeSize = CGSize(width: 2.0, height: 11.0)
|
||||
let holePath = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: 5.0 - UIScreenPixel, y: (size.height - holeSize.height) / 2.0), size: holeSize), cornerRadius: holeSize.width / 2.0)
|
||||
context.addPath(holePath.cgPath)
|
||||
context.fillPath()
|
||||
})?.withRenderingMode(.alwaysTemplate)
|
||||
// if !ghost {
|
||||
// context.setBlendMode(.clear)
|
||||
// let holeSize = CGSize(width: 2.0, height: 11.0)
|
||||
// let holePath = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: 5.0 - UIScreenPixel, y: (size.height - holeSize.height) / 2.0), size: holeSize), cornerRadius: holeSize.width / 2.0)
|
||||
// context.addPath(holePath.cgPath)
|
||||
// context.fillPath()
|
||||
// }
|
||||
})?.withRenderingMode(.alwaysTemplate).resizableImage(withCapInsets: UIEdgeInsets(top: 10.0, left: 0.0, bottom: 10.0, right: 0.0))
|
||||
|
||||
self.zoneView.image = UIImage()
|
||||
self.zoneView.isUserInteractionEnabled = true
|
||||
@ -753,26 +794,39 @@ private class TrimView: UIView {
|
||||
self.leftHandleView.image = handleImage
|
||||
self.leftHandleView.isUserInteractionEnabled = true
|
||||
self.leftHandleView.tintColor = .white
|
||||
self.leftHandleView.contentMode = .scaleToFill
|
||||
self.leftHandleView.hitTestSlop = UIEdgeInsets(top: -8.0, left: -9.0, bottom: -8.0, right: -9.0)
|
||||
|
||||
self.rightHandleView.image = handleImage
|
||||
self.rightHandleView.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
|
||||
self.rightHandleView.isUserInteractionEnabled = true
|
||||
self.rightHandleView.tintColor = .white
|
||||
self.rightHandleView.contentMode = .scaleToFill
|
||||
self.rightHandleView.hitTestSlop = UIEdgeInsets(top: -8.0, left: -9.0, bottom: -8.0, right: -9.0)
|
||||
|
||||
self.borderView.image = generateImage(CGSize(width: 1.0, height: scrubberHeight), rotatedContext: { size, context in
|
||||
self.borderView.image = generateImage(CGSize(width: 1.0, height: height), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: .zero, size: size))
|
||||
context.setFillColor(UIColor.white.cgColor)
|
||||
context.fill(CGRect(origin: .zero, size: CGSize(width: size.width, height: borderHeight)))
|
||||
context.fill(CGRect(origin: CGPoint(x: 0.0, y: size.height - borderHeight), size: CGSize(width: size.width, height: scrubberHeight)))
|
||||
})?.withRenderingMode(.alwaysTemplate)
|
||||
context.fill(CGRect(origin: CGPoint(x: 0.0, y: size.height - borderHeight), size: CGSize(width: size.width, height: height)))
|
||||
})?.withRenderingMode(.alwaysTemplate).resizableImage(withCapInsets: UIEdgeInsets(top: 10.0, left: 0.0, bottom: 10.0, right: 0.0))
|
||||
self.borderView.tintColor = .white
|
||||
self.borderView.isUserInteractionEnabled = false
|
||||
|
||||
self.leftCapsuleView.clipsToBounds = true
|
||||
self.leftCapsuleView.layer.cornerRadius = 1.0
|
||||
self.leftCapsuleView.backgroundColor = UIColor(rgb: 0x343436)
|
||||
|
||||
self.rightCapsuleView.clipsToBounds = true
|
||||
self.rightCapsuleView.layer.cornerRadius = 1.0
|
||||
self.rightCapsuleView.backgroundColor = UIColor(rgb: 0x343436)
|
||||
|
||||
self.addSubview(self.zoneView)
|
||||
self.addSubview(self.leftHandleView)
|
||||
self.leftHandleView.addSubview(self.leftCapsuleView)
|
||||
|
||||
self.addSubview(self.rightHandleView)
|
||||
self.rightHandleView.addSubview(self.rightCapsuleView)
|
||||
self.addSubview(self.borderView)
|
||||
|
||||
self.zoneView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handleZoneHandlePan(_:))))
|
||||
@ -936,6 +990,10 @@ private class TrimView: UIView {
|
||||
let rightHandleFrame = CGRect(origin: CGPoint(x: max(leftHandleFrame.maxX, rightHandlePosition - handleWidth / 2.0), y: 0.0), size: CGSize(width: handleWidth, height: scrubberSize.height))
|
||||
transition.setFrame(view: self.rightHandleView, frame: rightHandleFrame)
|
||||
|
||||
let capsuleSize = CGSize(width: 2.0, height: 11.0)
|
||||
transition.setFrame(view: self.leftCapsuleView, frame: CGRect(origin: CGPoint(x: 5.0 - UIScreenPixel, y: floorToScreenPixels((leftHandleFrame.height - capsuleSize.height) / 2.0)), size: capsuleSize))
|
||||
transition.setFrame(view: self.rightCapsuleView, frame: CGRect(origin: CGPoint(x: 5.0 - UIScreenPixel, y: floorToScreenPixels((leftHandleFrame.height - capsuleSize.height) / 2.0)), size: capsuleSize))
|
||||
|
||||
let zoneFrame = CGRect(x: leftHandleFrame.maxX, y: 0.0, width: rightHandleFrame.minX - leftHandleFrame.maxX, height: scrubberSize.height)
|
||||
transition.setFrame(view: self.zoneView, frame: zoneFrame)
|
||||
|
||||
|
@ -1704,6 +1704,7 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
private var currentHasChannels: Bool?
|
||||
func update(component: ShareWithPeersScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<ViewControllerComponentContainer.Environment>, transition: Transition) -> CGSize {
|
||||
guard !self.isDismissed else {
|
||||
return availableSize
|
||||
@ -1715,6 +1716,23 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
contentTransition = .immediate
|
||||
}
|
||||
|
||||
var hasCategories = false
|
||||
var hasChannels = false
|
||||
if case .stories = component.stateContext.subject {
|
||||
if let peerId = self.sendAsPeerId, peerId.isGroupOrChannel {
|
||||
} else {
|
||||
hasCategories = true
|
||||
}
|
||||
let sendAsPeersCount = component.stateContext.stateValue?.sendAsPeers.count ?? 1
|
||||
if sendAsPeersCount > 1 {
|
||||
hasChannels = true
|
||||
}
|
||||
if let currentHasChannels = self.currentHasChannels, currentHasChannels != hasChannels {
|
||||
contentTransition = .spring(duration: 0.4)
|
||||
}
|
||||
self.currentHasChannels = hasChannels
|
||||
}
|
||||
|
||||
let environment = environment[ViewControllerComponentContainer.Environment.self].value
|
||||
let themeUpdated = self.environment?.theme !== environment.theme
|
||||
|
||||
@ -1976,20 +1994,7 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
environment: {},
|
||||
containerSize: CGSize(width: itemsContainerWidth, height: 1000.0)
|
||||
)
|
||||
|
||||
var hasCategories = false
|
||||
var hasChannels = false
|
||||
if case .stories = component.stateContext.subject {
|
||||
if let peerId = self.sendAsPeerId, peerId.isGroupOrChannel {
|
||||
} else {
|
||||
hasCategories = true
|
||||
}
|
||||
let sendAsPeersCount = component.stateContext.stateValue?.sendAsPeers.count ?? 1
|
||||
if sendAsPeersCount > 1 {
|
||||
hasChannels = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var footersTotalHeight: CGFloat = 0.0
|
||||
if case let .stories(editing) = component.stateContext.subject {
|
||||
let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.freeTextColor)
|
||||
|
Loading…
x
Reference in New Issue
Block a user