mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
e6e7d52ebf
commit
dc0d4d25bf
@ -899,7 +899,9 @@ public protocol SharedAccountContext: AnyObject {
|
|||||||
|
|
||||||
func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController
|
func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController
|
||||||
|
|
||||||
func makeMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController
|
func makeMediaPickerScreen(context: AccountContext, completion: @escaping (Any) -> Void) -> ViewController
|
||||||
|
|
||||||
|
func makeStoryMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController
|
||||||
|
|
||||||
func makeProxySettingsController(sharedContext: SharedAccountContext, account: UnauthorizedAccount) -> ViewController
|
func makeProxySettingsController(sharedContext: SharedAccountContext, account: UnauthorizedAccount) -> ViewController
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import UIKit
|
|||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import AVFoundation
|
import AVFoundation
|
||||||
import CoreImage
|
import CoreImage
|
||||||
|
import TelegramCore
|
||||||
|
|
||||||
final class CameraSession {
|
final class CameraSession {
|
||||||
private let singleSession: AVCaptureSession?
|
private let singleSession: AVCaptureSession?
|
||||||
@ -83,10 +84,8 @@ final class CameraDeviceContext {
|
|||||||
private var preferredMaxDimensions: CMVideoDimensions {
|
private var preferredMaxDimensions: CMVideoDimensions {
|
||||||
if self.additional {
|
if self.additional {
|
||||||
//if case .iPhoneXS = DeviceModel.current {
|
//if case .iPhoneXS = DeviceModel.current {
|
||||||
return CMVideoDimensions(width: 1440, height: 1080)
|
// return CMVideoDimensions(width: 1440, height: 1080)
|
||||||
//} else {
|
return CMVideoDimensions(width: 1920, height: 1440)
|
||||||
// return CMVideoDimensions(width: 1920, height: 1440)
|
|
||||||
//}
|
|
||||||
} else {
|
} else {
|
||||||
return CMVideoDimensions(width: 1920, height: 1080)
|
return CMVideoDimensions(width: 1920, height: 1080)
|
||||||
}
|
}
|
||||||
@ -198,13 +197,22 @@ private final class CameraContext {
|
|||||||
self.mainDeviceContext.output.processCodes = { [weak self] codes in
|
self.mainDeviceContext.output.processCodes = { [weak self] codes in
|
||||||
self?.detectedCodesPipe.putNext(codes)
|
self?.detectedCodesPipe.putNext(codes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(self.sessionRuntimeError),
|
||||||
|
name: .AVCaptureSessionRuntimeError,
|
||||||
|
object: self.session.session
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var isSessionRunning = false
|
||||||
func startCapture() {
|
func startCapture() {
|
||||||
guard !self.session.session.isRunning else {
|
guard !self.session.session.isRunning else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.session.session.startRunning()
|
self.session.session.startRunning()
|
||||||
|
self.isSessionRunning = self.session.session.isRunning
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopCapture(invalidate: Bool = false) {
|
func stopCapture(invalidate: Bool = false) {
|
||||||
@ -516,6 +524,27 @@ private final class CameraContext {
|
|||||||
var detectedCodes: Signal<[CameraCode], NoError> {
|
var detectedCodes: Signal<[CameraCode], NoError> {
|
||||||
return self.detectedCodesPipe.signal()
|
return self.detectedCodesPipe.signal()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func sessionInterruptionEnded(notification: NSNotification) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func sessionRuntimeError(notification: NSNotification) {
|
||||||
|
guard let errorValue = notification.userInfo?[AVCaptureSessionErrorKey] as? NSError else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let error = AVError(_nsError: errorValue)
|
||||||
|
Logger.shared.log("Camera", "Runtime error: \(error)")
|
||||||
|
|
||||||
|
if error.code == .mediaServicesWereReset {
|
||||||
|
self.queue.async {
|
||||||
|
if self.isSessionRunning {
|
||||||
|
self.session.session.startRunning()
|
||||||
|
self.isSessionRunning = self.session.session.isRunning
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class Camera {
|
public final class Camera {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import AVFoundation
|
import AVFoundation
|
||||||
|
import TelegramCore
|
||||||
|
|
||||||
class CameraInput {
|
class CameraInput {
|
||||||
var videoInput: AVCaptureDeviceInput?
|
var videoInput: AVCaptureDeviceInput?
|
||||||
@ -32,6 +33,8 @@ class CameraInput {
|
|||||||
} else {
|
} else {
|
||||||
session.session.addInput(videoInput)
|
session.session.addInput(videoInput)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Logger.shared.log("Camera", "Can't add video input")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,6 +48,8 @@ class CameraInput {
|
|||||||
self.audioInput = audioInput
|
self.audioInput = audioInput
|
||||||
if session.session.canAddInput(audioInput) {
|
if session.session.canAddInput(audioInput) {
|
||||||
session.session.addInput(audioInput)
|
session.session.addInput(audioInput)
|
||||||
|
} else {
|
||||||
|
Logger.shared.log("Camera", "Can't add audio input")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import SwiftSignalKit
|
|||||||
import CoreImage
|
import CoreImage
|
||||||
import Vision
|
import Vision
|
||||||
import VideoToolbox
|
import VideoToolbox
|
||||||
|
import TelegramCore
|
||||||
|
|
||||||
public enum VideoCaptureResult: Equatable {
|
public enum VideoCaptureResult: Equatable {
|
||||||
case finished((String, UIImage, Bool), (String, UIImage, Bool)?, Double, [(Bool, Double)], Double)
|
case finished((String, UIImage, Bool), (String, UIImage, Bool)?, Double, [(Bool, Double)], Double)
|
||||||
@ -124,6 +125,8 @@ final class CameraOutput: NSObject {
|
|||||||
session.session.addOutput(self.videoOutput)
|
session.session.addOutput(self.videoOutput)
|
||||||
}
|
}
|
||||||
self.videoOutput.setSampleBufferDelegate(self, queue: self.queue)
|
self.videoOutput.setSampleBufferDelegate(self, queue: self.queue)
|
||||||
|
} else {
|
||||||
|
Logger.shared.log("Camera", "Can't add video output")
|
||||||
}
|
}
|
||||||
if audio, session.session.canAddOutput(self.audioOutput) {
|
if audio, session.session.canAddOutput(self.audioOutput) {
|
||||||
session.session.addOutput(self.audioOutput)
|
session.session.addOutput(self.audioOutput)
|
||||||
@ -135,6 +138,8 @@ final class CameraOutput: NSObject {
|
|||||||
} else {
|
} else {
|
||||||
session.session.addOutput(self.photoOutput)
|
session.session.addOutput(self.photoOutput)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Logger.shared.log("Camera", "Can't add photo output")
|
||||||
}
|
}
|
||||||
if metadata, session.session.canAddOutput(self.metadataOutput) {
|
if metadata, session.session.canAddOutput(self.metadataOutput) {
|
||||||
session.session.addOutput(self.metadataOutput)
|
session.session.addOutput(self.metadataOutput)
|
||||||
@ -152,6 +157,8 @@ final class CameraOutput: NSObject {
|
|||||||
if session.session.canAddConnection(previewConnection) {
|
if session.session.canAddConnection(previewConnection) {
|
||||||
session.session.addConnection(previewConnection)
|
session.session.addConnection(previewConnection)
|
||||||
self.previewConnection = previewConnection
|
self.previewConnection = previewConnection
|
||||||
|
} else {
|
||||||
|
Logger.shared.log("Camera", "Can't add preview connection")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,6 +166,8 @@ final class CameraOutput: NSObject {
|
|||||||
if session.session.canAddConnection(videoConnection) {
|
if session.session.canAddConnection(videoConnection) {
|
||||||
session.session.addConnection(videoConnection)
|
session.session.addConnection(videoConnection)
|
||||||
self.videoConnection = videoConnection
|
self.videoConnection = videoConnection
|
||||||
|
} else {
|
||||||
|
Logger.shared.log("Camera", "Can't add video connection")
|
||||||
}
|
}
|
||||||
|
|
||||||
if photo {
|
if photo {
|
||||||
@ -168,6 +177,8 @@ final class CameraOutput: NSObject {
|
|||||||
self.photoConnection = photoConnection
|
self.photoConnection = photoConnection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Logger.shared.log("Camera", "Can't get video port")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
375
submodules/DrawingUI/Sources/DrawingEntitySnapTool.swift
Normal file
375
submodules/DrawingUI/Sources/DrawingEntitySnapTool.swift
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
private let snapTimeout = 1.0
|
||||||
|
|
||||||
|
class DrawingEntitySnapTool {
|
||||||
|
enum SnapType {
|
||||||
|
case centerX
|
||||||
|
case centerY
|
||||||
|
case top
|
||||||
|
case left
|
||||||
|
case right
|
||||||
|
case bottom
|
||||||
|
case rotation(CGFloat?)
|
||||||
|
|
||||||
|
static var allPositionTypes: [SnapType] {
|
||||||
|
return [
|
||||||
|
.centerX,
|
||||||
|
.centerY,
|
||||||
|
.top,
|
||||||
|
.left,
|
||||||
|
.right,
|
||||||
|
.bottom
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SnapState {
|
||||||
|
let skipped: CGFloat
|
||||||
|
let waitForLeave: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
private var topEdgeState: SnapState?
|
||||||
|
private var leftEdgeState: SnapState?
|
||||||
|
private var rightEdgeState: SnapState?
|
||||||
|
private var bottomEdgeState: SnapState?
|
||||||
|
|
||||||
|
private var xState: SnapState?
|
||||||
|
private var yState: SnapState?
|
||||||
|
|
||||||
|
private var rotationState: (angle: CGFloat, skipped: CGFloat, waitForLeave: Bool)?
|
||||||
|
|
||||||
|
var onSnapUpdated: (SnapType, Bool) -> Void = { _, _ in }
|
||||||
|
|
||||||
|
var previousTopEdgeSnapTimestamp: Double?
|
||||||
|
var previousLeftEdgeSnapTimestamp: Double?
|
||||||
|
var previousRightEdgeSnapTimestamp: Double?
|
||||||
|
var previousBottomEdgeSnapTimestamp: Double?
|
||||||
|
|
||||||
|
var previousXSnapTimestamp: Double?
|
||||||
|
var previousYSnapTimestamp: Double?
|
||||||
|
var previousRotationSnapTimestamp: Double?
|
||||||
|
|
||||||
|
func reset() {
|
||||||
|
self.topEdgeState = nil
|
||||||
|
self.leftEdgeState = nil
|
||||||
|
self.rightEdgeState = nil
|
||||||
|
self.bottomEdgeState = nil
|
||||||
|
self.xState = nil
|
||||||
|
self.yState = nil
|
||||||
|
|
||||||
|
for type in SnapType.allPositionTypes {
|
||||||
|
self.onSnapUpdated(type, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rotationReset() {
|
||||||
|
self.rotationState = nil
|
||||||
|
self.onSnapUpdated(.rotation(nil), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func maybeSkipFromStart(entityView: DrawingEntityView, position: CGPoint) {
|
||||||
|
self.topEdgeState = nil
|
||||||
|
self.leftEdgeState = nil
|
||||||
|
self.rightEdgeState = nil
|
||||||
|
self.bottomEdgeState = nil
|
||||||
|
|
||||||
|
self.xState = nil
|
||||||
|
self.yState = nil
|
||||||
|
|
||||||
|
let snapXDelta: CGFloat = (entityView.superview?.frame.width ?? 0.0) * 0.02
|
||||||
|
let snapYDelta: CGFloat = (entityView.superview?.frame.width ?? 0.0) * 0.02
|
||||||
|
|
||||||
|
if let snapLocation = (entityView.superview as? DrawingEntitiesView)?.getEntityCenterPosition() {
|
||||||
|
if position.x > snapLocation.x - snapXDelta && position.x < snapLocation.x + snapXDelta {
|
||||||
|
self.xState = SnapState(skipped: 0.0, waitForLeave: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if position.y > snapLocation.y - snapYDelta && position.y < snapLocation.y + snapYDelta {
|
||||||
|
self.yState = SnapState(skipped: 0.0, waitForLeave: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(entityView: DrawingEntityView, velocity: CGPoint, delta: CGPoint, updatedPosition: CGPoint, size: CGSize) -> CGPoint {
|
||||||
|
var updatedPosition = updatedPosition
|
||||||
|
|
||||||
|
guard let snapCenterLocation = (entityView.superview as? DrawingEntitiesView)?.getEntityCenterPosition() else {
|
||||||
|
return updatedPosition
|
||||||
|
}
|
||||||
|
let snapEdgeLocations = (entityView.superview as? DrawingEntitiesView)?.getEntityEdgePositions()
|
||||||
|
|
||||||
|
let currentTimestamp = CACurrentMediaTime()
|
||||||
|
|
||||||
|
let snapDelta: CGFloat = (entityView.superview?.frame.width ?? 0.0) * 0.02
|
||||||
|
let snapVelocity: CGFloat = snapDelta * 12.0
|
||||||
|
let snapSkipTranslation: CGFloat = snapDelta * 2.0
|
||||||
|
|
||||||
|
let topPoint = updatedPosition.y - size.height / 2.0
|
||||||
|
let leftPoint = updatedPosition.x - size.width / 2.0
|
||||||
|
let rightPoint = updatedPosition.x + size.width / 2.0
|
||||||
|
let bottomPoint = updatedPosition.y + size.height / 2.0
|
||||||
|
|
||||||
|
func process(
|
||||||
|
state: SnapState?,
|
||||||
|
velocity: CGFloat,
|
||||||
|
delta: CGFloat,
|
||||||
|
value: CGFloat,
|
||||||
|
snapVelocity: CGFloat,
|
||||||
|
snapToValue: CGFloat?,
|
||||||
|
snapDelta: CGFloat,
|
||||||
|
snapSkipTranslation: CGFloat,
|
||||||
|
previousSnapTimestamp: Double?,
|
||||||
|
onSnapUpdated: (Bool) -> Void
|
||||||
|
) -> (
|
||||||
|
value: CGFloat,
|
||||||
|
state: SnapState?,
|
||||||
|
snapTimestamp: Double?
|
||||||
|
) {
|
||||||
|
var updatedValue = value
|
||||||
|
var updatedState = state
|
||||||
|
var updatedPreviousSnapTimestamp = previousSnapTimestamp
|
||||||
|
if abs(velocity) < snapVelocity || state?.waitForLeave == true {
|
||||||
|
if let snapToValue {
|
||||||
|
if let state {
|
||||||
|
let skipped = state.skipped
|
||||||
|
let waitForLeave = state.waitForLeave
|
||||||
|
if waitForLeave {
|
||||||
|
if value > snapToValue - snapDelta * 2.0 && value < snapToValue + snapDelta * 2.0 {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
updatedState = nil
|
||||||
|
}
|
||||||
|
} else if abs(skipped) < snapSkipTranslation {
|
||||||
|
updatedState = SnapState(skipped: skipped + delta, waitForLeave: false)
|
||||||
|
updatedValue = snapToValue
|
||||||
|
} else {
|
||||||
|
updatedState = SnapState(skipped: snapSkipTranslation, waitForLeave: true)
|
||||||
|
onSnapUpdated(false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if value > snapToValue - snapDelta && value < snapToValue + snapDelta {
|
||||||
|
if let previousSnapTimestamp, currentTimestamp - previousSnapTimestamp < snapTimeout {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
updatedPreviousSnapTimestamp = currentTimestamp
|
||||||
|
updatedState = SnapState(skipped: 0.0, waitForLeave: false)
|
||||||
|
updatedValue = snapToValue
|
||||||
|
onSnapUpdated(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updatedState = nil
|
||||||
|
onSnapUpdated(false)
|
||||||
|
}
|
||||||
|
return (updatedValue, updatedState, updatedPreviousSnapTimestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
let (updatedXValue, updatedXState, updatedXPreviousTimestamp) = process(
|
||||||
|
state: self.xState,
|
||||||
|
velocity: velocity.x,
|
||||||
|
delta: delta.x,
|
||||||
|
value: updatedPosition.x,
|
||||||
|
snapVelocity: snapVelocity,
|
||||||
|
snapToValue: snapCenterLocation.x,
|
||||||
|
snapDelta: snapDelta,
|
||||||
|
snapSkipTranslation: snapSkipTranslation,
|
||||||
|
previousSnapTimestamp: self.previousXSnapTimestamp,
|
||||||
|
onSnapUpdated: { [weak self] snapped in
|
||||||
|
self?.onSnapUpdated(.centerX, snapped)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.xState = updatedXState
|
||||||
|
self.previousXSnapTimestamp = updatedXPreviousTimestamp
|
||||||
|
|
||||||
|
let (updatedYValue, updatedYState, updatedYPreviousTimestamp) = process(
|
||||||
|
state: self.yState,
|
||||||
|
velocity: velocity.y,
|
||||||
|
delta: delta.y,
|
||||||
|
value: updatedPosition.y,
|
||||||
|
snapVelocity: snapVelocity,
|
||||||
|
snapToValue: snapCenterLocation.y,
|
||||||
|
snapDelta: snapDelta,
|
||||||
|
snapSkipTranslation: snapSkipTranslation,
|
||||||
|
previousSnapTimestamp: self.previousYSnapTimestamp,
|
||||||
|
onSnapUpdated: { [weak self] snapped in
|
||||||
|
self?.onSnapUpdated(.centerY, snapped)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.yState = updatedYState
|
||||||
|
self.previousYSnapTimestamp = updatedYPreviousTimestamp
|
||||||
|
|
||||||
|
if let snapEdgeLocations {
|
||||||
|
if updatedXState == nil {
|
||||||
|
let (updatedXLeftEdgeValue, updatedLeftEdgeState, updatedLeftEdgePreviousTimestamp) = process(
|
||||||
|
state: self.leftEdgeState,
|
||||||
|
velocity: velocity.x,
|
||||||
|
delta: delta.x,
|
||||||
|
value: leftPoint,
|
||||||
|
snapVelocity: snapVelocity,
|
||||||
|
snapToValue: snapEdgeLocations.left,
|
||||||
|
snapDelta: snapDelta,
|
||||||
|
snapSkipTranslation: snapSkipTranslation,
|
||||||
|
previousSnapTimestamp: self.previousLeftEdgeSnapTimestamp,
|
||||||
|
onSnapUpdated: { [weak self] snapped in
|
||||||
|
self?.onSnapUpdated(.left, snapped)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.leftEdgeState = updatedLeftEdgeState
|
||||||
|
self.previousLeftEdgeSnapTimestamp = updatedLeftEdgePreviousTimestamp
|
||||||
|
|
||||||
|
if updatedLeftEdgeState != nil {
|
||||||
|
updatedPosition.x = updatedXLeftEdgeValue + size.width / 2.0
|
||||||
|
|
||||||
|
self.rightEdgeState = nil
|
||||||
|
self.previousRightEdgeSnapTimestamp = nil
|
||||||
|
} else {
|
||||||
|
let (updatedXRightEdgeValue, updatedRightEdgeState, updatedRightEdgePreviousTimestamp) = process(
|
||||||
|
state: self.rightEdgeState,
|
||||||
|
velocity: velocity.x,
|
||||||
|
delta: delta.x,
|
||||||
|
value: rightPoint,
|
||||||
|
snapVelocity: snapVelocity,
|
||||||
|
snapToValue: snapEdgeLocations.right,
|
||||||
|
snapDelta: snapDelta,
|
||||||
|
snapSkipTranslation: snapSkipTranslation,
|
||||||
|
previousSnapTimestamp: self.previousRightEdgeSnapTimestamp,
|
||||||
|
onSnapUpdated: { [weak self] snapped in
|
||||||
|
self?.onSnapUpdated(.right, snapped)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.rightEdgeState = updatedRightEdgeState
|
||||||
|
self.previousRightEdgeSnapTimestamp = updatedRightEdgePreviousTimestamp
|
||||||
|
|
||||||
|
updatedPosition.x = updatedXRightEdgeValue - size.width / 2.0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updatedPosition.x = updatedXValue
|
||||||
|
}
|
||||||
|
|
||||||
|
if updatedYState == nil {
|
||||||
|
let (updatedYTopEdgeValue, updatedTopEdgeState, updatedTopEdgePreviousTimestamp) = process(
|
||||||
|
state: self.topEdgeState,
|
||||||
|
velocity: velocity.y,
|
||||||
|
delta: delta.y,
|
||||||
|
value: topPoint,
|
||||||
|
snapVelocity: snapVelocity,
|
||||||
|
snapToValue: snapEdgeLocations.top,
|
||||||
|
snapDelta: snapDelta,
|
||||||
|
snapSkipTranslation: snapSkipTranslation,
|
||||||
|
previousSnapTimestamp: self.previousTopEdgeSnapTimestamp,
|
||||||
|
onSnapUpdated: { [weak self] snapped in
|
||||||
|
self?.onSnapUpdated(.top, snapped)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.topEdgeState = updatedTopEdgeState
|
||||||
|
self.previousTopEdgeSnapTimestamp = updatedTopEdgePreviousTimestamp
|
||||||
|
|
||||||
|
if updatedTopEdgeState != nil {
|
||||||
|
updatedPosition.y = updatedYTopEdgeValue + size.height / 2.0
|
||||||
|
|
||||||
|
self.bottomEdgeState = nil
|
||||||
|
self.previousBottomEdgeSnapTimestamp = nil
|
||||||
|
} else {
|
||||||
|
let (updatedYBottomEdgeValue, updatedBottomEdgeState, updatedBottomEdgePreviousTimestamp) = process(
|
||||||
|
state: self.bottomEdgeState,
|
||||||
|
velocity: velocity.y,
|
||||||
|
delta: delta.y,
|
||||||
|
value: bottomPoint,
|
||||||
|
snapVelocity: snapVelocity,
|
||||||
|
snapToValue: snapEdgeLocations.bottom,
|
||||||
|
snapDelta: snapDelta,
|
||||||
|
snapSkipTranslation: snapSkipTranslation,
|
||||||
|
previousSnapTimestamp: self.previousBottomEdgeSnapTimestamp,
|
||||||
|
onSnapUpdated: { [weak self] snapped in
|
||||||
|
self?.onSnapUpdated(.bottom, snapped)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.bottomEdgeState = updatedBottomEdgeState
|
||||||
|
self.previousBottomEdgeSnapTimestamp = updatedBottomEdgePreviousTimestamp
|
||||||
|
|
||||||
|
updatedPosition.y = updatedYBottomEdgeValue - size.height / 2.0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updatedPosition.y = updatedYValue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updatedPosition.x = updatedXValue
|
||||||
|
updatedPosition.y = updatedYValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
private let snapRotations: [CGFloat] = [0.0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0]
|
||||||
|
func maybeSkipFromStart(entityView: DrawingEntityView, rotation: CGFloat) {
|
||||||
|
self.rotationState = nil
|
||||||
|
|
||||||
|
let snapDelta: CGFloat = 0.25
|
||||||
|
for snapRotation in self.snapRotations {
|
||||||
|
let snapRotation = snapRotation * .pi
|
||||||
|
if rotation > snapRotation - snapDelta && rotation < snapRotation + snapDelta {
|
||||||
|
self.rotationState = (snapRotation, 0.0, true)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(entityView: DrawingEntityView, velocity: CGFloat, delta: CGFloat, updatedRotation: CGFloat) -> CGFloat {
|
||||||
|
var updatedRotation = updatedRotation
|
||||||
|
if updatedRotation < 0.0 {
|
||||||
|
updatedRotation = 2.0 * .pi + updatedRotation
|
||||||
|
} else if updatedRotation > 2.0 * .pi {
|
||||||
|
while updatedRotation > 2.0 * .pi {
|
||||||
|
updatedRotation -= 2.0 * .pi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentTimestamp = CACurrentMediaTime()
|
||||||
|
|
||||||
|
let snapDelta: CGFloat = 0.01
|
||||||
|
let snapVelocity: CGFloat = snapDelta * 35.0
|
||||||
|
let snapSkipRotation: CGFloat = snapDelta * 40.0
|
||||||
|
|
||||||
|
if abs(velocity) < snapVelocity || self.rotationState?.waitForLeave == true {
|
||||||
|
if let (snapRotation, skipped, waitForLeave) = self.rotationState {
|
||||||
|
if waitForLeave {
|
||||||
|
if updatedRotation > snapRotation - snapDelta * 2.0 && updatedRotation < snapRotation + snapDelta {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
self.rotationState = nil
|
||||||
|
}
|
||||||
|
} else if abs(skipped) < snapSkipRotation {
|
||||||
|
self.rotationState = (snapRotation, skipped + delta, false)
|
||||||
|
updatedRotation = snapRotation
|
||||||
|
} else {
|
||||||
|
self.rotationState = (snapRotation, snapSkipRotation, true)
|
||||||
|
self.onSnapUpdated(.rotation(nil), false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for snapRotation in self.snapRotations {
|
||||||
|
let snapRotation = snapRotation * .pi
|
||||||
|
if updatedRotation > snapRotation - snapDelta && updatedRotation < snapRotation + snapDelta {
|
||||||
|
if let previousRotationSnapTimestamp, currentTimestamp - previousRotationSnapTimestamp < snapTimeout {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
self.previousRotationSnapTimestamp = currentTimestamp
|
||||||
|
self.rotationState = (snapRotation, 0.0, false)
|
||||||
|
updatedRotation = snapRotation
|
||||||
|
self.onSnapUpdated(.rotation(snapRotation), true)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.rotationState = nil
|
||||||
|
self.onSnapUpdated(.rotation(nil), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedRotation
|
||||||
|
}
|
||||||
|
}
|
@ -3172,39 +3172,41 @@ public final class DrawingToolsInteraction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func presentEyedropper(retryLaterForVideo: Bool = true, dismissed: @escaping () -> Void) {
|
func presentEyedropper(retryLaterForVideo: Bool = true, dismissed: @escaping () -> Void) {
|
||||||
// self.entitiesView.pause()
|
self.entitiesView.pause()
|
||||||
//
|
|
||||||
// if self.isVideo && retryLaterForVideo {
|
if self.isVideo && retryLaterForVideo {
|
||||||
// self.updateVideoPlayback(false)
|
self.updateVideoPlayback(false)
|
||||||
// Queue.mainQueue().after(0.1) {
|
Queue.mainQueue().after(0.1) {
|
||||||
// self.presentEyedropper(retryLaterForVideo: false, dismissed: dismissed)
|
self.presentEyedropper(retryLaterForVideo: false, dismissed: dismissed)
|
||||||
// }
|
}
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// guard let currentImage = self.getCurrentImage() else {
|
guard let currentImage = self.getCurrentImage() else {
|
||||||
// self.entitiesView.play()
|
self.entitiesView.play()
|
||||||
// self.updateVideoPlayback(true)
|
self.updateVideoPlayback(true)
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// let sourceImage = generateImage(self.drawingView.imageSize, contextGenerator: { size, context in
|
let sourceImage = generateImage(self.drawingView.imageSize, contextGenerator: { size, context in
|
||||||
// let bounds = CGRect(origin: .zero, size: size)
|
let bounds = CGRect(origin: .zero, size: size)
|
||||||
// if let cgImage = currentImage.cgImage {
|
if let cgImage = currentImage.cgImage {
|
||||||
// context.draw(cgImage, in: bounds)
|
context.draw(cgImage, in: bounds)
|
||||||
// }
|
}
|
||||||
// if let cgImage = self.drawingView.drawingImage?.cgImage {
|
if let cgImage = self.drawingView.drawingImage?.cgImage {
|
||||||
// context.draw(cgImage, in: bounds)
|
context.draw(cgImage, in: bounds)
|
||||||
// }
|
}
|
||||||
// context.translateBy(x: size.width / 2.0, y: size.height / 2.0)
|
context.translateBy(x: size.width / 2.0, y: size.height / 2.0)
|
||||||
// context.scaleBy(x: 1.0, y: -1.0)
|
context.scaleBy(x: 1.0, y: -1.0)
|
||||||
// context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0)
|
context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0)
|
||||||
// self.entitiesView.layer.render(in: context)
|
self.entitiesView.layer.render(in: context)
|
||||||
// }, opaque: true, scale: 1.0)
|
}, opaque: true, scale: 1.0)
|
||||||
// guard let sourceImage = sourceImage else {
|
guard let sourceImage = sourceImage else {
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
|
let _ = sourceImage
|
||||||
|
|
||||||
// let eyedropperView = EyedropperView(containerSize: controller.contentWrapperView.frame.size, drawingView: self.drawingView, sourceImage: sourceImage)
|
// let eyedropperView = EyedropperView(containerSize: controller.contentWrapperView.frame.size, drawingView: self.drawingView, sourceImage: sourceImage)
|
||||||
// eyedropperView.completed = { [weak self] color in
|
// eyedropperView.completed = { [weak self] color in
|
||||||
// if let self {
|
// if let self {
|
||||||
|
@ -179,12 +179,46 @@ public final class DrawingStickerEntityView: DrawingEntityView {
|
|||||||
self.setNeedsLayout()
|
self.setNeedsLayout()
|
||||||
}
|
}
|
||||||
} else if let image = self.image {
|
} else if let image = self.image {
|
||||||
|
func drawImageWithOrientation(_ image: UIImage, size: CGSize, in context: CGContext) {
|
||||||
|
let imageSize: CGSize
|
||||||
|
|
||||||
|
switch image.imageOrientation {
|
||||||
|
case .left, .leftMirrored, .right, .rightMirrored:
|
||||||
|
imageSize = CGSize(width: size.height, height: size.width)
|
||||||
|
default:
|
||||||
|
imageSize = size
|
||||||
|
}
|
||||||
|
|
||||||
|
let imageRect = CGRect(origin: .zero, size: imageSize)
|
||||||
|
|
||||||
|
switch image.imageOrientation {
|
||||||
|
case .down, .downMirrored:
|
||||||
|
context.translateBy(x: imageSize.width, y: imageSize.height)
|
||||||
|
context.rotate(by: CGFloat.pi)
|
||||||
|
case .left, .leftMirrored:
|
||||||
|
context.translateBy(x: imageSize.width, y: 0)
|
||||||
|
context.rotate(by: CGFloat.pi / 2)
|
||||||
|
case .right, .rightMirrored:
|
||||||
|
context.translateBy(x: 0, y: imageSize.height)
|
||||||
|
context.rotate(by: -CGFloat.pi / 2)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// switch image.imageOrientation {
|
||||||
|
// case .leftMirrored, .rightMirrored:
|
||||||
|
// context.scaleBy(x: -1, y: 1)
|
||||||
|
// default:
|
||||||
|
// context.scaleBy(x: 1, y: -1)
|
||||||
|
// }
|
||||||
|
|
||||||
|
context.draw(image.cgImage!, in: imageRect)
|
||||||
|
}
|
||||||
|
|
||||||
self.imageNode.setSignal(.single({ arguments -> DrawingContext? in
|
self.imageNode.setSignal(.single({ arguments -> DrawingContext? in
|
||||||
let context = DrawingContext(size: arguments.drawingSize, opaque: false, clear: true)
|
let context = DrawingContext(size: arguments.drawingSize, opaque: false, clear: true)
|
||||||
context?.withFlippedContext({ ctx in
|
context?.withFlippedContext({ ctx in
|
||||||
if let cgImage = image.cgImage {
|
drawImageWithOrientation(image, size: arguments.drawingSize, in: ctx)
|
||||||
ctx.draw(cgImage, in: CGRect(origin: .zero, size: arguments.drawingSize))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
return context
|
return context
|
||||||
}))
|
}))
|
||||||
@ -644,385 +678,15 @@ final class DrawingStickerEntititySelectionView: DrawingEntitySelectionView {
|
|||||||
self.border.lineWidth = 2.0 / self.scale
|
self.border.lineWidth = 2.0 / self.scale
|
||||||
|
|
||||||
if entity.isRectangle {
|
if entity.isRectangle {
|
||||||
|
let aspectRatio = entity.baseSize.width / entity.baseSize.height
|
||||||
|
|
||||||
let width: CGFloat = self.bounds.width - inset * 2.0
|
let width: CGFloat = self.bounds.width - inset * 2.0
|
||||||
let height: CGFloat = self.bounds.height - inset * 2.0
|
let height: CGFloat = self.bounds.height / aspectRatio - inset * 2.0
|
||||||
|
|
||||||
let cornerRadius: CGFloat = 12.0 - self.scale
|
let cornerRadius: CGFloat = 12.0 - self.scale
|
||||||
self.border.path = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: inset, y: inset), size: CGSize(width: width, height: height)), cornerRadius: cornerRadius).cgPath
|
self.border.path = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: floorToScreenPixels((self.bounds.width - width) / 2.0), y: floorToScreenPixels((self.bounds.height - height) / 2.0)), size: CGSize(width: width, height: height)), cornerRadius: cornerRadius).cgPath
|
||||||
} else {
|
} else {
|
||||||
self.border.path = UIBezierPath(ovalIn: CGRect(origin: CGPoint(x: inset, y: inset), size: CGSize(width: self.bounds.width - inset * 2.0, height: self.bounds.height - inset * 2.0))).cgPath
|
self.border.path = UIBezierPath(ovalIn: CGRect(origin: CGPoint(x: inset, y: inset), size: CGSize(width: self.bounds.width - inset * 2.0, height: self.bounds.height - inset * 2.0))).cgPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let snapTimeout = 1.0
|
|
||||||
|
|
||||||
class DrawingEntitySnapTool {
|
|
||||||
enum SnapType {
|
|
||||||
case centerX
|
|
||||||
case centerY
|
|
||||||
case top
|
|
||||||
case left
|
|
||||||
case right
|
|
||||||
case bottom
|
|
||||||
case rotation(CGFloat?)
|
|
||||||
|
|
||||||
static var allPositionTypes: [SnapType] {
|
|
||||||
return [
|
|
||||||
.centerX,
|
|
||||||
.centerY,
|
|
||||||
.top,
|
|
||||||
.left,
|
|
||||||
.right,
|
|
||||||
.bottom
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SnapState {
|
|
||||||
let skipped: CGFloat
|
|
||||||
let waitForLeave: Bool
|
|
||||||
}
|
|
||||||
|
|
||||||
private var topEdgeState: SnapState?
|
|
||||||
private var leftEdgeState: SnapState?
|
|
||||||
private var rightEdgeState: SnapState?
|
|
||||||
private var bottomEdgeState: SnapState?
|
|
||||||
|
|
||||||
private var xState: SnapState?
|
|
||||||
private var yState: SnapState?
|
|
||||||
|
|
||||||
private var rotationState: (angle: CGFloat, skipped: CGFloat, waitForLeave: Bool)?
|
|
||||||
|
|
||||||
var onSnapUpdated: (SnapType, Bool) -> Void = { _, _ in }
|
|
||||||
|
|
||||||
var previousTopEdgeSnapTimestamp: Double?
|
|
||||||
var previousLeftEdgeSnapTimestamp: Double?
|
|
||||||
var previousRightEdgeSnapTimestamp: Double?
|
|
||||||
var previousBottomEdgeSnapTimestamp: Double?
|
|
||||||
|
|
||||||
var previousXSnapTimestamp: Double?
|
|
||||||
var previousYSnapTimestamp: Double?
|
|
||||||
var previousRotationSnapTimestamp: Double?
|
|
||||||
|
|
||||||
func reset() {
|
|
||||||
self.topEdgeState = nil
|
|
||||||
self.leftEdgeState = nil
|
|
||||||
self.rightEdgeState = nil
|
|
||||||
self.bottomEdgeState = nil
|
|
||||||
self.xState = nil
|
|
||||||
self.yState = nil
|
|
||||||
|
|
||||||
for type in SnapType.allPositionTypes {
|
|
||||||
self.onSnapUpdated(type, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func rotationReset() {
|
|
||||||
self.rotationState = nil
|
|
||||||
self.onSnapUpdated(.rotation(nil), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func maybeSkipFromStart(entityView: DrawingEntityView, position: CGPoint) {
|
|
||||||
self.topEdgeState = nil
|
|
||||||
self.leftEdgeState = nil
|
|
||||||
self.rightEdgeState = nil
|
|
||||||
self.bottomEdgeState = nil
|
|
||||||
|
|
||||||
self.xState = nil
|
|
||||||
self.yState = nil
|
|
||||||
|
|
||||||
let snapXDelta: CGFloat = (entityView.superview?.frame.width ?? 0.0) * 0.02
|
|
||||||
let snapYDelta: CGFloat = (entityView.superview?.frame.width ?? 0.0) * 0.02
|
|
||||||
|
|
||||||
if let snapLocation = (entityView.superview as? DrawingEntitiesView)?.getEntityCenterPosition() {
|
|
||||||
if position.x > snapLocation.x - snapXDelta && position.x < snapLocation.x + snapXDelta {
|
|
||||||
self.xState = SnapState(skipped: 0.0, waitForLeave: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
if position.y > snapLocation.y - snapYDelta && position.y < snapLocation.y + snapYDelta {
|
|
||||||
self.yState = SnapState(skipped: 0.0, waitForLeave: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func update(entityView: DrawingEntityView, velocity: CGPoint, delta: CGPoint, updatedPosition: CGPoint, size: CGSize) -> CGPoint {
|
|
||||||
var updatedPosition = updatedPosition
|
|
||||||
|
|
||||||
guard let snapCenterLocation = (entityView.superview as? DrawingEntitiesView)?.getEntityCenterPosition() else {
|
|
||||||
return updatedPosition
|
|
||||||
}
|
|
||||||
let snapEdgeLocations = (entityView.superview as? DrawingEntitiesView)?.getEntityEdgePositions()
|
|
||||||
|
|
||||||
let currentTimestamp = CACurrentMediaTime()
|
|
||||||
|
|
||||||
let snapDelta: CGFloat = (entityView.superview?.frame.width ?? 0.0) * 0.02
|
|
||||||
let snapVelocity: CGFloat = snapDelta * 12.0
|
|
||||||
let snapSkipTranslation: CGFloat = snapDelta * 2.0
|
|
||||||
|
|
||||||
let topPoint = updatedPosition.y - size.height / 2.0
|
|
||||||
let leftPoint = updatedPosition.x - size.width / 2.0
|
|
||||||
let rightPoint = updatedPosition.x + size.width / 2.0
|
|
||||||
let bottomPoint = updatedPosition.y + size.height / 2.0
|
|
||||||
|
|
||||||
func process(
|
|
||||||
state: SnapState?,
|
|
||||||
velocity: CGFloat,
|
|
||||||
delta: CGFloat,
|
|
||||||
value: CGFloat,
|
|
||||||
snapVelocity: CGFloat,
|
|
||||||
snapToValue: CGFloat?,
|
|
||||||
snapDelta: CGFloat,
|
|
||||||
snapSkipTranslation: CGFloat,
|
|
||||||
previousSnapTimestamp: Double?,
|
|
||||||
onSnapUpdated: (Bool) -> Void
|
|
||||||
) -> (
|
|
||||||
value: CGFloat,
|
|
||||||
state: SnapState?,
|
|
||||||
snapTimestamp: Double?
|
|
||||||
) {
|
|
||||||
var updatedValue = value
|
|
||||||
var updatedState = state
|
|
||||||
var updatedPreviousSnapTimestamp = previousSnapTimestamp
|
|
||||||
if abs(velocity) < snapVelocity || state?.waitForLeave == true {
|
|
||||||
if let snapToValue {
|
|
||||||
if let state {
|
|
||||||
let skipped = state.skipped
|
|
||||||
let waitForLeave = state.waitForLeave
|
|
||||||
if waitForLeave {
|
|
||||||
if value > snapToValue - snapDelta * 2.0 && value < snapToValue + snapDelta * 2.0 {
|
|
||||||
|
|
||||||
} else {
|
|
||||||
updatedState = nil
|
|
||||||
}
|
|
||||||
} else if abs(skipped) < snapSkipTranslation {
|
|
||||||
updatedState = SnapState(skipped: skipped + delta, waitForLeave: false)
|
|
||||||
updatedValue = snapToValue
|
|
||||||
} else {
|
|
||||||
updatedState = SnapState(skipped: snapSkipTranslation, waitForLeave: true)
|
|
||||||
onSnapUpdated(false)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if value > snapToValue - snapDelta && value < snapToValue + snapDelta {
|
|
||||||
if let previousSnapTimestamp, currentTimestamp - previousSnapTimestamp < snapTimeout {
|
|
||||||
|
|
||||||
} else {
|
|
||||||
updatedPreviousSnapTimestamp = currentTimestamp
|
|
||||||
updatedState = SnapState(skipped: 0.0, waitForLeave: false)
|
|
||||||
updatedValue = snapToValue
|
|
||||||
onSnapUpdated(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
updatedState = nil
|
|
||||||
onSnapUpdated(false)
|
|
||||||
}
|
|
||||||
return (updatedValue, updatedState, updatedPreviousSnapTimestamp)
|
|
||||||
}
|
|
||||||
|
|
||||||
let (updatedXValue, updatedXState, updatedXPreviousTimestamp) = process(
|
|
||||||
state: self.xState,
|
|
||||||
velocity: velocity.x,
|
|
||||||
delta: delta.x,
|
|
||||||
value: updatedPosition.x,
|
|
||||||
snapVelocity: snapVelocity,
|
|
||||||
snapToValue: snapCenterLocation.x,
|
|
||||||
snapDelta: snapDelta,
|
|
||||||
snapSkipTranslation: snapSkipTranslation,
|
|
||||||
previousSnapTimestamp: self.previousXSnapTimestamp,
|
|
||||||
onSnapUpdated: { [weak self] snapped in
|
|
||||||
self?.onSnapUpdated(.centerX, snapped)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.xState = updatedXState
|
|
||||||
self.previousXSnapTimestamp = updatedXPreviousTimestamp
|
|
||||||
|
|
||||||
let (updatedYValue, updatedYState, updatedYPreviousTimestamp) = process(
|
|
||||||
state: self.yState,
|
|
||||||
velocity: velocity.y,
|
|
||||||
delta: delta.y,
|
|
||||||
value: updatedPosition.y,
|
|
||||||
snapVelocity: snapVelocity,
|
|
||||||
snapToValue: snapCenterLocation.y,
|
|
||||||
snapDelta: snapDelta,
|
|
||||||
snapSkipTranslation: snapSkipTranslation,
|
|
||||||
previousSnapTimestamp: self.previousYSnapTimestamp,
|
|
||||||
onSnapUpdated: { [weak self] snapped in
|
|
||||||
self?.onSnapUpdated(.centerY, snapped)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.yState = updatedYState
|
|
||||||
self.previousYSnapTimestamp = updatedYPreviousTimestamp
|
|
||||||
|
|
||||||
if let snapEdgeLocations {
|
|
||||||
if updatedXState == nil {
|
|
||||||
let (updatedXLeftEdgeValue, updatedLeftEdgeState, updatedLeftEdgePreviousTimestamp) = process(
|
|
||||||
state: self.leftEdgeState,
|
|
||||||
velocity: velocity.x,
|
|
||||||
delta: delta.x,
|
|
||||||
value: leftPoint,
|
|
||||||
snapVelocity: snapVelocity,
|
|
||||||
snapToValue: snapEdgeLocations.left,
|
|
||||||
snapDelta: snapDelta,
|
|
||||||
snapSkipTranslation: snapSkipTranslation,
|
|
||||||
previousSnapTimestamp: self.previousLeftEdgeSnapTimestamp,
|
|
||||||
onSnapUpdated: { [weak self] snapped in
|
|
||||||
self?.onSnapUpdated(.left, snapped)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.leftEdgeState = updatedLeftEdgeState
|
|
||||||
self.previousLeftEdgeSnapTimestamp = updatedLeftEdgePreviousTimestamp
|
|
||||||
|
|
||||||
if updatedLeftEdgeState != nil {
|
|
||||||
updatedPosition.x = updatedXLeftEdgeValue + size.width / 2.0
|
|
||||||
|
|
||||||
self.rightEdgeState = nil
|
|
||||||
self.previousRightEdgeSnapTimestamp = nil
|
|
||||||
} else {
|
|
||||||
let (updatedXRightEdgeValue, updatedRightEdgeState, updatedRightEdgePreviousTimestamp) = process(
|
|
||||||
state: self.rightEdgeState,
|
|
||||||
velocity: velocity.x,
|
|
||||||
delta: delta.x,
|
|
||||||
value: rightPoint,
|
|
||||||
snapVelocity: snapVelocity,
|
|
||||||
snapToValue: snapEdgeLocations.right,
|
|
||||||
snapDelta: snapDelta,
|
|
||||||
snapSkipTranslation: snapSkipTranslation,
|
|
||||||
previousSnapTimestamp: self.previousRightEdgeSnapTimestamp,
|
|
||||||
onSnapUpdated: { [weak self] snapped in
|
|
||||||
self?.onSnapUpdated(.right, snapped)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.rightEdgeState = updatedRightEdgeState
|
|
||||||
self.previousRightEdgeSnapTimestamp = updatedRightEdgePreviousTimestamp
|
|
||||||
|
|
||||||
updatedPosition.x = updatedXRightEdgeValue - size.width / 2.0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
updatedPosition.x = updatedXValue
|
|
||||||
}
|
|
||||||
|
|
||||||
if updatedYState == nil {
|
|
||||||
let (updatedYTopEdgeValue, updatedTopEdgeState, updatedTopEdgePreviousTimestamp) = process(
|
|
||||||
state: self.topEdgeState,
|
|
||||||
velocity: velocity.y,
|
|
||||||
delta: delta.y,
|
|
||||||
value: topPoint,
|
|
||||||
snapVelocity: snapVelocity,
|
|
||||||
snapToValue: snapEdgeLocations.top,
|
|
||||||
snapDelta: snapDelta,
|
|
||||||
snapSkipTranslation: snapSkipTranslation,
|
|
||||||
previousSnapTimestamp: self.previousTopEdgeSnapTimestamp,
|
|
||||||
onSnapUpdated: { [weak self] snapped in
|
|
||||||
self?.onSnapUpdated(.top, snapped)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.topEdgeState = updatedTopEdgeState
|
|
||||||
self.previousTopEdgeSnapTimestamp = updatedTopEdgePreviousTimestamp
|
|
||||||
|
|
||||||
if updatedTopEdgeState != nil {
|
|
||||||
updatedPosition.y = updatedYTopEdgeValue + size.height / 2.0
|
|
||||||
|
|
||||||
self.bottomEdgeState = nil
|
|
||||||
self.previousBottomEdgeSnapTimestamp = nil
|
|
||||||
} else {
|
|
||||||
let (updatedYBottomEdgeValue, updatedBottomEdgeState, updatedBottomEdgePreviousTimestamp) = process(
|
|
||||||
state: self.bottomEdgeState,
|
|
||||||
velocity: velocity.y,
|
|
||||||
delta: delta.y,
|
|
||||||
value: bottomPoint,
|
|
||||||
snapVelocity: snapVelocity,
|
|
||||||
snapToValue: snapEdgeLocations.bottom,
|
|
||||||
snapDelta: snapDelta,
|
|
||||||
snapSkipTranslation: snapSkipTranslation,
|
|
||||||
previousSnapTimestamp: self.previousBottomEdgeSnapTimestamp,
|
|
||||||
onSnapUpdated: { [weak self] snapped in
|
|
||||||
self?.onSnapUpdated(.bottom, snapped)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.bottomEdgeState = updatedBottomEdgeState
|
|
||||||
self.previousBottomEdgeSnapTimestamp = updatedBottomEdgePreviousTimestamp
|
|
||||||
|
|
||||||
updatedPosition.y = updatedYBottomEdgeValue - size.height / 2.0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
updatedPosition.y = updatedYValue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
updatedPosition.x = updatedXValue
|
|
||||||
updatedPosition.y = updatedYValue
|
|
||||||
}
|
|
||||||
|
|
||||||
return updatedPosition
|
|
||||||
}
|
|
||||||
|
|
||||||
private let snapRotations: [CGFloat] = [0.0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0]
|
|
||||||
func maybeSkipFromStart(entityView: DrawingEntityView, rotation: CGFloat) {
|
|
||||||
self.rotationState = nil
|
|
||||||
|
|
||||||
let snapDelta: CGFloat = 0.25
|
|
||||||
for snapRotation in self.snapRotations {
|
|
||||||
let snapRotation = snapRotation * .pi
|
|
||||||
if rotation > snapRotation - snapDelta && rotation < snapRotation + snapDelta {
|
|
||||||
self.rotationState = (snapRotation, 0.0, true)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func update(entityView: DrawingEntityView, velocity: CGFloat, delta: CGFloat, updatedRotation: CGFloat) -> CGFloat {
|
|
||||||
var updatedRotation = updatedRotation
|
|
||||||
if updatedRotation < 0.0 {
|
|
||||||
updatedRotation = 2.0 * .pi + updatedRotation
|
|
||||||
} else if updatedRotation > 2.0 * .pi {
|
|
||||||
while updatedRotation > 2.0 * .pi {
|
|
||||||
updatedRotation -= 2.0 * .pi
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let currentTimestamp = CACurrentMediaTime()
|
|
||||||
|
|
||||||
let snapDelta: CGFloat = 0.01
|
|
||||||
let snapVelocity: CGFloat = snapDelta * 35.0
|
|
||||||
let snapSkipRotation: CGFloat = snapDelta * 40.0
|
|
||||||
|
|
||||||
if abs(velocity) < snapVelocity || self.rotationState?.waitForLeave == true {
|
|
||||||
if let (snapRotation, skipped, waitForLeave) = self.rotationState {
|
|
||||||
if waitForLeave {
|
|
||||||
if updatedRotation > snapRotation - snapDelta * 2.0 && updatedRotation < snapRotation + snapDelta {
|
|
||||||
|
|
||||||
} else {
|
|
||||||
self.rotationState = nil
|
|
||||||
}
|
|
||||||
} else if abs(skipped) < snapSkipRotation {
|
|
||||||
self.rotationState = (snapRotation, skipped + delta, false)
|
|
||||||
updatedRotation = snapRotation
|
|
||||||
} else {
|
|
||||||
self.rotationState = (snapRotation, snapSkipRotation, true)
|
|
||||||
self.onSnapUpdated(.rotation(nil), false)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for snapRotation in self.snapRotations {
|
|
||||||
let snapRotation = snapRotation * .pi
|
|
||||||
if updatedRotation > snapRotation - snapDelta && updatedRotation < snapRotation + snapDelta {
|
|
||||||
if let previousRotationSnapTimestamp, currentTimestamp - previousRotationSnapTimestamp < snapTimeout {
|
|
||||||
|
|
||||||
} else {
|
|
||||||
self.previousRotationSnapTimestamp = currentTimestamp
|
|
||||||
self.rotationState = (snapRotation, 0.0, false)
|
|
||||||
updatedRotation = snapRotation
|
|
||||||
self.onSnapUpdated(.rotation(snapRotation), true)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.rotationState = nil
|
|
||||||
self.onSnapUpdated(.rotation(nil), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return updatedRotation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -912,7 +912,13 @@ public class StickerPickerScreen: ViewController {
|
|||||||
useOpaqueTheme: false,
|
useOpaqueTheme: false,
|
||||||
hideBackground: true,
|
hideBackground: true,
|
||||||
stateContext: nil,
|
stateContext: nil,
|
||||||
addImage: nil
|
addImage: { [weak self] in
|
||||||
|
if let self {
|
||||||
|
self.controller?.completion(nil)
|
||||||
|
self.controller?.dismiss(animated: true)
|
||||||
|
self.controller?.presentGallery()
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var stickerPeekBehavior: EmojiContentPeekBehaviorImpl?
|
var stickerPeekBehavior: EmojiContentPeekBehaviorImpl?
|
||||||
@ -1170,8 +1176,12 @@ public class StickerPickerScreen: ViewController {
|
|||||||
useOpaqueTheme: false,
|
useOpaqueTheme: false,
|
||||||
hideBackground: true,
|
hideBackground: true,
|
||||||
stateContext: nil,
|
stateContext: nil,
|
||||||
addImage: {
|
addImage: { [weak self] in
|
||||||
|
if let self {
|
||||||
|
self.controller?.completion(nil)
|
||||||
|
self.controller?.dismiss(animated: true)
|
||||||
|
self.controller?.presentGallery()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1627,6 +1637,8 @@ public class StickerPickerScreen: ViewController {
|
|||||||
|
|
||||||
public var completion: (DrawingStickerEntity.Content?) -> Void = { _ in }
|
public var completion: (DrawingStickerEntity.Content?) -> Void = { _ in }
|
||||||
|
|
||||||
|
public var presentGallery: () -> Void = { }
|
||||||
|
|
||||||
public init(context: AccountContext, inputData: Signal<StickerPickerInputData, NoError>) {
|
public init(context: AccountContext, inputData: Signal<StickerPickerInputData, NoError>) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = defaultDarkColorPresentationTheme
|
self.theme = defaultDarkColorPresentationTheme
|
||||||
|
@ -137,6 +137,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
case `default`
|
case `default`
|
||||||
case wallpaper
|
case wallpaper
|
||||||
case story
|
case story
|
||||||
|
case addImage
|
||||||
}
|
}
|
||||||
|
|
||||||
case assets(PHAssetCollection?, AssetsMode)
|
case assets(PHAssetCollection?, AssetsMode)
|
||||||
@ -250,7 +251,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
self.presentationData = controller.presentationData
|
self.presentationData = controller.presentationData
|
||||||
|
|
||||||
var assetType: PHAssetMediaType?
|
var assetType: PHAssetMediaType?
|
||||||
if case let .assets(_, mode) = controller.subject, case .wallpaper = mode {
|
if case let .assets(_, mode) = controller.subject, [.wallpaper, .addImage].contains(mode) {
|
||||||
assetType = .image
|
assetType = .image
|
||||||
}
|
}
|
||||||
let mediaAssetsContext = MediaAssetsContext(assetType: assetType)
|
let mediaAssetsContext = MediaAssetsContext(assetType: assetType)
|
||||||
@ -404,7 +405,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
self.gridNode.scrollView.alwaysBounceVertical = true
|
self.gridNode.scrollView.alwaysBounceVertical = true
|
||||||
self.gridNode.scrollView.showsVerticalScrollIndicator = false
|
self.gridNode.scrollView.showsVerticalScrollIndicator = false
|
||||||
|
|
||||||
if case let .assets(_, mode) = controller.subject, [.wallpaper, .story].contains(mode) {
|
if case let .assets(_, mode) = controller.subject, [.wallpaper .addImage, .story].contains(mode) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
let selectionGesture = MediaPickerGridSelectionGesture<TGMediaSelectableItem>()
|
let selectionGesture = MediaPickerGridSelectionGesture<TGMediaSelectableItem>()
|
||||||
@ -1379,6 +1380,9 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
self.titleView.title = presentationData.strings.Attachment_Gallery
|
self.titleView.title = presentationData.strings.Attachment_Gallery
|
||||||
case .wallpaper:
|
case .wallpaper:
|
||||||
self.titleView.title = presentationData.strings.Conversation_Theme_ChooseWallpaperTitle
|
self.titleView.title = presentationData.strings.Conversation_Theme_ChooseWallpaperTitle
|
||||||
|
case .addImage:
|
||||||
|
//TODO:localize
|
||||||
|
self.titleView.title = "Add Image"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -2184,6 +2188,28 @@ public func wallpaperMediaPickerController(
|
|||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func mediaPickerController(
|
||||||
|
context: AccountContext,
|
||||||
|
completion: @escaping (Any) -> Void
|
||||||
|
) -> ViewController {
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||||
|
let updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>) = (presentationData, .single(presentationData))
|
||||||
|
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: nil, buttons: [.standalone], initialButton: .standalone, fromMenu: false, hasTextInput: false, makeEntityInputView: {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
controller.requestController = { _, present in
|
||||||
|
let mediaPickerController = MediaPickerScreen(context: context, updatedPresentationData: updatedPresentationData, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .addImage), mainButtonState: nil, mainButtonAction: nil)
|
||||||
|
mediaPickerController.customSelection = { controller, result in
|
||||||
|
completion(result)
|
||||||
|
controller.dismiss(animated: true)
|
||||||
|
}
|
||||||
|
present(mediaPickerController, mediaPickerController.mediaPickerContext)
|
||||||
|
}
|
||||||
|
controller.navigationPresentation = .flatModal
|
||||||
|
controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
public func storyMediaPickerController(
|
public func storyMediaPickerController(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
getSourceRect: @escaping () -> CGRect,
|
getSourceRect: @escaping () -> CGRect,
|
||||||
|
@ -113,6 +113,7 @@ swift_library(
|
|||||||
"//submodules/MediaPickerUI:MediaPickerUI",
|
"//submodules/MediaPickerUI:MediaPickerUI",
|
||||||
"//submodules/ImageBlur:ImageBlur",
|
"//submodules/ImageBlur:ImageBlur",
|
||||||
"//submodules/AttachmentUI:AttachmentUI",
|
"//submodules/AttachmentUI:AttachmentUI",
|
||||||
|
"//submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -53,6 +53,8 @@ extension SettingsSearchableItemIcon {
|
|||||||
return PresentationResourcesSettings.devices
|
return PresentationResourcesSettings.devices
|
||||||
case .premium:
|
case .premium:
|
||||||
return PresentationResourcesSettings.premium
|
return PresentationResourcesSettings.premium
|
||||||
|
case .stories:
|
||||||
|
return PresentationResourcesSettings.stories
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import NotificationPeerExceptionController
|
|||||||
import QrCodeUI
|
import QrCodeUI
|
||||||
import PremiumUI
|
import PremiumUI
|
||||||
import StorageUsageScreen
|
import StorageUsageScreen
|
||||||
|
import PeerInfoStoryGridScreen
|
||||||
|
|
||||||
enum SettingsSearchableItemIcon {
|
enum SettingsSearchableItemIcon {
|
||||||
case profile
|
case profile
|
||||||
@ -41,6 +42,7 @@ enum SettingsSearchableItemIcon {
|
|||||||
case deleteAccount
|
case deleteAccount
|
||||||
case devices
|
case devices
|
||||||
case premium
|
case premium
|
||||||
|
case stories
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SettingsSearchableItemId: Hashable {
|
public enum SettingsSearchableItemId: Hashable {
|
||||||
@ -62,6 +64,7 @@ public enum SettingsSearchableItemId: Hashable {
|
|||||||
case deleteAccount(Int32)
|
case deleteAccount(Int32)
|
||||||
case devices(Int32)
|
case devices(Int32)
|
||||||
case premium(Int32)
|
case premium(Int32)
|
||||||
|
case stories(Int32)
|
||||||
|
|
||||||
private var namespace: Int32 {
|
private var namespace: Int32 {
|
||||||
switch self {
|
switch self {
|
||||||
@ -101,6 +104,8 @@ public enum SettingsSearchableItemId: Hashable {
|
|||||||
return 18
|
return 18
|
||||||
case .premium:
|
case .premium:
|
||||||
return 19
|
return 19
|
||||||
|
case .stories:
|
||||||
|
return 20
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +128,8 @@ public enum SettingsSearchableItemId: Hashable {
|
|||||||
let .chatFolders(id),
|
let .chatFolders(id),
|
||||||
let .deleteAccount(id),
|
let .deleteAccount(id),
|
||||||
let .devices(id),
|
let .devices(id),
|
||||||
let .premium(id):
|
let .premium(id),
|
||||||
|
let .stories(id):
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -172,6 +178,8 @@ public enum SettingsSearchableItemId: Hashable {
|
|||||||
self = .devices(id)
|
self = .devices(id)
|
||||||
case 19:
|
case 19:
|
||||||
self = .premium(id)
|
self = .premium(id)
|
||||||
|
case 20:
|
||||||
|
self = .stories(id)
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -340,6 +348,25 @@ private func premiumSearchableItems(context: AccountContext) -> [SettingsSearcha
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func storiesSearchableItems(context: AccountContext) -> [SettingsSearchableItem] {
|
||||||
|
let icon: SettingsSearchableItemIcon = .stories
|
||||||
|
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
|
||||||
|
|
||||||
|
var result: [SettingsSearchableItem] = []
|
||||||
|
|
||||||
|
//TODO:localize
|
||||||
|
result.append(SettingsSearchableItem(id: .stories(0), title: "My Stories", alternate: synonyms(strings.SettingsSearch_Synonyms_Premium), icon: icon, breadcrumbs: [], present: { context, _, present in
|
||||||
|
present(.push, PeerInfoStoryGridScreen(context: context, peerId: context.account.peerId, scope: .saved))
|
||||||
|
}))
|
||||||
|
|
||||||
|
result.append(SettingsSearchableItem(id: .stories(1), title: "Stories Archive", alternate: synonyms(strings.SettingsSearch_Synonyms_Premium), icon: icon, breadcrumbs: [], present: { context, _, present in
|
||||||
|
present(.push, PeerInfoStoryGridScreen(context: context, peerId: context.account.peerId, scope: .archive))
|
||||||
|
}))
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private func callSearchableItems(context: AccountContext) -> [SettingsSearchableItem] {
|
private func callSearchableItems(context: AccountContext) -> [SettingsSearchableItem] {
|
||||||
let icon: SettingsSearchableItemIcon = .calls
|
let icon: SettingsSearchableItemIcon = .calls
|
||||||
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
|
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
|
||||||
@ -1008,6 +1035,9 @@ func settingsSearchableItems(context: AccountContext, notificationExceptionsList
|
|||||||
|
|
||||||
let premiumItems = premiumSearchableItems(context: context)
|
let premiumItems = premiumSearchableItems(context: context)
|
||||||
allItems.append(contentsOf: premiumItems)
|
allItems.append(contentsOf: premiumItems)
|
||||||
|
|
||||||
|
let storiesItems = storiesSearchableItems(context: context)
|
||||||
|
allItems.append(contentsOf: storiesItems)
|
||||||
|
|
||||||
if watchAppInstalled {
|
if watchAppInstalled {
|
||||||
let watch = SettingsSearchableItem(id: .watch(0), title: strings.Settings_AppleWatch, alternate: synonyms(strings.SettingsSearch_Synonyms_Watch), icon: .watch, breadcrumbs: [], present: { context, _, present in
|
let watch = SettingsSearchableItem(id: .watch(0), title: strings.Settings_AppleWatch, alternate: synonyms(strings.SettingsSearch_Synonyms_Watch), icon: .watch, breadcrumbs: [], present: { context, _, present in
|
||||||
|
@ -1370,7 +1370,7 @@ public class CameraScreen: ViewController {
|
|||||||
|
|
||||||
private var isDismissing = false
|
private var isDismissing = false
|
||||||
@objc private func handlePan(_ gestureRecognizer: UIPanGestureRecognizer) {
|
@objc private func handlePan(_ gestureRecognizer: UIPanGestureRecognizer) {
|
||||||
guard let controller = self.controller else {
|
guard let controller = self.controller, let layout = self.validLayout else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let translation = gestureRecognizer.translation(in: gestureRecognizer.view)
|
let translation = gestureRecognizer.translation(in: gestureRecognizer.view)
|
||||||
@ -1380,7 +1380,7 @@ public class CameraScreen: ViewController {
|
|||||||
case .changed:
|
case .changed:
|
||||||
if self.componentExternalState.isRecording {
|
if self.componentExternalState.isRecording {
|
||||||
|
|
||||||
} else {
|
} else if case .compact = layout.metrics.widthClass {
|
||||||
if translation.x < -10.0 || self.isDismissing {
|
if translation.x < -10.0 || self.isDismissing {
|
||||||
self.isDismissing = true
|
self.isDismissing = true
|
||||||
let transitionFraction = 1.0 - max(0.0, translation.x * -1.0) / self.frame.width
|
let transitionFraction = 1.0 - max(0.0, translation.x * -1.0) / self.frame.width
|
||||||
@ -2179,7 +2179,7 @@ public class CameraScreen: ViewController {
|
|||||||
if let current = self.galleryController {
|
if let current = self.galleryController {
|
||||||
controller = current
|
controller = current
|
||||||
} else {
|
} else {
|
||||||
controller = self.context.sharedContext.makeMediaPickerScreen(context: self.context, getSourceRect: { [weak self] in
|
controller = self.context.sharedContext.makeStoryMediaPickerScreen(context: self.context, getSourceRect: { [weak self] in
|
||||||
if let self {
|
if let self {
|
||||||
if let galleryButton = self.node.componentHost.findTaggedView(tag: galleryButtonTag) {
|
if let galleryButton = self.node.componentHost.findTaggedView(tag: galleryButtonTag) {
|
||||||
return galleryButton.convert(galleryButton.bounds, to: self.view).offsetBy(dx: 0.0, dy: -15.0)
|
return galleryButton.convert(galleryButton.bounds, to: self.view).offsetBy(dx: 0.0, dy: -15.0)
|
||||||
|
@ -588,6 +588,18 @@ public final class EntityKeyboardComponent: Component {
|
|||||||
}
|
}
|
||||||
).minSize(CGSize(width: 38.0, height: 38.0)))))
|
).minSize(CGSize(width: 38.0, height: 38.0)))))
|
||||||
}
|
}
|
||||||
|
if let addImage = component.stickerContent?.inputInteractionHolder.inputInteraction?.addImage {
|
||||||
|
contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "stickers", component: AnyComponent(Button(
|
||||||
|
content: AnyComponent(BundleIconComponent(
|
||||||
|
name: "Media Editor/AddImage",
|
||||||
|
tintColor: component.theme.chat.inputMediaPanel.panelIconColor,
|
||||||
|
maxSize: nil
|
||||||
|
)),
|
||||||
|
action: {
|
||||||
|
addImage()
|
||||||
|
}
|
||||||
|
).minSize(CGSize(width: 38.0, height: 38.0)))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let deleteBackwards = component.emojiContent?.inputInteractionHolder.inputInteraction?.deleteBackwards
|
let deleteBackwards = component.emojiContent?.inputInteractionHolder.inputInteraction?.deleteBackwards
|
||||||
@ -689,9 +701,8 @@ public final class EntityKeyboardComponent: Component {
|
|||||||
component.switchToTextInput()
|
component.switchToTextInput()
|
||||||
}
|
}
|
||||||
).minSize(CGSize(width: 38.0, height: 38.0)))))
|
).minSize(CGSize(width: 38.0, height: 38.0)))))
|
||||||
}
|
} else if let addImage = component.emojiContent?.inputInteractionHolder.inputInteraction?.addImage {
|
||||||
if let addImage = component.stickerContent?.inputInteractionHolder.inputInteraction?.addImage {
|
contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "emoji", component: AnyComponent(Button(
|
||||||
contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "image", component: AnyComponent(Button(
|
|
||||||
content: AnyComponent(BundleIconComponent(
|
content: AnyComponent(BundleIconComponent(
|
||||||
name: "Media Editor/AddImage",
|
name: "Media Editor/AddImage",
|
||||||
tintColor: component.theme.chat.inputMediaPanel.panelIconColor,
|
tintColor: component.theme.chat.inputMediaPanel.panelIconColor,
|
||||||
|
@ -1843,6 +1843,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
switch subject {
|
switch subject {
|
||||||
case .image, .video:
|
case .image, .video:
|
||||||
isSavingAvailable = !controller.isEditingStory
|
isSavingAvailable = !controller.isEditingStory
|
||||||
|
case .draft:
|
||||||
|
isSavingAvailable = true
|
||||||
default:
|
default:
|
||||||
isSavingAvailable = false
|
isSavingAvailable = false
|
||||||
}
|
}
|
||||||
@ -2704,34 +2706,27 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
self.saveTooltip = tooltipController
|
self.saveTooltip = tooltipController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private weak var storyArchiveTooltip: ViewController?
|
func presentGallery() {
|
||||||
func presentStoryArchiveTooltip(sourceView: UIView) {
|
|
||||||
guard let controller = self.controller else {
|
guard let controller = self.controller else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let galleryController = self.context.sharedContext.makeMediaPickerScreen(context: self.context, completion: { [weak self] result in
|
||||||
if let storyArchiveTooltip = self.storyArchiveTooltip {
|
guard let self, let asset = result as? PHAsset else {
|
||||||
storyArchiveTooltip.dismiss(animated: true)
|
return
|
||||||
self.storyArchiveTooltip = nil
|
}
|
||||||
}
|
|
||||||
|
let options = PHImageRequestOptions()
|
||||||
let parentFrame = self.view.convert(self.bounds, to: nil)
|
options.deliveryMode = .highQualityFormat
|
||||||
let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0)
|
PHImageManager.default().requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .default, options: options) { [weak self] image, _ in
|
||||||
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 5.0), size: CGSize())
|
if let self, let image {
|
||||||
|
Queue.mainQueue().async {
|
||||||
let text: String
|
self.interaction?.insertEntity(DrawingStickerEntity(content: .image(image, true)), scale: 2.5)
|
||||||
if controller.state.privacy.pin {
|
}
|
||||||
text = "Story will be kept on your page."
|
}
|
||||||
} else {
|
}
|
||||||
text = "Story will disappear in 24 hours."
|
|
||||||
}
|
|
||||||
|
|
||||||
let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: text), location: .point(location, .bottom), displayDuration: .default, inset: 7.0, cornerRadius: 9.0, shouldDismissOnTouch: { _, _ in
|
|
||||||
return .ignore
|
|
||||||
})
|
})
|
||||||
self.storyArchiveTooltip = tooltipController
|
controller.push(galleryController)
|
||||||
self.controller?.present(tooltipController, in: .current)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateModalTransitionFactor(_ value: CGFloat, transition: ContainedViewLayoutTransition) {
|
func updateModalTransitionFactor(_ value: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
@ -2886,6 +2881,11 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
self.updateModalTransitionFactor(transitionFactor, transition: transition)
|
self.updateModalTransitionFactor(transitionFactor, transition: transition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
controller.presentGallery = { [weak self] in
|
||||||
|
if let self {
|
||||||
|
self.presentGallery()
|
||||||
|
}
|
||||||
|
}
|
||||||
self.stickerScreen = controller
|
self.stickerScreen = controller
|
||||||
self.controller?.present(controller, in: .window(.root))
|
self.controller?.present(controller, in: .window(.root))
|
||||||
return
|
return
|
||||||
|
@ -1476,7 +1476,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||||||
let inset: CGFloat
|
let inset: CGFloat
|
||||||
if case let .stories(editing) = component.stateContext.subject {
|
if case let .stories(editing) = component.stateContext.subject {
|
||||||
if editing {
|
if editing {
|
||||||
inset = 430.0
|
inset = 446.0
|
||||||
} else {
|
} else {
|
||||||
inset = 605.0
|
inset = 605.0
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "photo_24.pdf",
|
"filename" : "image_30.pdf",
|
||||||
"idiom" : "universal"
|
"idiom" : "universal"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
160
submodules/TelegramUI/Images.xcassets/Media Editor/AddImage.imageset/image_30.pdf
vendored
Normal file
160
submodules/TelegramUI/Images.xcassets/Media Editor/AddImage.imageset/image_30.pdf
vendored
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<< /Length 3 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 5.335022 5.334991 cm
|
||||||
|
1.000000 1.000000 1.000000 scn
|
||||||
|
7.065000 19.330017 m
|
||||||
|
7.035460 19.330017 l
|
||||||
|
5.940372 19.330021 5.077796 19.330025 4.383656 19.273312 c
|
||||||
|
3.675523 19.215456 3.084329 19.095276 2.547134 18.821562 c
|
||||||
|
1.669358 18.374313 0.955704 17.660660 0.508455 16.782883 c
|
||||||
|
0.234740 16.245687 0.114562 15.654494 0.056705 14.946362 c
|
||||||
|
-0.000008 14.252222 -0.000005 13.389645 0.000000 12.294557 c
|
||||||
|
0.000000 12.265017 l
|
||||||
|
0.000000 7.065017 l
|
||||||
|
0.000000 7.035477 l
|
||||||
|
-0.000005 5.940390 -0.000008 5.077813 0.056705 4.383673 c
|
||||||
|
0.114562 3.675540 0.234740 3.084345 0.508455 2.547152 c
|
||||||
|
0.955704 1.669374 1.669358 0.955721 2.547134 0.508471 c
|
||||||
|
3.084329 0.234756 3.675523 0.114578 4.383656 0.056721 c
|
||||||
|
5.077746 0.000010 5.940250 0.000013 7.035228 0.000017 c
|
||||||
|
7.035275 0.000017 l
|
||||||
|
7.035323 0.000017 l
|
||||||
|
7.035370 0.000017 l
|
||||||
|
7.065000 0.000017 l
|
||||||
|
12.265000 0.000017 l
|
||||||
|
12.294630 0.000017 l
|
||||||
|
12.294676 0.000017 l
|
||||||
|
12.294722 0.000017 l
|
||||||
|
12.294767 0.000017 l
|
||||||
|
13.389750 0.000013 14.252252 0.000010 14.946344 0.056721 c
|
||||||
|
15.654477 0.114578 16.245672 0.234756 16.782866 0.508471 c
|
||||||
|
17.660643 0.955721 18.374296 1.669374 18.821547 2.547152 c
|
||||||
|
19.095261 3.084345 19.215439 3.675540 19.273296 4.383673 c
|
||||||
|
19.330008 5.077765 19.330004 5.940269 19.330000 7.035251 c
|
||||||
|
19.330000 7.035296 l
|
||||||
|
19.330000 7.035342 l
|
||||||
|
19.330000 7.035388 l
|
||||||
|
19.330000 7.065018 l
|
||||||
|
19.330000 12.265018 l
|
||||||
|
19.330000 12.294647 l
|
||||||
|
19.330000 12.294695 l
|
||||||
|
19.330000 12.294742 l
|
||||||
|
19.330000 12.294788 l
|
||||||
|
19.330004 13.389767 19.330008 14.252271 19.273296 14.946362 c
|
||||||
|
19.215439 15.654494 19.095261 16.245687 18.821547 16.782883 c
|
||||||
|
18.374296 17.660660 17.660643 18.374313 16.782866 18.821562 c
|
||||||
|
16.245672 19.095276 15.654477 19.215456 14.946344 19.273312 c
|
||||||
|
14.252204 19.330025 13.389627 19.330021 12.294539 19.330017 c
|
||||||
|
12.264999 19.330017 l
|
||||||
|
7.065000 19.330017 l
|
||||||
|
h
|
||||||
|
3.150942 17.636524 m
|
||||||
|
3.469394 17.798782 3.866076 17.896591 4.491960 17.947729 c
|
||||||
|
5.125607 17.999500 5.933921 18.000017 7.065000 18.000017 c
|
||||||
|
12.264999 18.000017 l
|
||||||
|
13.396078 18.000017 14.204392 17.999500 14.838039 17.947729 c
|
||||||
|
15.463923 17.896591 15.860606 17.798782 16.179058 17.636524 c
|
||||||
|
16.806580 17.316786 17.316771 16.806597 17.636507 16.179075 c
|
||||||
|
17.798767 15.860623 17.896576 15.463942 17.947712 14.838057 c
|
||||||
|
17.999483 14.204411 18.000000 13.396095 18.000000 12.265018 c
|
||||||
|
18.000000 7.065018 l
|
||||||
|
18.000000 6.309615 17.999769 5.698176 17.984194 5.186587 c
|
||||||
|
15.466902 7.867111 l
|
||||||
|
14.693245 8.690934 13.378143 8.669102 12.632261 7.820047 c
|
||||||
|
11.520865 6.554921 l
|
||||||
|
6.709533 11.633550 l
|
||||||
|
5.937637 12.448328 4.633699 12.427637 3.888045 11.588776 c
|
||||||
|
1.330000 8.710975 l
|
||||||
|
1.330000 12.265017 l
|
||||||
|
1.330000 13.396095 1.330517 14.204410 1.382288 14.838057 c
|
||||||
|
1.433425 15.463942 1.531234 15.860623 1.693493 16.179075 c
|
||||||
|
2.013231 16.806597 2.523421 17.316786 3.150942 17.636524 c
|
||||||
|
h
|
||||||
|
14.497394 6.956641 m
|
||||||
|
17.768957 3.472939 l
|
||||||
|
17.730608 3.355179 17.686632 3.249336 17.636507 3.150959 c
|
||||||
|
17.316771 2.523438 16.806580 2.013247 16.179058 1.693510 c
|
||||||
|
16.167240 1.687489 16.155315 1.681555 16.143274 1.675711 c
|
||||||
|
12.439418 5.585338 l
|
||||||
|
13.631460 6.942265 l
|
||||||
|
13.859314 7.201635 14.261054 7.208306 14.497394 6.956641 c
|
||||||
|
h
|
||||||
|
1.330082 6.709152 m
|
||||||
|
1.330711 5.761131 1.336117 5.057091 1.382288 4.491978 c
|
||||||
|
1.433425 3.866094 1.531234 3.469411 1.693493 3.150959 c
|
||||||
|
2.013231 2.523438 2.523421 2.013247 3.150942 1.693510 c
|
||||||
|
3.469394 1.531250 3.866076 1.433441 4.491960 1.382305 c
|
||||||
|
5.125607 1.330534 5.933922 1.330017 7.065000 1.330017 c
|
||||||
|
12.265000 1.330017 l
|
||||||
|
13.261761 1.330017 14.007863 1.330418 14.604662 1.365944 c
|
||||||
|
5.744016 10.718849 l
|
||||||
|
5.508215 10.967751 5.109884 10.961430 4.882100 10.705172 c
|
||||||
|
1.330082 6.709152 l
|
||||||
|
h
|
||||||
|
14.040000 12.165017 m
|
||||||
|
15.075534 12.165017 15.915000 13.004483 15.915000 14.040017 c
|
||||||
|
15.915000 15.075551 15.075534 15.915017 14.040000 15.915017 c
|
||||||
|
13.004466 15.915017 12.165000 15.075551 12.165000 14.040017 c
|
||||||
|
12.165000 13.004483 13.004466 12.165017 14.040000 12.165017 c
|
||||||
|
h
|
||||||
|
f*
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
3865
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||||
|
/Resources 1 0 R
|
||||||
|
/Contents 2 0 R
|
||||||
|
/Parent 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /Kids [ 4 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Pages 5 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000003955 00000 n
|
||||||
|
0000003978 00000 n
|
||||||
|
0000004151 00000 n
|
||||||
|
0000004225 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
4284
|
||||||
|
%%EOF
|
@ -1,154 +0,0 @@
|
|||||||
%PDF-1.7
|
|
||||||
|
|
||||||
1 0 obj
|
|
||||||
<< >>
|
|
||||||
endobj
|
|
||||||
|
|
||||||
2 0 obj
|
|
||||||
<< /Length 3 0 R >>
|
|
||||||
stream
|
|
||||||
/DeviceRGB CS
|
|
||||||
/DeviceRGB cs
|
|
||||||
q
|
|
||||||
1.000000 0.000000 -0.000000 1.000000 3.835083 3.834961 cm
|
|
||||||
1.000000 1.000000 1.000000 scn
|
|
||||||
5.465000 16.330017 m
|
|
||||||
5.436178 16.330017 l
|
|
||||||
5.436174 16.330017 l
|
|
||||||
4.620535 16.330023 3.967872 16.330027 3.440454 16.286936 c
|
|
||||||
2.899074 16.242702 2.431364 16.149773 2.001125 15.930555 c
|
|
||||||
1.311511 15.579180 0.750837 15.018506 0.399461 14.328892 c
|
|
||||||
0.180244 13.898653 0.087314 13.430943 0.043081 12.889563 c
|
|
||||||
-0.000010 12.362144 -0.000006 11.709482 0.000000 10.893843 c
|
|
||||||
0.000000 10.893839 l
|
|
||||||
0.000000 10.865017 l
|
|
||||||
0.000000 5.465017 l
|
|
||||||
0.000000 5.436195 l
|
|
||||||
-0.000006 4.620555 -0.000010 3.967890 0.043081 3.440471 c
|
|
||||||
0.087314 2.899090 0.180244 2.431380 0.399461 2.001142 c
|
|
||||||
0.750837 1.311528 1.311511 0.750854 2.001125 0.399478 c
|
|
||||||
2.431364 0.180260 2.899074 0.087330 3.440454 0.043098 c
|
|
||||||
3.967863 0.000008 4.620512 0.000011 5.436130 0.000017 c
|
|
||||||
5.436194 0.000017 l
|
|
||||||
5.465001 0.000017 l
|
|
||||||
10.865000 0.000017 l
|
|
||||||
10.893806 0.000017 l
|
|
||||||
10.893871 0.000017 l
|
|
||||||
11.709489 0.000011 12.362138 0.000008 12.889546 0.043098 c
|
|
||||||
13.430927 0.087330 13.898637 0.180260 14.328876 0.399478 c
|
|
||||||
15.018489 0.750854 15.579163 1.311528 15.930539 2.001142 c
|
|
||||||
16.149757 2.431380 16.242687 2.899091 16.286919 3.440471 c
|
|
||||||
16.330009 3.967880 16.330006 4.620529 16.330000 5.436146 c
|
|
||||||
16.330000 5.436212 l
|
|
||||||
16.330000 5.465017 l
|
|
||||||
16.330000 10.865017 l
|
|
||||||
16.330000 10.893824 l
|
|
||||||
16.330000 10.893888 l
|
|
||||||
16.330006 11.709505 16.330009 12.362154 16.286919 12.889563 c
|
|
||||||
16.242687 13.430943 16.149757 13.898653 15.930539 14.328892 c
|
|
||||||
15.579163 15.018506 15.018489 15.579180 14.328876 15.930555 c
|
|
||||||
13.898637 16.149773 13.430926 16.242702 12.889546 16.286936 c
|
|
||||||
12.362126 16.330029 11.709461 16.330023 10.893822 16.330017 c
|
|
||||||
10.865000 16.330017 l
|
|
||||||
5.465000 16.330017 l
|
|
||||||
h
|
|
||||||
2.604932 14.745517 m
|
|
||||||
2.816429 14.853280 3.089626 14.923841 3.548759 14.961352 c
|
|
||||||
4.015654 14.999499 4.613948 15.000017 5.465000 15.000017 c
|
|
||||||
10.865000 15.000017 l
|
|
||||||
11.716052 15.000017 12.314346 14.999499 12.781241 14.961352 c
|
|
||||||
13.240374 14.923841 13.513572 14.853280 13.725068 14.745517 c
|
|
||||||
14.164426 14.521652 14.521636 14.164443 14.745501 13.725084 c
|
|
||||||
14.853263 13.513588 14.923823 13.240391 14.961336 12.781259 c
|
|
||||||
14.999483 12.314363 15.000000 11.716068 15.000000 10.865017 c
|
|
||||||
15.000000 5.465017 l
|
|
||||||
15.000000 5.137442 14.999924 4.847313 14.997654 4.587710 c
|
|
||||||
12.903418 6.817746 l
|
|
||||||
12.230759 7.534022 11.087341 7.515037 10.438833 6.776826 c
|
|
||||||
9.645941 5.874260 l
|
|
||||||
5.897148 9.831320 l
|
|
||||||
5.226022 10.539732 4.092310 10.521740 3.444000 9.792391 c
|
|
||||||
1.330000 7.414142 l
|
|
||||||
1.330000 10.865017 l
|
|
||||||
1.330000 11.716068 1.330518 12.314363 1.368664 12.781259 c
|
|
||||||
1.406177 13.240391 1.476737 13.513588 1.584500 13.725084 c
|
|
||||||
1.808364 14.164443 2.165574 14.521652 2.604932 14.745517 c
|
|
||||||
h
|
|
||||||
11.933909 5.907277 m
|
|
||||||
14.833861 2.819281 l
|
|
||||||
14.807792 2.739742 14.778378 2.669474 14.745501 2.604949 c
|
|
||||||
14.521636 2.165591 14.164426 1.808381 13.725068 1.584517 c
|
|
||||||
13.714809 1.579343 l
|
|
||||||
10.564494 4.904676 l
|
|
||||||
11.438032 5.899043 l
|
|
||||||
11.568513 6.047573 11.798571 6.051393 11.933909 5.907277 c
|
|
||||||
h
|
|
||||||
1.330001 5.412228 m
|
|
||||||
1.330038 4.589128 1.331311 4.005958 1.368664 3.548775 c
|
|
||||||
1.406177 3.089643 1.476737 2.816445 1.584500 2.604949 c
|
|
||||||
1.808364 2.165591 2.165574 1.808381 2.604932 1.584517 c
|
|
||||||
2.816429 1.476754 3.089626 1.406194 3.548759 1.368681 c
|
|
||||||
4.015654 1.330534 4.613949 1.330017 5.465001 1.330017 c
|
|
||||||
10.865000 1.330017 l
|
|
||||||
11.357291 1.330017 11.765009 1.330190 12.111642 1.337719 c
|
|
||||||
4.931631 8.916620 l
|
|
||||||
4.796600 9.059153 4.568496 9.055533 4.438055 8.908787 c
|
|
||||||
1.330001 5.412228 l
|
|
||||||
h
|
|
||||||
11.664969 10.165024 m
|
|
||||||
12.493397 10.165024 13.164969 10.836597 13.164969 11.665024 c
|
|
||||||
13.164969 12.493451 12.493397 13.165024 11.664969 13.165024 c
|
|
||||||
10.836542 13.165024 10.164969 12.493451 10.164969 11.665024 c
|
|
||||||
10.164969 10.836597 10.836542 10.165024 11.664969 10.165024 c
|
|
||||||
h
|
|
||||||
f*
|
|
||||||
n
|
|
||||||
Q
|
|
||||||
|
|
||||||
endstream
|
|
||||||
endobj
|
|
||||||
|
|
||||||
3 0 obj
|
|
||||||
3694
|
|
||||||
endobj
|
|
||||||
|
|
||||||
4 0 obj
|
|
||||||
<< /Annots []
|
|
||||||
/Type /Page
|
|
||||||
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
|
||||||
/Resources 1 0 R
|
|
||||||
/Contents 2 0 R
|
|
||||||
/Parent 5 0 R
|
|
||||||
>>
|
|
||||||
endobj
|
|
||||||
|
|
||||||
5 0 obj
|
|
||||||
<< /Kids [ 4 0 R ]
|
|
||||||
/Count 1
|
|
||||||
/Type /Pages
|
|
||||||
>>
|
|
||||||
endobj
|
|
||||||
|
|
||||||
6 0 obj
|
|
||||||
<< /Pages 5 0 R
|
|
||||||
/Type /Catalog
|
|
||||||
>>
|
|
||||||
endobj
|
|
||||||
|
|
||||||
xref
|
|
||||||
0 7
|
|
||||||
0000000000 65535 f
|
|
||||||
0000000010 00000 n
|
|
||||||
0000000034 00000 n
|
|
||||||
0000003784 00000 n
|
|
||||||
0000003807 00000 n
|
|
||||||
0000003980 00000 n
|
|
||||||
0000004054 00000 n
|
|
||||||
trailer
|
|
||||||
<< /ID [ (some) (id) ]
|
|
||||||
/Root 6 0 R
|
|
||||||
/Size 7
|
|
||||||
>>
|
|
||||||
startxref
|
|
||||||
4113
|
|
||||||
%%EOF
|
|
@ -1843,7 +1843,11 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
return StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: mainStickerPack, stickerPacks: stickerPacks, loadedStickerPacks: loadedStickerPacks, parentNavigationController: parentNavigationController, sendSticker: sendSticker)
|
return StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: mainStickerPack, stickerPacks: stickerPacks, loadedStickerPacks: loadedStickerPacks, parentNavigationController: parentNavigationController, sendSticker: sendSticker)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController {
|
public func makeMediaPickerScreen(context: AccountContext, completion: @escaping (Any) -> Void) -> ViewController {
|
||||||
|
return mediaPickerController(context: context, completion: completion)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makeStoryMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController {
|
||||||
return storyMediaPickerController(context: context, getSourceRect: getSourceRect, completion: completion, dismissed: dismissed)
|
return storyMediaPickerController(context: context, getSourceRect: getSourceRect, completion: completion, dismissed: dismissed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user