Various fixes

This commit is contained in:
Ilya Laktyushin 2023-07-06 21:28:29 +02:00
parent 7b9836682d
commit a4297cf1c5
27 changed files with 577 additions and 200 deletions

View File

@ -161,7 +161,6 @@ private final class CameraContext {
}
}
private var videoOrientation: AVCaptureVideoOrientation?
init(queue: Queue, session: CameraSession, configuration: Camera.Configuration, metrics: Camera.Metrics, previewView: CameraSimplePreviewView?, secondaryPreviewView: CameraSimplePreviewView?) {
self.queue = queue
self.session = session
@ -450,7 +449,7 @@ private final class CameraContext {
}
func takePhoto() -> Signal<PhotoCaptureResult, NoError> {
let orientation = self.videoOrientation ?? .portrait
let orientation = self.simplePreviewView?.videoPreviewLayer.connection?.videoOrientation ?? .portrait
if let additionalDeviceContext = self.additionalDeviceContext {
let dualPosition = self.positionValue
return combineLatest(

View File

@ -32,8 +32,7 @@ public class CameraSimplePreviewView: UIView {
} else {
statusBarOrientation = UIApplication.shared.statusBarOrientation
}
let videoOrientation: AVCaptureVideoOrientation = statusBarOrientation.videoOrientation
// videoPreviewLayer.frame = view.layer.bounds
let videoOrientation = statusBarOrientation.videoOrientation
self.videoPreviewLayer.connection?.videoOrientation = videoOrientation
self.videoPreviewLayer.removeAllAnimations()
}

View File

@ -157,6 +157,8 @@ public final class Button: Component {
super.init(frame: frame)
self.isExclusiveTouch = true
self.addSubview(self.contentView)
self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)

View File

@ -297,9 +297,8 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
if let shape = entity as? DrawingSimpleShapeEntity {
shape.position = center
shape.rotation = rotation
if setup {
shape.rotation = rotation
let size = self.newEntitySize()
shape.referenceDrawingSize = self.size
if shape.shapeType == .star {
@ -319,15 +318,15 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
}
} else if let sticker = entity as? DrawingStickerEntity {
sticker.position = center
sticker.rotation = rotation
if setup {
sticker.rotation = rotation
sticker.referenceDrawingSize = self.size
sticker.scale = zoomScale
}
} else if let bubble = entity as? DrawingBubbleEntity {
bubble.position = center
bubble.rotation = rotation
if setup {
bubble.rotation = rotation
let size = self.newEntitySize()
bubble.referenceDrawingSize = self.size
bubble.size = CGSize(width: size.width, height: round(size.height * 0.7))
@ -335,8 +334,8 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
}
} else if let text = entity as? DrawingTextEntity {
text.position = center
text.rotation = rotation
if setup {
text.rotation = rotation
text.referenceDrawingSize = self.size
text.width = floor(self.size.width * 0.9)
text.fontSize = 0.08

View File

@ -911,7 +911,8 @@ public class StickerPickerScreen: ViewController {
externalExpansionView: nil,
useOpaqueTheme: false,
hideBackground: true,
stateContext: nil
stateContext: nil,
addImage: nil
)
var stickerPeekBehavior: EmojiContentPeekBehaviorImpl?
@ -1168,7 +1169,10 @@ public class StickerPickerScreen: ViewController {
externalExpansionView: nil,
useOpaqueTheme: false,
hideBackground: true,
stateContext: nil
stateContext: nil,
addImage: {
}
)
if let (layout, navigationHeight) = self.currentLayout {

View File

@ -1595,7 +1595,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
externalExpansionView: self.view,
useOpaqueTheme: false,
hideBackground: false,
stateContext: nil
stateContext: nil,
addImage: nil
)
}

View File

@ -688,7 +688,8 @@ final class AvatarEditorScreenComponent: Component {
externalExpansionView: nil,
useOpaqueTheme: true,
hideBackground: true,
stateContext: nil
stateContext: nil,
addImage: nil
)
data.stickers?.inputInteractionHolder.inputInteraction = EmojiPagerContentComponent.InputInteraction(
@ -816,7 +817,8 @@ final class AvatarEditorScreenComponent: Component {
externalExpansionView: nil,
useOpaqueTheme: true,
hideBackground: true,
stateContext: nil
stateContext: nil,
addImage: nil
)
self.state?.updated(transition: .immediate)

View File

@ -81,6 +81,8 @@ public final class CameraButton: Component {
super.init(frame: frame)
self.isExclusiveTouch = true
self.addSubview(self.contentView)
self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)

View File

@ -377,6 +377,9 @@ private final class CameraScreenComponent: CombinedComponent {
if let lastFlipTimestamp = self.lastFlipTimestamp, currentTimestamp - lastFlipTimestamp < 1.0 {
return
}
if let lastDualCameraTimestamp = self.lastDualCameraTimestamp, currentTimestamp - lastDualCameraTimestamp < 1.5 {
return
}
self.lastFlipTimestamp = currentTimestamp
self.camera.togglePosition()
@ -391,6 +394,9 @@ private final class CameraScreenComponent: CombinedComponent {
if let lastDualCameraTimestamp = self.lastDualCameraTimestamp, currentTimestamp - lastDualCameraTimestamp < 1.5 {
return
}
if let lastFlipTimestamp = self.lastFlipTimestamp, currentTimestamp - lastFlipTimestamp < 1.0 {
return
}
self.lastDualCameraTimestamp = currentTimestamp
let isEnabled = !self.cameraState.isDualCamEnabled
@ -532,122 +538,6 @@ private final class CameraScreenComponent: CombinedComponent {
}
}
let topControlInset: CGFloat = 20.0
if case .none = state.cameraState.recording, !state.isTransitioning {
let cancelButton = cancelButton.update(
component: CameraButton(
content: AnyComponentWithIdentity(
id: "cancel",
component: AnyComponent(
Image(
image: state.image(.cancel),
size: CGSize(width: 40.0, height: 40.0)
)
)
),
action: {
guard let controller = controller() as? CameraScreen else {
return
}
controller.requestDismiss(animated: true)
}
).tagged(cancelButtonTag),
availableSize: CGSize(width: 40.0, height: 40.0),
transition: .immediate
)
context.add(cancelButton
.position(CGPoint(x: isTablet ? smallPanelWidth / 2.0 : topControlInset + cancelButton.size.width / 2.0, y: max(environment.statusBarHeight + 5.0, environment.safeInsets.top + topControlInset) + cancelButton.size.height / 2.0))
.appear(.default(scale: true))
.disappear(.default(scale: true))
)
let flashContentComponent: AnyComponentWithIdentity<Empty>
if component.hasAppeared {
let flashIconName: String
switch state.cameraState.flashMode {
case .off:
flashIconName = "flash_off"
case .on:
flashIconName = "flash_on"
case .auto:
flashIconName = "flash_auto"
@unknown default:
flashIconName = "flash_off"
}
flashContentComponent = AnyComponentWithIdentity(
id: "animatedIcon",
component: AnyComponent(
LottieAnimationComponent(
animation: LottieAnimationComponent.AnimationItem(
name: flashIconName,
mode: !state.cameraState.flashModeDidChange ? .still(position: .end) : .animating(loop: false),
range: nil,
waitForCompletion: false
),
colors: [:],
size: CGSize(width: 40.0, height: 40.0)
)
)
)
} else {
flashContentComponent = AnyComponentWithIdentity(
id: "staticIcon",
component: AnyComponent(
BundleIconComponent(
name: "Camera/FlashOffIcon",
tintColor: nil
)
)
)
}
let flashButton = flashButton.update(
component: CameraButton(
content: flashContentComponent,
action: { [weak state] in
guard let state else {
return
}
state.toggleFlashMode()
}
).tagged(flashButtonTag),
availableSize: CGSize(width: 40.0, height: 40.0),
transition: .immediate
)
context.add(flashButton
.position(CGPoint(x: isTablet ? availableSize.width - smallPanelWidth / 2.0 : availableSize.width - topControlInset - flashButton.size.width / 2.0 - 5.0, y: max(environment.statusBarHeight + 5.0, environment.safeInsets.top + topControlInset) + flashButton.size.height / 2.0))
.appear(.default(scale: true))
.disappear(.default(scale: true))
)
if !isTablet && Camera.isDualCamSupported {
let dualButton = dualButton.update(
component: CameraButton(
content: AnyComponentWithIdentity(
id: "dual",
component: AnyComponent(
DualIconComponent(isSelected: state.cameraState.isDualCamEnabled)
)
),
action: { [weak state] in
guard let state else {
return
}
state.toggleDualCamera()
}
).tagged(dualButtonTag),
availableSize: CGSize(width: 40.0, height: 40.0),
transition: .immediate
)
context.add(dualButton
.position(CGPoint(x: availableSize.width - topControlInset - flashButton.size.width / 2.0 - 52.0, y: max(environment.statusBarHeight + 5.0, environment.safeInsets.top + topControlInset) + dualButton.size.height / 2.0 + 1.0))
.appear(.default(scale: true))
.disappear(.default(scale: true))
)
}
}
if case .holding = state.cameraState.recording {
} else {
@ -771,6 +661,122 @@ private final class CameraScreenComponent: CombinedComponent {
.position(captureControlsPosition)
)
let topControlInset: CGFloat = 20.0
if case .none = state.cameraState.recording, !state.isTransitioning {
let cancelButton = cancelButton.update(
component: CameraButton(
content: AnyComponentWithIdentity(
id: "cancel",
component: AnyComponent(
Image(
image: state.image(.cancel),
size: CGSize(width: 40.0, height: 40.0)
)
)
),
action: {
guard let controller = controller() as? CameraScreen else {
return
}
controller.requestDismiss(animated: true)
}
).tagged(cancelButtonTag),
availableSize: CGSize(width: 40.0, height: 40.0),
transition: .immediate
)
context.add(cancelButton
.position(CGPoint(x: isTablet ? smallPanelWidth / 2.0 : topControlInset + cancelButton.size.width / 2.0, y: max(environment.statusBarHeight + 5.0, environment.safeInsets.top + topControlInset) + cancelButton.size.height / 2.0))
.appear(.default(scale: true))
.disappear(.default(scale: true))
)
let flashContentComponent: AnyComponentWithIdentity<Empty>
if component.hasAppeared {
let flashIconName: String
switch state.cameraState.flashMode {
case .off:
flashIconName = "flash_off"
case .on:
flashIconName = "flash_on"
case .auto:
flashIconName = "flash_auto"
@unknown default:
flashIconName = "flash_off"
}
flashContentComponent = AnyComponentWithIdentity(
id: "animatedIcon",
component: AnyComponent(
LottieAnimationComponent(
animation: LottieAnimationComponent.AnimationItem(
name: flashIconName,
mode: !state.cameraState.flashModeDidChange ? .still(position: .end) : .animating(loop: false),
range: nil,
waitForCompletion: false
),
colors: [:],
size: CGSize(width: 40.0, height: 40.0)
)
)
)
} else {
flashContentComponent = AnyComponentWithIdentity(
id: "staticIcon",
component: AnyComponent(
BundleIconComponent(
name: "Camera/FlashOffIcon",
tintColor: nil
)
)
)
}
let flashButton = flashButton.update(
component: CameraButton(
content: flashContentComponent,
action: { [weak state] in
guard let state else {
return
}
state.toggleFlashMode()
}
).tagged(flashButtonTag),
availableSize: CGSize(width: 40.0, height: 40.0),
transition: .immediate
)
context.add(flashButton
.position(CGPoint(x: isTablet ? availableSize.width - smallPanelWidth / 2.0 : availableSize.width - topControlInset - flashButton.size.width / 2.0 - 5.0, y: max(environment.statusBarHeight + 5.0, environment.safeInsets.top + topControlInset) + flashButton.size.height / 2.0))
.appear(.default(scale: true))
.disappear(.default(scale: true))
)
if !isTablet && Camera.isDualCamSupported {
let dualButton = dualButton.update(
component: CameraButton(
content: AnyComponentWithIdentity(
id: "dual",
component: AnyComponent(
DualIconComponent(isSelected: state.cameraState.isDualCamEnabled)
)
),
action: { [weak state] in
guard let state else {
return
}
state.toggleDualCamera()
}
).tagged(dualButtonTag),
availableSize: CGSize(width: 40.0, height: 40.0),
transition: .immediate
)
context.add(dualButton
.position(CGPoint(x: availableSize.width - topControlInset - flashButton.size.width / 2.0 - 52.0, y: max(environment.statusBarHeight + 5.0, environment.safeInsets.top + topControlInset) + dualButton.size.height / 2.0 + 1.0))
.appear(.default(scale: true))
.disappear(.default(scale: true))
)
}
}
if isTablet {
let flipButton = flipButton.update(
component: CameraButton(
@ -1037,7 +1043,7 @@ public class CameraScreen: ViewController {
}
}
fileprivate final class Node: ViewControllerTracingNode {
fileprivate final class Node: ViewControllerTracingNode, UIGestureRecognizerDelegate {
private weak var controller: CameraScreen?
private let context: AccountContext
private let updateState: ActionSlot<CameraState>
@ -1297,6 +1303,7 @@ public class CameraScreen: ViewController {
self.changingPositionDisposable?.dispose()
}
private var pipPanGestureRecognizer: UIPanGestureRecognizer?
override func didLoad() {
super.didLoad()
@ -1304,26 +1311,47 @@ public class CameraScreen: ViewController {
self.view.disablesInteractiveKeyboardGestureRecognizer = true
let pinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(self.handlePinch(_:)))
self.mainPreviewContainerView.addGestureRecognizer(pinchGestureRecognizer)
self.previewContainerView.addGestureRecognizer(pinchGestureRecognizer)
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(_:)))
panGestureRecognizer.delegate = self
panGestureRecognizer.maximumNumberOfTouches = 1
self.mainPreviewContainerView.addGestureRecognizer(panGestureRecognizer)
self.previewContainerView.addGestureRecognizer(panGestureRecognizer)
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
self.mainPreviewContainerView.addGestureRecognizer(tapGestureRecognizer)
self.previewContainerView.addGestureRecognizer(tapGestureRecognizer)
let doubleGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.handleDoubleTap(_:)))
doubleGestureRecognizer.numberOfTapsRequired = 2
self.mainPreviewContainerView.addGestureRecognizer(doubleGestureRecognizer)
self.previewContainerView.addGestureRecognizer(doubleGestureRecognizer)
let pipPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePipPan(_:)))
self.additionalPreviewContainerView.addGestureRecognizer(pipPanGestureRecognizer)
pipPanGestureRecognizer.delegate = self
self.previewContainerView.addGestureRecognizer(pipPanGestureRecognizer)
self.pipPanGestureRecognizer = pipPanGestureRecognizer
self.camera.focus(at: CGPoint(x: 0.5, y: 0.5), autoFocus: true)
self.camera.startCapture()
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer is UIPanGestureRecognizer {
return false
}
return true
}
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
let location = gestureRecognizer.location(in: gestureRecognizer.view)
if gestureRecognizer === self.pipPanGestureRecognizer {
if !self.isDualCamEnabled {
return false
}
return self.additionalPreviewContainerView.frame.contains(location)
}
return self.hasAppeared
}
@objc private func handlePinch(_ gestureRecognizer: UIPinchGestureRecognizer) {
switch gestureRecognizer.state {
case .changed:
@ -1469,7 +1497,9 @@ public class CameraScreen: ViewController {
let sourceLocalFrame = sourceView.convert(transitionIn.sourceRect, to: self.view)
let sourceScale = sourceLocalFrame.width / self.previewContainerView.frame.width
self.previewContainerView.layer.animatePosition(from: sourceLocalFrame.center, to: self.previewContainerView.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
self.previewContainerView.layer.animatePosition(from: sourceLocalFrame.center, to: self.previewContainerView.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
self.requestUpdateLayout(hasAppeared: true, transition: .immediate)
})
self.previewContainerView.layer.animateScale(from: sourceScale, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
let minSide = min(self.previewContainerView.bounds.width, self.previewContainerView.bounds.height)
@ -1977,7 +2007,7 @@ public class CameraScreen: ViewController {
additionalPreviewView = self.additionalPreviewView
}
let mainPreviewInnerFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((previewSize.width - mainPreviewInnerSize.width) / 2.0), y: floorToScreenPixels((previewSize.height - mainPreviewInnerSize.height) / 2.0)), size: mainPreviewInnerSize)
let mainPreviewInnerFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((previewFrame.width - mainPreviewInnerSize.width) / 2.0), y: floorToScreenPixels((previewFrame.height - mainPreviewInnerSize.height) / 2.0)), size: mainPreviewInnerSize)
let additionalPreviewInnerFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((circleSide - additionalPreviewInnerSize.width) / 2.0), y: floorToScreenPixels((circleSide - additionalPreviewInnerSize.height) / 2.0)), size: additionalPreviewInnerSize)
if mainPreviewView.superview != self.mainPreviewContainerView {

View File

@ -62,6 +62,8 @@ final class ModeComponent: Component {
init() {
super.init(frame: .zero)
self.isExclusiveTouch = true
self.addTarget(self, action: #selector(self.buttonPressed), for: .touchUpInside)
}

View File

@ -1311,7 +1311,8 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
externalExpansionView: nil,
useOpaqueTheme: false,
hideBackground: false,
stateContext: self.stateContext?.emojiState
stateContext: self.stateContext?.emojiState,
addImage: nil
)
self.stickerInputInteraction = EmojiPagerContentComponent.InputInteraction(
@ -1609,7 +1610,8 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
externalExpansionView: nil,
useOpaqueTheme: false,
hideBackground: false,
stateContext: nil
stateContext: nil,
addImage: nil
)
@ -2499,7 +2501,8 @@ public final class EntityInputView: UIInputView, AttachmentTextInputPanelInputVi
externalExpansionView: nil,
useOpaqueTheme: false,
hideBackground: hideBackground,
stateContext: nil
stateContext: nil,
addImage: nil
)
let semaphore = DispatchSemaphore(value: 0)

View File

@ -676,7 +676,8 @@ public final class EmojiStatusSelectionController: ViewController {
externalExpansionView: nil,
useOpaqueTheme: true,
hideBackground: false,
stateContext: nil
stateContext: nil,
addImage: nil
)
strongSelf.refreshLayout(transition: .immediate)

View File

@ -2322,6 +2322,7 @@ public final class EmojiPagerContentComponent: Component {
public let hideBackground: Bool
public let scrollingStickersGridPromise = ValuePromise<Bool>(false)
public let stateContext: StateContext?
public let addImage: (() -> Void)?
public init(
performItemAction: @escaping (AnyHashable, Item, UIView, CGRect, CALayer, Bool) -> Void,
@ -2347,7 +2348,8 @@ public final class EmojiPagerContentComponent: Component {
externalExpansionView: UIView?,
useOpaqueTheme: Bool,
hideBackground: Bool,
stateContext: StateContext?
stateContext: StateContext?,
addImage: (() -> Void)?
) {
self.performItemAction = performItemAction
self.deleteBackwards = deleteBackwards
@ -2373,6 +2375,7 @@ public final class EmojiPagerContentComponent: Component {
self.useOpaqueTheme = useOpaqueTheme
self.hideBackground = hideBackground
self.stateContext = stateContext
self.addImage = addImage
}
}

View File

@ -409,7 +409,8 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode
externalExpansionView: nil,
useOpaqueTheme: true,
hideBackground: false,
stateContext: nil
stateContext: nil,
addImage: nil
)
self.dataDisposable = (

View File

@ -690,6 +690,18 @@ public final class EntityKeyboardComponent: Component {
}
).minSize(CGSize(width: 38.0, height: 38.0)))))
}
if let addImage = component.stickerContent?.inputInteractionHolder.inputInteraction?.addImage {
contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "image", 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)))))
}
}
if let _ = deleteBackwards {

View File

@ -950,7 +950,8 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent {
externalExpansionView: nil,
useOpaqueTheme: true,
hideBackground: false,
stateContext: nil
stateContext: nil,
addImage: nil
)
}
}

