Various fixes

This commit is contained in:
Ilya Laktyushin 2023-07-14 04:35:23 +02:00
parent 0b7869688d
commit 5e5f19e6e9
12 changed files with 516 additions and 73 deletions

View File

@ -82,6 +82,16 @@ public final class DeviceAccess {
return self.locationPromise.get()
}
private static let cameraPromise = Promise<Bool?>(nil)
static var camera: Signal<Bool?, NoError> {
return self.cameraPromise.get()
}
private static let microphonePromise = Promise<Bool?>(nil)
static var microphone: Signal<Bool?, NoError> {
return self.microphonePromise.get()
}
public static func isMicrophoneAccessAuthorized() -> Bool? {
return AVAudioSession.sharedInstance().recordPermission == .granted
}
@ -248,12 +258,72 @@ public final class DeviceAccess {
}
}
)
case .camera:
return Signal { subscriber in
let status = AVCaptureDevice.authorizationStatus(for: .video)
switch status {
case .authorized:
subscriber.putNext(.allowed)
case .denied, .restricted:
subscriber.putNext(.denied)
case .notDetermined:
subscriber.putNext(.notDetermined)
@unknown default:
fatalError()
}
subscriber.putCompletion()
return EmptyDisposable
}
|> then(self.camera
|> mapToSignal { authorized -> Signal<AccessType, NoError> in
if let authorized = authorized {
return .single(authorized ? .allowed : .denied)
} else {
return .complete()
}
}
)
case .microphone:
return Signal { subscriber in
let status = AVCaptureDevice.authorizationStatus(for: .audio)
switch status {
case .authorized:
subscriber.putNext(.allowed)
case .denied, .restricted:
subscriber.putNext(.denied)
case .notDetermined:
subscriber.putNext(.notDetermined)
@unknown default:
fatalError()
}
subscriber.putCompletion()
return EmptyDisposable
}
|> then(self.microphone
|> mapToSignal { authorized -> Signal<AccessType, NoError> in
if let authorized = authorized {
return .single(authorized ? .allowed : .denied)
} else {
return .complete()
}
}
)
default:
return .single(.notDetermined)
}
}
public static func authorizeAccess(to subject: DeviceAccessSubject, onlyCheck: Bool = false, registerForNotifications: ((@escaping (Bool) -> Void) -> Void)? = nil, requestSiriAuthorization: ((@escaping (Bool) -> Void) -> Void)? = nil, locationManager: LocationManager? = nil, presentationData: PresentationData? = nil, present: @escaping (ViewController, Any?) -> Void = { _, _ in }, openSettings: @escaping () -> Void = { }, displayNotificationFromBackground: @escaping (String) -> Void = { _ in }, _ completion: @escaping (Bool) -> Void = { _ in }) {
public static func authorizeAccess(
to subject: DeviceAccessSubject,
onlyCheck: Bool = false,
registerForNotifications: ((@escaping (Bool) -> Void) -> Void)? = nil,
requestSiriAuthorization: ((@escaping (Bool) -> Void) -> Void)? = nil,
locationManager: LocationManager? = nil,
presentationData: PresentationData? = nil,
present: @escaping (ViewController, Any?) -> Void = { _, _ in },
openSettings: @escaping () -> Void = { },
displayNotificationFromBackground: @escaping (String) -> Void = { _ in },
_ completion: @escaping (Bool) -> Void = { _ in }) {
switch subject {
case let .camera(cameraSubject):
let status = AVCaptureDevice.authorizationStatus(for: .video)
@ -262,6 +332,7 @@ public final class DeviceAccess {
AVCaptureDevice.requestAccess(for: AVMediaType.video) { response in
Queue.mainQueue().async {
completion(response)
self.cameraPromise.set(.single(response))
if !response, let presentationData = presentationData {
let text: String
switch cameraSubject {
@ -331,6 +402,7 @@ public final class DeviceAccess {
displayNotificationFromBackground(text)
}
}
self.microphonePromise.set(.single(granted))
}
})
}

View File

@ -694,6 +694,8 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
if let selectedEntityView = self.selectedEntityView, let selectionView = selectedEntityView.selectionView {
if !self.hasBin {
selectionView.handlePan(gestureRecognizer)
} else if let stickerEntity = selectedEntityView.entity as? DrawingStickerEntity, case .dualVideoReference = stickerEntity.content {
selectionView.handlePan(gestureRecognizer)
} else {
var isTrappedInBin = false
let scale = 100.0 / selectedEntityView.bounds.size.width

View File

@ -2930,7 +2930,7 @@ public class DrawingScreen: ViewController, TGPhotoDrawingInterfaceController, U
}
let images = imageItems as! [UIImage]
if images.count == 1, let image = images.first, max(image.size.width, image.size.height) > 1.0 {
let entity = DrawingStickerEntity(content: .image(image, false))
let entity = DrawingStickerEntity(content: .image(image, .sticker))
strongSelf.node.insertEntity.invoke(entity)
}
}

View File

@ -585,7 +585,7 @@ public class StickerPickerScreen: ViewController {
CTLineDraw(line, context)
context.translateBy(x: -lineOrigin.x, y: -lineOrigin.y)
}) {
strongSelf.controller?.completion(.image(image, false))
strongSelf.controller?.completion(.image(image, .sticker))
}
strongSelf.controller?.dismiss(animated: true)
}

View File

@ -71,12 +71,16 @@ swift_library(
"//submodules/Components/BlurredBackgroundComponent",
"//submodules/Components/LottieAnimationComponent:LottieAnimationComponent",
"//submodules/Components/BundleIconComponent:BundleIconComponent",
"//submodules/TelegramUI/Components/ButtonComponent",
"//submodules/TelegramUI/Components/LottieComponent",
"//submodules/TooltipUI",
"//submodules/TelegramUI/Components/MediaEditor",
"//submodules/Components/MetalImageView",
"//submodules/TelegramUI/Components/CameraButtonComponent",
"//submodules/Utils/VolumeButtons",
"//submodules/TelegramNotices",
"//submodules/DeviceAccess",
],
visibility = [
"//visibility:public",

View File

@ -21,6 +21,7 @@ import BundleIconComponent
import CameraButtonComponent
import VolumeButtons
import TelegramNotices
import DeviceAccess
let videoRedColor = UIColor(rgb: 0xff3b30)
@ -87,6 +88,8 @@ private final class CameraScreenComponent: CombinedComponent {
let context: AccountContext
let cameraState: CameraState
let cameraAuthorizationStatus: AccessType
let microphoneAuthorizationStatus: AccessType
let hasAppeared: Bool
let isVisible: Bool
let panelWidth: CGFloat
@ -101,6 +104,8 @@ private final class CameraScreenComponent: CombinedComponent {
init(
context: AccountContext,
cameraState: CameraState,
cameraAuthorizationStatus: AccessType,
microphoneAuthorizationStatus: AccessType,
hasAppeared: Bool,
isVisible: Bool,
panelWidth: CGFloat,
@ -114,6 +119,8 @@ private final class CameraScreenComponent: CombinedComponent {
) {
self.context = context
self.cameraState = cameraState
self.cameraAuthorizationStatus = cameraAuthorizationStatus
self.microphoneAuthorizationStatus = microphoneAuthorizationStatus
self.hasAppeared = hasAppeared
self.isVisible = isVisible
self.panelWidth = panelWidth
@ -133,6 +140,12 @@ private final class CameraScreenComponent: CombinedComponent {
if lhs.cameraState != rhs.cameraState {
return false
}
if lhs.cameraAuthorizationStatus != rhs.cameraAuthorizationStatus {
return false
}
if lhs.microphoneAuthorizationStatus != rhs.microphoneAuthorizationStatus {
return false
}
if lhs.hasAppeared != rhs.hasAppeared {
return false
}
@ -167,10 +180,6 @@ private final class CameraScreenComponent: CombinedComponent {
}
}
private var cameraAuthorizationStatus: AVAuthorizationStatus = .notDetermined
private var microphoneAuthorizationStatus: AVAuthorizationStatus = .notDetermined
private var galleryAuthorizationStatus: PHAuthorizationStatus = .notDetermined
private let context: AccountContext
private let present: (ViewController) -> Void
private let completion: ActionSlot<Signal<CameraScreen.Result, NoError>>
@ -502,6 +511,7 @@ private final class CameraScreenComponent: CombinedComponent {
}
static var body: Body {
let placeholder = Child(PlaceholderComponent.self)
let cancelButton = Child(CameraButton.self)
let captureControls = Child(CaptureControlsComponent.self)
let zoomControl = Child(ZoomComponent.self)
@ -535,13 +545,52 @@ private final class CameraScreenComponent: CombinedComponent {
let panelWidth = min(component.panelWidth, 185.0)
var controlsBottomInset: CGFloat = 0.0
if !isTablet {
let previewHeight = floorToScreenPixels(availableSize.width * 1.77778)
if !isTablet {
if availableSize.height < previewHeight + 30.0 {
controlsBottomInset = -48.0
}
}
let hasAllRequiredAccess: Bool
switch component.cameraAuthorizationStatus {
case .notDetermined:
hasAllRequiredAccess = true
case .allowed:
switch component.microphoneAuthorizationStatus {
case .notDetermined:
hasAllRequiredAccess = true
case .allowed:
hasAllRequiredAccess = true
default:
hasAllRequiredAccess = false
}
default:
hasAllRequiredAccess = false
}
if !hasAllRequiredAccess {
let accountContext = component.context
let placeholder = placeholder.update(
component: PlaceholderComponent(
context: component.context,
mode: .denied,
action: {
accountContext.sharedContext.applicationBindings.openSettings()
}
),
availableSize: CGSize(width: availableSize.width, height: previewHeight),
transition: context.transition
)
context.add(placeholder
.position(CGPoint(x: availableSize.width / 2.0, y: environment.safeInsets.top + previewHeight / 2.0))
.clipsToBounds(true)
.cornerRadius(11.0)
.appear(.default(alpha: true))
.disappear(.default(alpha: true))
)
}
if case .holding = component.cameraState.recording {
} else {
@ -593,7 +642,8 @@ private final class CameraScreenComponent: CombinedComponent {
let captureControls = captureControls.update(
component: CaptureControlsComponent(
isTablet: isTablet,
hasAppeared: component.hasAppeared,
hasAppeared: component.hasAppeared && hasAllRequiredAccess,
hasAccess: hasAllRequiredAccess,
shutterState: shutterState,
lastGalleryAsset: state.lastGalleryAsset,
tag: captureControlsTag,
@ -739,6 +789,7 @@ private final class CameraScreenComponent: CombinedComponent {
)
}
if hasAllRequiredAccess {
let flashButton = flashButton.update(
component: CameraButton(
content: flashContentComponent,
@ -782,8 +833,9 @@ private final class CameraScreenComponent: CombinedComponent {
)
}
}
}
if isTablet {
if isTablet && hasAllRequiredAccess {
let flipButton = flipButton.update(
component: CameraButton(
content: AnyComponentWithIdentity(
@ -807,6 +859,8 @@ private final class CameraScreenComponent: CombinedComponent {
)
context.add(flipButton
.position(CGPoint(x: smallPanelWidth / 2.0, y: availableSize.height / 2.0))
.appear(.default(scale: true))
.disappear(.default(scale: true))
)
}
@ -885,7 +939,7 @@ private final class CameraScreenComponent: CombinedComponent {
}
}
if case .none = component.cameraState.recording, !state.isTransitioning {
if case .none = component.cameraState.recording, !state.isTransitioning && hasAllRequiredAccess {
let availableModeControlSize: CGSize
if isTablet {
availableModeControlSize = CGSize(width: panelWidth, height: 120.0)
@ -1142,6 +1196,11 @@ public class CameraScreen: ViewController {
}
}
private var cameraAuthorizationStatus: AccessType = .notDetermined
private var microphoneAuthorizationStatus: AccessType = .notDetermined
private var galleryAuthorizationStatus: AccessType = .notDetermined
private var authorizationStatusDisposables = DisposableSet()
init(controller: CameraScreen) {
self.controller = controller
self.context = controller.context
@ -1312,12 +1371,33 @@ public class CameraScreen: ViewController {
}
self.idleTimerExtensionDisposable.set(self.context.sharedContext.applicationBindings.pushIdleTimerExtension())
self.authorizationStatusDisposables.add((DeviceAccess.authorizationStatus(subject: .camera(.video))
|> deliverOnMainQueue).start(next: { [weak self] status in
if let self {
self.cameraAuthorizationStatus = status
self.requestUpdateLayout(hasAppeared: self.hasAppeared, transition: .easeInOut(duration: 0.2))
self.maybeSetupCamera()
}
}))
self.authorizationStatusDisposables.add((DeviceAccess.authorizationStatus(subject: .microphone(.video))
|> deliverOnMainQueue).start(next: { [weak self] status in
if let self {
self.microphoneAuthorizationStatus = status
self.requestUpdateLayout(hasAppeared: self.hasAppeared, transition: .easeInOut(duration: 0.2))
self.maybeSetupCamera()
}
}))
}
deinit {
self.cameraStateDisposable?.dispose()
self.changingPositionDisposable?.dispose()
self.idleTimerExtensionDisposable.dispose()
self.authorizationStatusDisposables.dispose()
}
private var pipPanGestureRecognizer: UIPanGestureRecognizer?
@ -1346,11 +1426,23 @@ public class CameraScreen: ViewController {
pipPanGestureRecognizer.delegate = self
self.previewContainerView.addGestureRecognizer(pipPanGestureRecognizer)
self.pipPanGestureRecognizer = pipPanGestureRecognizer
self.setupCamera()
}
func setupCamera() {
private func maybeSetupCamera() {
if case .allowed = self.cameraAuthorizationStatus, case .allowed = self.microphoneAuthorizationStatus {
self.setupCamera()
}
}
private func requestDeviceAccess() {
DeviceAccess.authorizeAccess(to: .camera(.video), { granted in
if granted {
DeviceAccess.authorizeAccess(to: .microphone(.video))
}
})
}
private func setupCamera() {
guard self.camera == nil else {
return
}
@ -1461,6 +1553,10 @@ public class CameraScreen: ViewController {
camera.startCapture()
self.camera = camera
if self.hasAppeared {
self.maybePresentTooltips()
}
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
@ -1581,7 +1677,7 @@ public class CameraScreen: ViewController {
CATransaction.begin()
CATransaction.setDisableActions(true)
self.requestUpdateLayout(hasAppeared: false, transition: .immediate)
self.requestUpdateLayout(hasAppeared: self.hasAppeared, transition: .immediate)
CATransaction.commit()
self.additionalPreviewContainerView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
@ -1866,9 +1962,12 @@ public class CameraScreen: ViewController {
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY + 3.0), size: CGSize())
let accountManager = self.context.sharedContext.accountManager
let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: "Take photos or videos to share with all\nyour contacts or close friends at once."), textAlignment: .center, location: .point(location, .bottom), displayDuration: .custom(4.5), inset: 16.0, shouldDismissOnTouch: { point, containerFrame in
let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: "Take photos or videos to share with all\nyour contacts or close friends at once."), textAlignment: .center, location: .point(location, .bottom), displayDuration: .custom(4.5), inset: 16.0, shouldDismissOnTouch: { [weak self] point, containerFrame in
if containerFrame.contains(point) {
let _ = ApplicationSpecificNotice.incrementStoriesCameraTip(accountManager: accountManager).start()
Queue.mainQueue().justDispatch {
self?.maybePresentTooltips()
}
return .dismiss(consume: true)
}
return .ignore
@ -2006,7 +2105,11 @@ public class CameraScreen: ViewController {
self.hasAppeared = hasAppeared
transition = transition.withUserData(CameraScreenTransition.finishedAnimateIn)
if self.camera != nil {
self.maybePresentTooltips()
} else if case .notDetermined = self.cameraAuthorizationStatus {
self.requestDeviceAccess()
}
}
let componentSize = self.componentHost.update(
@ -2015,6 +2118,8 @@ public class CameraScreen: ViewController {
CameraScreenComponent(
context: self.context,
cameraState: self.cameraState,
cameraAuthorizationStatus: self.cameraAuthorizationStatus,
microphoneAuthorizationStatus: self.microphoneAuthorizationStatus,
hasAppeared: self.hasAppeared,
isVisible: self.cameraIsActive && !self.hasGallery,
panelWidth: panelWidth,

View File

@ -446,6 +446,7 @@ final class CaptureControlsComponent: Component {
let isTablet: Bool
let hasAppeared: Bool
let hasAccess: Bool
let shutterState: ShutterButtonState
let lastGalleryAsset: PHAsset?
let tag: AnyObject?
@ -463,6 +464,7 @@ final class CaptureControlsComponent: Component {
init(
isTablet: Bool,
hasAppeared: Bool,
hasAccess: Bool,
shutterState: ShutterButtonState,
lastGalleryAsset: PHAsset?,
tag: AnyObject?,
@ -479,6 +481,7 @@ final class CaptureControlsComponent: Component {
) {
self.isTablet = isTablet
self.hasAppeared = hasAppeared
self.hasAccess = hasAccess
self.shutterState = shutterState
self.lastGalleryAsset = lastGalleryAsset
self.tag = tag
@ -501,6 +504,9 @@ final class CaptureControlsComponent: Component {
if lhs.hasAppeared != rhs.hasAppeared {
return false
}
if lhs.hasAccess != rhs.hasAccess {
return false
}
if lhs.shutterState != rhs.shutterState {
return false
}
@ -944,7 +950,7 @@ final class CaptureControlsComponent: Component {
transition.setAlpha(view: galleryButtonView, alpha: isRecording || isTransitioning ? 0.0 : 1.0)
}
if !component.isTablet {
if !component.isTablet && component.hasAccess {
let flipButtonOriginX = availableSize.width - 48.0 - buttonSideInset
let flipButtonMaskFrame: CGRect = CGRect(origin: CGPoint(x: availableSize.width / 2.0 - (flipButtonOriginX + 22.0) + 6.0 + self.shutterOffsetX, y: 8.0), size: CGSize(width: 32.0, height: 32.0))
@ -1153,10 +1159,13 @@ final class CaptureControlsComponent: Component {
self.addSubview(shutterButtonView)
}
let alpha: CGFloat = component.hasAccess ? 1.0 : 0.3
transition.setBounds(view: shutterButtonView, bounds: CGRect(origin: .zero, size: shutterButtonFrame.size))
transition.setPosition(view: shutterButtonView, position: shutterButtonFrame.center)
transition.setScale(view: shutterButtonView, scale: isTransitioning ? 0.01 : 1.0)
transition.setAlpha(view: shutterButtonView, alpha: isTransitioning ? 0.0 : 1.0)
transition.setAlpha(view: shutterButtonView, alpha: isTransitioning ? 0.0 : alpha)
shutterButtonView.isUserInteractionEnabled = component.hasAccess
}
if let buttonView = self.flipButtonView.view as? CameraButton.View, let contentView = buttonView.contentView.componentView as? FlipButtonContentComponent.View {

View File

@ -0,0 +1,224 @@
import Foundation
import UIKit
import Display
import ComponentFlow
import SwiftSignalKit
import TelegramCore
import AccountContext
import BundleIconComponent
import MultilineTextComponent
import ButtonComponent
import LottieComponent
final class PlaceholderComponent: Component {
typealias EnvironmentType = Empty
enum Mode {
case request
case denied
}
let context: AccountContext
let mode: Mode
let action: () -> Void
init(
context: AccountContext,
mode: Mode,
action: @escaping () -> Void
) {
self.context = context
self.mode = mode
self.action = action
}
static func ==(lhs: PlaceholderComponent, rhs: PlaceholderComponent) -> Bool {
if lhs.context !== rhs.context {
return false
}
if lhs.mode != rhs.mode {
return false
}
return true
}
public final class View: UIView {
private let animation = ComponentView<Empty>()
private let title = ComponentView<Empty>()
private let text = ComponentView<Empty>()
private let button = ComponentView<Empty>()
private var component: PlaceholderComponent?
private weak var state: EmptyComponentState?
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor(rgb: 0x1c1c1e)
// if #available(iOS 13.0, *) {
// self.layer.cornerCurve = .continuous
// }
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(component: PlaceholderComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
self.component = component
self.state = state
let sideInset: CGFloat = 36.0
let animationHeight: CGFloat = 120.0
let title: String = "Allow Telegram to access your camera and microphone"
let text: String = "This lets you share photos and record videos."
let buttonTitle: String = "Open Settings"
let animationSize = self.animation.update(
transition: .immediate,
component: AnyComponent(LottieComponent(
content: LottieComponent.AppBundleContent(name: "Photos")
)),
environment: {},
containerSize: CGSize(width: animationHeight, height: animationHeight)
)
let titleSize = self.title.update(
transition: .immediate,
component: AnyComponent(
MultilineTextComponent(
text: .plain(NSAttributedString(string: title, font: Font.semibold(17.0), textColor: UIColor.white)),
horizontalAlignment: .center,
maximumNumberOfLines: 0
)
),
environment: {},
containerSize: CGSize(width: availableSize.width - sideInset * 3.0, height: availableSize.height)
)
let textSize = self.text.update(
transition: .immediate,
component: AnyComponent(
MultilineTextComponent(
text: .plain(NSAttributedString(string: text, font: Font.regular(15.0), textColor: UIColor(rgb: 0x98989f))),
horizontalAlignment: .center,
maximumNumberOfLines: 0
)
),
environment: {},
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: availableSize.height)
)
let buttonSize = self.button.update(
transition: .immediate,
component: AnyComponent(
ButtonComponent(
background: ButtonComponent.Background(
color: UIColor(rgb: 0x007aff),
foreground: .white,
pressedColor: UIColor(rgb: 0x007aff, alpha: 0.55)
),
content: AnyComponentWithIdentity(
id: buttonTitle,
component: AnyComponent(ButtonTextContentComponent(
text: buttonTitle,
badge: 0,
textColor: .white,
badgeBackground: .clear,
badgeForeground: .clear
))
),
isEnabled: true,
displaysProgress: false,
action: { [weak self] in
if let self {
self.component?.action()
}
}
)
),
environment: {},
containerSize: CGSize(width: 240.0, height: 50.0)
)
let titleSpacing: CGFloat = 12.0
let textSpacing: CGFloat = 14.0
let buttonSpacing: CGFloat = 18.0
let totalHeight = animationSize.height + titleSpacing + titleSize.height + textSpacing + textSize.height + buttonSpacing + buttonSize.height
var originY = floorToScreenPixels((availableSize.height - totalHeight) / 2.0)
let animationFrame = CGRect(
origin: CGPoint(
x: floorToScreenPixels((availableSize.width - animationSize.width) / 2.0),
y: originY
),
size: animationSize
)
if let view = self.animation.view as? LottieComponent.View {
if view.superview == nil {
self.addSubview(view)
Queue.mainQueue().justDispatch {
view.playOnce()
}
}
view.frame = animationFrame
}
originY += animationSize.height + titleSpacing
let titleFrame = CGRect(
origin: CGPoint(
x: floorToScreenPixels((availableSize.width - titleSize.width) / 2.0),
y: originY
),
size: titleSize
)
if let view = self.title.view {
if view.superview == nil {
self.addSubview(view)
}
view.frame = titleFrame
}
originY += titleSize.height + textSpacing
let textFrame = CGRect(
origin: CGPoint(
x: floorToScreenPixels((availableSize.width - textSize.width) / 2.0),
y: originY
),
size: textSize
)
if let view = self.text.view {
if view.superview == nil {
self.addSubview(view)
}
view.frame = textFrame
}
originY += textSize.height + buttonSpacing
let buttonFrame = CGRect(
origin: CGPoint(
x: floorToScreenPixels((availableSize.width - buttonSize.width) / 2.0),
y: originY
),
size: buttonSize
)
if let view = self.button.view {
if view.superview == nil {
self.addSubview(view)
}
view.frame = buttonFrame
}
return availableSize
}
}
func makeView() -> View {
return View()
}
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}

View File

@ -14,8 +14,13 @@ private func fullEntityMediaPath(_ path: String) -> String {
public final class DrawingStickerEntity: DrawingEntity, Codable {
public enum Content: Equatable {
public enum ImageType: Equatable {
case sticker
case rectangle
case dualPhoto
}
case file(TelegramMediaFile)
case image(UIImage, Bool)
case image(UIImage, ImageType)
case video(String, UIImage?, Bool)
case dualVideoReference
@ -27,9 +32,9 @@ public final class DrawingStickerEntity: DrawingEntity, Codable {
} else {
return false
}
case let .image(lhsImage, lhsIsRectangle):
if case let .image(rhsImage, rhsIsRectangle) = rhs {
return lhsImage === rhsImage && lhsIsRectangle == rhsIsRectangle
case let .image(lhsImage, lhsImageType):
if case let .image(rhsImage, rhsImageType) = rhs {
return lhsImage === rhsImage && lhsImageType == rhsImageType
} else {
return false
}
@ -56,6 +61,7 @@ public final class DrawingStickerEntity: DrawingEntity, Codable {
case videoImagePath
case videoMirrored
case isRectangle
case isDualPhoto
case dualVideo
case referenceDrawingSize
case position
@ -121,8 +127,8 @@ public final class DrawingStickerEntity: DrawingEntity, Codable {
public var isRectangle: Bool {
switch self.content {
case let .image(_, isRectangle):
return isRectangle
case let .image(_, imageType):
return imageType == .rectangle
default:
return false
}
@ -157,7 +163,16 @@ public final class DrawingStickerEntity: DrawingEntity, Codable {
self.content = .file(file)
} else if let imagePath = try container.decodeIfPresent(String.self, forKey: .imagePath), let image = UIImage(contentsOfFile: fullEntityMediaPath(imagePath)) {
let isRectangle = try container.decodeIfPresent(Bool.self, forKey: .isRectangle) ?? false
self.content = .image(image, isRectangle)
let isDualPhoto = try container.decodeIfPresent(Bool.self, forKey: .isDualPhoto) ?? false
let imageType: Content.ImageType
if isDualPhoto {
imageType = .dualPhoto
} else if isRectangle {
imageType = .rectangle
} else {
imageType = .sticker
}
self.content = .image(image, imageType)
} else if let videoPath = try container.decodeIfPresent(String.self, forKey: .videoPath) {
var imageValue: UIImage?
if let imagePath = try container.decodeIfPresent(String.self, forKey: .videoImagePath), let image = UIImage(contentsOfFile: fullEntityMediaPath(imagePath)) {
@ -182,7 +197,7 @@ public final class DrawingStickerEntity: DrawingEntity, Codable {
switch self.content {
case let .file(file):
try container.encode(file, forKey: .file)
case let .image(image, isRectangle):
case let .image(image, imageType):
let imagePath = "\(self.uuid).png"
let fullImagePath = fullEntityMediaPath(imagePath)
if let imageData = image.pngData() {
@ -190,7 +205,14 @@ public final class DrawingStickerEntity: DrawingEntity, Codable {
try? imageData.write(to: URL(fileURLWithPath: fullImagePath))
try container.encodeIfPresent(imagePath, forKey: .imagePath)
}
try container.encode(isRectangle, forKey: .isRectangle)
switch imageType {
case .dualPhoto:
try container.encode(true, forKey: .isDualPhoto)
case .rectangle:
try container.encode(true, forKey: .isRectangle)
default:
break
}
case let .video(path, image, videoMirrored):
try container.encode(path, forKey: .videoPath)
let imagePath = "\(self.uuid).jpg"

View File

@ -481,7 +481,8 @@ public final class MediaEditor {
if player == nil {
self.updateRenderChain()
self.maybeGeneratePersonSegmentation(image)
let _ = image
// self.maybeGeneratePersonSegmentation(image)
}
if let player {

View File

@ -1167,13 +1167,13 @@ final class MediaEditorScreenComponent: Component {
switch data {
case let .sticker(image, _):
if max(image.size.width, image.size.height) > 1.0 {
let entity = DrawingStickerEntity(content: .image(image, false))
let entity = DrawingStickerEntity(content: .image(image, .sticker))
controller.node.interaction?.insertEntity(entity, scale: 1.0)
self.deactivateInput()
}
case let .images(images):
if images.count == 1, let image = images.first, max(image.size.width, image.size.height) > 1.0 {
let entity = DrawingStickerEntity(content: .image(image, true))
let entity = DrawingStickerEntity(content: .image(image, .rectangle))
controller.node.interaction?.insertEntity(entity, scale: 2.5)
self.deactivateInput()
}
@ -1925,8 +1925,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
if let cgImage = additionalImage.cgImage {
context.draw(cgImage, in: CGRect(origin: CGPoint(x: (size.width - additionalImage.size.width) / 2.0, y: (size.height - additionalImage.size.height) / 2.0), size: additionalImage.size))
}
})
let imageEntity = DrawingStickerEntity(content: .image(image ?? additionalImage, false))
}, scale: 1.0)
let imageEntity = DrawingStickerEntity(content: .image(image ?? additionalImage, .dualPhoto))
imageEntity.referenceDrawingSize = storyDimensions
imageEntity.scale = 1.625
imageEntity.position = position.getPosition(storyDimensions)
@ -2730,7 +2730,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
PHImageManager.default().requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .default, options: options) { [weak self] image, _ in
if let self, let image {
Queue.mainQueue().async {
self.interaction?.insertEntity(DrawingStickerEntity(content: .image(image, true)), scale: 2.5)
self.interaction?.insertEntity(DrawingStickerEntity(content: .image(image, .rectangle)), scale: 2.5)
}
}
}
@ -4110,7 +4110,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
}
let images = imageItems as! [UIImage]
if images.count == 1, let image = images.first, max(image.size.width, image.size.height) > 1.0 {
self.node.interaction?.insertEntity(DrawingStickerEntity(content: .image(image, false)), scale: 2.5)
self.node.interaction?.insertEntity(DrawingStickerEntity(content: .image(image, .sticker)), scale: 2.5)
}
}
}

View File

@ -978,7 +978,7 @@ final class ShareWithPeersScreenComponent: Component {
}
let fadeTransition = Transition.easeInOut(duration: 0.25)
if let searchStateContext = self.searchStateContext, case let .search(query) = searchStateContext.subject, let value = searchStateContext.stateValue, value.peers.isEmpty {
if let searchStateContext = self.searchStateContext, case let .search(query, _) = searchStateContext.subject, let value = searchStateContext.stateValue, value.peers.isEmpty {
let sideInset: CGFloat = 44.0
let emptyAnimationHeight = 148.0
let topInset: CGFloat = topOffset + itemLayout.containerInset + 40.0
@ -1280,10 +1280,14 @@ final class ShareWithPeersScreenComponent: Component {
)
if !self.navigationTextFieldState.text.isEmpty {
if let searchStateContext = self.searchStateContext, searchStateContext.subject == .search(self.navigationTextFieldState.text) {
var onlyContacts = false
if component.initialPrivacy.base == .closeFriends || component.initialPrivacy.base == .contacts {
onlyContacts = true
}
if let searchStateContext = self.searchStateContext, searchStateContext.subject == .search(query: self.navigationTextFieldState.text, onlyContacts: onlyContacts) {
} else {
self.searchStateDisposable?.dispose()
let searchStateContext = ShareWithPeersScreen.StateContext(context: component.context, subject: .search(self.navigationTextFieldState.text))
let searchStateContext = ShareWithPeersScreen.StateContext(context: component.context, subject: .search(query: self.navigationTextFieldState.text, onlyContacts: onlyContacts))
var applyState = false
self.searchStateDisposable = (searchStateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in
guard let self else {
@ -1335,7 +1339,7 @@ final class ShareWithPeersScreenComponent: Component {
sideInset: sideInset,
title: "Name",
peer: nil,
subtitle: "sub",
subtitle: self.searchStateContext != nil ? "" : "sub",
subtitleAccessory: .none,
presence: nil,
selectionState: .editing(isSelected: false, isTinted: false),
@ -1753,7 +1757,7 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
case stories(editing: Bool)
case chats
case contacts(EngineStoryPrivacy.Base)
case search(String)
case search(query: String, onlyContacts: Bool)
}
fileprivate var stateValue: State?
@ -1889,7 +1893,7 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
self.readySubject.set(true)
})
case let .search(query):
case let .search(query, _):
self.stateDisposable = (context.engine.contacts.searchLocalPeers(query: query)
|> deliverOnMainQueue).start(next: { [weak self] peers in
guard let self else {