Various fixes

This commit is contained in:
Ilya Laktyushin 2023-07-10 04:00:52 +02:00
parent 80079dba5a
commit fdff55d7ea
14 changed files with 219 additions and 149 deletions

View File

@ -870,7 +870,7 @@ public protocol SharedAccountContext: AnyObject {
func makeStorageManagementController(context: AccountContext) -> ViewController func makeStorageManagementController(context: AccountContext) -> ViewController
func makeAttachmentFileController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, bannedSendMedia: (Int32, Bool)?, presentGallery: @escaping () -> Void, presentFiles: @escaping () -> Void, send: @escaping (AnyMediaReference) -> Void) -> AttachmentFileController func makeAttachmentFileController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, bannedSendMedia: (Int32, Bool)?, presentGallery: @escaping () -> Void, presentFiles: @escaping () -> Void, send: @escaping (AnyMediaReference) -> Void) -> AttachmentFileController
func makeGalleryCaptionPanelView(context: AccountContext, chatLocation: ChatLocation, customEmojiAvailable: Bool, present: @escaping (ViewController) -> Void, presentInGlobalOverlay: @escaping (ViewController) -> Void) -> NSObject? func makeGalleryCaptionPanelView(context: AccountContext, chatLocation: ChatLocation, customEmojiAvailable: Bool, present: @escaping (ViewController) -> Void, presentInGlobalOverlay: @escaping (ViewController) -> Void) -> NSObject?
func makeHashtagSearchController(context: AccountContext, peer: EnginePeer?, query: String) -> ViewController func makeHashtagSearchController(context: AccountContext, peer: EnginePeer?, query: String, all: Bool) -> ViewController
func makeMyStoriesController(context: AccountContext, isArchive: Bool) -> ViewController func makeMyStoriesController(context: AccountContext, isArchive: Bool) -> ViewController
func navigateToChatController(_ params: NavigateToChatControllerParams) func navigateToChatController(_ params: NavigateToChatControllerParams)
func navigateToForumChannel(context: AccountContext, peerId: EnginePeer.Id, navigationController: NavigationController) func navigateToForumChannel(context: AccountContext, peerId: EnginePeer.Id, navigationController: NavigationController)

View File

