From 2c056063415722115b57a24ef97d35b26e3fb4fc Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 11 Jul 2023 18:35:22 +0200 Subject: [PATCH] Various fixes --- .../Sources/AccountContext.swift | 1 - submodules/Camera/Sources/Camera.swift | 205 +++++++++--------- .../Camera/Sources/CameraPreviewView.swift | 17 +- .../Sources/ChatListController.swift | 2 +- .../Sources/StickerPickerScreen.swift | 8 +- .../Sources/MediaPickerScreen.swift | 13 +- .../Messages/TelegramEngineMessages.swift | 17 +- .../CameraScreen/Sources/CameraScreen.swift | 105 ++++++--- .../Sources/CameraStoredState.swift | 73 ------- .../Sources/ChatScheduleTimeController.swift | 3 + .../Sources/MediaEditorValues.swift | 22 +- .../Sources/MediaEditorScreen.swift | 17 +- .../Sources/StoryPreviewComponent.swift | 2 +- .../MessageInputActionButtonComponent.swift | 62 ++++-- .../Sources/MessageInputPanelComponent.swift | 13 +- .../Sources/StoryChatContent.swift | 32 ++- .../Sources/StoryContent.swift | 22 +- .../StoryItemSetContainerComponent.swift | 47 ++-- ...StoryItemSetContainerViewSendMessage.swift | 121 ++++++++++- .../Sources/PostboxKeys.swift | 2 - .../TooltipUI/Sources/TooltipScreen.swift | 7 +- 21 files changed, 465 insertions(+), 326 deletions(-) delete mode 100644 submodules/TelegramUI/Components/CameraScreen/Sources/CameraStoredState.swift diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index c15d30700e..39f72e18ca 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -1112,7 +1112,6 @@ public struct StoriesConfiguration { default: posting = .disabled } - posting = .enabled return StoriesConfiguration(posting: posting) } else { return .defaultValue diff --git a/submodules/Camera/Sources/Camera.swift b/submodules/Camera/Sources/Camera.swift index 4130947992..bb2e1dcbf9 100644 --- a/submodules/Camera/Sources/Camera.swift +++ b/submodules/Camera/Sources/Camera.swift @@ -108,7 +108,7 @@ private final class CameraContext { private let session: CameraSession - private var mainDeviceContext: CameraDeviceContext + private var mainDeviceContext: CameraDeviceContext? private var additionalDeviceContext: CameraDeviceContext? private let cameraImageContext = CIContext() @@ -132,11 +132,11 @@ private final class CameraContext { private var lastSnapshotTimestamp: Double = CACurrentMediaTime() private var lastAdditionalSnapshotTimestamp: Double = CACurrentMediaTime() - private func savePreviewSnapshot(pixelBuffer: CVPixelBuffer, mirror: Bool) { + private func savePreviewSnapshot(pixelBuffer: CVPixelBuffer, front: Bool) { Queue.concurrentDefaultQueue().async { var ciImage = CIImage(cvImageBuffer: pixelBuffer) let size = ciImage.extent.size - if mirror { + if front { var transform = CGAffineTransformMakeScale(1.0, -1.0) transform = CGAffineTransformTranslate(transform, 0.0, -size.height) ciImage = ciImage.transformed(by: transform) @@ -144,7 +144,7 @@ private final class CameraContext { ciImage = ciImage.clampedToExtent().applyingGaussianBlur(sigma: 40.0).cropped(to: CGRect(origin: .zero, size: size)) if let cgImage = self.cameraImageContext.createCGImage(ciImage, from: ciImage.extent) { let uiImage = UIImage(cgImage: cgImage, scale: 1.0, orientation: .right) - if mirror { + if front { CameraSimplePreviewView.saveLastFrontImage(uiImage) } else { CameraSimplePreviewView.saveLastBackImage(uiImage) @@ -163,41 +163,8 @@ private final class CameraContext { self.positionValue = configuration.position self._positionPromise = ValuePromise(configuration.position) - self.mainDeviceContext = CameraDeviceContext(session: session, exclusive: true, additional: false) - self.configure { - self.mainDeviceContext.configure(position: configuration.position, previewView: self.simplePreviewView, audio: configuration.audio, photo: configuration.photo, metadata: configuration.metadata) - } - - self.mainDeviceContext.output.processSampleBuffer = { [weak self] sampleBuffer, pixelBuffer, connection in - guard let self else { - return - } - self.previewNode?.enqueue(sampleBuffer) - - let timestamp = CACurrentMediaTime() - if timestamp > self.lastSnapshotTimestamp + 2.5 { - var mirror = false - if #available(iOS 13.0, *) { - mirror = connection.inputPorts.first?.sourceDevicePosition == .front - } - self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror) - self.lastSnapshotTimestamp = timestamp - } - } - - self.mainDeviceContext.output.processFaceLandmarks = { [weak self] observations in - guard let self else { - return - } - if let previewView = self.previewView { - previewView.drawFaceObservations(observations) - } - } - - self.mainDeviceContext.output.processCodes = { [weak self] codes in - self?.detectedCodesPipe.putNext(codes) - } - + self.setDualCameraEnabled(configuration.isDualEnabled, change: false) + NotificationCenter.default.addObserver( self, selector: #selector(self.sessionRuntimeError), @@ -217,10 +184,10 @@ private final class CameraContext { func stopCapture(invalidate: Bool = false) { if invalidate { - self.mainDeviceContext.device.resetZoom() + self.mainDeviceContext?.device.resetZoom() self.configure { - self.mainDeviceContext.invalidate() + self.mainDeviceContext?.invalidate() } } @@ -237,11 +204,11 @@ private final class CameraContext { focusMode = .autoFocus exposureMode = .autoExpose } - self.mainDeviceContext.device.setFocusPoint(point, focusMode: focusMode, exposureMode: exposureMode, monitorSubjectAreaChange: true) + self.mainDeviceContext?.device.setFocusPoint(point, focusMode: focusMode, exposureMode: exposureMode, monitorSubjectAreaChange: true) } func setFps(_ fps: Float64) { - self.mainDeviceContext.device.fps = fps + self.mainDeviceContext?.device.fps = fps } private var modeChange: Camera.ModeChange = .none { @@ -259,7 +226,10 @@ private final class CameraContext { private var positionValue: Camera.Position = .back func togglePosition() { - if self.isDualCameraEnabled { + guard let mainDeviceContext = self.mainDeviceContext else { + return + } + if self.isDualCameraEnabled == true { let targetPosition: Camera.Position if case .back = self.positionValue { targetPosition = .front @@ -269,13 +239,13 @@ private final class CameraContext { self.positionValue = targetPosition self._positionPromise.set(targetPosition) - self.mainDeviceContext.output.markPositionChange(position: targetPosition) + mainDeviceContext.output.markPositionChange(position: targetPosition) } else { self.configure { - self.mainDeviceContext.invalidate() + self.mainDeviceContext?.invalidate() let targetPosition: Camera.Position - if case .back = self.mainDeviceContext.device.position { + if case .back = mainDeviceContext.device.position { targetPosition = .front } else { targetPosition = .back @@ -284,7 +254,7 @@ private final class CameraContext { self._positionPromise.set(targetPosition) self.modeChange = .position - self.mainDeviceContext.configure(position: targetPosition, previewView: self.simplePreviewView, audio: self.initialConfiguration.audio, photo: self.initialConfiguration.photo, metadata: self.initialConfiguration.metadata) + mainDeviceContext.configure(position: targetPosition, previewView: self.simplePreviewView, audio: self.initialConfiguration.audio, photo: self.initialConfiguration.photo, metadata: self.initialConfiguration.metadata) self.queue.after(0.5) { self.modeChange = .none @@ -295,13 +265,13 @@ private final class CameraContext { public func setPosition(_ position: Camera.Position) { self.configure { - self.mainDeviceContext.invalidate() + self.mainDeviceContext?.invalidate() self._positionPromise.set(position) self.positionValue = position self.modeChange = .position - self.mainDeviceContext.configure(position: position, previewView: self.simplePreviewView, audio: self.initialConfiguration.audio, photo: self.initialConfiguration.photo, metadata: self.initialConfiguration.metadata) + self.mainDeviceContext?.configure(position: position, previewView: self.simplePreviewView, audio: self.initialConfiguration.audio, photo: self.initialConfiguration.photo, metadata: self.initialConfiguration.metadata) self.queue.after(0.5) { self.modeChange = .none @@ -309,103 +279,111 @@ private final class CameraContext { } } - private var isDualCameraEnabled = false - public func setDualCameraEnabled(_ enabled: Bool) { + private var isDualCameraEnabled: Bool? + public func setDualCameraEnabled(_ enabled: Bool, change: Bool = true) { guard enabled != self.isDualCameraEnabled else { return } self.isDualCameraEnabled = enabled - self.modeChange = .dualCamera + if change { + self.modeChange = .dualCamera + } + if enabled { self.configure { - self.mainDeviceContext.invalidate() + self.mainDeviceContext?.invalidate() self.mainDeviceContext = CameraDeviceContext(session: self.session, exclusive: false, additional: false) - self.mainDeviceContext.configure(position: .back, previewView: self.simplePreviewView, audio: self.initialConfiguration.audio, photo: self.initialConfiguration.photo, metadata: self.initialConfiguration.metadata) + self.mainDeviceContext?.configure(position: .back, previewView: self.simplePreviewView, audio: self.initialConfiguration.audio, photo: self.initialConfiguration.photo, metadata: self.initialConfiguration.metadata) self.additionalDeviceContext = CameraDeviceContext(session: self.session, exclusive: false, additional: true) self.additionalDeviceContext?.configure(position: .front, previewView: self.secondaryPreviewView, audio: false, photo: true, metadata: false) } - self.mainDeviceContext.output.processSampleBuffer = { [weak self] sampleBuffer, pixelBuffer, connection in - guard let self else { + self.mainDeviceContext?.output.processSampleBuffer = { [weak self] sampleBuffer, pixelBuffer, connection in + guard let self, let mainDeviceContext = self.mainDeviceContext else { return } self.previewNode?.enqueue(sampleBuffer) let timestamp = CACurrentMediaTime() - if timestamp > self.lastSnapshotTimestamp + 2.5 { - var mirror = false + if timestamp > self.lastSnapshotTimestamp + 2.5, !mainDeviceContext.output.isRecording { + var front = false if #available(iOS 13.0, *) { - mirror = connection.inputPorts.first?.sourceDevicePosition == .front + front = connection.inputPorts.first?.sourceDevicePosition == .front } - self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror) + self.savePreviewSnapshot(pixelBuffer: pixelBuffer, front: front) self.lastSnapshotTimestamp = timestamp } } self.additionalDeviceContext?.output.processSampleBuffer = { [weak self] sampleBuffer, pixelBuffer, connection in - guard let self else { + guard let self, let additionalDeviceContext = self.additionalDeviceContext else { return } let timestamp = CACurrentMediaTime() - if timestamp > self.lastAdditionalSnapshotTimestamp + 2.5 { - var mirror = false + if timestamp > self.lastAdditionalSnapshotTimestamp + 2.5, !additionalDeviceContext.output.isRecording { + var front = false if #available(iOS 13.0, *) { - mirror = connection.inputPorts.first?.sourceDevicePosition == .front + front = connection.inputPorts.first?.sourceDevicePosition == .front } - self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror) + self.savePreviewSnapshot(pixelBuffer: pixelBuffer, front: front) self.lastAdditionalSnapshotTimestamp = timestamp } } } else { self.configure { - self.mainDeviceContext.invalidate() + self.mainDeviceContext?.invalidate() self.additionalDeviceContext?.invalidate() self.additionalDeviceContext = nil self.mainDeviceContext = CameraDeviceContext(session: self.session, exclusive: true, additional: false) - self.mainDeviceContext.configure(position: self.positionValue, previewView: self.simplePreviewView, audio: self.initialConfiguration.audio, photo: self.initialConfiguration.photo, metadata: self.initialConfiguration.metadata) + self.mainDeviceContext?.configure(position: self.positionValue, previewView: self.simplePreviewView, audio: self.initialConfiguration.audio, photo: self.initialConfiguration.photo, metadata: self.initialConfiguration.metadata) } - self.mainDeviceContext.output.processSampleBuffer = { [weak self] sampleBuffer, pixelBuffer, connection in - guard let self else { + self.mainDeviceContext?.output.processSampleBuffer = { [weak self] sampleBuffer, pixelBuffer, connection in + guard let self, let mainDeviceContext = self.mainDeviceContext else { return } self.previewNode?.enqueue(sampleBuffer) let timestamp = CACurrentMediaTime() - if timestamp > self.lastSnapshotTimestamp + 2.5, !self.mainDeviceContext.output.isRecording { - var mirror = false + if timestamp > self.lastSnapshotTimestamp + 2.5, !mainDeviceContext.output.isRecording { + var front = false if #available(iOS 13.0, *) { - mirror = connection.inputPorts.first?.sourceDevicePosition == .front + front = connection.inputPorts.first?.sourceDevicePosition == .front } - self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror) + self.savePreviewSnapshot(pixelBuffer: pixelBuffer, front: front) self.lastSnapshotTimestamp = timestamp } } + self.mainDeviceContext?.output.processCodes = { [weak self] codes in + self?.detectedCodesPipe.putNext(codes) + } } - if #available(iOS 13.0, *), let previewView = self.simplePreviewView { - if enabled, let secondaryPreviewView = self.secondaryPreviewView { - let _ = (combineLatest(previewView.isPreviewing, secondaryPreviewView.isPreviewing) - |> map { first, second in - return first && second + if change { + if #available(iOS 13.0, *), let previewView = self.simplePreviewView { + if enabled, let secondaryPreviewView = self.secondaryPreviewView { + let _ = (combineLatest(previewView.isPreviewing, secondaryPreviewView.isPreviewing) + |> map { first, second in + return first && second + } + |> filter { $0 } + |> take(1) + |> delay(0.1, queue: self.queue) + |> deliverOn(self.queue)).start(next: { [weak self] _ in + self?.modeChange = .none + }) + } else { + let _ = (previewView.isPreviewing + |> filter { $0 } + |> take(1) + |> deliverOn(self.queue)).start(next: { [weak self] _ in + self?.modeChange = .none + }) } - |> filter { $0 } - |> take(1) - |> delay(0.1, queue: self.queue) - |> deliverOn(self.queue)).start(next: { [weak self] _ in - self?.modeChange = .none - }) } else { - let _ = (previewView.isPreviewing - |> filter { $0 } - |> take(1) - |> deliverOn(self.queue)).start(next: { [weak self] _ in - self?.modeChange = .none - }) - } - } else { - self.queue.after(0.4) { - self.modeChange = .none + self.queue.after(0.4) { + self.modeChange = .none + } } } } @@ -417,15 +395,15 @@ private final class CameraContext { } var hasTorch: Signal { - return self.mainDeviceContext.device.isTorchAvailable + return self.mainDeviceContext?.device.isTorchAvailable ?? .never() } func setTorchActive(_ active: Bool) { - self.mainDeviceContext.device.setTorchActive(active) + self.mainDeviceContext?.device.setTorchActive(active) } var isFlashActive: Signal { - return self.mainDeviceContext.output.isFlashActive + return self.mainDeviceContext?.output.isFlashActive ?? .never() } private var _flashMode: Camera.FlashMode = .off { @@ -443,19 +421,22 @@ private final class CameraContext { } func setZoomLevel(_ zoomLevel: CGFloat) { - self.mainDeviceContext.device.setZoomLevel(zoomLevel) + self.mainDeviceContext?.device.setZoomLevel(zoomLevel) } func setZoomDelta(_ zoomDelta: CGFloat) { - self.mainDeviceContext.device.setZoomDelta(zoomDelta) + self.mainDeviceContext?.device.setZoomDelta(zoomDelta) } func takePhoto() -> Signal { + guard let mainDeviceContext = self.mainDeviceContext else { + return .complete() + } let orientation = self.simplePreviewView?.videoPreviewLayer.connection?.videoOrientation ?? .portrait if let additionalDeviceContext = self.additionalDeviceContext { let dualPosition = self.positionValue return combineLatest( - self.mainDeviceContext.output.takePhoto(orientation: orientation, flashMode: self._flashMode), + mainDeviceContext.output.takePhoto(orientation: orientation, flashMode: self._flashMode), additionalDeviceContext.output.takePhoto(orientation: orientation, flashMode: self._flashMode) ) |> map { main, additional in if case let .finished(mainImage, _, _) = main, case let .finished(additionalImage, _, _) = additional { @@ -469,29 +450,35 @@ private final class CameraContext { } } |> distinctUntilChanged } else { - return self.mainDeviceContext.output.takePhoto(orientation: orientation, flashMode: self._flashMode) + return mainDeviceContext.output.takePhoto(orientation: orientation, flashMode: self._flashMode) } } public func startRecording() -> Signal { - self.mainDeviceContext.device.setTorchMode(self._flashMode) + guard let mainDeviceContext = self.mainDeviceContext else { + return .complete() + } + mainDeviceContext.device.setTorchMode(self._flashMode) if let additionalDeviceContext = self.additionalDeviceContext { return combineLatest( - self.mainDeviceContext.output.startRecording(isDualCamera: true, position: self.positionValue), + mainDeviceContext.output.startRecording(isDualCamera: true, position: self.positionValue), additionalDeviceContext.output.startRecording(isDualCamera: true) ) |> map { value, _ in return value } } else { - return self.mainDeviceContext.output.startRecording(isDualCamera: false) + return mainDeviceContext.output.startRecording(isDualCamera: false) } } public func stopRecording() -> Signal { + guard let mainDeviceContext = self.mainDeviceContext else { + return .complete() + } if let additionalDeviceContext = self.additionalDeviceContext { return combineLatest( - self.mainDeviceContext.output.stopRecording(), + mainDeviceContext.output.stopRecording(), additionalDeviceContext.output.stopRecording() ) |> mapToSignal { main, additional in if case let .finished(mainResult, _, duration, positionChangeTimestamps, _) = main, case let .finished(additionalResult, _, _, _, _) = additional { @@ -506,7 +493,7 @@ private final class CameraContext { } } else { let mirror = self.positionValue == .front - return self.mainDeviceContext.output.stopRecording() + return mainDeviceContext.output.stopRecording() |> map { result -> VideoCaptureResult in if case let .finished(mainResult, _, duration, positionChangeTimestamps, time) = result { var transitionImage = mainResult.1 @@ -557,14 +544,16 @@ public final class Camera { public struct Configuration { let preset: Preset let position: Position + let isDualEnabled: Bool let audio: Bool let photo: Bool let metadata: Bool let preferredFps: Double - public init(preset: Preset, position: Position, audio: Bool, photo: Bool, metadata: Bool, preferredFps: Double) { + public init(preset: Preset, position: Position, isDualEnabled: Bool = false, audio: Bool, photo: Bool, metadata: Bool, preferredFps: Double) { self.preset = preset self.position = position + self.isDualEnabled = isDualEnabled self.audio = audio self.photo = photo self.metadata = metadata diff --git a/submodules/Camera/Sources/CameraPreviewView.swift b/submodules/Camera/Sources/CameraPreviewView.swift index 9402aaadda..73046cbe8d 100644 --- a/submodules/Camera/Sources/CameraPreviewView.swift +++ b/submodules/Camera/Sources/CameraPreviewView.swift @@ -76,24 +76,9 @@ public class CameraSimplePreviewView: UIView { super.init(frame: frame) self.videoPreviewLayer.videoGravity = main ? .resizeAspectFill : .resizeAspect - self.placeholderView.contentMode = main ? .scaleAspectFill : .scaleAspectFit - self.addSubview(self.placeholderView) - if main { - if #available(iOS 13.0, *) { - self.previewingDisposable = (self.isPreviewing - |> filter { $0 } - |> take(1) - |> deliverOnMainQueue).start(next: { [weak self] _ in - self?.removePlaceholder(delay: 0.15) - }) - } else { - Queue.mainQueue().after(0.35) { - self.removePlaceholder(delay: 0.15) - } - } - } + self.addSubview(self.placeholderView) } required init?(coder: NSCoder) { diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index db5801bb61..35a83cc050 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -2586,7 +2586,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController context: context, account: context.account, sharedContext: context.sharedContext, - text: .markdown(text: "Posting stories is currently available only to subscribers of [Telegram Premium]()."), + text: .markdown(text: "Posting stories is currently available only\nto subscribers of [Telegram Premium]()."), style: .customBlur(UIColor(rgb: 0x2a2a2a), 2.0), icon: .none, location: .point(location, .top), diff --git a/submodules/DrawingUI/Sources/StickerPickerScreen.swift b/submodules/DrawingUI/Sources/StickerPickerScreen.swift index 99edb0f2e1..22da1fd1f9 100644 --- a/submodules/DrawingUI/Sources/StickerPickerScreen.swift +++ b/submodules/DrawingUI/Sources/StickerPickerScreen.swift @@ -219,6 +219,8 @@ private final class StickerSelectionComponent: Component { let topPanelHeight: CGFloat = 42.0 + let defaultToEmoji = component.getController()?.defaultToEmoji ?? false + let context = component.context let stickerPeekBehavior = EmojiContentPeekBehaviorImpl( context: context, @@ -247,7 +249,7 @@ private final class StickerSelectionComponent: Component { gifContent: nil, hasRecentGifs: false, availableGifSearchEmojies: [], - defaultToEmojiTab: false, + defaultToEmojiTab: defaultToEmoji, externalTopPanelContainer: self.panelHostView, externalBottomPanelContainer: nil, displayTopPanelBackground: .blur, @@ -1629,6 +1631,7 @@ public class StickerPickerScreen: ViewController { private let context: AccountContext private let theme: PresentationTheme private let inputData: Signal + fileprivate let defaultToEmoji: Bool private var currentLayout: ContainerViewLayout? @@ -1639,10 +1642,11 @@ public class StickerPickerScreen: ViewController { public var presentGallery: () -> Void = { } - public init(context: AccountContext, inputData: Signal) { + public init(context: AccountContext, inputData: Signal, defaultToEmoji: Bool = false) { self.context = context self.theme = defaultDarkColorPresentationTheme self.inputData = inputData + self.defaultToEmoji = defaultToEmoji super.init(navigationBarPresentationData: nil) diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index c9a5f21a7f..10a54ea68b 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -642,7 +642,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } #endif - if case .notDetermined = cameraAccess, !self.requestedCameraAccess { + if !stories, case .notDetermined = cameraAccess, !self.requestedCameraAccess { self.requestedCameraAccess = true self.mediaAssetsContext.requestCameraAccess() } @@ -1301,8 +1301,15 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { if let bannedSendPhotos = self.controller?.bannedSendPhotos, let bannedSendVideos = self.controller?.bannedSendVideos { bannedSendMedia = (max(bannedSendPhotos.0, bannedSendVideos.0), bannedSendPhotos.1 || bannedSendVideos.1) } - + if case let .noAccess(cameraAccess) = self.state { + var hasCamera = cameraAccess == .authorized + if let subject = self.controller?.subject, case .assets(_, .story) = subject { + hasCamera = false + + self.controller?.navigationItem.rightBarButtonItem = nil + } + var placeholderTransition = transition let placeholderNode: MediaPickerPlaceholderNode if let current = self.placeholderNode { @@ -1326,7 +1333,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.updateNavigation(transition: .immediate) } - placeholderNode.update(layout: layout, theme: self.presentationData.theme, strings: self.presentationData.strings, hasCamera: cameraAccess == .authorized, transition: placeholderTransition) + placeholderNode.update(layout: layout, theme: self.presentationData.theme, strings: self.presentationData.strings, hasCamera: hasCamera, transition: placeholderTransition) placeholderTransition.updateFrame(node: placeholderNode, frame: innerBounds) } else if let placeholderNode = self.placeholderNode, bannedSendMedia == nil { self.placeholderNode = nil diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index f0350e1920..ccf4a09496 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -224,13 +224,22 @@ public extension TelegramEngine { to peerId: EnginePeer.Id, replyTo replyToMessageId: EngineMessage.Id?, storyId: StoryId? = nil, - content: EngineOutgoingMessageContent + content: EngineOutgoingMessageContent, + silentPosting: Bool = false, + scheduleTime: Int32? = nil ) -> Signal<[MessageId?], NoError> { - let message: EnqueueMessage? + var message: EnqueueMessage? if case let .contextResult(results, result) = content { - message = self.outgoingMessageWithChatContextResult(to: peerId, threadId: nil, botId: results.botId, result: result, replyToMessageId: replyToMessageId, replyToStoryId: storyId, hideVia: true, silentPosting: false, scheduleTime: nil, correlationId: nil) + message = self.outgoingMessageWithChatContextResult(to: peerId, threadId: nil, botId: results.botId, result: result, replyToMessageId: replyToMessageId, replyToStoryId: storyId, hideVia: true, silentPosting: silentPosting, scheduleTime: scheduleTime, correlationId: nil) } else { var attributes: [MessageAttribute] = [] + if silentPosting { + attributes.append(NotificationInfoMessageAttribute(flags: .muted)) + } + if let scheduleTime = scheduleTime { + attributes.append(OutgoingScheduleInfoMessageAttribute(scheduleTime: scheduleTime)) + } + var text: String = "" var mediaReference: AnyMediaReference? switch content { @@ -257,6 +266,8 @@ public extension TelegramEngine { ) } + + guard let message = message else { return .complete() } diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift index a19e21448a..0dd2417d37 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift @@ -391,7 +391,7 @@ private final class CameraScreenComponent: CombinedComponent { self.lastDualCameraTimestamp = currentTimestamp controller.node.dismissAllTooltips() - let _ = ApplicationSpecificNotice.incrementStoriesDualCameraTip(accountManager: self.context.sharedContext.accountManager).start() + let _ = ApplicationSpecificNotice.incrementStoriesDualCameraTip(accountManager: self.context.sharedContext.accountManager, count: 2).start() let isEnabled = !controller.cameraState.isDualCameraEnabled camera.setDualCameraEnabled(isEnabled) @@ -772,7 +772,7 @@ private final class CameraScreenComponent: CombinedComponent { transition: .immediate ) context.add(dualButton - .position(CGPoint(x: availableSize.width - topControlInset - flashButton.size.width / 2.0 - 52.0, y: max(environment.statusBarHeight + 5.0, environment.safeInsets.top + topControlInset) + dualButton.size.height / 2.0 + 1.0)) + .position(CGPoint(x: availableSize.width - topControlInset - flashButton.size.width / 2.0 - 58.0, y: max(environment.statusBarHeight + 5.0, environment.safeInsets.top + topControlInset) + dualButton.size.height / 2.0 + 2.0)) .appear(.default(scale: true)) .disappear(.default(scale: true)) ) @@ -1133,13 +1133,7 @@ public class CameraScreen: ViewController { } else if dualCamWasEnabled != isDualCameraEnabled { self.requestUpdateLayout(hasAppeared: self.hasAppeared, transition: .spring(duration: 0.4)) - updateCameraStoredStateInteractively(engine: self.context.engine) { current in - if let current { - return current.withIsDualCameraEnabled(isDualCameraEnabled) - } else { - return CameraStoredState(isDualCameraEnabled: isDualCameraEnabled, dualCameraPosition: self.pipPosition) - } - } + UserDefaults.standard.set(isDualCameraEnabled as NSNumber, forKey: "TelegramStoryCameraIsDualEnabled") } } } @@ -1149,9 +1143,9 @@ public class CameraScreen: ViewController { self.context = controller.context self.updateState = ActionSlot() self.toggleCameraPositionAction = ActionSlot() - + self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - + self.backgroundView = UIView() self.backgroundView.backgroundColor = UIColor(rgb: 0x000000) @@ -1170,6 +1164,22 @@ public class CameraScreen: ViewController { self.previewBlurView = BlurView() self.previewBlurView.isUserInteractionEnabled = false + var isDualCameraEnabled = true + if let isDualCameraEnabledValue = UserDefaults.standard.object(forKey: "TelegramStoryCameraIsDualEnabled") as? NSNumber { + isDualCameraEnabled = isDualCameraEnabledValue.boolValue + } + + var dualCameraPosition: PIPPosition = .topRight + if let dualCameraPositionValue = UserDefaults.standard.object(forKey: "TelegramStoryCameraDualPosition") as? NSNumber { + dualCameraPosition = PIPPosition(rawValue: dualCameraPositionValue.int32Value) ?? .topRight + } + self.pipPosition = dualCameraPosition + + var cameraFrontPosition = false + if let cameraFrontPositionValue = UserDefaults.standard.object(forKey: "TelegramStoryCameraUseFrontPosition") as? NSNumber, cameraFrontPositionValue.boolValue { + cameraFrontPosition = true + } + self.mainPreviewContainerView = UIView() self.mainPreviewContainerView.clipsToBounds = true self.mainPreviewView = CameraSimplePreviewView(frame: .zero, main: true) @@ -1177,12 +1187,13 @@ public class CameraScreen: ViewController { self.additionalPreviewContainerView = UIView() self.additionalPreviewContainerView.clipsToBounds = true self.additionalPreviewView = CameraSimplePreviewView(frame: .zero, main: false) - - var cameraFrontPosition = false - if let useCameraFrontPosition = UserDefaults.standard.object(forKey: "TelegramStoryCameraUseFrontPosition") as? NSNumber, useCameraFrontPosition.boolValue { - cameraFrontPosition = true + + if isDualCameraEnabled { + self.mainPreviewView.resetPlaceholder(front: false) + self.additionalPreviewView.resetPlaceholder(front: true) + } else { + self.mainPreviewView.resetPlaceholder(front: cameraFrontPosition) } - self.mainPreviewView.resetPlaceholder(front: cameraFrontPosition) self.cameraState = CameraState( mode: .photo, @@ -1191,7 +1202,7 @@ public class CameraScreen: ViewController { flashModeDidChange: false, recording: .none, duration: 0.0, - isDualCameraEnabled: false + isDualCameraEnabled: isDualCameraEnabled ) self.previewFrameLeftDimView = UIView() @@ -1265,6 +1276,35 @@ public class CameraScreen: ViewController { } } + if #available(iOS 13.0, *) { + if isDualCameraEnabled { + let _ = (combineLatest( + queue: Queue.mainQueue(), + self.mainPreviewView.isPreviewing, + self.additionalPreviewView.isPreviewing + ) + |> filter { $0 && $1 } + |> take(1)).start(next: { [weak self] _, _ in + self?.mainPreviewView.removePlaceholder(delay: 0.15) + self?.additionalPreviewView.removePlaceholder(delay: 0.15) + }) + } else { + let _ = (self.mainPreviewView.isPreviewing + |> filter { $0 } + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] _ in + self?.mainPreviewView.removePlaceholder(delay: 0.15) + }) + } + } else { + Queue.mainQueue().after(0.35) { + self.mainPreviewView.removePlaceholder(delay: 0.15) + if isDualCameraEnabled { + self.additionalPreviewView.removePlaceholder(delay: 0.15) + } + } + } + self.idleTimerExtensionDisposable.set(self.context.sharedContext.applicationBindings.pushIdleTimerExtension()) } @@ -1313,6 +1353,7 @@ public class CameraScreen: ViewController { configuration: Camera.Configuration( preset: .hd1920x1080, position: self.cameraState.position, + isDualEnabled: self.cameraState.isDualCameraEnabled, audio: true, photo: true, metadata: false, @@ -1341,6 +1382,7 @@ public class CameraScreen: ViewController { } }) + var isFirstTime = true self.changingPositionDisposable = combineLatest( queue: Queue.mainQueue(), camera.modeChange, @@ -1397,9 +1439,13 @@ public class CameraScreen: ViewController { }) } - if self.cameraState.isDualCameraEnabled { - self.mainPreviewView.removePlaceholder() - self.additionalPreviewView.removePlaceholder() + if isFirstTime { + isFirstTime = false + } else { + if self.cameraState.isDualCameraEnabled { + self.mainPreviewView.removePlaceholder() + self.additionalPreviewView.removePlaceholder() + } } } } @@ -1508,13 +1554,7 @@ public class CameraScreen: ViewController { self.pipPosition = pipPositionForLocation(layout: layout, position: location, velocity: velocity) self.containerLayoutUpdated(layout: layout, transition: .spring(duration: 0.4)) - updateCameraStoredStateInteractively(engine: self.context.engine) { current in - if let current { - return current.withDualCameraPosition(self.pipPosition) - } else { - return CameraStoredState(isDualCameraEnabled: true, dualCameraPosition: self.pipPosition) - } - } + UserDefaults.standard.set(self.pipPosition.rawValue as NSNumber, forKey: "TelegramStoryCameraDualPosition") default: break } @@ -1797,13 +1837,12 @@ public class CameraScreen: ViewController { } let parentFrame = self.view.convert(self.bounds, to: nil) - let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0) - let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.maxY + 3.0), size: CGSize()) + let location = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0) let accountManager = self.context.sharedContext.accountManager - let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: "Enable Dual Camera Mode"), location: .point(location, .top), displayDuration: .manual, inset: 16.0, shouldDismissOnTouch: { point, containerFrame in + let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: "Tap here to disable\nthe selfie camera"), textAlignment: .center, location: .point(location, .right), displayDuration: .custom(5.0), inset: 16.0, shouldDismissOnTouch: { point, containerFrame in if containerFrame.contains(point) { - let _ = ApplicationSpecificNotice.incrementStoriesDualCameraTip(accountManager: accountManager).start() + let _ = ApplicationSpecificNotice.incrementStoriesDualCameraTip(accountManager: accountManager, count: 2).start() return .dismiss(consume: true) } return .ignore @@ -1846,7 +1885,7 @@ public class CameraScreen: ViewController { guard let self else { return } - if count < 1 { + if count < 2 { self.presentDualCameraTooltip() } }) @@ -2040,7 +2079,7 @@ public class CameraScreen: ViewController { self.appliedDualCamera = isDualCameraEnabled let circleSide = floorToScreenPixels(previewSize.width * 160.0 / 430.0) - let circleOffset = CGPoint(x: previewSize.width * 224.0 / 1080.0, y: previewSize.width * 520.0 / 1080.0) + let circleOffset = CGPoint(x: previewSize.width * 224.0 / 1080.0, y: previewSize.width * 480.0 / 1080.0) var origin: CGPoint switch self.pipPosition { diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraStoredState.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraStoredState.swift deleted file mode 100644 index dc5793e6d8..0000000000 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraStoredState.swift +++ /dev/null @@ -1,73 +0,0 @@ -import Foundation -import UIKit -import SwiftSignalKit -import TelegramCore -import TelegramUIPreferences -import MediaEditor - -public final class CameraStoredState: Codable { - private enum CodingKeys: String, CodingKey { - case isDualCameraEnabled - case dualCameraPosition - } - - public let isDualCameraEnabled: Bool - public let dualCameraPosition: CameraScreen.PIPPosition - - public init( - isDualCameraEnabled: Bool, - dualCameraPosition: CameraScreen.PIPPosition - ) { - self.isDualCameraEnabled = isDualCameraEnabled - self.dualCameraPosition = dualCameraPosition - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - - self.isDualCameraEnabled = try container.decode(Bool.self, forKey: .isDualCameraEnabled) - self.dualCameraPosition = CameraScreen.PIPPosition(rawValue: try container.decode(Int32.self, forKey: .dualCameraPosition)) ?? .topRight - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(self.isDualCameraEnabled, forKey: .isDualCameraEnabled) - try container.encode(self.dualCameraPosition.rawValue, forKey: .dualCameraPosition) - } - - public func withIsDualCameraEnabled(_ isDualCameraEnabled: Bool) -> CameraStoredState { - return CameraStoredState(isDualCameraEnabled: isDualCameraEnabled, dualCameraPosition: self.dualCameraPosition) - } - - public func withDualCameraPosition(_ dualCameraPosition: CameraScreen.PIPPosition) -> CameraStoredState { - return CameraStoredState(isDualCameraEnabled: self.isDualCameraEnabled, dualCameraPosition: dualCameraPosition) - } -} - -func cameraStoredState(engine: TelegramEngine) -> Signal { - let key = EngineDataBuffer(length: 4) - key.setInt32(0, value: 0) - - return engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: ApplicationSpecificItemCacheCollectionId.cameraState, id: key)) - |> map { entry -> CameraStoredState? in - return entry?.get(CameraStoredState.self) - } -} - -func updateCameraStoredStateInteractively(engine: TelegramEngine, _ f: @escaping (CameraStoredState?) -> CameraStoredState?) { - let key = EngineDataBuffer(length: 4) - key.setInt32(0, value: 0) - - let _ = (engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: ApplicationSpecificItemCacheCollectionId.cameraState, id: key)) - |> map { entry -> CameraStoredState? in - return entry?.get(CameraStoredState.self) - } - |> mapToSignal { state -> Signal in - if let updatedState = f(state) { - return engine.itemCache.put(collectionId: ApplicationSpecificItemCacheCollectionId.cameraState, id: key, item: updatedState) - } else { - return engine.itemCache.remove(collectionId: ApplicationSpecificItemCacheCollectionId.cameraState, id: key) - } - }).start() -} diff --git a/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeController.swift b/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeController.swift index 7e8a2c5462..22964531cb 100644 --- a/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeController.swift +++ b/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeController.swift @@ -37,6 +37,8 @@ public final class ChatScheduleTimeController: ViewController { private var presentationData: PresentationData private var presentationDataDisposable: Disposable? + public var dismissed: () -> Void = {} + public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: PeerId, mode: ChatScheduleTimeControllerMode, style: ChatScheduleTimeControllerStyle, currentTime: Int32? = nil, minimalTime: Int32? = nil, dismissByTapOutside: Bool = true, completion: @escaping (Int32) -> Void) { self.context = context self.peerId = peerId @@ -105,6 +107,7 @@ public final class ChatScheduleTimeController: ViewController { } override public func dismiss(completion: (() -> Void)? = nil) { + self.dismissed() self.controllerNode.animateOut(completion: completion) } diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift index b5da12df92..a12727b141 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift @@ -1136,17 +1136,19 @@ public func recommendedVideoExportConfiguration(values: MediaEditorValues, durat let compressionProperties: [String: Any] let codecType: AVVideoCodecType - if hasHEVCHardwareEncoder { - var bitrate: Int = 3700 - if image { + var bitrate: Int = 3700 + if image { + bitrate = 5000 + } else { + if duration < 10 { + bitrate = 5800 + } else if duration < 20 { + bitrate = 5500 + } else if duration < 30 { bitrate = 5000 - } else { - if duration < 10 { - bitrate = 5500 - } else if duration < 25 { - bitrate = 4500 - } } + } + if hasHEVCHardwareEncoder { codecType = AVVideoCodecType.hevc compressionProperties = [ AVVideoAverageBitRateKey: bitrate * 1000, @@ -1155,7 +1157,7 @@ public func recommendedVideoExportConfiguration(values: MediaEditorValues, durat } else { codecType = AVVideoCodecType.h264 compressionProperties = [ - AVVideoAverageBitRateKey: 3800000, + AVVideoAverageBitRateKey: bitrate * 1000, AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel, AVVideoH264EntropyModeKey: AVVideoH264EntropyModeCABAC ] diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 94b1afd3bc..03216f96ba 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -1117,7 +1117,7 @@ final class MediaEditorScreenComponent: Component { } self.deactivateInput() }, - sendMessageOptionsAction: { }, + sendMessageOptionsAction: nil, sendStickerAction: { _ in }, setMediaRecordingActive: nil, lockMediaRecording: nil, @@ -2797,6 +2797,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate private var drawingScreen: DrawingScreen? private var stickerScreen: StickerPickerScreen? + private var defaultToEmoji = false private var previousDrawingData: Data? private var previousDrawingEntities: [DrawingEntity]? @@ -2895,7 +2896,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate } switch mode { case .sticker: - let controller = StickerPickerScreen(context: self.context, inputData: self.stickerPickerInputData.get()) + self.mediaEditor?.stop() + let controller = StickerPickerScreen(context: self.context, inputData: self.stickerPickerInputData.get(), defaultToEmoji: self.defaultToEmoji) controller.completion = { [weak self] content in if let self { if let content { @@ -2905,8 +2907,17 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate self.hasAnyChanges = true self.controller?.isSavingAvailable = true self.controller?.requestLayout(transition: .immediate) + + if case let .file(file) = content { + if file.isCustomEmoji { + self.defaultToEmoji = true + } else { + self.defaultToEmoji = false + } + } } self.stickerScreen = nil + self.mediaEditor?.play() } } controller.customModalStyleOverlayTransitionFactorUpdated = { [weak self, weak controller] transition in @@ -3140,7 +3151,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate case bottomRight func getPosition(_ size: CGSize) -> CGPoint { - let offset = CGPoint(x: 224.0, y: 520.0) + let offset = CGPoint(x: 224.0, y: 480.0) switch self { case .topLeft: return CGPoint(x: offset.x, y: offset.y) diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StoryPreviewComponent.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StoryPreviewComponent.swift index 665ec1cbe5..ea111a1b23 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StoryPreviewComponent.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/StoryPreviewComponent.swift @@ -258,7 +258,7 @@ final class StoryPreviewComponent: Component { presentController: { _ in }, presentInGlobalOverlay: { _ in }, sendMessageAction: { }, - sendMessageOptionsAction: { }, + sendMessageOptionsAction: nil, sendStickerAction: { _ in }, setMediaRecordingActive: { _, _, _ in }, lockMediaRecording: nil, diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputActionButtonComponent.swift b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputActionButtonComponent.swift index 62f82d4c2a..2b848557c6 100644 --- a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputActionButtonComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputActionButtonComponent.swift @@ -46,7 +46,7 @@ public final class MessageInputActionButtonComponent: Component { public let mode: Mode public let action: (Mode, Action, Bool) -> Void - public let longPressAction: () -> Void + public let longPressAction: ((UIView, ContextGesture?) -> Void)? public let switchMediaInputMode: () -> Void public let updateMediaCancelFraction: (CGFloat) -> Void public let lockMediaRecording: () -> Void @@ -62,7 +62,7 @@ public final class MessageInputActionButtonComponent: Component { public init( mode: Mode, action: @escaping (Mode, Action, Bool) -> Void, - longPressAction: @escaping () -> Void, + longPressAction: ((UIView, ContextGesture?) -> Void)?, switchMediaInputMode: @escaping () -> Void, updateMediaCancelFraction: @escaping (CGFloat) -> Void, lockMediaRecording: @escaping () -> Void, @@ -113,9 +113,14 @@ public final class MessageInputActionButtonComponent: Component { return true } - public final class View: HighlightTrackingButton { + public final class View: UIView { private var micButton: ChatTextInputMediaRecordingButton? + + public let button: HighlightTrackingButtonNode + public let referenceNode: ContextReferenceContentNode + public let containerNode: ContextControllerSourceNode private let sendIconView: UIImageView + private var moreButton: MoreHeaderButton? private var component: MessageInputActionButtonComponent? @@ -124,13 +129,31 @@ public final class MessageInputActionButtonComponent: Component { override init(frame: CGRect) { self.sendIconView = UIImageView() + self.button = HighlightTrackingButtonNode() + self.referenceNode = ContextReferenceContentNode() + self.containerNode = ContextControllerSourceNode() + super.init(frame: frame) + + self.addSubview(self.button.view) + self.containerNode.addSubnode(self.referenceNode) + self.referenceNode.view.addSubview(self.sendIconView) + self.button.addSubnode(self.containerNode) + + self.containerNode.shouldBegin = { [weak self] location in + guard let self, let component = self.component, let _ = component.longPressAction else { + return false + } + return true + } + self.containerNode.activated = { [weak self] gesture, _ in + guard let self, let component = self.component, let longPressAction = component.longPressAction else { + return + } + longPressAction(self, gesture) + } - self.isMultipleTouchEnabled = false - - self.addSubview(self.sendIconView) - - self.highligthedChanged = { [weak self] highlighted in + self.button.highligthedChanged = { [weak self] highlighted in guard let self else { return } @@ -141,8 +164,10 @@ public final class MessageInputActionButtonComponent: Component { transition.setSublayerTransform(view: self, transform: CATransform3DMakeScale(scale, scale, 1.0)) } - self.addTarget(self, action: #selector(self.touchDown), for: .touchDown) - self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) + self.button.addTarget(self, action: #selector(self.touchDown), forControlEvents: .touchDown) + self.button.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside) +// but.addTarget(self, action: #selector(self.touchDown), for: .touchDown) +// self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) } required init?(coder: NSCoder) { @@ -162,10 +187,11 @@ public final class MessageInputActionButtonComponent: Component { } component.action(component.mode, .up, false) } - - override public func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { - return super.continueTracking(touch, with: event) - } + +// public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { +// let result = super.hitTest(point, with: event) +// return result +// } func update(component: MessageInputActionButtonComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { let previousComponent = self.component @@ -174,6 +200,8 @@ public final class MessageInputActionButtonComponent: Component { let themeUpdated = previousComponent?.theme !== component.theme + self.containerNode.isUserInteractionEnabled = component.longPressAction != nil + if self.micButton == nil { let micButton = ChatTextInputMediaRecordingButton( context: component.context, @@ -240,7 +268,7 @@ public final class MessageInputActionButtonComponent: Component { } } - if self.moreButton == nil { + if case .more = component.mode, self.moreButton == nil { let moreButton = MoreHeaderButton(color: .white) self.moreButton = moreButton self.addSubnode(moreButton) @@ -340,6 +368,10 @@ public final class MessageInputActionButtonComponent: Component { } } + transition.setFrame(view: self.button.view, frame: CGRect(origin: .zero, size: availableSize)) + transition.setFrame(view: self.containerNode.view, frame: CGRect(origin: .zero, size: availableSize)) + transition.setFrame(view: self.referenceNode.view, frame: CGRect(origin: .zero, size: availableSize)) + transition.setAlpha(view: self.sendIconView, alpha: sendAlpha) transition.setScale(view: self.sendIconView, scale: sendAlpha == 0.0 ? 0.01 : 1.0) diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift index e6c13b1016..d37f624d0a 100644 --- a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift @@ -72,7 +72,7 @@ public final class MessageInputPanelComponent: Component { public let presentController: (ViewController) -> Void public let presentInGlobalOverlay: (ViewController) -> Void public let sendMessageAction: () -> Void - public let sendMessageOptionsAction: () -> Void + public let sendMessageOptionsAction: ((UIView, ContextGesture?) -> Void)? public let sendStickerAction: (TelegramMediaFile) -> Void public let setMediaRecordingActive: ((Bool, Bool, Bool) -> Void)? public let lockMediaRecording: (() -> Void)? @@ -114,7 +114,7 @@ public final class MessageInputPanelComponent: Component { presentController: @escaping (ViewController) -> Void, presentInGlobalOverlay: @escaping (ViewController) -> Void, sendMessageAction: @escaping () -> Void, - sendMessageOptionsAction: @escaping () -> Void, + sendMessageOptionsAction: ((UIView, ContextGesture?) -> Void)?, sendStickerAction: @escaping (TelegramMediaFile) -> Void, setMediaRecordingActive: ((Bool, Bool, Bool) -> Void)?, lockMediaRecording: (() -> Void)?, @@ -747,7 +747,7 @@ public final class MessageInputPanelComponent: Component { break } }, - longPressAction: {}, + longPressAction: nil, switchMediaInputMode: { }, updateMediaCancelFraction: { _ in @@ -926,12 +926,7 @@ public final class MessageInputPanelComponent: Component { break } }, - longPressAction: { [weak self] in - guard let self, let component = self.component else { - return - } - component.sendMessageOptionsAction() - }, + longPressAction: component.sendMessageOptionsAction, switchMediaInputMode: { [weak self] in guard let self else { return diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift index b0483abbb1..a11fe9115e 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift @@ -52,6 +52,7 @@ public final class StoryContentContextImpl: StoryContentContext { PostboxViewKey.cachedPeerData(peerId: peerId), PostboxViewKey.storiesState(key: .peer(peerId)), PostboxViewKey.storyItems(peerId: peerId), + PostboxViewKey.peerPresences(peerIds: Set([peerId])) ] if peerId == context.account.peerId { inputKeys.append(PostboxViewKey.storiesState(key: .local)) @@ -112,6 +113,11 @@ public final class StoryContentContextImpl: StoryContentContext { return } let additionalPeerData: StoryContentContextState.AdditionalPeerData + var peerPresence: PeerPresence? + if let presencesView = views.views[PostboxViewKey.peerPresences(peerIds: Set([peerId]))] as? PeerPresencesView { + peerPresence = presencesView.presences[peerId] + } + if let cachedPeerDataView = views.views[PostboxViewKey.cachedPeerData(peerId: peerId)] as? CachedPeerDataView, let cachedUserData = cachedPeerDataView.cachedPeerData as? CachedUserData { var isMuted = false if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings { @@ -119,9 +125,17 @@ public final class StoryContentContextImpl: StoryContentContext { } else { isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: nil) } - additionalPeerData = StoryContentContextState.AdditionalPeerData(isMuted: isMuted, areVoiceMessagesAvailable: cachedUserData.voiceMessagesAvailable) + additionalPeerData = StoryContentContextState.AdditionalPeerData( + isMuted: isMuted, + areVoiceMessagesAvailable: cachedUserData.voiceMessagesAvailable, + presence: peerPresence.flatMap { EnginePeer.Presence($0) } + ) } else { - additionalPeerData = StoryContentContextState.AdditionalPeerData(isMuted: true, areVoiceMessagesAvailable: true) + additionalPeerData = StoryContentContextState.AdditionalPeerData( + isMuted: true, + areVoiceMessagesAvailable: true, + presence: peerPresence.flatMap { EnginePeer.Presence($0) } + ) } let state = stateView.value?.get(Stories.PeerState.self) @@ -923,6 +937,7 @@ public final class SingleStoryContentContextImpl: StoryContentContext { self.storyDisposable = (combineLatest(queue: .mainQueue(), context.engine.data.subscribe( TelegramEngine.EngineData.Item.Peer.Peer(id: storyId.peerId), + TelegramEngine.EngineData.Item.Peer.Presence(id: storyId.peerId), TelegramEngine.EngineData.Item.Peer.AreVoiceMessagesAvailable(id: storyId.peerId), TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: storyId.peerId), TelegramEngine.EngineData.Item.NotificationSettings.Global() @@ -960,18 +975,19 @@ public final class SingleStoryContentContextImpl: StoryContentContext { return } - let (peer, areVoiceMessagesAvailable, notificationSettings, globalNotificationSettings) = data + let (peer, presence, areVoiceMessagesAvailable, notificationSettings, globalNotificationSettings) = data let (item, peers, allEntityFiles) = itemAndPeers guard let peer else { return } - + let isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: notificationSettings._asNotificationSettings()) let additionalPeerData = StoryContentContextState.AdditionalPeerData( isMuted: isMuted, - areVoiceMessagesAvailable: areVoiceMessagesAvailable + areVoiceMessagesAvailable: areVoiceMessagesAvailable, + presence: presence ) if item == nil { @@ -1098,6 +1114,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { self.storyDisposable = (combineLatest(queue: .mainQueue(), context.engine.data.subscribe( TelegramEngine.EngineData.Item.Peer.Peer(id: peerId), + TelegramEngine.EngineData.Item.Peer.Presence(id: peerId), TelegramEngine.EngineData.Item.Peer.AreVoiceMessagesAvailable(id: peerId), TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peerId), TelegramEngine.EngineData.Item.NotificationSettings.Global() @@ -1111,7 +1128,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { return } - let (peer, areVoiceMessagesAvailable, notificationSettings, globalNotificationSettings) = data + let (peer, presence, areVoiceMessagesAvailable, notificationSettings, globalNotificationSettings) = data guard let peer else { return @@ -1121,7 +1138,8 @@ public final class PeerStoryListContentContextImpl: StoryContentContext { let additionalPeerData = StoryContentContextState.AdditionalPeerData( isMuted: isMuted, - areVoiceMessagesAvailable: areVoiceMessagesAvailable + areVoiceMessagesAvailable: areVoiceMessagesAvailable, + presence: presence ) self.listState = state diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift index b5447bb87e..aff2bd9ba7 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContent.swift @@ -113,19 +113,31 @@ public final class StoryContentItem: Equatable { public final class StoryContentContextState { public final class AdditionalPeerData: Equatable { - public static func == (lhs: StoryContentContextState.AdditionalPeerData, rhs: StoryContentContextState.AdditionalPeerData) -> Bool { - return lhs.isMuted == rhs.isMuted && lhs.areVoiceMessagesAvailable == rhs.areVoiceMessagesAvailable - } - public let isMuted: Bool public let areVoiceMessagesAvailable: Bool + public let presence: EnginePeer.Presence? public init( isMuted: Bool, - areVoiceMessagesAvailable: Bool + areVoiceMessagesAvailable: Bool, + presence: EnginePeer.Presence? ) { self.isMuted = isMuted self.areVoiceMessagesAvailable = areVoiceMessagesAvailable + self.presence = presence + } + + public static func == (lhs: StoryContentContextState.AdditionalPeerData, rhs: StoryContentContextState.AdditionalPeerData) -> Bool { + if lhs.isMuted != rhs.isMuted { + return false + } + if lhs.areVoiceMessagesAvailable != rhs.areVoiceMessagesAvailable { + return false + } + if lhs.presence != rhs.presence { + return false + } + return true } } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index 632d23202b..6b7276baed 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -1728,11 +1728,11 @@ public final class StoryItemSetContainerComponent: Component { } self.sendMessageContext.performSendMessageAction(view: self) }, - sendMessageOptionsAction: { [weak self] in + sendMessageOptionsAction: { [weak self] sourceView, gesture in guard let self else { return } - self.sendMessageContext.presentSendMessageOptions(view: self) + self.sendMessageContext.presentSendMessageOptions(view: self, sourceView: sourceView, gesture: gesture) }, sendStickerAction: { [weak self] sticker in guard let self else { @@ -2231,7 +2231,7 @@ public final class StoryItemSetContainerComponent: Component { mode: .more, action: { _, _, _ in }, - longPressAction: {}, + longPressAction: nil, switchMediaInputMode: { }, updateMediaCancelFraction: { _ in @@ -2838,7 +2838,7 @@ public final class StoryItemSetContainerComponent: Component { presentationData: presentationData, content: .sticker(context: context, file: animation, loop: false, title: nil, text: "Reaction Sent.", undoText: "View in Chat", customAction: { [weak self] in if let messageId = messageIds.first, let self { - self.navigateToPeer(peer: peer, chat: true, messageId: messageId) + self.navigateToPeer(peer: peer, chat: true, subject: messageId.flatMap { .message(id: .id($0), highlight: false, timecode: nil) }) } }), elevatedLayout: false, @@ -3179,7 +3179,7 @@ public final class StoryItemSetContainerComponent: Component { } } - func navigateToPeer(peer: EnginePeer, chat: Bool, messageId: EngineMessage.Id? = nil) { + func navigateToPeer(peer: EnginePeer, chat: Bool, subject: ChatControllerSubject? = nil) { guard let component = self.component else { return } @@ -3189,11 +3189,7 @@ public final class StoryItemSetContainerComponent: Component { guard let navigationController = controller.navigationController as? NavigationController else { return } - if messageId != nil || chat { - var subject: ChatControllerSubject? - if let messageId { - subject = .message(id: .id(messageId), highlight: false, timecode: nil) - } + if subject != nil || chat { component.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: component.context, chatLocation: .peer(peer), subject: subject, keepStack: .always, animated: true, pushController: { [weak controller, weak navigationController] chatController, animated, completion in guard let controller, let navigationController else { return @@ -3537,12 +3533,12 @@ public final class StoryItemSetContainerComponent: Component { } } - private func performMyMoreAction(sourceView: UIView, gesture: ContextGesture?) { + func dismissAllTooltips() { guard let component = self.component, let controller = component.controller() else { return } - component.controller()?.forEachController { c in + controller.forEachController { c in if let c = c as? UndoOverlayController { c.dismiss() } else if let c = c as? TooltipScreen { @@ -3550,6 +3546,14 @@ public final class StoryItemSetContainerComponent: Component { } return true } + } + + private func performMyMoreAction(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) var items: [ContextMenuItem] = [] @@ -3753,7 +3757,7 @@ public final class StoryItemSetContainerComponent: Component { let contextItems = ContextController.Items(content: .list(items), tip: tip, tipSignal: tipSignal) - let contextController = ContextController(account: component.context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView)), items: .single(contextItems), gesture: gesture) + let contextController = ContextController(account: component.context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView, position: .bottom)), items: .single(contextItems), gesture: gesture) contextController.dismissed = { [weak self] in guard let self else { return @@ -3779,12 +3783,7 @@ public final class StoryItemSetContainerComponent: Component { return } - component.controller()?.forEachController { c in - if let c = c as? UndoOverlayController { - c.dismiss() - } - return true - } + self.dismissAllTooltips() let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme) var items: [ContextMenuItem] = [] @@ -3976,7 +3975,7 @@ public final class StoryItemSetContainerComponent: Component { let contextItems = ContextController.Items(content: .list(items), tip: tip, tipSignal: tipSignal) - let contextController = ContextController(account: component.context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView)), items: .single(contextItems), gesture: gesture) + let contextController = ContextController(account: component.context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView, position: .bottom)), items: .single(contextItems), gesture: gesture) contextController.dismissed = { [weak self] in guard let self else { return @@ -4000,20 +3999,22 @@ public final class StoryItemSetContainerComponent: Component { } } -private final class HeaderContextReferenceContentSource: ContextReferenceContentSource { +final class HeaderContextReferenceContentSource: ContextReferenceContentSource { private let controller: ViewController private let sourceView: UIView + private let position: ContextControllerReferenceViewInfo.ActionsPosition var keepInPlace: Bool { return true } - init(controller: ViewController, sourceView: UIView) { + init(controller: ViewController, sourceView: UIView, position: ContextControllerReferenceViewInfo.ActionsPosition) { self.controller = controller self.sourceView = sourceView + self.position = position } func transitionInfo() -> ContextControllerReferenceViewInfo? { - return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds, actionsPosition: .bottom) + return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds, actionsPosition: self.position) } } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index ded26c75bc..df3ca81399 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -40,6 +40,8 @@ import OpenInExternalAppUI import SafariServices import MediaPasteboardUI import WebPBinding +import ContextUI +import ChatScheduleTimeController final class StoryItemSetContainerSendMessage { enum InputMode { @@ -331,7 +333,7 @@ final class StoryItemSetContainerSendMessage { } } - private func presentMessageSentTooltip(view: StoryItemSetContainerComponent.View, peer: EnginePeer, messageId: EngineMessage.Id?) { + private func presentMessageSentTooltip(view: StoryItemSetContainerComponent.View, peer: EnginePeer, messageId: EngineMessage.Id?, isScheduled: Bool = false) { guard let component = view.component, let controller = component.controller() as? StoryContainerScreen else { return } @@ -341,14 +343,17 @@ final class StoryItemSetContainerSendMessage { } let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + + let text = isScheduled ? "Message Scheduled" : "Message Sent" + let tooltipScreen = UndoOverlayController( presentationData: presentationData, - content: .actionSucceeded(title: "", text: "Message Sent", cancel: messageId != nil ? "View in Chat" : "", destructive: false), + content: .actionSucceeded(title: "", text: text, cancel: messageId != nil ? "View in Chat" : "", destructive: false), elevatedLayout: false, animateInAsReplacement: false, action: { [weak view, weak self] action in if case .undo = action, let messageId { - view?.navigateToPeer(peer: peer, chat: true, messageId: messageId) + view?.navigateToPeer(peer: peer, chat: true, subject: isScheduled ? .scheduledMessages : .message(id: .id(messageId), highlight: false, timecode: nil)) } self?.tooltipScreen = nil view?.updateIsProgressPaused() @@ -360,12 +365,110 @@ final class StoryItemSetContainerSendMessage { view.updateIsProgressPaused() } - func presentSendMessageOptions(view: StoryItemSetContainerComponent.View) { + func presentSendMessageOptions(view: StoryItemSetContainerComponent.View, sourceView: UIView, gesture: ContextGesture?) { + guard let component = view.component, let controller = component.controller() as? StoryContainerScreen else { + return + } + view.dismissAllTooltips() + + var sendWhenOnlineAvailable = false + if let presence = component.slice.additionalPeerData.presence, case .present = presence.status { + sendWhenOnlineAvailable = true + } + + let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme) + var items: [ContextMenuItem] = [] + + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_SendMessage_SendSilently, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/SilentIcon"), color: theme.contextMenu.primaryColor) + }, action: { [weak self, weak view] _, a in + a(.default) + + guard let self, let view else { + return + } + self.performSendMessageAction(view: view, silentPosting: true) + }))) + + if sendWhenOnlineAvailable { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_SendMessage_SendWhenOnline, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/WhenOnlineIcon"), color: theme.contextMenu.primaryColor) + }, action: { [weak self, weak view] _, a in + a(.default) + + guard let self, let view else { + return + } + self.performSendMessageAction(view: view, scheduleTime: scheduleWhenOnlineTimestamp) + }))) + } + + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_SendMessage_ScheduleMessage, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/ScheduleIcon"), color: theme.contextMenu.primaryColor) + }, action: { [weak self, weak view] _, a in + a(.default) + + guard let self, let view else { + return + } + self.presentScheduleTimePicker(view: view) + }))) + + + let contextItems = ContextController.Items(content: .list(items)) + + let contextController = ContextController(account: component.context.account, presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView, position: .top)), items: .single(contextItems), gesture: gesture) + contextController.dismissed = { [weak view] in + guard let view else { + return + } + view.contextController = nil + view.updateIsProgressPaused() + } + view.contextController = contextController + view.updateIsProgressPaused() + controller.present(contextController, in: .window(.root)) + } + + func presentScheduleTimePicker( + view: StoryItemSetContainerComponent.View + ) { + guard let component = view.component else { + return + } + let focusedItem = component.slice.item + guard let peerId = focusedItem.peerId else { + return + } + let controller = component.controller() as? StoryContainerScreen + + var sendWhenOnlineAvailable = false + if let presence = component.slice.additionalPeerData.presence, case .present = presence.status { + sendWhenOnlineAvailable = true + } + + let timeController = ChatScheduleTimeController(context: component.context, updatedPresentationData: nil, peerId: peerId, mode: .scheduledMessages(sendWhenOnlineAvailable: sendWhenOnlineAvailable), style: .media, currentTime: nil, minimalTime: nil, dismissByTapOutside: true, completion: { [weak self, weak view] time in + guard let self, let view else { + return + } + self.performSendMessageAction(view: view, scheduleTime: time) + }) + timeController.dismissed = { [weak self, weak view] in + guard let self, let view else { + return + } + self.actionSheet = nil + view.updateIsProgressPaused() + } + view.endEditing(true) + controller?.present(timeController, in: .window(.root)) + + self.actionSheet = timeController + view.updateIsProgressPaused() } func performSendMessageAction( - view: StoryItemSetContainerComponent.View + view: StoryItemSetContainerComponent.View, + silentPosting: Bool = false, + scheduleTime: Int32? = nil ) { guard let component = view.component else { return @@ -406,11 +509,13 @@ final class StoryItemSetContainerSendMessage { to: peerId, replyTo: nil, storyId: focusedStoryId, - content: .text(text.string, entities) + content: .text(text.string, entities), + silentPosting: silentPosting, + scheduleTime: scheduleTime ) |> deliverOnMainQueue).start(next: { [weak self, weak view] messageIds in Queue.mainQueue().after(0.3) { if let self, let view { - self.presentMessageSentTooltip(view: view, peer: peer, messageId: messageIds.first.flatMap { $0 }) + self.presentMessageSentTooltip(view: view, peer: peer, messageId: messageIds.first.flatMap { $0 }, isScheduled: scheduleTime != nil) } } }) @@ -2181,7 +2286,7 @@ final class StoryItemSetContainerSendMessage { |> deliverOnMainQueue).start(next: { [weak self, weak view] messageIds in Queue.mainQueue().after(0.3) { if let view { - self?.presentMessageSentTooltip(view: view, peer: peer, messageId: messageIds.first.flatMap { $0 }) + self?.presentMessageSentTooltip(view: view, peer: peer, messageId: messageIds.first.flatMap { $0 }, isScheduled: scheduleTime != nil) } } }) diff --git a/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift b/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift index 197e9212f1..9d2a07b477 100644 --- a/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift +++ b/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift @@ -78,7 +78,6 @@ private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 { case translationState = 10 case storySource = 11 case mediaEditorState = 12 - case cameraState = 13 } public struct ApplicationSpecificItemCacheCollectionId { @@ -93,7 +92,6 @@ public struct ApplicationSpecificItemCacheCollectionId { public static let translationState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.translationState.rawValue) public static let storySource = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.storySource.rawValue) public static let mediaEditorState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.mediaEditorState.rawValue) - public static let cameraState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.cameraState.rawValue) } private enum ApplicationSpecificOrderedItemListCollectionIdValues: Int32 { diff --git a/submodules/TooltipUI/Sources/TooltipScreen.swift b/submodules/TooltipUI/Sources/TooltipScreen.swift index c52e3e25cd..7776d648e1 100644 --- a/submodules/TooltipUI/Sources/TooltipScreen.swift +++ b/submodules/TooltipUI/Sources/TooltipScreen.swift @@ -542,7 +542,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode { animationSpacing = 8.0 } - let containerWidth = max(100.0, min(layout.size.width, 614.0) - (sideInset + layout.safeInsets.left) * 2.0) + let containerWidth = max(100.0, min(layout.size.width, 614.0) - sideInset * 2.0) var actionSize: CGSize = .zero @@ -652,11 +652,12 @@ private final class TooltipScreenNode: ViewControllerTracingNode { self.arrowNode.frame = arrowBounds self.arrowGradientNode?.frame = CGRect(origin: CGPoint(x: -arrowFrame.minX + backgroundFrame.minX, y: 0.0), size: backgroundFrame.size) case .right: - arrowFrame = CGRect(origin: CGPoint(x: backgroundFrame.width + arrowSize.height, y: rect.midY), size: CGSize(width: arrowSize.height, height: arrowSize.width)) + let arrowCenterY = floorToScreenPixels(rect.midY - arrowSize.height / 2.0) + arrowFrame = CGRect(origin: CGPoint(x: backgroundFrame.width + arrowSize.height, y: self.view.convert(CGPoint(x: 0.0, y: arrowCenterY), to: self.arrowContainer.supernode?.view).y), size: CGSize(width: arrowSize.height, height: arrowSize.width)) ContainedViewLayoutTransition.immediate.updateTransformRotation(node: self.arrowContainer, angle: -CGFloat.pi / 2.0) - transition.updateFrame(node: self.arrowContainer, frame: arrowFrame.offsetBy(dx: 8.0 - UIScreenPixel, dy: 16.0 + -backgroundFrame.minY - floorToScreenPixels((backgroundFrame.height + 20.0 - arrowSize.width) / 2.0))) + transition.updateFrame(node: self.arrowContainer, frame: arrowFrame.offsetBy(dx: 8.0 - UIScreenPixel, dy: 0.0)) let arrowBounds = CGRect(origin: .zero, size: arrowSize) self.arrowNode.frame = arrowBounds