mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Various fixes
This commit is contained in:
parent
80079dba5a
commit
fdff55d7ea
@ -870,7 +870,7 @@ public protocol SharedAccountContext: AnyObject {
|
||||
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 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 navigateToChatController(_ params: NavigateToChatControllerParams)
|
||||
func navigateToForumChannel(context: AccountContext, peerId: EnginePeer.Id, navigationController: NavigationController)
|
||||
|
@ -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 effectNode: ShimmerEffectNode
|
||||
private let maskNode: ASImageNode
|
||||
private var currentParams: (size: CGSize, presentationData: PresentationData, key: ChatListSearchPaneKey)?
|
||||
|
||||
init(key: ChatListSearchPaneKey) {
|
||||
public init(key: ChatListSearchPaneKey) {
|
||||
self.backgroundColorNode = ASDisplayNode()
|
||||
self.effectNode = ShimmerEffectNode()
|
||||
self.maskNode = ASImageNode()
|
||||
@ -3388,7 +3388,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
|
||||
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 {
|
||||
self.currentParams = (size, presentationData, key)
|
||||
|
||||
|
@ -52,6 +52,7 @@ private func prepareForRendering(entityView: DrawingEntityView) {
|
||||
public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
|
||||
private let context: AccountContext
|
||||
private let size: CGSize
|
||||
private let hasBin: Bool
|
||||
|
||||
weak var drawingView: DrawingView?
|
||||
public weak var selectionContainerView: DrawingSelectionContainerView?
|
||||
@ -85,9 +86,10 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
|
||||
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
|
||||
public init(context: AccountContext, size: CGSize) {
|
||||
public init(context: AccountContext, size: CGSize, hasBin: Bool = false) {
|
||||
self.context = context
|
||||
self.size = size
|
||||
self.hasBin = hasBin
|
||||
|
||||
super.init(frame: CGRect(origin: .zero, size: size))
|
||||
|
||||
@ -690,67 +692,71 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
|
||||
public func handlePan(_ gestureRecognizer: UIPanGestureRecognizer) {
|
||||
let location = gestureRecognizer.location(in: self)
|
||||
if let selectedEntityView = self.selectedEntityView, let selectionView = selectedEntityView.selectionView {
|
||||
var isTrappedInBin = false
|
||||
let scale = 100.0 / selectedEntityView.bounds.size.width
|
||||
switch gestureRecognizer.state {
|
||||
case .changed:
|
||||
if self.updateBin(location: location) {
|
||||
isTrappedInBin = true
|
||||
}
|
||||
case .ended, .cancelled:
|
||||
let _ = self.updateBin(location: nil)
|
||||
if selectedEntityView.isTrappedInBin {
|
||||
selectedEntityView.layer.animateScale(from: scale, to: 0.01, duration: 0.2, removeOnCompletion: false)
|
||||
selectedEntityView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||
self.remove(uuid: selectedEntityView.entity.uuid)
|
||||
})
|
||||
self.selectEntity(nil)
|
||||
|
||||
Queue.mainQueue().after(0.3, {
|
||||
self.onInteractionUpdated(false)
|
||||
})
|
||||
return
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
let transition = Transition.easeInOut(duration: 0.2)
|
||||
if isTrappedInBin, let binView = self.bin.view {
|
||||
if !selectedEntityView.isTrappedInBin {
|
||||
let refs = [
|
||||
self.xAxisView,
|
||||
self.yAxisView,
|
||||
self.topEdgeView,
|
||||
self.leftEdgeView,
|
||||
self.rightEdgeView,
|
||||
self.bottomEdgeView
|
||||
]
|
||||
for ref in refs {
|
||||
transition.setAlpha(view: ref, alpha: 0.0)
|
||||
}
|
||||
self.edgePreviewUpdated(false)
|
||||
|
||||
selectedEntityView.isTrappedInBin = true
|
||||
transition.setAlpha(view: selectionView, alpha: 0.0)
|
||||
transition.animatePosition(view: selectionView, from: selectionView.center, to: self.convert(binView.center, to: selectionView.superview))
|
||||
transition.animateScale(view: selectionView, from: 0.0, to: -0.5, additive: true)
|
||||
|
||||
transition.setPosition(view: selectedEntityView, position: binView.center)
|
||||
|
||||
let rotation = selectedEntityView.layer.transform.decompose().rotation
|
||||
var transform = CATransform3DMakeScale(scale, scale, 1.0)
|
||||
transform = CATransform3DRotate(transform, CGFloat(rotation.z), 0.0, 0.0, 1.0)
|
||||
|
||||
transition.setTransform(view: selectedEntityView, transform: transform)
|
||||
}
|
||||
} else {
|
||||
if selectedEntityView.isTrappedInBin {
|
||||
selectedEntityView.isTrappedInBin = false
|
||||
transition.setAlpha(view: selectionView, alpha: 1.0)
|
||||
selectedEntityView.layer.animateScale(from: scale, to: selectedEntityView.entity.scale, duration: 0.13)
|
||||
}
|
||||
if !self.hasBin {
|
||||
selectionView.handlePan(gestureRecognizer)
|
||||
} else {
|
||||
var isTrappedInBin = false
|
||||
let scale = 100.0 / selectedEntityView.bounds.size.width
|
||||
switch gestureRecognizer.state {
|
||||
case .changed:
|
||||
if self.updateBin(location: location) {
|
||||
isTrappedInBin = true
|
||||
}
|
||||
case .ended, .cancelled:
|
||||
let _ = self.updateBin(location: nil)
|
||||
if selectedEntityView.isTrappedInBin {
|
||||
selectedEntityView.layer.animateScale(from: scale, to: 0.01, duration: 0.2, removeOnCompletion: false)
|
||||
selectedEntityView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||
self.remove(uuid: selectedEntityView.entity.uuid)
|
||||
})
|
||||
self.selectEntity(nil)
|
||||
|
||||
Queue.mainQueue().after(0.3, {
|
||||
self.onInteractionUpdated(false)
|
||||
})
|
||||
return
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
let transition = Transition.easeInOut(duration: 0.2)
|
||||
if isTrappedInBin, let binView = self.bin.view {
|
||||
if !selectedEntityView.isTrappedInBin {
|
||||
let refs = [
|
||||
self.xAxisView,
|
||||
self.yAxisView,
|
||||
self.topEdgeView,
|
||||
self.leftEdgeView,
|
||||
self.rightEdgeView,
|
||||
self.bottomEdgeView
|
||||
]
|
||||
for ref in refs {
|
||||
transition.setAlpha(view: ref, alpha: 0.0)
|
||||
}
|
||||
self.edgePreviewUpdated(false)
|
||||
|
||||
selectedEntityView.isTrappedInBin = true
|
||||
transition.setAlpha(view: selectionView, alpha: 0.0)
|
||||
transition.animatePosition(view: selectionView, from: selectionView.center, to: self.convert(binView.center, to: selectionView.superview))
|
||||
transition.animateScale(view: selectionView, from: 0.0, to: -0.5, additive: true)
|
||||
|
||||
transition.setPosition(view: selectedEntityView, position: binView.center)
|
||||
|
||||
let rotation = selectedEntityView.layer.transform.decompose().rotation
|
||||
var transform = CATransform3DMakeScale(scale, scale, 1.0)
|
||||
transform = CATransform3DRotate(transform, CGFloat(rotation.z), 0.0, 0.0, 1.0)
|
||||
|
||||
transition.setTransform(view: selectedEntityView, transform: transform)
|
||||
}
|
||||
} else {
|
||||
if selectedEntityView.isTrappedInBin {
|
||||
selectedEntityView.isTrappedInBin = false
|
||||
transition.setAlpha(view: selectionView, alpha: 1.0)
|
||||
selectedEntityView.layer.animateScale(from: scale, to: selectedEntityView.entity.scale, duration: 0.13)
|
||||
}
|
||||
selectionView.handlePan(gestureRecognizer)
|
||||
}
|
||||
}
|
||||
} else if gestureRecognizer.numberOfTouches == 1, let viewToSelect = self.entity(at: location) {
|
||||
self.selectEntity(viewToSelect.entity)
|
||||
|
@ -2965,7 +2965,7 @@ public final class DrawingToolsInteraction {
|
||||
private let onInteractionUpdated: (Bool) -> Void
|
||||
private let onTextEditingEnded: (Bool) -> Void
|
||||
|
||||
private let getCurrentImage: () -> UIImage?
|
||||
public let getCurrentImage: () -> UIImage?
|
||||
private let getControllerNode: () -> ASDisplayNode?
|
||||
private let present: (ViewController, PresentationContextType, Any?) -> Void
|
||||
private let addSubview: (UIView) -> Void
|
||||
|
@ -329,7 +329,7 @@ public final class DrawingStickerEntityView: DrawingEntityView {
|
||||
if size.width > 0 && 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 imageSize = self.dimensions.aspectFitted(boundingSize)
|
||||
@ -656,7 +656,47 @@ final class DrawingStickerEntititySelectionView: DrawingEntitySelectionView {
|
||||
let handleSize = CGSize(width: 9.0 / self.scale, height: 9.0 / self.scale)
|
||||
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 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 = [
|
||||
self.leftHandle,
|
||||
self.rightHandle
|
||||
@ -668,29 +708,8 @@ final class DrawingStickerEntititySelectionView: DrawingEntitySelectionView {
|
||||
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)
|
||||
|
||||
|
||||
let radius = (self.bounds.width - inset * 2.0) / 2.0
|
||||
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
|
||||
}
|
||||
self.leftHandle.position = CGPoint(x: actualInset, y: self.bounds.midY)
|
||||
self.rightHandle.position = CGPoint(x: self.bounds.maxX - actualInset, y: self.bounds.midY)
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ public final class HashtagSearchController: TelegramBaseController {
|
||||
private let context: AccountContext
|
||||
private let peer: EnginePeer?
|
||||
private let query: String
|
||||
let all: Bool
|
||||
private var transitionDisposable: Disposable?
|
||||
private let openMessageFromSearchDisposable = MetaDisposable()
|
||||
|
||||
@ -30,10 +31,11 @@ public final class HashtagSearchController: TelegramBaseController {
|
||||
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.peer = peer
|
||||
self.query = query
|
||||
self.all = all
|
||||
|
||||
self.animationCache = context.animationCache
|
||||
self.animationRenderer = context.animationRenderer
|
||||
|
@ -11,17 +11,16 @@ import ChatListSearchItemHeader
|
||||
final class HashtagSearchControllerNode: ASDisplayNode {
|
||||
private let context: AccountContext
|
||||
private weak var controller: HashtagSearchController?
|
||||
private let query: String
|
||||
|
||||
private let navigationBar: NavigationBar?
|
||||
|
||||
private let segmentedControlNode: SegmentedControlNode
|
||||
let listNode: ListView
|
||||
let shimmerNode: ChatListSearchShimmerNode
|
||||
|
||||
let chatController: ChatController?
|
||||
|
||||
|
||||
private let query: String
|
||||
|
||||
private var containerLayout: (ContainerViewLayout, CGFloat)?
|
||||
private var enqueuedTransitions: [(ChatListSearchContainerTransition, Bool)] = []
|
||||
private var hasValidLayout = false
|
||||
@ -34,6 +33,10 @@ final class HashtagSearchControllerNode: ASDisplayNode {
|
||||
|
||||
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.accessibilityPageScrolledString = { row, count in
|
||||
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(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 {
|
||||
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.addSubnode(self.listNode)
|
||||
self.listNode.isHidden = true
|
||||
// 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.segmentedControlNode.selectedIndexChanged = { [weak self] index in
|
||||
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.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
|
||||
|
||||
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
|
||||
|
@ -188,6 +188,10 @@ final class MediaPickerGridItemNode: GridItemNode {
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else if let (draft, _) = self.currentDraftState {
|
||||
let tag = Month(localTimestamp: draft.timestamp).packedValue
|
||||
self._cachedTag = tag
|
||||
return tag
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
@ -433,7 +433,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
||||
case .began:
|
||||
return .single(.pendingImage)
|
||||
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:
|
||||
return .complete()
|
||||
}
|
||||
@ -470,7 +470,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
||||
self.resultDisposable.set((self.camera.stopRecording()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
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
|
||||
@ -1075,7 +1075,7 @@ public class CameraScreen: ViewController {
|
||||
private var appliedDualCam = false
|
||||
private var cameraPosition: Camera.Position = .back
|
||||
|
||||
private var pipPosition: PIPPosition = .bottomRight
|
||||
private var pipPosition: PIPPosition = .topRight
|
||||
|
||||
fileprivate var previewBlurPromise = ValuePromise<Bool>(false)
|
||||
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.setBounds(view: self.backgroundView, bounds: CGRect(origin: .zero, size: layout.size))
|
||||
|
||||
transition.setPosition(view: self.containerView, position: CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0))
|
||||
if !self.hasGallery {
|
||||
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.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 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.containerView.layer, transform: CGAffineTransform(translationX: offsetX, y: 0.0))
|
||||
|
||||
let scale: CGFloat = max(0.8, min(1.0, 0.8 + 0.2 * transitionFraction))
|
||||
transition.updateSublayerTransformScaleAndOffset(layer: self.node.containerView.layer, scale: scale, offset: CGPoint(x: -offsetX * 1.0 / scale * 0.5, y: 0.0), completion: { _ in
|
||||
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
|
||||
completion()
|
||||
})
|
||||
} else {
|
||||
completion()
|
||||
})
|
||||
}
|
||||
|
||||
let dimAlpha = 0.6 * (1.0 - transitionFraction)
|
||||
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 {
|
||||
position = .topLeft
|
||||
} else if result.x == 1.0 && result.y == 0.0 {
|
||||
|
@ -271,9 +271,6 @@ private func makeEditorImageFrameComposition(context: CIContext, inputImage: CII
|
||||
baseScale = entityBaseScale
|
||||
} else 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
|
||||
|
@ -66,6 +66,7 @@ public final class MediaEditorDraft: Codable, Equatable {
|
||||
case values
|
||||
case caption
|
||||
case privacy
|
||||
case timestamp
|
||||
}
|
||||
|
||||
public let path: String
|
||||
@ -76,8 +77,9 @@ public final class MediaEditorDraft: Codable, Equatable {
|
||||
public let values: MediaEditorValues
|
||||
public let caption: NSAttributedString
|
||||
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.isVideo = isVideo
|
||||
self.thumbnail = thumbnail
|
||||
@ -86,6 +88,7 @@ public final class MediaEditorDraft: Codable, Equatable {
|
||||
self.values = values
|
||||
self.caption = caption
|
||||
self.privacy = privacy
|
||||
self.timestamp = timestamp
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
@ -117,6 +120,8 @@ public final class MediaEditorDraft: Codable, Equatable {
|
||||
} else {
|
||||
self.privacy = nil
|
||||
}
|
||||
|
||||
self.timestamp = try container.decodeIfPresent(Int32.self, forKey: .timestamp) ?? 1688909663
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
@ -145,6 +150,7 @@ public final class MediaEditorDraft: Codable, Equatable {
|
||||
} else {
|
||||
try container.encodeNil(forKey: .privacy)
|
||||
}
|
||||
try container.encode(self.timestamp, forKey: .timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -341,7 +341,7 @@ final class MediaEditorScreenComponent: Component {
|
||||
}
|
||||
},
|
||||
dismissTextInput: {
|
||||
|
||||
|
||||
},
|
||||
insertText: { [weak self] text in
|
||||
if let self {
|
||||
@ -1719,7 +1719,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
self.gradientView = UIImageView()
|
||||
|
||||
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 = {
|
||||
return CGPoint(x: storyDimensions.width / 2.0, y: storyDimensions.height / 2.0)
|
||||
}
|
||||
@ -2100,8 +2100,37 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
}).start()
|
||||
}
|
||||
},
|
||||
getCurrentImage: {
|
||||
return nil
|
||||
getCurrentImage: { [weak self] in
|
||||
guard let self else {
|
||||
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
|
||||
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)
|
||||
}
|
||||
|
||||
@ -2906,36 +2941,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
existingStickerPickerInputData: self.stickerPickerInputData
|
||||
)
|
||||
controller.getCurrentImage = { [weak self] in
|
||||
guard let self else {
|
||||
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)
|
||||
return self?.interaction?.getCurrentImage()
|
||||
}
|
||||
controller.updateVideoPlayback = { [weak self] play in
|
||||
guard let self else {
|
||||
@ -3592,6 +3598,13 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
let caption = self.getCaption()
|
||||
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 {
|
||||
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
|
||||
@ -3604,7 +3617,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) {
|
||||
let path = "\(Int64.random(in: .min ... .max)).jpg"
|
||||
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()))
|
||||
if let 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
|
||||
if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) {
|
||||
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())
|
||||
if let 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 {
|
||||
removeStoryDraft(engine: self.context.engine, path: draft.path, delete: !draft.isVideo)
|
||||
removeStoryDraft(engine: self.context.engine, path: draft.path, delete: false)
|
||||
}
|
||||
} else {
|
||||
if let image = mediaEditor.resultImage {
|
||||
|
@ -2472,7 +2472,7 @@ final class StoryItemSetContainerSendMessage {
|
||||
return
|
||||
}
|
||||
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)
|
||||
}
|
||||
}))
|
||||
|
@ -1720,8 +1720,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return inputPanelNode
|
||||
}
|
||||
|
||||
public func makeHashtagSearchController(context: AccountContext, peer: EnginePeer?, query: String) -> ViewController {
|
||||
return HashtagSearchController(context: context, peer: peer, query: query)
|
||||
public func makeHashtagSearchController(context: AccountContext, peer: EnginePeer?, query: String, all: Bool) -> ViewController {
|
||||
return HashtagSearchController(context: context, peer: peer, query: query, all: all)
|
||||
}
|
||||
|
||||
public func makeMyStoriesController(context: AccountContext, isArchive: Bool) -> ViewController {
|
||||
|
Loading…
x
Reference in New Issue
Block a user