View File

@ -85,7 +85,21 @@ public final class DrawingStickerEntity: DrawingEntity, Codable {
public var baseSize: CGSize {
let size = max(10.0, min(self.referenceDrawingSize.width, self.referenceDrawingSize.height) * 0.25)
return CGSize(width: size, height: size)
let dimensions: CGSize
switch self.content {
case let .image(image, _):
dimensions = image.size
case let .file(file):
dimensions = file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0)
case let .video(_, image, _):
dimensions = image?.size ?? CGSize(width: 512.0, height: 512.0)
case .dualVideoReference:
dimensions = CGSize(width: 512.0, height: 512.0)
}
let boundingSize = CGSize(width: size, height: size)
return dimensions.fitted(boundingSize)
}
public var isAnimated: Bool {

View File

@ -269,6 +269,9 @@ private func makeEditorImageFrameComposition(context: CIContext, inputImage: CII
var baseScale: CGFloat = 1.0
if let baseSize = entity.baseSize {
baseScale = baseSize.width / image.extent.width
if baseSize.width != baseSize.height {
baseScale *= min(baseSize.width, baseSize.height) / max(baseSize.width, baseSize.height)
}
}
var transform = CGAffineTransform.identity

View File

@ -126,6 +126,7 @@ private class MediaEditorComposerStickerEntity: MediaEditorComposerEntity {
switch content {
case let .file(file):
let dimensions = file.dimensions ?? PixelDimensions(width: 512, height: 512)
if file.isAnimatedSticker || file.isVideoSticker || file.mimeType == "video/webm" {
self.isAnimated = true
self.isVideoSticker = file.isVideoSticker || file.mimeType == "video/webm"
@ -133,7 +134,6 @@ private class MediaEditorComposerStickerEntity: MediaEditorComposerEntity {
self.source = AnimatedStickerResourceSource(account: account, resource: file.resource, isVideo: isVideoSticker)
let pathPrefix = account.postbox.mediaBox.shortLivedResourceCachePathPrefix(file.resource.id)
if let source = self.source {
let dimensions = file.dimensions ?? PixelDimensions(width: 512, height: 512)
let fitToSize: CGSize
if self.isStatic {
fitToSize = CGSize(width: 768, height: 768)
@ -334,6 +334,7 @@ private class MediaEditorComposerStickerEntity: MediaEditorComposerEntity {
let options = NSMutableDictionary()
options.setObject(ioSurfaceProperties, forKey: kCVPixelBufferIOSurfacePropertiesKey as NSString)
var pixelBuffer: CVPixelBuffer?
CVPixelBufferCreate(
kCFAllocatorDefault,

View File

@ -383,6 +383,9 @@ public final class MediaEditorValues: Codable, Equatable {
if self.videoTrimRange != nil {
return true
}
if self.drawing != nil {
return true
}
if !self.entities.isEmpty {
return true
}

View File

@ -930,10 +930,10 @@ final class MediaEditorScreenComponent: Component {
}
)),
environment: {},
containerSize: CGSize(width: availableSize.width - scrubberInset * 2.0, height: availableSize.height)
containerSize: CGSize(width: previewSize.width - scrubberInset * 2.0, height: availableSize.height)
)
let scrubberFrame = CGRect(origin: CGPoint(x: scrubberInset, y: availableSize.height - environment.safeInsets.bottom - scrubberSize.height - 8.0 + controlsBottomInset), size: scrubberSize)
let scrubberFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - scrubberSize.width) / 2.0), y: availableSize.height - environment.safeInsets.bottom - scrubberSize.height - 8.0 + controlsBottomInset), size: scrubberSize)
if let scrubberView = self.scrubber.view {
if scrubberView.superview == nil {
if let inputPanelBackgroundView = self.inputPanelBackground.view, inputPanelBackgroundView.superview != nil {

View File

@ -928,6 +928,7 @@ public final class MessageInputPanelComponent: Component {
}
self.currentMediaInputIsVoice = !self.currentMediaInputIsVoice
self.hapticFeedback.impact(.medium)
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
},
updateMediaCancelFraction: { [weak self] mediaCancelFraction in

View File

@ -139,6 +139,105 @@ private final class StoryLongPressRecognizer: UILongPressGestureRecognizer {
}
}
private final class StoryPinchGesture: UIPinchGestureRecognizer {
private final class Target {
var updated: (() -> Void)?
@objc func onGesture(_ gesture: UIPinchGestureRecognizer) {
self.updated?()
}
}
private let target: Target
private(set) var currentTransform: (CGFloat, CGPoint, CGPoint)?
var began: (() -> Void)?
var updated: ((CGFloat, CGPoint, CGPoint) -> Void)?
var ended: (() -> Void)?
private var initialLocation: CGPoint?
private var pinchLocation = CGPoint()
private var currentOffset = CGPoint()
private var currentNumberOfTouches = 0
init() {
self.target = Target()
super.init(target: self.target, action: #selector(self.target.onGesture(_:)))
self.target.updated = { [weak self] in
self?.gestureUpdated()
}
}
override func reset() {
super.reset()
self.currentNumberOfTouches = 0
self.initialLocation = nil
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
//self.currentTouches.formUnion(touches)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesEnded(touches, with: event)
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesCancelled(touches, with: event)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
}
private func gestureUpdated() {
switch self.state {
case .began:
self.currentOffset = CGPoint()
let pinchLocation = self.location(in: self.view)
self.pinchLocation = pinchLocation
self.initialLocation = pinchLocation
let scale = max(1.0, self.scale)
self.currentTransform = (scale, self.pinchLocation, self.currentOffset)
self.currentNumberOfTouches = self.numberOfTouches
self.began?()
case .changed:
let locationSum = self.location(in: self.view)
if self.numberOfTouches < 2 && self.currentNumberOfTouches >= 2 {
self.initialLocation = CGPoint(x: locationSum.x - self.currentOffset.x, y: locationSum.y - self.currentOffset.y)
}
self.currentNumberOfTouches = self.numberOfTouches
if let initialLocation = self.initialLocation {
self.currentOffset = CGPoint(x: locationSum.x - initialLocation.x, y: locationSum.y - initialLocation.y)
}
if let (scale, pinchLocation, _) = self.currentTransform {
self.currentTransform = (scale, pinchLocation, self.currentOffset)
self.updated?(scale, pinchLocation, self.currentOffset)
}
let scale = max(1.0, self.scale)
self.currentTransform = (scale, self.pinchLocation, self.currentOffset)
self.updated?(scale, self.pinchLocation, self.currentOffset)
case .ended, .cancelled:
self.ended?()
default:
break
}
}
}
private final class StoryContainerScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -351,7 +450,28 @@ private final class StoryContainerScreenComponent: Component {
self.longPressRecognizer = longPressRecognizer
self.addGestureRecognizer(longPressRecognizer)
let pinchRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(self.pinchGesture(_:)))
let pinchRecognizer = StoryPinchGesture()
pinchRecognizer.delegate = self
pinchRecognizer.updated = { [weak self] scale, pinchLocation, offset in
guard let self else {
return
}
var pinchLocation = pinchLocation
if let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id] {
if let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View {
pinchLocation = self.convert(pinchLocation, to: itemSetComponentView)
}
}
self.itemSetPinchState = StoryItemSetContainerComponent.PinchState(scale: scale, location: pinchLocation, offset: offset)
self.state?.updated(transition: .immediate)
}
pinchRecognizer.ended = { [weak self] in
guard let self else {
return
}
self.itemSetPinchState = nil
self.state?.updated(transition: Transition(animation: .curve(duration: 0.3, curve: .spring)))
}
self.addGestureRecognizer(pinchRecognizer)
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
@ -431,6 +551,13 @@ private final class StoryContainerScreenComponent: Component {
self.volumeButtonsListenerShouldBeActiveDisposable?.dispose()
}
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer is StoryPinchGesture {
return !hasFirstResponder(self)
}
return true
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
guard let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View else {
return false
@ -581,10 +708,21 @@ private final class StoryContainerScreenComponent: Component {
@objc private func dismissPanGesture(_ recognizer: UIPanGestureRecognizer) {
switch recognizer.state {
case .began:
self.dismissAllTooltips()
if let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id] {
if let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View {
if itemSetComponentView.hasActiveDeactivateableInput() {
itemSetComponentView.deactivateInput()
recognizer.isEnabled = false
recognizer.isEnabled = true
return
}
}
}
self.verticalPanState = ItemSetPanState(fraction: 0.0, didBegin: true)
self.state?.updated(transition: .immediate)
self.dismissAllTooltips()
case .changed:
let translation = recognizer.translation(in: self)
self.verticalPanState = ItemSetPanState(fraction: max(-1.0, min(1.0, translation.y / self.bounds.height)), didBegin: true)
@ -656,34 +794,17 @@ private final class StoryContainerScreenComponent: Component {
guard case .recognized = recognizer.state else {
return
}
let location = recognizer.location(in: recognizer.view)
if let currentItemView = self.visibleItemSetViews.first?.value {
if location.x < currentItemView.frame.minX {
self.navigate(direction: .previous)
} else if location.x > currentItemView.frame.maxX {
self.navigate(direction: .next)
}
}
}
@objc private func pinchGesture(_ recognizer: UIPinchGestureRecognizer) {
switch recognizer.state {
case .began, .changed:
let location = recognizer.location(in: self)
let scale = max(1.0, recognizer.scale)
if let itemSetPinchState = self.itemSetPinchState {
let offset = CGPoint(x: location.x - itemSetPinchState.location.x , y: location.y - itemSetPinchState.location.y)
self.itemSetPinchState = StoryItemSetContainerComponent.PinchState(scale: scale, location: itemSetPinchState.location, offset: offset)
if let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let currentItemView = itemSetView.view.view as? StoryItemSetContainerComponent.View {
if currentItemView.hasActiveDeactivateableInput() {
currentItemView.deactivateInput()
} else {
self.itemSetPinchState = StoryItemSetContainerComponent.PinchState(scale: scale, location: location, offset: .zero)
if location.x < currentItemView.frame.minX {
self.navigate(direction: .previous)
} else if location.x > currentItemView.frame.maxX {
self.navigate(direction: .next)
}
}
self.state?.updated(transition: .immediate)
case .cancelled, .ended:
self.itemSetPinchState = nil
self.state?.updated(transition: Transition(animation: .curve(duration: 0.3, curve: .spring)))
default:
break
}
}
@ -996,6 +1117,7 @@ private final class StoryContainerScreenComponent: Component {
}
}
var presentationContextInsets = UIEdgeInsets()
if !currentSlices.isEmpty, let focusedIndex {
for i in max(0, focusedIndex - 1) ... min(focusedIndex + 1, currentSlices.count - 1) {
var isItemVisible = false
@ -1053,6 +1175,10 @@ private final class StoryContainerScreenComponent: Component {
itemSetContainerInsets.top = 0.0
itemSetContainerInsets.bottom = floorToScreenPixels((availableSize.height - itemSetContainerSize.height) / 2.0)
itemSetContainerSafeInsets.bottom = 0.0
presentationContextInsets.left = floorToScreenPixels((availableSize.width - itemSetContainerSize.width) / 2.0)
presentationContextInsets.right = presentationContextInsets.left
presentationContextInsets.bottom = itemSetContainerInsets.bottom
}
let _ = itemSetView.view.update(
@ -1327,8 +1453,8 @@ private final class StoryContainerScreenComponent: Component {
size: availableSize,
metrics: environment.metrics,
deviceMetrics: environment.deviceMetrics,
intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: contentDerivedBottomInset, right: 0.0),
safeInsets: UIEdgeInsets(),
intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: contentDerivedBottomInset + presentationContextInsets.bottom, right: 0.0),
safeInsets: UIEdgeInsets(top: 0.0, left: presentationContextInsets.left, bottom: 0.0, right: presentationContextInsets.right),
additionalInsets: UIEdgeInsets(),
statusBarHeight: nil,
inputHeight: nil,

View File

@ -648,25 +648,30 @@ public final class StoryItemSetContainerComponent: Component {
return false
}
private func deactivateInput() {
func hasActiveDeactivateableInput() -> Bool {
if let view = self.inputPanel.view as? MessageInputPanelComponent.View {
if view.canDeactivateInput() && (hasFirstResponder(self) || self.sendMessageContext.currentInputMode == .media) {
return true
}
}
return false
}
func deactivateInput() {
if let view = self.inputPanel.view as? MessageInputPanelComponent.View, view.canDeactivateInput() {
self.sendMessageContext.currentInputMode = .text
if hasFirstResponder(self) {
view.deactivateInput()
} else {
self.state?.updated(transition: .spring(duration: 0.4).withUserData(TextFieldComponent.AnimationHint(kind: .textFocusChanged)))
}
}
}
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state, let component = self.component, let itemLayout = self.itemLayout {
if hasFirstResponder(self) || self.sendMessageContext.currentInputMode == .media {
if let view = self.inputPanel.view as? MessageInputPanelComponent.View {
if view.canDeactivateInput() {
self.sendMessageContext.currentInputMode = .text
if hasFirstResponder(self) {
view.deactivateInput()
} else {
self.state?.updated(transition: .spring(duration: 0.4).withUserData(TextFieldComponent.AnimationHint(kind: .textFocusChanged)))
}
} else {
view.animateError()
}
}
self.deactivateInput()
} else if self.displayViewList {
let point = recognizer.location(in: self)
@ -2271,7 +2276,7 @@ public final class StoryItemSetContainerComponent: Component {
let tooltipScreen = TooltipScreen(
account: component.context.account,
sharedContext: component.context.sharedContext,
text: .plain(text: "This video has no sound"), style: .default, location: TooltipScreen.Location.point(soundButtonView.convert(soundButtonView.bounds, to: self).offsetBy(dx: 1.0, dy: -10.0), .top), displayDuration: .infinite, shouldDismissOnTouch: { _, _ in
text: .plain(text: "This video has no sound"), style: .default, location: TooltipScreen.Location.point(soundButtonView.convert(soundButtonView.bounds, to: nil).offsetBy(dx: 1.0, dy: -10.0), .top), displayDuration: .infinite, shouldDismissOnTouch: { _, _ in
return .dismiss(consume: true)
}
)
@ -3154,9 +3159,6 @@ public final class StoryItemSetContainerComponent: Component {
}
let subject: Signal<MediaEditorScreen.Subject?, NoError>
// if let source {
// subject = .single(.draft(source, Int64(id)))
// } else {
var duration: Double?
let media = item.media._asMedia()

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "photo_24.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,154 @@
%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

View File

@ -8067,7 +8067,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
case .proxy:
self.controller?.push(proxySettingsController(context: self.context))
case .stories:
self.controller?.push(PeerInfoStoryGridScreen(context: self.context, peerId: self.context.account.peerId, scope: .saved))
push(PeerInfoStoryGridScreen(context: self.context, peerId: self.context.account.peerId, scope: .saved))
case .savedMessages:
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId))
|> deliverOnMainQueue).start(next: { [weak self] peer in
@ -8247,7 +8247,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
case .emojiStatus:
self.headerNode.invokeDisplayPremiumIntro()
case .powerSaving:
self.controller?.push(energySavingSettingsScreen(context: self.context))
push(energySavingSettingsScreen(context: self.context))
}
}