@ -3368,13 +3368,13 @@ private final class ShimmerEffectNode: ASDisplayNode {
} }
} }
private final class ChatListSearchShimmerNode: ASDisplayNode { public final class ChatListSearchShimmerNode: ASDisplayNode {
private let backgroundColorNode: ASDisplayNode private let backgroundColorNode: ASDisplayNode
private let effectNode: ShimmerEffectNode private let effectNode: ShimmerEffectNode
private let maskNode: ASImageNode private let maskNode: ASImageNode
private var currentParams: (size: CGSize, presentationData: PresentationData, key: ChatListSearchPaneKey)? private var currentParams: (size: CGSize, presentationData: PresentationData, key: ChatListSearchPaneKey)?
init(key: ChatListSearchPaneKey) { public init(key: ChatListSearchPaneKey) {
self.backgroundColorNode = ASDisplayNode() self.backgroundColorNode = ASDisplayNode()
self.effectNode = ShimmerEffectNode() self.effectNode = ShimmerEffectNode()
self.maskNode = ASImageNode() self.maskNode = ASImageNode()
@ -3388,7 +3388,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
self.addSubnode(self.maskNode) self.addSubnode(self.maskNode)
} }
func update(context: AccountContext, size: CGSize, presentationData: PresentationData, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, key: ChatListSearchPaneKey, hasSelection: Bool, transition: ContainedViewLayoutTransition) { public func update(context: AccountContext, size: CGSize, presentationData: PresentationData, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, key: ChatListSearchPaneKey, hasSelection: Bool, transition: ContainedViewLayoutTransition) {
if self.currentParams?.size != size || self.currentParams?.presentationData !== presentationData || self.currentParams?.key != key { if self.currentParams?.size != size || self.currentParams?.presentationData !== presentationData || self.currentParams?.key != key {
self.currentParams = (size, presentationData, key) self.currentParams = (size, presentationData, key)

View File

@ -52,6 +52,7 @@ private func prepareForRendering(entityView: DrawingEntityView) {
public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView { public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
private let context: AccountContext private let context: AccountContext
private let size: CGSize private let size: CGSize
private let hasBin: Bool
weak var drawingView: DrawingView? weak var drawingView: DrawingView?
public weak var selectionContainerView: DrawingSelectionContainerView? public weak var selectionContainerView: DrawingSelectionContainerView?
@ -85,9 +86,10 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
private let hapticFeedback = HapticFeedback() private let hapticFeedback = HapticFeedback()
public init(context: AccountContext, size: CGSize) { public init(context: AccountContext, size: CGSize, hasBin: Bool = false) {
self.context = context self.context = context
self.size = size self.size = size
self.hasBin = hasBin
super.init(frame: CGRect(origin: .zero, size: size)) super.init(frame: CGRect(origin: .zero, size: size))
@ -690,6 +692,9 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
public func handlePan(_ gestureRecognizer: UIPanGestureRecognizer) { public func handlePan(_ gestureRecognizer: UIPanGestureRecognizer) {
let location = gestureRecognizer.location(in: self) let location = gestureRecognizer.location(in: self)
if let selectedEntityView = self.selectedEntityView, let selectionView = selectedEntityView.selectionView { if let selectedEntityView = self.selectedEntityView, let selectionView = selectedEntityView.selectionView {
if !self.hasBin {
selectionView.handlePan(gestureRecognizer)
} else {
var isTrappedInBin = false var isTrappedInBin = false
let scale = 100.0 / selectedEntityView.bounds.size.width let scale = 100.0 / selectedEntityView.bounds.size.width
switch gestureRecognizer.state { switch gestureRecognizer.state {
@ -752,6 +757,7 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
} }
selectionView.handlePan(gestureRecognizer) selectionView.handlePan(gestureRecognizer)
} }
}
} else if gestureRecognizer.numberOfTouches == 1, let viewToSelect = self.entity(at: location) { } else if gestureRecognizer.numberOfTouches == 1, let viewToSelect = self.entity(at: location) {
self.selectEntity(viewToSelect.entity) self.selectEntity(viewToSelect.entity)
} else if gestureRecognizer.numberOfTouches == 2, let mediaEntityView = self.subviews.first(where: { $0 is DrawingEntityMediaView }) as? DrawingEntityMediaView { } else if gestureRecognizer.numberOfTouches == 2, let mediaEntityView = self.subviews.first(where: { $0 is DrawingEntityMediaView }) as? DrawingEntityMediaView {

View File

@ -2965,7 +2965,7 @@ public final class DrawingToolsInteraction {
private let onInteractionUpdated: (Bool) -> Void private let onInteractionUpdated: (Bool) -> Void
private let onTextEditingEnded: (Bool) -> Void private let onTextEditingEnded: (Bool) -> Void
private let getCurrentImage: () -> UIImage? public let getCurrentImage: () -> UIImage?
private let getControllerNode: () -> ASDisplayNode? private let getControllerNode: () -> ASDisplayNode?
private let present: (ViewController, PresentationContextType, Any?) -> Void private let present: (ViewController, PresentationContextType, Any?) -> Void
private let addSubview: (UIView) -> Void private let addSubview: (UIView) -> Void

View File

@ -329,7 +329,7 @@ public final class DrawingStickerEntityView: DrawingEntityView {
if size.width > 0 && self.currentSize != size { if size.width > 0 && self.currentSize != size {
self.currentSize = size self.currentSize = size
let sideSize: CGFloat = size.width let sideSize: CGFloat = max(size.width, size.height)
let boundingSize = CGSize(width: sideSize, height: sideSize) let boundingSize = CGSize(width: sideSize, height: sideSize)
let imageSize = self.dimensions.aspectFitted(boundingSize) let imageSize = self.dimensions.aspectFitted(boundingSize)
@ -657,6 +657,46 @@ final class DrawingStickerEntititySelectionView: DrawingEntitySelectionView {
let handlePath = CGPath(ellipseIn: CGRect(origin: CGPoint(x: (bounds.width - handleSize.width) / 2.0, y: (bounds.height - handleSize.height) / 2.0), size: handleSize), transform: nil) let handlePath = CGPath(ellipseIn: CGRect(origin: CGPoint(x: (bounds.width - handleSize.width) / 2.0, y: (bounds.height - handleSize.height) / 2.0), size: handleSize), transform: nil)
let lineWidth = (1.0 + UIScreenPixel) / self.scale let lineWidth = (1.0 + UIScreenPixel) / self.scale
let radius = (self.bounds.width - inset * 2.0) / 2.0
let circumference: CGFloat = 2.0 * .pi * radius
let relativeDashLength: CGFloat = 0.25
self.border.lineWidth = 2.0 / self.scale
let actualInset: CGFloat
if entity.isRectangle {
let aspectRatio = entity.baseSize.width / entity.baseSize.height
let width: CGFloat
let height: CGFloat
if entity.baseSize.width > entity.baseSize.height {
width = self.bounds.width - inset * 2.0
height = self.bounds.height / aspectRatio - inset * 2.0
} else {
width = self.bounds.width * aspectRatio - inset * 2.0
height = self.bounds.height - inset * 2.0
}
actualInset = floorToScreenPixels((self.bounds.width - width) / 2.0)
let cornerRadius: CGFloat = 12.0 - self.scale
let perimeter: CGFloat = 2.0 * (width + height - cornerRadius * (4.0 - .pi))
let count = 12
let dashLength = perimeter / CGFloat(count)
self.border.lineDashPattern = [dashLength * relativeDashLength, dashLength * relativeDashLength] as [NSNumber]
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 {
actualInset = inset
let count = 10
let dashLength = circumference / CGFloat(count)
self.border.lineDashPattern = [dashLength * relativeDashLength, dashLength * relativeDashLength] as [NSNumber]
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
}
let handles = [ let handles = [
self.leftHandle, self.leftHandle,
self.rightHandle self.rightHandle
@ -668,29 +708,8 @@ final class DrawingStickerEntititySelectionView: DrawingEntitySelectionView {
handle.lineWidth = lineWidth handle.lineWidth = lineWidth
} }
self.leftHandle.position = CGPoint(x: inset, y: self.bounds.midY)
self.rightHandle.position = CGPoint(x: self.bounds.maxX - inset, y: self.bounds.midY)
self.leftHandle.position = CGPoint(x: actualInset, y: self.bounds.midY)
let radius = (self.bounds.width - inset * 2.0) / 2.0 self.rightHandle.position = CGPoint(x: self.bounds.maxX - actualInset, y: self.bounds.midY)
let circumference: CGFloat = 2.0 * .pi * radius
let count = 10
let relativeDashLength: CGFloat = 0.25
let dashLength = circumference / CGFloat(count)
self.border.lineDashPattern = [dashLength * relativeDashLength, dashLength * relativeDashLength] as [NSNumber]
self.border.lineWidth = 2.0 / self.scale
if entity.isRectangle {
let aspectRatio = entity.baseSize.width / entity.baseSize.height
let width: CGFloat = self.bounds.width - inset * 2.0
let height: CGFloat = self.bounds.height / aspectRatio - inset * 2.0
let cornerRadius: CGFloat = 12.0 - self.scale
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 {
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
}
} }
} }

View File

@ -17,6 +17,7 @@ public final class HashtagSearchController: TelegramBaseController {
private let context: AccountContext private let context: AccountContext
private let peer: EnginePeer? private let peer: EnginePeer?
private let query: String private let query: String
let all: Bool
private var transitionDisposable: Disposable? private var transitionDisposable: Disposable?
private let openMessageFromSearchDisposable = MetaDisposable() private let openMessageFromSearchDisposable = MetaDisposable()
@ -30,10 +31,11 @@ public final class HashtagSearchController: TelegramBaseController {
return self.displayNode as! HashtagSearchControllerNode return self.displayNode as! HashtagSearchControllerNode
} }
public init(context: AccountContext, peer: EnginePeer?, query: String) { public init(context: AccountContext, peer: EnginePeer?, query: String, all: Bool = false) {
self.context = context self.context = context
self.peer = peer self.peer = peer
self.query = query self.query = query
self.all = all
self.animationCache = context.animationCache self.animationCache = context.animationCache
self.animationRenderer = context.animationRenderer self.animationRenderer = context.animationRenderer

View File

@ -11,17 +11,16 @@ import ChatListSearchItemHeader
final class HashtagSearchControllerNode: ASDisplayNode { final class HashtagSearchControllerNode: ASDisplayNode {
private let context: AccountContext private let context: AccountContext
private weak var controller: HashtagSearchController? private weak var controller: HashtagSearchController?
private let query: String
private let navigationBar: NavigationBar? private let navigationBar: NavigationBar?
private let segmentedControlNode: SegmentedControlNode private let segmentedControlNode: SegmentedControlNode
let listNode: ListView let listNode: ListView
let shimmerNode: ChatListSearchShimmerNode
let chatController: ChatController? let chatController: ChatController?
private let query: String
private var containerLayout: (ContainerViewLayout, CGFloat)? private var containerLayout: (ContainerViewLayout, CGFloat)?
private var enqueuedTransitions: [(ChatListSearchContainerTransition, Bool)] = [] private var enqueuedTransitions: [(ChatListSearchContainerTransition, Bool)] = []
private var hasValidLayout = false private var hasValidLayout = false
@ -34,6 +33,10 @@ final class HashtagSearchControllerNode: ASDisplayNode {
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.shimmerNode = ChatListSearchShimmerNode(key: .chats)
self.shimmerNode.isUserInteractionEnabled = false
self.shimmerNode.allowsGroupOpacity = true
self.listNode = ListView() self.listNode = ListView()
self.listNode.accessibilityPageScrolledString = { row, count in self.listNode.accessibilityPageScrolledString = { row, count in
return presentationData.strings.VoiceOver_ScrollStatus(row, count).string return presentationData.strings.VoiceOver_ScrollStatus(row, count).string
@ -48,7 +51,7 @@ final class HashtagSearchControllerNode: ASDisplayNode {
items.append(peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) ?? "") items.append(peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) ?? "")
} }
items.append(presentationData.strings.HashtagSearch_AllChats) items.append(presentationData.strings.HashtagSearch_AllChats)
self.segmentedControlNode = SegmentedControlNode(theme: SegmentedControlTheme(theme: presentationData.theme), items: items.map { SegmentedControlItem(title: $0) }, selectedIndex: 0) self.segmentedControlNode = SegmentedControlNode(theme: SegmentedControlTheme(theme: presentationData.theme), items: items.map { SegmentedControlItem(title: $0) }, selectedIndex: controller.all ? 1 : 0)
if let peer = peer { if let peer = peer {
self.chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .inline(navigationController)) self.chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .inline(navigationController))
@ -65,7 +68,15 @@ final class HashtagSearchControllerNode: ASDisplayNode {
self.backgroundColor = presentationData.theme.chatList.backgroundColor self.backgroundColor = presentationData.theme.chatList.backgroundColor
self.addSubnode(self.listNode) self.addSubnode(self.listNode)
// self.addSubnode(self.shimmerNode)
if controller.all {
self.chatController?.displayNode.isHidden = true
self.listNode.isHidden = false
} else {
self.chatController?.displayNode.isHidden = false
self.listNode.isHidden = true self.listNode.isHidden = true
}
self.segmentedControlNode.selectedIndexChanged = { [weak self] index in self.segmentedControlNode.selectedIndexChanged = { [weak self] index in
if let strongSelf = self { if let strongSelf = self {
@ -164,6 +175,11 @@ final class HashtagSearchControllerNode: ASDisplayNode {
self.listNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height) self.listNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)
self.listNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0) self.listNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0)
let overflowInset: CGFloat = 0.0
let topInset = navigationBarHeight
self.shimmerNode.frame = CGRect(origin: CGPoint(x: overflowInset, y: topInset), size: CGSize(width: layout.size.width - overflowInset * 2.0, height: layout.size.height))
self.shimmerNode.update(context: self.context, size: CGSize(width: layout.size.width - overflowInset * 2.0, height: layout.size.height), presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, animationCache: self.context.animationCache, animationRenderer: self.context.animationRenderer, key: .chats, hasSelection: false, transition: transition)
insets.top += 4.0 insets.top += 4.0
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition) let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)

View File

@ -188,6 +188,10 @@ final class MediaPickerGridItemNode: GridItemNode {
} else { } else {
return nil return nil
} }
} else if let (draft, _) = self.currentDraftState {
let tag = Month(localTimestamp: draft.timestamp).packedValue
self._cachedTag = tag
return tag
} else { } else {
return nil return nil
} }

View File

@ -433,7 +433,7 @@ private final class CameraScreenComponent: CombinedComponent {
case .began: case .began:
return .single(.pendingImage) return .single(.pendingImage)
case let .finished(image, additionalImage, _): case let .finished(image, additionalImage, _):
return .single(.image(CameraScreen.Result.Image(image: image, additionalImage: additionalImage, additionalImagePosition: .bottomRight))) return .single(.image(CameraScreen.Result.Image(image: image, additionalImage: additionalImage, additionalImagePosition: .topRight)))
case .failed: case .failed:
return .complete() return .complete()
} }
@ -470,7 +470,7 @@ private final class CameraScreenComponent: CombinedComponent {
self.resultDisposable.set((self.camera.stopRecording() self.resultDisposable.set((self.camera.stopRecording()
|> deliverOnMainQueue).start(next: { [weak self] result in |> deliverOnMainQueue).start(next: { [weak self] result in
if let self, case let .finished(mainResult, additionalResult, duration, positionChangeTimestamps, _) = result { if let self, case let .finished(mainResult, additionalResult, duration, positionChangeTimestamps, _) = result {
self.completion.invoke(.single(.video(CameraScreen.Result.Video(videoPath: mainResult.0, coverImage: mainResult.1, mirror: mainResult.2, additionalVideoPath: additionalResult?.0, additionalCoverImage: additionalResult?.1, dimensions: PixelDimensions(width: 1080, height: 1920), duration: duration, positionChangeTimestamps: positionChangeTimestamps, additionalVideoPosition: .bottomRight)))) self.completion.invoke(.single(.video(CameraScreen.Result.Video(videoPath: mainResult.0, coverImage: mainResult.1, mirror: mainResult.2, additionalVideoPath: additionalResult?.0, additionalCoverImage: additionalResult?.1, dimensions: PixelDimensions(width: 1080, height: 1920), duration: duration, positionChangeTimestamps: positionChangeTimestamps, additionalVideoPosition: .topRight))))
} }
})) }))
self.isTransitioning = true self.isTransitioning = true
@ -1075,7 +1075,7 @@ public class CameraScreen: ViewController {
private var appliedDualCam = false private var appliedDualCam = false
private var cameraPosition: Camera.Position = .back private var cameraPosition: Camera.Position = .back
private var pipPosition: PIPPosition = .bottomRight private var pipPosition: PIPPosition = .topRight
fileprivate var previewBlurPromise = ValuePromise<Bool>(false) fileprivate var previewBlurPromise = ValuePromise<Bool>(false)
private let animateFlipAction = ActionSlot<Void>() private let animateFlipAction = ActionSlot<Void>()
@ -1941,7 +1941,9 @@ public class CameraScreen: ViewController {
transition.setPosition(view: self.backgroundView, position: CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0)) transition.setPosition(view: self.backgroundView, position: CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0))
transition.setBounds(view: self.backgroundView, bounds: CGRect(origin: .zero, size: layout.size)) transition.setBounds(view: self.backgroundView, bounds: CGRect(origin: .zero, size: layout.size))
if !self.hasGallery {
transition.setPosition(view: self.containerView, position: CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0)) transition.setPosition(view: self.containerView, position: CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0))
}
transition.setBounds(view: self.containerView, bounds: CGRect(origin: .zero, size: layout.size)) transition.setBounds(view: self.containerView, bounds: CGRect(origin: .zero, size: layout.size))
transition.setFrame(view: self.transitionDimView, frame: CGRect(origin: .zero, size: layout.size)) transition.setFrame(view: self.transitionDimView, frame: CGRect(origin: .zero, size: layout.size))
@ -2293,11 +2295,16 @@ public class CameraScreen: ViewController {
let transitionFraction = max(0.0, min(1.0, transitionFraction)) let transitionFraction = max(0.0, min(1.0, transitionFraction))
let offsetX = floorToScreenPixels((1.0 - transitionFraction) * self.node.frame.width * -1.0) let offsetX = floorToScreenPixels((1.0 - transitionFraction) * self.node.frame.width * -1.0)
transition.updateTransform(layer: self.node.backgroundView.layer, transform: CGAffineTransform(translationX: offsetX, y: 0.0)) transition.updateTransform(layer: self.node.backgroundView.layer, transform: CGAffineTransform(translationX: offsetX, y: 0.0))
transition.updateTransform(layer: self.node.containerView.layer, transform: CGAffineTransform(translationX: offsetX, y: 0.0))
let scale: CGFloat = max(0.8, min(1.0, 0.8 + 0.2 * transitionFraction)) let scale: CGFloat = max(0.8, min(1.0, 0.8 + 0.2 * transitionFraction))
if !self.node.hasGallery {
transition.updateTransform(layer: self.node.containerView.layer, transform: CGAffineTransform(translationX: offsetX, y: 0.0))
transition.updateSublayerTransformScaleAndOffset(layer: self.node.containerView.layer, scale: scale, offset: CGPoint(x: -offsetX * 1.0 / scale * 0.5, y: 0.0), completion: { _ in transition.updateSublayerTransformScaleAndOffset(layer: self.node.containerView.layer, scale: scale, offset: CGPoint(x: -offsetX * 1.0 / scale * 0.5, y: 0.0), completion: { _ in
completion() completion()
}) })
} else {
completion()
}
let dimAlpha = 0.6 * (1.0 - transitionFraction) let dimAlpha = 0.6 * (1.0 - transitionFraction)
transition.updateAlpha(layer: self.node.transitionDimView.layer, alpha: dimAlpha) transition.updateAlpha(layer: self.node.transitionDimView.layer, alpha: dimAlpha)
@ -2549,7 +2556,7 @@ private func pipPositionForLocation(layout: ContainerViewLayout, position: CGPoi
} }
} }
var position: CameraScreen.PIPPosition = .bottomRight var position: CameraScreen.PIPPosition = .topRight
if result.x == 0.0 && result.y == 0.0 { if result.x == 0.0 && result.y == 0.0 {
position = .topLeft position = .topLeft
} else if result.x == 1.0 && result.y == 0.0 { } else if result.x == 1.0 && result.y == 0.0 {

View File

@ -271,9 +271,6 @@ private func makeEditorImageFrameComposition(context: CIContext, inputImage: CII
baseScale = entityBaseScale baseScale = entityBaseScale
} else if let baseSize = entity.baseSize { } else if let baseSize = entity.baseSize {
baseScale = baseSize.width / image.extent.width 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 var transform = CGAffineTransform.identity

View File

@ -66,6 +66,7 @@ public final class MediaEditorDraft: Codable, Equatable {
case values case values
case caption case caption
case privacy case privacy
case timestamp
} }
public let path: String public let path: String
@ -76,8 +77,9 @@ public final class MediaEditorDraft: Codable, Equatable {
public let values: MediaEditorValues public let values: MediaEditorValues
public let caption: NSAttributedString public let caption: NSAttributedString
public let privacy: MediaEditorResultPrivacy? public let privacy: MediaEditorResultPrivacy?
public let timestamp: Int32
public init(path: String, isVideo: Bool, thumbnail: UIImage, dimensions: PixelDimensions, duration: Double?, values: MediaEditorValues, caption: NSAttributedString, privacy: MediaEditorResultPrivacy?) { public init(path: String, isVideo: Bool, thumbnail: UIImage, dimensions: PixelDimensions, duration: Double?, values: MediaEditorValues, caption: NSAttributedString, privacy: MediaEditorResultPrivacy?, timestamp: Int32) {
self.path = path self.path = path
self.isVideo = isVideo self.isVideo = isVideo
self.thumbnail = thumbnail self.thumbnail = thumbnail
@ -86,6 +88,7 @@ public final class MediaEditorDraft: Codable, Equatable {
self.values = values self.values = values
self.caption = caption self.caption = caption
self.privacy = privacy self.privacy = privacy
self.timestamp = timestamp
} }
public init(from decoder: Decoder) throws { public init(from decoder: Decoder) throws {
@ -117,6 +120,8 @@ public final class MediaEditorDraft: Codable, Equatable {
} else { } else {
self.privacy = nil self.privacy = nil
} }
self.timestamp = try container.decodeIfPresent(Int32.self, forKey: .timestamp) ?? 1688909663
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
@ -145,6 +150,7 @@ public final class MediaEditorDraft: Codable, Equatable {
} else { } else {
try container.encodeNil(forKey: .privacy) try container.encodeNil(forKey: .privacy)
} }
try container.encode(self.timestamp, forKey: .timestamp)
} }
} }

View File

@ -1719,7 +1719,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
self.gradientView = UIImageView() self.gradientView = UIImageView()
self.entitiesContainerView = UIView(frame: CGRect(origin: .zero, size: storyDimensions)) self.entitiesContainerView = UIView(frame: CGRect(origin: .zero, size: storyDimensions))
self.entitiesView = DrawingEntitiesView(context: controller.context, size: storyDimensions) self.entitiesView = DrawingEntitiesView(context: controller.context, size: storyDimensions, hasBin: true)
self.entitiesView.getEntityCenterPosition = { self.entitiesView.getEntityCenterPosition = {
return CGPoint(x: storyDimensions.width / 2.0, y: storyDimensions.height / 2.0) return CGPoint(x: storyDimensions.width / 2.0, y: storyDimensions.height / 2.0)
} }
@ -2100,8 +2100,37 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
}).start() }).start()
} }
}, },
getCurrentImage: { getCurrentImage: { [weak self] in
guard let self else {
return nil return nil
}
let colorSpace = CGColorSpaceCreateDeviceRGB()
let imageSize = CGSize(width: 1080, height: 1920)
let context = DrawingContext(size: imageSize, scale: 1.0, opaque: true, colorSpace: colorSpace)
context?.withFlippedContext { context in
if let gradientImage = self.gradientView.image?.cgImage {
context.draw(gradientImage, in: CGRect(origin: .zero, size: imageSize))
}
if let image = self.mediaEditor?.resultImage, let values = self.mediaEditor?.values {
let initialScale: CGFloat
if image.size.height > image.size.width {
initialScale = max(imageSize.width / image.size.width, imageSize.height / image.size.height)
} else {
initialScale = imageSize.width / image.size.width
}
let scale = initialScale * values.cropScale
context.translateBy(x: imageSize.width / 2.0 + values.cropOffset.x, y: imageSize.height / 2.0 - values.cropOffset.y)
context.rotate(by: -values.cropRotation)
context.scaleBy(x: scale, y: scale)
if let cgImage = image.cgImage {
context.draw(cgImage, in: CGRect(x: -image.size.width / 2.0, y: -image.size.height / 2.0, width: image.size.width, height: image.size.height))
}
}
}
return context?.generateImage(colorSpace: colorSpace)
}, },
getControllerNode: { [weak self] in getControllerNode: { [weak self] in
return self return self
@ -2718,6 +2747,12 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
} }
} }
}) })
galleryController.customModalStyleOverlayTransitionFactorUpdated = { [weak self, weak galleryController] transition in
if let self, let galleryController {
let transitionFactor = galleryController.modalStyleOverlayTransitionFactor
self.updateModalTransitionFactor(transitionFactor, transition: transition)
}
}
controller.push(galleryController) controller.push(galleryController)
} }
@ -2906,36 +2941,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
existingStickerPickerInputData: self.stickerPickerInputData existingStickerPickerInputData: self.stickerPickerInputData
) )
controller.getCurrentImage = { [weak self] in controller.getCurrentImage = { [weak self] in
guard let self else { return self?.interaction?.getCurrentImage()
return nil
}
let colorSpace = CGColorSpaceCreateDeviceRGB()
let imageSize = CGSize(width: 1080, height: 1920)
let context = DrawingContext(size: imageSize, scale: 1.0, opaque: true, colorSpace: colorSpace)
context?.withFlippedContext { context in
if let gradientImage = self.gradientView.image?.cgImage {
context.draw(gradientImage, in: CGRect(origin: .zero, size: imageSize))
}
if let image = self.mediaEditor?.resultImage, let values = self.mediaEditor?.values {
let initialScale: CGFloat
if image.size.height > image.size.width {
initialScale = max(imageSize.width / image.size.width, imageSize.height / image.size.height)
} else {
initialScale = imageSize.width / image.size.width
}
let scale = initialScale * values.cropScale
context.translateBy(x: imageSize.width / 2.0 + values.cropOffset.x, y: imageSize.height / 2.0 - values.cropOffset.y)
context.rotate(by: -values.cropRotation)
context.scaleBy(x: scale, y: scale)
if let cgImage = image.cgImage {
context.draw(cgImage, in: CGRect(x: -image.size.width / 2.0, y: -image.size.height / 2.0, width: image.size.width, height: image.size.height))
}
}
}
return context?.generateImage(colorSpace: colorSpace)
} }
controller.updateVideoPlayback = { [weak self] play in controller.updateVideoPlayback = { [weak self] play in
guard let self else { guard let self else {
@ -3592,6 +3598,13 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
let caption = self.getCaption() let caption = self.getCaption()
let duration = mediaEditor.duration ?? 0.0 let duration = mediaEditor.duration ?? 0.0
var timestamp: Int32
if case let .draft(draft, _) = subject {
timestamp = draft.timestamp
} else {
timestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
}
if let resultImage = mediaEditor.resultImage { if let resultImage = mediaEditor.resultImage {
mediaEditor.seek(0.0, andPlay: false) mediaEditor.seek(0.0, andPlay: false)
makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: resultImage, dimensions: storyDimensions, values: values, time: .zero, completion: { resultImage in makeEditorImageComposition(context: self.node.ciContext, account: self.context.account, inputImage: resultImage, dimensions: storyDimensions, values: values, time: .zero, completion: { resultImage in
@ -3604,7 +3617,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) { if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) {
let path = "\(Int64.random(in: .min ... .max)).jpg" let path = "\(Int64.random(in: .min ... .max)).jpg"
if let data = image.jpegData(compressionQuality: 0.87) { if let data = image.jpegData(compressionQuality: 0.87) {
let draft = MediaEditorDraft(path: path, isVideo: false, thumbnail: thumbnailImage, dimensions: dimensions, duration: nil, values: values, caption: caption, privacy: privacy) let draft = MediaEditorDraft(path: path, isVideo: false, thumbnail: thumbnailImage, dimensions: dimensions, duration: nil, values: values, caption: caption, privacy: privacy, timestamp: timestamp)
try? data.write(to: URL(fileURLWithPath: draft.fullPath())) try? data.write(to: URL(fileURLWithPath: draft.fullPath()))
if let id { if let id {
saveStorySource(engine: self.context.engine, item: draft, id: id) saveStorySource(engine: self.context.engine, item: draft, id: id)
@ -3618,7 +3631,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
let saveVideoDraft: (String, PixelDimensions, Double) -> Void = { videoPath, dimensions, duration in let saveVideoDraft: (String, PixelDimensions, Double) -> Void = { videoPath, dimensions, duration in
if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) { if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) {
let path = "\(Int64.random(in: .min ... .max)).mp4" let path = "\(Int64.random(in: .min ... .max)).mp4"
let draft = MediaEditorDraft(path: path, isVideo: true, thumbnail: thumbnailImage, dimensions: dimensions, duration: duration, values: values, caption: caption, privacy: privacy) let draft = MediaEditorDraft(path: path, isVideo: true, thumbnail: thumbnailImage, dimensions: dimensions, duration: duration, values: values, caption: caption, privacy: privacy, timestamp: timestamp)
try? FileManager.default.moveItem(atPath: videoPath, toPath: draft.fullPath()) try? FileManager.default.moveItem(atPath: videoPath, toPath: draft.fullPath())
if let id { if let id {
saveStorySource(engine: self.context.engine, item: draft, id: id) saveStorySource(engine: self.context.engine, item: draft, id: id)
@ -3860,7 +3873,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
}) })
if case let .draft(draft, id) = subject, id == nil { if case let .draft(draft, id) = subject, id == nil {
removeStoryDraft(engine: self.context.engine, path: draft.path, delete: !draft.isVideo) removeStoryDraft(engine: self.context.engine, path: draft.path, delete: false)
} }
} else { } else {
if let image = mediaEditor.resultImage { if let image = mediaEditor.resultImage {

View File

@ -2472,7 +2472,7 @@ final class StoryItemSetContainerSendMessage {
return return
} }
if !hashtag.isEmpty { if !hashtag.isEmpty {
let searchController = component.context.sharedContext.makeHashtagSearchController(context: component.context, peer: peer.flatMap(EnginePeer.init), query: hashtag) let searchController = component.context.sharedContext.makeHashtagSearchController(context: component.context, peer: peer.flatMap(EnginePeer.init), query: hashtag, all: true)
navigationController.pushViewController(searchController) navigationController.pushViewController(searchController)
} }
})) }))

View File

@ -1720,8 +1720,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return inputPanelNode return inputPanelNode
} }
public func makeHashtagSearchController(context: AccountContext, peer: EnginePeer?, query: String) -> ViewController { public func makeHashtagSearchController(context: AccountContext, peer: EnginePeer?, query: String, all: Bool) -> ViewController {
return HashtagSearchController(context: context, peer: peer, query: query) return HashtagSearchController(context: context, peer: peer, query: query, all: all)
} }
public func makeMyStoriesController(context: AccountContext, isArchive: Bool) -> ViewController { public func makeMyStoriesController(context: AccountContext, isArchive: Bool) -> ViewController {