mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various fixes
This commit is contained in:
parent
0d69b86b92
commit
5ad9671d70
@ -161,6 +161,7 @@ private final class CameraContext {
|
|||||||
self.secondaryPreviewView = secondaryPreviewView
|
self.secondaryPreviewView = secondaryPreviewView
|
||||||
|
|
||||||
self.positionValue = configuration.position
|
self.positionValue = configuration.position
|
||||||
|
self._positionPromise = ValuePromise<Camera.Position>(configuration.position)
|
||||||
|
|
||||||
self.mainDeviceContext = CameraDeviceContext(session: session, exclusive: true, additional: false)
|
self.mainDeviceContext = CameraDeviceContext(session: session, exclusive: true, additional: false)
|
||||||
self.configure {
|
self.configure {
|
||||||
@ -251,14 +252,14 @@ private final class CameraContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var _positionPromise = ValuePromise<Camera.Position>(.unspecified)
|
private var _positionPromise: ValuePromise<Camera.Position>
|
||||||
var position: Signal<Camera.Position, NoError> {
|
var position: Signal<Camera.Position, NoError> {
|
||||||
return self._positionPromise.get()
|
return self._positionPromise.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var positionValue: Camera.Position = .back
|
private var positionValue: Camera.Position = .back
|
||||||
func togglePosition() {
|
func togglePosition() {
|
||||||
if self.isDualCamEnabled {
|
if self.isDualCameraEnabled {
|
||||||
let targetPosition: Camera.Position
|
let targetPosition: Camera.Position
|
||||||
if case .back = self.positionValue {
|
if case .back = self.positionValue {
|
||||||
targetPosition = .front
|
targetPosition = .front
|
||||||
@ -308,12 +309,12 @@ private final class CameraContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var isDualCamEnabled = false
|
private var isDualCameraEnabled = false
|
||||||
public func setDualCamEnabled(_ enabled: Bool) {
|
public func setDualCameraEnabled(_ enabled: Bool) {
|
||||||
guard enabled != self.isDualCamEnabled else {
|
guard enabled != self.isDualCameraEnabled else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.isDualCamEnabled = enabled
|
self.isDualCameraEnabled = enabled
|
||||||
|
|
||||||
self.modeChange = .dualCamera
|
self.modeChange = .dualCamera
|
||||||
if enabled {
|
if enabled {
|
||||||
@ -659,10 +660,10 @@ public final class Camera {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func setDualCamEnabled(_ enabled: Bool) {
|
public func setDualCameraEnabled(_ enabled: Bool) {
|
||||||
self.queue.async {
|
self.queue.async {
|
||||||
if let context = self.contextRef?.takeUnretainedValue() {
|
if let context = self.contextRef?.takeUnretainedValue() {
|
||||||
context.setDualCamEnabled(enabled)
|
context.setDualCameraEnabled(enabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2568,16 +2568,41 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
switch self.storyPostingAvailability {
|
switch self.storyPostingAvailability {
|
||||||
case .premium:
|
case .premium:
|
||||||
guard self.isPremium else {
|
guard self.isPremium else {
|
||||||
let context = self.context
|
if let componentView = self.chatListHeaderView() {
|
||||||
var replaceImpl: ((ViewController) -> Void)?
|
var sourceFrame: CGRect?
|
||||||
let controller = context.sharedContext.makePremiumDemoController(context: self.context, subject: .stories, action: {
|
if fromList {
|
||||||
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .stories)
|
if let (transitionView, _) = componentView.storyPeerListView()?.transitionViewForItem(peerId: self.context.account.peerId) {
|
||||||
replaceImpl?(controller)
|
sourceFrame = transitionView.convert(transitionView.bounds, to: nil).offsetBy(dx: 18.0 - UIScreenPixel, dy: 1.0)
|
||||||
})
|
}
|
||||||
replaceImpl = { [weak controller] c in
|
} else {
|
||||||
controller?.replace(with: c)
|
if let rightButtonView = componentView.rightButtonViews["story"] {
|
||||||
|
sourceFrame = rightButtonView.convert(rightButtonView.bounds, to: nil).offsetBy(dx: 5.0, dy: -8.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let sourceFrame {
|
||||||
|
let context = self.context
|
||||||
|
let location = CGRect(origin: CGPoint(x: sourceFrame.midX, y: sourceFrame.maxY), size: CGSize())
|
||||||
|
let tooltipController = TooltipScreen(
|
||||||
|
context: context,
|
||||||
|
account: context.account,
|
||||||
|
sharedContext: context.sharedContext,
|
||||||
|
text: .markdown(text: "Posting stories is currently available only to subscribers of [Telegram Premium]()."),
|
||||||
|
style: .customBlur(UIColor(rgb: 0x2a2a2a), 2.0),
|
||||||
|
icon: .none,
|
||||||
|
location: .point(location, .top),
|
||||||
|
shouldDismissOnTouch: { [weak self] point, containerFrame in
|
||||||
|
if containerFrame.contains(point) {
|
||||||
|
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .stories)
|
||||||
|
self?.push(controller)
|
||||||
|
return .dismiss(consume: true)
|
||||||
|
} else {
|
||||||
|
return .dismiss(consume: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.present(tooltipController, in: .window(.root))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.push(controller)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case .disabled:
|
case .disabled:
|
||||||
|
@ -12,6 +12,186 @@ private let imageManager: PHCachingImageManager = {
|
|||||||
|
|
||||||
private let assetsQueue = Queue()
|
private let assetsQueue = Queue()
|
||||||
|
|
||||||
|
final class AssetDownloadManager {
|
||||||
|
private final class DownloadingAssetContext {
|
||||||
|
let identifier: String
|
||||||
|
let updated: () -> Void
|
||||||
|
|
||||||
|
var status: AssetDownloadStatus = .progress(0.0)
|
||||||
|
var disposable: Disposable?
|
||||||
|
|
||||||
|
init(identifier: String, updated: @escaping () -> Void) {
|
||||||
|
self.identifier = identifier
|
||||||
|
self.updated = updated
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.disposable?.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private let queue = Queue()
|
||||||
|
private var currentAssetContext: DownloadingAssetContext?
|
||||||
|
|
||||||
|
init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
}
|
||||||
|
|
||||||
|
func download(asset: PHAsset) {
|
||||||
|
if let currentAssetContext = self.currentAssetContext {
|
||||||
|
currentAssetContext.disposable?.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
let queue = self.queue
|
||||||
|
let identifier = asset.localIdentifier
|
||||||
|
|
||||||
|
let assetContext = DownloadingAssetContext(identifier: identifier, updated: { [weak self] in
|
||||||
|
queue.async {
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let currentAssetContext = self.currentAssetContext, currentAssetContext.identifier == identifier, let bag = self.progressObserverContexts[identifier] {
|
||||||
|
for f in bag.copyItems() {
|
||||||
|
f(currentAssetContext.status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
assetContext.disposable = (downloadAssetMediaData(asset)
|
||||||
|
|> deliverOn(queue)).start(next: { [weak self] status in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let currentAssetContext = self.currentAssetContext, currentAssetContext.identifier == identifier {
|
||||||
|
currentAssetContext.status = status
|
||||||
|
currentAssetContext.updated()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
self.currentAssetContext = assetContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func cancel(identifier: String) {
|
||||||
|
if let currentAssetContext = self.currentAssetContext, currentAssetContext.identifier == identifier {
|
||||||
|
currentAssetContext.disposable?.dispose()
|
||||||
|
self.currentAssetContext = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var progressObserverContexts: [String: Bag<(AssetDownloadStatus) -> Void>] = [:]
|
||||||
|
private func downloadProgress(identifier: String, next: @escaping (AssetDownloadStatus) -> Void) -> Disposable {
|
||||||
|
let bag: Bag<(AssetDownloadStatus) -> Void>
|
||||||
|
if let current = self.progressObserverContexts[identifier] {
|
||||||
|
bag = current
|
||||||
|
} else {
|
||||||
|
bag = Bag()
|
||||||
|
self.progressObserverContexts[identifier] = bag
|
||||||
|
}
|
||||||
|
|
||||||
|
let index = bag.add(next)
|
||||||
|
if let currentAssetContext = self.currentAssetContext, currentAssetContext.identifier == identifier {
|
||||||
|
next(currentAssetContext.status)
|
||||||
|
} else {
|
||||||
|
next(.progress(0.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
let queue = self.queue
|
||||||
|
return ActionDisposable { [weak self, weak bag] in
|
||||||
|
queue.async {
|
||||||
|
guard let `self` = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let bag = bag, let listBag = self.progressObserverContexts[identifier], listBag === bag {
|
||||||
|
bag.remove(index)
|
||||||
|
if bag.isEmpty {
|
||||||
|
self.progressObserverContexts.removeValue(forKey: identifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadProgress(identifier: String) -> Signal<AssetDownloadStatus, NoError> {
|
||||||
|
return Signal { [weak self] subscriber in
|
||||||
|
if let self {
|
||||||
|
return self.downloadProgress(identifier: identifier, next: { status in
|
||||||
|
subscriber.putNext(status)
|
||||||
|
if case .completed = status {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkIfAssetIsLocal(_ asset: PHAsset) -> Signal<Bool, NoError> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let options = PHImageRequestOptions()
|
||||||
|
options.isNetworkAccessAllowed = false
|
||||||
|
|
||||||
|
let requestId: PHImageRequestID
|
||||||
|
if #available(iOS 13, *) {
|
||||||
|
requestId = imageManager.requestImageDataAndOrientation(for: asset, options: options) { data, _, _, _ in
|
||||||
|
if data != nil {
|
||||||
|
subscriber.putNext(data != nil)
|
||||||
|
}
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
requestId = imageManager.requestImageData(for: asset, options: options) { data, _, _, _ in
|
||||||
|
if data != nil {
|
||||||
|
subscriber.putNext(data != nil)
|
||||||
|
}
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
imageManager.cancelImageRequest(requestId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AssetDownloadStatus {
|
||||||
|
case progress(Float)
|
||||||
|
case completed
|
||||||
|
}
|
||||||
|
|
||||||
|
private func downloadAssetMediaData(_ asset: PHAsset) -> Signal<AssetDownloadStatus, NoError> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let options = PHImageRequestOptions()
|
||||||
|
options.isNetworkAccessAllowed = true
|
||||||
|
options.progressHandler = { progress, _, _, _ in
|
||||||
|
subscriber.putNext(.progress(Float(progress)))
|
||||||
|
}
|
||||||
|
|
||||||
|
let requestId: PHImageRequestID
|
||||||
|
if #available(iOS 13, *) {
|
||||||
|
requestId = imageManager.requestImageDataAndOrientation(for: asset, options: options) { data, _, _, _ in
|
||||||
|
if data != nil {
|
||||||
|
subscriber.putNext(.completed)
|
||||||
|
}
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
requestId = imageManager.requestImageData(for: asset, options: options) { data, _, _, _ in
|
||||||
|
if data != nil {
|
||||||
|
subscriber.putNext(.completed)
|
||||||
|
}
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
imageManager.cancelImageRequest(requestId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func assetImage(fetchResult: PHFetchResult<PHAsset>, index: Int, targetSize: CGSize, exact: Bool, deliveryMode: PHImageRequestOptionsDeliveryMode = .opportunistic, synchronous: Bool = false) -> Signal<UIImage?, NoError> {
|
func assetImage(fetchResult: PHFetchResult<PHAsset>, index: Int, targetSize: CGSize, exact: Bool, deliveryMode: PHImageRequestOptionsDeliveryMode = .opportunistic, synchronous: Bool = false) -> Signal<UIImage?, NoError> {
|
||||||
let asset = fetchResult[index]
|
let asset = fetchResult[index]
|
||||||
return assetImage(asset: asset, targetSize: targetSize, exact: exact, deliveryMode: deliveryMode, synchronous: synchronous)
|
return assetImage(asset: asset, targetSize: targetSize, exact: exact, deliveryMode: deliveryMode, synchronous: synchronous)
|
||||||
@ -68,3 +248,10 @@ func assetVideo(fetchResult: PHFetchResult<PHAsset>, index: Int) -> Signal<AVAss
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension PHAsset {
|
||||||
|
var isLocallyAvailable: Bool? {
|
||||||
|
let resourceArray = PHAssetResource.assetResources(for: self)
|
||||||
|
return resourceArray.first?.value(forKey: "locallyAvailable") as? Bool
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -777,6 +777,10 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
|
|
||||||
private weak var currentGalleryController: TGModernGalleryController?
|
private weak var currentGalleryController: TGModernGalleryController?
|
||||||
|
|
||||||
|
private func requestAssetDownload(_ asset: PHAsset) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private var openingMedia = false
|
private var openingMedia = false
|
||||||
fileprivate func openMedia(fetchResult: PHFetchResult<PHAsset>, index: Int, immediateThumbnail: UIImage?) {
|
fileprivate func openMedia(fetchResult: PHFetchResult<PHAsset>, index: Int, immediateThumbnail: UIImage?) {
|
||||||
guard let controller = self.controller, let interaction = controller.interaction, let (layout, _) = self.validLayout, !self.openingMedia else {
|
guard let controller = self.controller, let interaction = controller.interaction, let (layout, _) = self.validLayout, !self.openingMedia else {
|
||||||
@ -788,7 +792,29 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
|
|
||||||
if let customSelection = controller.customSelection {
|
if let customSelection = controller.customSelection {
|
||||||
self.openingMedia = true
|
self.openingMedia = true
|
||||||
customSelection(controller, fetchResult[index])
|
|
||||||
|
let asset = fetchResult[index]
|
||||||
|
customSelection(controller, asset)
|
||||||
|
|
||||||
|
// let isLocallyAvailable = asset.isLocallyAvailable
|
||||||
|
//
|
||||||
|
// if let isLocallyAvailable {
|
||||||
|
// if isLocallyAvailable {
|
||||||
|
// customSelection(controller, asset)
|
||||||
|
// } else {
|
||||||
|
// self.requestAssetDownload(asset)
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// let _ = (checkIfAssetIsLocal(asset)
|
||||||
|
// |> deliverOnMainQueue).start(next: { [weak self] isLocallyAvailable in
|
||||||
|
// if isLocallyAvailable {
|
||||||
|
// customSelection(controller, asset)
|
||||||
|
// } else {
|
||||||
|
// self?.requestAssetDownload(asset)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
Queue.mainQueue().after(0.3) {
|
Queue.mainQueue().after(0.3) {
|
||||||
self.openingMedia = false
|
self.openingMedia = false
|
||||||
}
|
}
|
||||||
|
@ -1729,7 +1729,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
if let strongSelf = self, (count < 2 && currentTimestamp > timestamp + 24 * 60 * 60) {
|
if let strongSelf = self, (count < 2 && currentTimestamp > timestamp + 24 * 60 * 60) {
|
||||||
strongSelf.displayedPreviewTooltip = true
|
strongSelf.displayedPreviewTooltip = true
|
||||||
|
|
||||||
let controller = TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: .plain(text: isDark ? strongSelf.presentationData.strings.WallpaperPreview_PreviewInDayMode : strongSelf.presentationData.strings.WallpaperPreview_PreviewInNightMode), style: .customBlur(UIColor(rgb: 0x333333, alpha: 0.35)), icon: nil, location: .point(frame.offsetBy(dx: 1.0, dy: 6.0), .bottom), displayDuration: .custom(3.0), inset: 3.0, shouldDismissOnTouch: { _, _ in
|
let controller = TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: .plain(text: isDark ? strongSelf.presentationData.strings.WallpaperPreview_PreviewInDayMode : strongSelf.presentationData.strings.WallpaperPreview_PreviewInNightMode), style: .customBlur(UIColor(rgb: 0x333333, alpha: 0.35), 0.0), icon: nil, location: .point(frame.offsetBy(dx: 1.0, dy: 6.0), .bottom), displayDuration: .custom(3.0), inset: 3.0, shouldDismissOnTouch: { _, _ in
|
||||||
return .dismiss(consume: false)
|
return .dismiss(consume: false)
|
||||||
})
|
})
|
||||||
strongSelf.galleryController()?.present(controller, in: .current)
|
strongSelf.galleryController()?.present(controller, in: .current)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,73 @@
|
|||||||
|
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<CameraStoredState?, NoError> {
|
||||||
|
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<Never, NoError> 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()
|
||||||
|
}
|
@ -1117,7 +1117,7 @@ final class CaptureControlsComponent: Component {
|
|||||||
),
|
),
|
||||||
minSize: hintIconSize,
|
minSize: hintIconSize,
|
||||||
action: {
|
action: {
|
||||||
component.flipTapped()
|
component.lockRecording()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
@ -341,41 +341,54 @@ private func verticesData(
|
|||||||
bottomRight = simd_float2(1.0, 1.0)
|
bottomRight = simd_float2(1.0, 1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
let relativeSize = CGSize(
|
let angle = Float(.pi - rotation)
|
||||||
width: size.width / containerSize.width,
|
let cosAngle = cos(angle)
|
||||||
height: size.height / containerSize.height
|
let sinAngle = sin(angle)
|
||||||
)
|
|
||||||
let relativeOffset = CGPoint(
|
|
||||||
x: position.x / containerSize.width,
|
|
||||||
y: position.y / containerSize.height
|
|
||||||
)
|
|
||||||
|
|
||||||
let rect = CGRect(
|
let centerX = Float(position.x)
|
||||||
origin: CGPoint(
|
let centerY = Float(position.y)
|
||||||
x: relativeOffset.x - relativeSize.width / 2.0,
|
|
||||||
y: relativeOffset.y - relativeSize.height / 2.0
|
let halfWidth = Float(size.width / 2.0)
|
||||||
),
|
let halfHeight = Float(size.height / 2.0)
|
||||||
size: relativeSize
|
|
||||||
)
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
VertexData(
|
VertexData(
|
||||||
pos: simd_float4(x: Float(rect.minX) * 2.0, y: Float(rect.minY) * 2.0, z: z, w: 1),
|
pos: simd_float4(
|
||||||
|
x: (centerX + (halfWidth * cosAngle) - (halfHeight * sinAngle)) / Float(containerSize.width) * 2.0,
|
||||||
|
y: (centerY + (halfWidth * sinAngle) + (halfHeight * cosAngle)) / Float(containerSize.height) * 2.0,
|
||||||
|
z: z,
|
||||||
|
w: 1
|
||||||
|
),
|
||||||
texCoord: topLeft,
|
texCoord: topLeft,
|
||||||
localPos: simd_float2(0.0, 0.0)
|
localPos: simd_float2(0.0, 0.0)
|
||||||
),
|
),
|
||||||
VertexData(
|
VertexData(
|
||||||
pos: simd_float4(x: Float(rect.maxX) * 2.0, y: Float(rect.minY) * 2.0, z: z, w: 1),
|
pos: simd_float4(
|
||||||
|
x: (centerX - (halfWidth * cosAngle) - (halfHeight * sinAngle)) / Float(containerSize.width) * 2.0,
|
||||||
|
y: (centerY - (halfWidth * sinAngle) + (halfHeight * cosAngle)) / Float(containerSize.height) * 2.0,
|
||||||
|
z: z,
|
||||||
|
w: 1
|
||||||
|
),
|
||||||
texCoord: topRight,
|
texCoord: topRight,
|
||||||
localPos: simd_float2(1.0, 0.0)
|
localPos: simd_float2(1.0, 0.0)
|
||||||
),
|
),
|
||||||
VertexData(
|
VertexData(
|
||||||
pos: simd_float4(x: Float(rect.minX) * 2.0, y: Float(rect.maxY) * 2.0, z: z, w: 1),
|
pos: simd_float4(
|
||||||
|
x: (centerX + (halfWidth * cosAngle) + (halfHeight * sinAngle)) / Float(containerSize.width) * 2.0,
|
||||||
|
y: (centerY + (halfWidth * sinAngle) - (halfHeight * cosAngle)) / Float(containerSize.height) * 2.0,
|
||||||
|
z: z,
|
||||||
|
w: 1
|
||||||
|
),
|
||||||
texCoord: bottomLeft,
|
texCoord: bottomLeft,
|
||||||
localPos: simd_float2(0.0, 1.0)
|
localPos: simd_float2(0.0, 1.0)
|
||||||
),
|
),
|
||||||
VertexData(
|
VertexData(
|
||||||
pos: simd_float4(x: Float(rect.maxX) * 2.0, y: Float(rect.maxY) * 2.0, z: z, w: 1),
|
pos: simd_float4(
|
||||||
|
x: (centerX - (halfWidth * cosAngle) + (halfHeight * sinAngle)) / Float(containerSize.width) * 2.0,
|
||||||
|
y: (centerY - (halfWidth * sinAngle) - (halfHeight * cosAngle)) / Float(containerSize.height) * 2.0,
|
||||||
|
z: z,
|
||||||
|
w: 1
|
||||||
|
),
|
||||||
texCoord: bottomRight,
|
texCoord: bottomRight,
|
||||||
localPos: simd_float2(1.0, 1.0)
|
localPos: simd_float2(1.0, 1.0)
|
||||||
)
|
)
|
||||||
@ -650,13 +663,13 @@ final class VideoInputScalePass: RenderPass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func process(input: MTLTexture, secondInput: MTLTexture?, timestamp: CMTime, device: MTLDevice, commandBuffer: MTLCommandBuffer) -> MTLTexture? {
|
func process(input: MTLTexture, secondInput: MTLTexture?, timestamp: CMTime, device: MTLDevice, commandBuffer: MTLCommandBuffer) -> MTLTexture? {
|
||||||
//#if targetEnvironment(simulator)
|
#if targetEnvironment(simulator)
|
||||||
//
|
|
||||||
//#else
|
#else
|
||||||
guard max(input.width, input.height) > 1920 || secondInput != nil else {
|
guard max(input.width, input.height) > 1920 || secondInput != nil else {
|
||||||
return input
|
return input
|
||||||
}
|
}
|
||||||
//#endif
|
#endif
|
||||||
|
|
||||||
let scaledSize = CGSize(width: input.width, height: input.height).fitted(CGSize(width: 1920.0, height: 1920.0))
|
let scaledSize = CGSize(width: input.width, height: input.height).fitted(CGSize(width: 1920.0, height: 1920.0))
|
||||||
let width: Int
|
let width: Int
|
||||||
@ -705,9 +718,9 @@ final class VideoInputScalePass: RenderPass {
|
|||||||
|
|
||||||
renderCommandEncoder.setRenderPipelineState(self.mainPipelineState!)
|
renderCommandEncoder.setRenderPipelineState(self.mainPipelineState!)
|
||||||
|
|
||||||
//#if targetEnvironment(simulator)
|
#if targetEnvironment(simulator)
|
||||||
// let secondInput = input
|
let secondInput = input
|
||||||
//#endif
|
#endif
|
||||||
|
|
||||||
let (mainVideoState, additionalVideoState, transitionVideoState) = self.transitionState(for: timestamp, mainInput: input, additionalInput: secondInput)
|
let (mainVideoState, additionalVideoState, transitionVideoState) = self.transitionState(for: timestamp, mainInput: input, additionalInput: secondInput)
|
||||||
|
|
||||||
|
@ -935,7 +935,9 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
|
|
||||||
let scrubberFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - scrubberSize.width) / 2.0), y: availableSize.height - environment.safeInsets.bottom - scrubberSize.height - 8.0 + controlsBottomInset), size: scrubberSize)
|
let scrubberFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - scrubberSize.width) / 2.0), y: availableSize.height - environment.safeInsets.bottom - scrubberSize.height - 8.0 + controlsBottomInset), size: scrubberSize)
|
||||||
if let scrubberView = self.scrubber.view {
|
if let scrubberView = self.scrubber.view {
|
||||||
|
var animateIn = false
|
||||||
if scrubberView.superview == nil {
|
if scrubberView.superview == nil {
|
||||||
|
animateIn = true
|
||||||
if let inputPanelBackgroundView = self.inputPanelBackground.view, inputPanelBackgroundView.superview != nil {
|
if let inputPanelBackgroundView = self.inputPanelBackground.view, inputPanelBackgroundView.superview != nil {
|
||||||
self.insertSubview(scrubberView, belowSubview: inputPanelBackgroundView)
|
self.insertSubview(scrubberView, belowSubview: inputPanelBackgroundView)
|
||||||
} else {
|
} else {
|
||||||
@ -945,6 +947,10 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
transition.setFrame(view: scrubberView, frame: scrubberFrame)
|
transition.setFrame(view: scrubberView, frame: scrubberFrame)
|
||||||
if !self.animatingButtons {
|
if !self.animatingButtons {
|
||||||
transition.setAlpha(view: scrubberView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0)
|
transition.setAlpha(view: scrubberView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0)
|
||||||
|
} else if animateIn {
|
||||||
|
scrubberView.layer.animatePosition(from: CGPoint(x: 0.0, y: 44.0), to: .zero, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||||
|
scrubberView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
|
scrubberView.layer.animateScale(from: 0.6, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3134,15 +3140,16 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
case bottomRight
|
case bottomRight
|
||||||
|
|
||||||
func getPosition(_ size: CGSize) -> CGPoint {
|
func getPosition(_ size: CGSize) -> CGPoint {
|
||||||
|
let offset = CGPoint(x: 224.0, y: 520.0)
|
||||||
switch self {
|
switch self {
|
||||||
case .topLeft:
|
case .topLeft:
|
||||||
return CGPoint(x: 224.0, y: 477.0)
|
return CGPoint(x: offset.x, y: offset.y)
|
||||||
case .topRight:
|
case .topRight:
|
||||||
return CGPoint(x: size.width - 224.0, y: 477.0)
|
return CGPoint(x: size.width - offset.x, y: offset.y)
|
||||||
case .bottomLeft:
|
case .bottomLeft:
|
||||||
return CGPoint(x: 224.0, y: size.height - 477.0)
|
return CGPoint(x: offset.x, y: size.height - offset.y)
|
||||||
case .bottomRight:
|
case .bottomRight:
|
||||||
return CGPoint(x: size.width - 224.0, y: size.height - 477.0)
|
return CGPoint(x: size.width - offset.x, y: size.height - offset.y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3374,28 +3381,29 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let controller = ShareWithPeersScreen(
|
||||||
self.push(
|
context: self.context,
|
||||||
ShareWithPeersScreen(
|
initialPrivacy: privacy,
|
||||||
context: self.context,
|
allowScreenshots: !isForwardingDisabled,
|
||||||
initialPrivacy: privacy,
|
pin: pin,
|
||||||
allowScreenshots: !isForwardingDisabled,
|
stateContext: stateContext,
|
||||||
pin: pin,
|
completion: { [weak self] result, isForwardingDisabled, pin in
|
||||||
stateContext: stateContext,
|
guard let self else {
|
||||||
completion: { [weak self] result, isForwardingDisabled, pin in
|
return
|
||||||
guard let self else {
|
}
|
||||||
return
|
if case .closeFriends = privacy.base {
|
||||||
}
|
let _ = self.context.engine.privacy.updateCloseFriends(peerIds: result.additionallyIncludePeers).start()
|
||||||
if case .closeFriends = privacy.base {
|
completion(EngineStoryPrivacy(base: .closeFriends, additionallyIncludePeers: []))
|
||||||
let _ = self.context.engine.privacy.updateCloseFriends(peerIds: result.additionallyIncludePeers).start()
|
} else {
|
||||||
completion(EngineStoryPrivacy(base: .closeFriends, additionallyIncludePeers: []))
|
completion(result)
|
||||||
} else {
|
}
|
||||||
completion(result)
|
},
|
||||||
}
|
editCategory: { _, _, _ in }
|
||||||
},
|
|
||||||
editCategory: { _, _, _ in }
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
controller.dismissed = {
|
||||||
|
self.node.mediaEditor?.play()
|
||||||
|
}
|
||||||
|
self.push(controller)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1235,7 +1235,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
centerInfoView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
centerInfoView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||||
}
|
}
|
||||||
if let moreButtonView = self.moreButton.view {
|
if let moreButtonView = self.moreButton.view {
|
||||||
moreButtonView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
moreButtonView.layer.animateAlpha(from: 0.0, to: moreButtonView.alpha, duration: 0.25)
|
||||||
}
|
}
|
||||||
if let soundButtonView = self.soundButton.view {
|
if let soundButtonView = self.soundButton.view {
|
||||||
soundButtonView.layer.animateAlpha(from: 0.0, to: soundButtonView.alpha, duration: 0.25)
|
soundButtonView.layer.animateAlpha(from: 0.0, to: soundButtonView.alpha, duration: 0.25)
|
||||||
@ -1361,7 +1361,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
centerInfoView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
|
centerInfoView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
|
||||||
}
|
}
|
||||||
if let moreButtonView = self.moreButton.view {
|
if let moreButtonView = self.moreButton.view {
|
||||||
moreButtonView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
|
moreButtonView.layer.animateAlpha(from: moreButtonView.alpha, to: 0.0, duration: 0.25, removeOnCompletion: false)
|
||||||
}
|
}
|
||||||
if let soundButtonView = self.soundButton.view {
|
if let soundButtonView = self.soundButton.view {
|
||||||
soundButtonView.layer.animateAlpha(from: soundButtonView.alpha, to: 0.0, duration: 0.25, removeOnCompletion: false)
|
soundButtonView.layer.animateAlpha(from: soundButtonView.alpha, to: 0.0, duration: 0.25, removeOnCompletion: false)
|
||||||
@ -1592,6 +1592,16 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
|
|
||||||
if self.component?.slice.item.storyItem.id != component.slice.item.storyItem.id {
|
if self.component?.slice.item.storyItem.id != component.slice.item.storyItem.id {
|
||||||
self.initializedOffset = false
|
self.initializedOffset = false
|
||||||
|
|
||||||
|
if let inputPanelView = self.inputPanel.view as? MessageInputPanelComponent.View {
|
||||||
|
Queue.mainQueue().justDispatch {
|
||||||
|
inputPanelView.clearSendMessageInput()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let tooltipScreen = self.sendMessageContext.tooltipScreen {
|
||||||
|
tooltipScreen.dismiss()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var itemsTransition = transition
|
var itemsTransition = transition
|
||||||
var resetScrollingOffsetWithItemTransition = false
|
var resetScrollingOffsetWithItemTransition = false
|
||||||
@ -3341,7 +3351,6 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
return .single(nil)
|
return .single(nil)
|
||||||
|> then(
|
|> then(
|
||||||
.single(.video(symlinkPath, nil, false, nil, nil, PixelDimensions(width: 720, height: 1280), duration ?? 0.0, [], .bottomRight))
|
.single(.video(symlinkPath, nil, false, nil, nil, PixelDimensions(width: 720, height: 1280), duration ?? 0.0, [], .bottomRight))
|
||||||
|> delay(0.1, queue: Queue.mainQueue())
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3848,7 +3857,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
account: component.context.account,
|
account: component.context.account,
|
||||||
sharedContext: component.context.sharedContext,
|
sharedContext: component.context.sharedContext,
|
||||||
text: .markdown(text: text),
|
text: .markdown(text: text),
|
||||||
style: .customBlur(UIColor(rgb: 0x1c1c1c)),
|
style: .customBlur(UIColor(rgb: 0x1c1c1c), 0.0),
|
||||||
icon: .peer(peer: component.slice.peer, isStory: true),
|
icon: .peer(peer: component.slice.peer, isStory: true),
|
||||||
action: TooltipScreen.Action(
|
action: TooltipScreen.Action(
|
||||||
title: "Undo",
|
title: "Undo",
|
||||||
|
@ -78,6 +78,7 @@ private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 {
|
|||||||
case translationState = 10
|
case translationState = 10
|
||||||
case storySource = 11
|
case storySource = 11
|
||||||
case mediaEditorState = 12
|
case mediaEditorState = 12
|
||||||
|
case cameraState = 13
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ApplicationSpecificItemCacheCollectionId {
|
public struct ApplicationSpecificItemCacheCollectionId {
|
||||||
@ -92,6 +93,7 @@ public struct ApplicationSpecificItemCacheCollectionId {
|
|||||||
public static let translationState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.translationState.rawValue)
|
public static let translationState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.translationState.rawValue)
|
||||||
public static let storySource = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.storySource.rawValue)
|
public static let storySource = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.storySource.rawValue)
|
||||||
public static let mediaEditorState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.mediaEditorState.rawValue)
|
public static let mediaEditorState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.mediaEditorState.rawValue)
|
||||||
|
public static let cameraState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.cameraState.rawValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ApplicationSpecificOrderedItemListCollectionIdValues: Int32 {
|
private enum ApplicationSpecificOrderedItemListCollectionIdValues: Int32 {
|
||||||
|
@ -240,7 +240,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
|||||||
if !hasArrow {
|
if !hasArrow {
|
||||||
let backgroundColor: UIColor
|
let backgroundColor: UIColor
|
||||||
var enableSaturation = true
|
var enableSaturation = true
|
||||||
if case let .customBlur(color) = style {
|
if case let .customBlur(color, _) = style {
|
||||||
backgroundColor = color
|
backgroundColor = color
|
||||||
enableSaturation = false
|
enableSaturation = false
|
||||||
} else {
|
} else {
|
||||||
@ -297,7 +297,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
|||||||
} else {
|
} else {
|
||||||
var enableSaturation = true
|
var enableSaturation = true
|
||||||
let backgroundColor: UIColor
|
let backgroundColor: UIColor
|
||||||
if case let .customBlur(color) = style {
|
if case let .customBlur(color, _) = style {
|
||||||
backgroundColor = color
|
backgroundColor = color
|
||||||
enableSaturation = false
|
enableSaturation = false
|
||||||
} else if case .light = style {
|
} else if case .light = style {
|
||||||
@ -356,10 +356,11 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
|||||||
case let .entities(text, entities):
|
case let .entities(text, entities):
|
||||||
attributedText = stringWithAppliedEntities(text, entities: entities, baseColor: textColor, linkColor: textColor, baseFont: baseFont, linkFont: baseFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: fixedFont, blockQuoteFont: baseFont, underlineLinks: true, external: false, message: nil)
|
attributedText = stringWithAppliedEntities(text, entities: entities, baseColor: textColor, linkColor: textColor, baseFont: baseFont, linkFont: baseFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: fixedFont, blockQuoteFont: baseFont, underlineLinks: true, external: false, message: nil)
|
||||||
case let .markdown(text):
|
case let .markdown(text):
|
||||||
|
let linkColor = UIColor(rgb: 0x64d2ff)
|
||||||
let markdownAttributes = MarkdownAttributes(
|
let markdownAttributes = MarkdownAttributes(
|
||||||
body: MarkdownAttributeSet(font: baseFont, textColor: textColor),
|
body: MarkdownAttributeSet(font: baseFont, textColor: textColor),
|
||||||
bold: MarkdownAttributeSet(font: boldFont, textColor: textColor),
|
bold: MarkdownAttributeSet(font: boldFont, textColor: textColor),
|
||||||
link: MarkdownAttributeSet(font: baseFont, textColor: textColor),
|
link: MarkdownAttributeSet(font: boldFont, textColor: linkColor),
|
||||||
linkAttribute: { _ in
|
linkAttribute: { _ in
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -560,14 +561,16 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
|||||||
|
|
||||||
var backgroundHeight: CGFloat
|
var backgroundHeight: CGFloat
|
||||||
switch self.tooltipStyle {
|
switch self.tooltipStyle {
|
||||||
case .default, .gradient, .customBlur, .wide:
|
case .default, .gradient:
|
||||||
backgroundHeight = max(animationSize.height, textSize.height) + contentVerticalInset * 2.0
|
backgroundHeight = max(animationSize.height, textSize.height) + contentVerticalInset * 2.0
|
||||||
|
case .wide:
|
||||||
|
backgroundHeight = max(animationSize.height, textSize.height) + contentVerticalInset * 2.0 + 4.0
|
||||||
|
case let .customBlur(_, inset):
|
||||||
|
backgroundHeight = max(animationSize.height, textSize.height) + contentVerticalInset * 2.0 + inset * 2.0
|
||||||
case .light:
|
case .light:
|
||||||
backgroundHeight = max(28.0, max(animationSize.height, textSize.height) + 4.0 * 2.0)
|
backgroundHeight = max(28.0, max(animationSize.height, textSize.height) + 4.0 * 2.0)
|
||||||
}
|
}
|
||||||
if case .wide = self.tooltipStyle {
|
if self.actionButtonNode != nil {
|
||||||
backgroundHeight += 4.0
|
|
||||||
} else if self.actionButtonNode != nil {
|
|
||||||
backgroundHeight += 4.0
|
backgroundHeight += 4.0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -739,6 +742,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var didRequestDismiss = false
|
||||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
if let event = event {
|
if let event = event {
|
||||||
if let _ = self.openActiveTextItem, let result = self.textNode.hitTest(self.view.convert(point, to: self.textNode.view), with: event) {
|
if let _ = self.openActiveTextItem, let result = self.textNode.hitTest(self.view.convert(point, to: self.textNode.view), with: event) {
|
||||||
@ -753,14 +757,19 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
|||||||
if let actionButtonNode = self.actionButtonNode, let result = actionButtonNode.hitTest(self.convert(point, to: actionButtonNode), with: event) {
|
if let actionButtonNode = self.actionButtonNode, let result = actionButtonNode.hitTest(self.convert(point, to: actionButtonNode), with: event) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
switch self.shouldDismissOnTouch(point, self.containerNode.frame) {
|
if !self.didRequestDismiss {
|
||||||
case .ignore:
|
switch self.shouldDismissOnTouch(point, self.containerNode.frame) {
|
||||||
break
|
case .ignore:
|
||||||
case let .dismiss(consume):
|
break
|
||||||
self.requestDismiss()
|
case let .dismiss(consume):
|
||||||
if consume {
|
self.requestDismiss()
|
||||||
return self.view
|
if consume {
|
||||||
|
self.didRequestDismiss = true
|
||||||
|
return self.view
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return self.view
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -915,7 +924,7 @@ public final class TooltipScreen: ViewController {
|
|||||||
public enum Style {
|
public enum Style {
|
||||||
case `default`
|
case `default`
|
||||||
case light
|
case light
|
||||||
case customBlur(UIColor)
|
case customBlur(UIColor, CGFloat)
|
||||||
case gradient(UIColor, UIColor)
|
case gradient(UIColor, UIColor)
|
||||||
case wide
|
case wide
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user