mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Camera and editor improvements
This commit is contained in:
parent
197e27d448
commit
803b887a2e
@ -895,7 +895,7 @@ public protocol SharedAccountContext: AnyObject {
|
|||||||
|
|
||||||
func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController
|
func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController
|
||||||
|
|
||||||
func makeMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping () -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController
|
func makeMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController
|
||||||
|
|
||||||
func makeProxySettingsController(sharedContext: SharedAccountContext, account: UnauthorizedAccount) -> ViewController
|
func makeProxySettingsController(sharedContext: SharedAccountContext, account: UnauthorizedAccount) -> ViewController
|
||||||
|
|
||||||
|
@ -449,6 +449,10 @@ public extension CALayer {
|
|||||||
self.animate(from: from as NSNumber, to: to as NSNumber, keyPath: "bounds.origin.y", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: duration, mediaTimingFunction: mediaTimingFunction, additive: true)
|
self.animate(from: from as NSNumber, to: to as NSNumber, keyPath: "bounds.origin.y", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: duration, mediaTimingFunction: mediaTimingFunction, additive: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func animateShapeLineWidth(from: CGFloat, to: CGFloat, duration: Double, delay: Double = 0.0, timingFunction: String = CAMediaTimingFunctionName.easeInEaseOut.rawValue, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||||
|
self.animate(from: NSNumber(value: Float(from)), to: NSNumber(value: Float(to)), keyPath: "lineWidth", timingFunction: timingFunction, duration: duration, delay: delay, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: additive, completion: completion)
|
||||||
|
}
|
||||||
|
|
||||||
func animatePositionKeyframes(values: [CGPoint], duration: Double, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
|
func animatePositionKeyframes(values: [CGPoint], duration: Double, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
|
||||||
self.animateKeyframes(values: values.map { NSValue(cgPoint: $0) }, duration: duration, keyPath: "position")
|
self.animateKeyframes(values: values.map { NSValue(cgPoint: $0) }, duration: duration, keyPath: "position")
|
||||||
}
|
}
|
||||||
|
@ -94,6 +94,7 @@ swift_library(
|
|||||||
"//submodules/TelegramNotices:TelegramNotices",
|
"//submodules/TelegramNotices:TelegramNotices",
|
||||||
"//submodules/FastBlur:FastBlur",
|
"//submodules/FastBlur:FastBlur",
|
||||||
"//submodules/TelegramUI/Components/MediaEditor",
|
"//submodules/TelegramUI/Components/MediaEditor",
|
||||||
|
"//submodules/ChatPresentationInterfaceState:ChatPresentationInterfaceState",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -753,25 +753,12 @@ private final class DrawingScreenComponent: CombinedComponent {
|
|||||||
hasTrending: true,
|
hasTrending: true,
|
||||||
forceHasPremium: true
|
forceHasPremium: true
|
||||||
)
|
)
|
||||||
|
|
||||||
let maskItems = EmojiPagerContentComponent.stickerInputData(
|
|
||||||
context: context,
|
|
||||||
animationCache: context.animationCache,
|
|
||||||
animationRenderer: context.animationRenderer,
|
|
||||||
stickerNamespaces: [Namespaces.ItemCollection.CloudMaskPacks],
|
|
||||||
stickerOrderedItemListCollectionIds: [],
|
|
||||||
chatPeerId: context.account.peerId,
|
|
||||||
hasSearch: true,
|
|
||||||
hasTrending: false,
|
|
||||||
forceHasPremium: true
|
|
||||||
)
|
|
||||||
|
|
||||||
let signal = combineLatest(queue: .mainQueue(),
|
let signal = combineLatest(queue: .mainQueue(),
|
||||||
emojiItems,
|
emojiItems,
|
||||||
stickerItems,
|
stickerItems
|
||||||
maskItems
|
) |> map { emoji, stickers -> StickerPickerInputData in
|
||||||
) |> map { emoji, stickers, masks -> StickerPickerInputData in
|
return StickerPickerInputData(emoji: emoji, stickers: stickers, masks: nil)
|
||||||
return StickerPickerInputData(emoji: emoji, stickers: stickers, masks: masks)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stickerPickerInputData.set(signal)
|
stickerPickerInputData.set(signal)
|
||||||
|
@ -15,6 +15,7 @@ import FeaturedStickersScreen
|
|||||||
import TelegramNotices
|
import TelegramNotices
|
||||||
import ChatEntityKeyboardInputNode
|
import ChatEntityKeyboardInputNode
|
||||||
import ContextUI
|
import ContextUI
|
||||||
|
import ChatPresentationInterfaceState
|
||||||
|
|
||||||
public struct StickerPickerInputData: Equatable {
|
public struct StickerPickerInputData: Equatable {
|
||||||
var emoji: EmojiPagerContentComponent
|
var emoji: EmojiPagerContentComponent
|
||||||
@ -90,7 +91,7 @@ private final class StickerSelectionComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class View: UIView {
|
public final class View: UIView {
|
||||||
private let keyboardView: ComponentView<Empty>
|
fileprivate let keyboardView: ComponentView<Empty>
|
||||||
private let keyboardClippingView: UIView
|
private let keyboardClippingView: UIView
|
||||||
private let panelHostView: PagerExternalTopPanelContainer
|
private let panelHostView: PagerExternalTopPanelContainer
|
||||||
private let panelBackgroundView: BlurredBackgroundView
|
private let panelBackgroundView: BlurredBackgroundView
|
||||||
@ -99,6 +100,12 @@ private final class StickerSelectionComponent: Component {
|
|||||||
private var component: StickerSelectionComponent?
|
private var component: StickerSelectionComponent?
|
||||||
private weak var state: EmptyComponentState?
|
private weak var state: EmptyComponentState?
|
||||||
|
|
||||||
|
private var interaction: ChatEntityKeyboardInputNode.Interaction?
|
||||||
|
private var inputNodeInteraction: ChatMediaInputNodeInteraction?
|
||||||
|
private let trendingGifsPromise = Promise<ChatMediaInputGifPaneTrendingState?>(nil)
|
||||||
|
|
||||||
|
private var forceUpdate = false
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
self.keyboardView = ComponentView<Empty>()
|
self.keyboardView = ComponentView<Empty>()
|
||||||
self.keyboardClippingView = UIView()
|
self.keyboardClippingView = UIView()
|
||||||
@ -112,6 +119,71 @@ private final class StickerSelectionComponent: Component {
|
|||||||
self.addSubview(self.panelBackgroundView)
|
self.addSubview(self.panelBackgroundView)
|
||||||
self.addSubview(self.panelSeparatorView)
|
self.addSubview(self.panelSeparatorView)
|
||||||
self.addSubview(self.panelHostView)
|
self.addSubview(self.panelHostView)
|
||||||
|
|
||||||
|
self.interaction = ChatEntityKeyboardInputNode.Interaction(
|
||||||
|
sendSticker: { file, silent, schedule, query, clearInput, sourceView, sourceRect, sourceLayer, _ in
|
||||||
|
let _ = file
|
||||||
|
let _ = silent
|
||||||
|
let _ = schedule
|
||||||
|
let _ = query
|
||||||
|
let _ = clearInput
|
||||||
|
let _ = sourceView
|
||||||
|
let _ = sourceRect
|
||||||
|
let _ = sourceLayer
|
||||||
|
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
sendEmoji: { _, _, _ in
|
||||||
|
},
|
||||||
|
sendGif: { _, _, _, _, _ in
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
sendBotContextResultAsGif: { _, _, _, _, _, _ in
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
updateChoosingSticker: { _ in },
|
||||||
|
switchToTextInput: {},
|
||||||
|
dismissTextInput: {},
|
||||||
|
insertText: { _ in
|
||||||
|
},
|
||||||
|
backwardsDeleteText: {},
|
||||||
|
presentController: { c, a in
|
||||||
|
let _ = c
|
||||||
|
let _ = a
|
||||||
|
},
|
||||||
|
presentGlobalOverlayController: { c, a in
|
||||||
|
let _ = c
|
||||||
|
let _ = a
|
||||||
|
},
|
||||||
|
getNavigationController: {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
requestLayout: { transition in
|
||||||
|
let _ = transition
|
||||||
|
})
|
||||||
|
|
||||||
|
self.inputNodeInteraction = ChatMediaInputNodeInteraction(
|
||||||
|
navigateToCollectionId: { _ in
|
||||||
|
},
|
||||||
|
navigateBackToStickers: {
|
||||||
|
},
|
||||||
|
setGifMode: { _ in
|
||||||
|
},
|
||||||
|
openSettings: {
|
||||||
|
},
|
||||||
|
openTrending: { _ in
|
||||||
|
},
|
||||||
|
dismissTrendingPacks: { _ in
|
||||||
|
},
|
||||||
|
toggleSearch: { _, _, _ in
|
||||||
|
},
|
||||||
|
openPeerSpecificSettings: {
|
||||||
|
},
|
||||||
|
dismissPeerSpecificSettings: {
|
||||||
|
},
|
||||||
|
clearRecentlyUsedStickers: {
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
@ -132,7 +204,19 @@ private final class StickerSelectionComponent: Component {
|
|||||||
|
|
||||||
let topPanelHeight: CGFloat = 42.0
|
let topPanelHeight: CGFloat = 42.0
|
||||||
|
|
||||||
//let context = component.context
|
let context = component.context
|
||||||
|
let stickerPeekBehavior = EmojiContentPeekBehaviorImpl(
|
||||||
|
context: context,
|
||||||
|
interaction: nil,
|
||||||
|
chatPeerId: nil,
|
||||||
|
present: { c, a in
|
||||||
|
let _ = c
|
||||||
|
let _ = a
|
||||||
|
// controller?.presentInGlobalOverlay(c, with: a)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
let trendingGifsPromise = self.trendingGifsPromise
|
||||||
let keyboardSize = self.keyboardView.update(
|
let keyboardSize = self.keyboardView.update(
|
||||||
transition: transition.withUserData(EmojiPagerContentComponent.SynchronousLoadBehavior(isDisabled: true)),
|
transition: transition.withUserData(EmojiPagerContentComponent.SynchronousLoadBehavior(isDisabled: true)),
|
||||||
component: AnyComponent(EntityKeyboardComponent(
|
component: AnyComponent(EntityKeyboardComponent(
|
||||||
@ -152,49 +236,52 @@ private final class StickerSelectionComponent: Component {
|
|||||||
externalBottomPanelContainer: nil,
|
externalBottomPanelContainer: nil,
|
||||||
displayTopPanelBackground: .blur,
|
displayTopPanelBackground: .blur,
|
||||||
topPanelExtensionUpdated: { _, _ in },
|
topPanelExtensionUpdated: { _, _ in },
|
||||||
hideInputUpdated: { _, _, _ in },
|
hideInputUpdated: { [weak self] _, _, transition in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.forceUpdate = true
|
||||||
|
self.state?.updated(transition: transition)
|
||||||
|
},
|
||||||
hideTopPanelUpdated: { _, _ in },
|
hideTopPanelUpdated: { _, _ in },
|
||||||
switchToTextInput: {},
|
switchToTextInput: {},
|
||||||
switchToGifSubject: { _ in },
|
switchToGifSubject: { _ in },
|
||||||
reorderItems: { _, _ in },
|
reorderItems: { _, _ in },
|
||||||
makeSearchContainerNode: { _ in
|
makeSearchContainerNode: { [weak self] content in
|
||||||
return nil
|
guard let self, let interaction = self.interaction, let inputNodeInteraction = self.inputNodeInteraction else {
|
||||||
},
|
return nil
|
||||||
// makeSearchContainerNode: { [weak self, weak controllerInteraction] content in
|
}
|
||||||
// guard let self, let controllerInteraction = controllerInteraction else {
|
|
||||||
// return nil
|
let mappedMode: ChatMediaInputSearchMode
|
||||||
// }
|
switch content {
|
||||||
//
|
case .stickers:
|
||||||
// let mappedMode: ChatMediaInputSearchMode
|
mappedMode = .sticker
|
||||||
// switch content {
|
case .gifs:
|
||||||
// case .stickers:
|
mappedMode = .gif
|
||||||
// mappedMode = .sticker
|
}
|
||||||
// case .gifs:
|
|
||||||
// mappedMode = .sticker
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||||
// }
|
let searchContainerNode = PaneSearchContainerNode(
|
||||||
//
|
context: context,
|
||||||
// let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
theme: presentationData.theme,
|
||||||
// let searchContainerNode = PaneSearchContainerNode(
|
strings: presentationData.strings,
|
||||||
// context: context,
|
interaction: interaction,
|
||||||
// theme: presentationData.theme,
|
inputNodeInteraction: inputNodeInteraction,
|
||||||
// strings: presentationData.strings,
|
mode: mappedMode,
|
||||||
// controllerInteraction: controllerInteraction,
|
trendingGifsPromise: trendingGifsPromise,
|
||||||
// inputNodeInteraction: inputNodeInteraction,
|
cancel: {
|
||||||
// mode: mappedMode,
|
},
|
||||||
// trendingGifsPromise: Promise(nil),
|
peekBehavior: stickerPeekBehavior
|
||||||
// cancel: {
|
)
|
||||||
// },
|
|
||||||
// peekBehavior: self.emojiInputInteraction?.peekBehavior
|
|
||||||
// )
|
|
||||||
// searchContainerNode.openGifContextMenu = { [weak self] item, sourceNode, sourceRect, gesture, isSaved in
|
// searchContainerNode.openGifContextMenu = { [weak self] item, sourceNode, sourceRect, gesture, isSaved in
|
||||||
// guard let self else {
|
// guard let self else {
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
// self.openGifContextMenu(file: item.file, contextResult: item.contextResult, sourceView: sourceNode.view, sourceRect: sourceRect, gesture: gesture, isSaved: isSaved)
|
// self.openGifContextMenu(file: item.file, contextResult: item.contextResult, sourceView: sourceNode.view, sourceRect: sourceRect, gesture: gesture, isSaved: isSaved)
|
||||||
// }
|
// }
|
||||||
//
|
|
||||||
// return searchContainerNode
|
return searchContainerNode
|
||||||
// },
|
},
|
||||||
contentIdUpdated: { _ in },
|
contentIdUpdated: { _ in },
|
||||||
deviceMetrics: component.deviceMetrics,
|
deviceMetrics: component.deviceMetrics,
|
||||||
hiddenInputHeight: 0.0,
|
hiddenInputHeight: 0.0,
|
||||||
@ -204,8 +291,10 @@ private final class StickerSelectionComponent: Component {
|
|||||||
clipContentToTopPanel: false
|
clipContentToTopPanel: false
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
|
forceUpdate: self.forceUpdate,
|
||||||
containerSize: availableSize
|
containerSize: availableSize
|
||||||
)
|
)
|
||||||
|
self.forceUpdate = false
|
||||||
if let keyboardComponentView = self.keyboardView.view {
|
if let keyboardComponentView = self.keyboardView.view {
|
||||||
if keyboardComponentView.superview == nil {
|
if keyboardComponentView.superview == nil {
|
||||||
self.keyboardClippingView.addSubview(keyboardComponentView)
|
self.keyboardClippingView.addSubview(keyboardComponentView)
|
||||||
@ -818,7 +907,12 @@ public class StickerPickerScreen: ViewController {
|
|||||||
deleteBackwards: nil,
|
deleteBackwards: nil,
|
||||||
openStickerSettings: nil,
|
openStickerSettings: nil,
|
||||||
openFeatured: nil,
|
openFeatured: nil,
|
||||||
openSearch: {
|
openSearch: { [weak self] in
|
||||||
|
if let self, let componentView = self.hostView.componentView as? StickerSelectionComponent.View {
|
||||||
|
if let pagerView = componentView.keyboardView.view as? EntityKeyboardComponent.View {
|
||||||
|
pagerView.openSearch()
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
addGroupAction: { [weak self] groupId, isPremiumLocked, _ in
|
addGroupAction: { [weak self] groupId, isPremiumLocked, _ in
|
||||||
guard let strongSelf = self, let controller = strongSelf.controller, let collectionId = groupId.base as? ItemCollectionId else {
|
guard let strongSelf = self, let controller = strongSelf.controller, let collectionId = groupId.base as? ItemCollectionId else {
|
||||||
|
@ -235,6 +235,7 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
self.gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
self.gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
self.typeIconNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
self.typeIconNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
self.durationNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
self.durationNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
|
self.draftNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
if animateSpoilerNode {
|
if animateSpoilerNode {
|
||||||
self.spoilerNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
self.spoilerNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
}
|
}
|
||||||
@ -254,7 +255,9 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
|
|
||||||
self.backgroundColor = theme.list.mediaPlaceholderColor
|
self.backgroundColor = theme.list.mediaPlaceholderColor
|
||||||
|
|
||||||
if self.currentDraftState == nil || self.currentDraftState?.0.path != draft.path || self.currentDraftState!.1 != index {
|
if self.currentDraftState == nil || self.currentDraftState?.0.path != draft.path || self.currentDraftState!.1 != index || self.currentState != nil {
|
||||||
|
self.currentState = nil
|
||||||
|
|
||||||
let imageSignal: Signal<UIImage?, NoError> = .single(draft.thumbnail)
|
let imageSignal: Signal<UIImage?, NoError> = .single(draft.thumbnail)
|
||||||
self.imageNode.setSignal(imageSignal)
|
self.imageNode.setSignal(imageSignal)
|
||||||
|
|
||||||
@ -262,10 +265,17 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
|
|
||||||
if self.draftNode.supernode == nil {
|
if self.draftNode.supernode == nil {
|
||||||
self.draftNode.attributedText = NSAttributedString(string: "Draft", font: Font.semibold(12.0), textColor: .white)
|
self.draftNode.attributedText = NSAttributedString(string: "Draft", font: Font.semibold(12.0), textColor: .white)
|
||||||
|
|
||||||
self.addSubnode(self.draftNode)
|
self.addSubnode(self.draftNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.typeIconNode.supernode != nil {
|
||||||
|
self.typeIconNode.removeFromSupernode()
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.durationNode.supernode != nil {
|
||||||
|
self.durationNode.removeFromSupernode()
|
||||||
|
}
|
||||||
|
|
||||||
self.setNeedsLayout()
|
self.setNeedsLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,10 +296,9 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
if self.backgroundNode.supernode == nil {
|
if self.backgroundNode.supernode == nil {
|
||||||
self.insertSubnode(self.backgroundNode, at: 0)
|
self.insertSubnode(self.backgroundNode, at: 0)
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
if self.draftNode.supernode != nil {
|
if self.draftNode.supernode != nil {
|
||||||
self.draftNode.removeFromSupernode()
|
self.draftNode.removeFromSupernode()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.currentMediaState == nil || self.currentMediaState!.0.uniqueIdentifier != media.identifier || self.currentMediaState!.1 != index {
|
if self.currentMediaState == nil || self.currentMediaState!.0.uniqueIdentifier != media.identifier || self.currentMediaState!.1 != index {
|
||||||
@ -319,13 +328,14 @@ final class MediaPickerGridItemNode: GridItemNode {
|
|||||||
if self.backgroundNode.supernode == nil {
|
if self.backgroundNode.supernode == nil {
|
||||||
self.insertSubnode(self.backgroundNode, at: 0)
|
self.insertSubnode(self.backgroundNode, at: 0)
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
if self.draftNode.supernode != nil {
|
if self.draftNode.supernode != nil {
|
||||||
self.draftNode.removeFromSupernode()
|
self.draftNode.removeFromSupernode()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.currentState == nil || self.currentState!.0 !== fetchResult || self.currentState!.1 != index {
|
if self.currentState == nil || self.currentState!.0 !== fetchResult || self.currentState!.1 != index || self.currentDraftState != nil {
|
||||||
|
self.currentDraftState = nil
|
||||||
|
|
||||||
self.backgroundNode.image = nil
|
self.backgroundNode.image = nil
|
||||||
let editingContext = interaction.editingState
|
let editingContext = interaction.editingState
|
||||||
let asset = fetchResult.object(at: index)
|
let asset = fetchResult.object(at: index)
|
||||||
|
@ -985,6 +985,17 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fileprivate func defaultTransitionView() -> UIView? {
|
||||||
|
var transitionNode: MediaPickerGridItemNode?
|
||||||
|
if let itemNode = self.gridNode.itemNodeAtPoint(.zero) {
|
||||||
|
if let itemNode = itemNode as? MediaPickerGridItemNode {
|
||||||
|
transitionNode = itemNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let transitionView = transitionNode?.transitionView(snapshot: false)
|
||||||
|
return transitionView
|
||||||
|
}
|
||||||
|
|
||||||
fileprivate func transitionView(for identifier: String, snapshot: Bool = true, hideSource: Bool = false) -> UIView? {
|
fileprivate func transitionView(for identifier: String, snapshot: Bool = true, hideSource: Bool = false) -> UIView? {
|
||||||
if let selectionNode = self.selectionNode, selectionNode.alpha > 0.0 {
|
if let selectionNode = self.selectionNode, selectionNode.alpha > 0.0 {
|
||||||
return selectionNode.transitionView(for: identifier, hideSource: hideSource)
|
return selectionNode.transitionView(for: identifier, hideSource: hideSource)
|
||||||
@ -1952,6 +1963,10 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fileprivate func defaultTransitionView() -> UIView? {
|
||||||
|
return self.controllerNode.defaultTransitionView()
|
||||||
|
}
|
||||||
|
|
||||||
fileprivate func transitionView(for identifier: String, snapshot: Bool, hideSource: Bool = false) -> UIView? {
|
fileprivate func transitionView(for identifier: String, snapshot: Bool, hideSource: Bool = false) -> UIView? {
|
||||||
return self.controllerNode.transitionView(for: identifier, snapshot: snapshot, hideSource: hideSource)
|
return self.controllerNode.transitionView(for: identifier, snapshot: snapshot, hideSource: hideSource)
|
||||||
}
|
}
|
||||||
@ -2172,7 +2187,7 @@ public func wallpaperMediaPickerController(
|
|||||||
public func storyMediaPickerController(
|
public func storyMediaPickerController(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
getSourceRect: @escaping () -> CGRect,
|
getSourceRect: @escaping () -> CGRect,
|
||||||
completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping () -> (UIView, CGRect)?, @escaping () -> Void) -> Void,
|
completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void,
|
||||||
dismissed: @escaping () -> Void
|
dismissed: @escaping () -> Void
|
||||||
) -> ViewController {
|
) -> ViewController {
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkColorPresentationTheme)
|
let presentationData = context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||||
@ -2188,9 +2203,18 @@ public func storyMediaPickerController(
|
|||||||
if let result = result as? MediaEditorDraft {
|
if let result = result as? MediaEditorDraft {
|
||||||
controller.updateHiddenMediaId(result.path)
|
controller.updateHiddenMediaId(result.path)
|
||||||
if let transitionView = controller.transitionView(for: result.path, snapshot: false) {
|
if let transitionView = controller.transitionView(for: result.path, snapshot: false) {
|
||||||
let transitionOut: () -> (UIView, CGRect)? = {
|
let transitionOut: (Bool?) -> (UIView, CGRect)? = { isNew in
|
||||||
if let transitionView = controller.transitionView(for: result.path, snapshot: false) {
|
if let isNew {
|
||||||
return (transitionView, transitionView.bounds)
|
controller.updateHiddenMediaId(result.path)
|
||||||
|
if isNew {
|
||||||
|
if let transitionView = controller.defaultTransitionView() {
|
||||||
|
return (transitionView, transitionView.bounds)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let transitionView = controller.transitionView(for: result.path, snapshot: false) {
|
||||||
|
return (transitionView, transitionView.bounds)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -2201,9 +2225,16 @@ public func storyMediaPickerController(
|
|||||||
} else if let result = result as? PHAsset {
|
} else if let result = result as? PHAsset {
|
||||||
controller.updateHiddenMediaId(result.localIdentifier)
|
controller.updateHiddenMediaId(result.localIdentifier)
|
||||||
if let transitionView = controller.transitionView(for: result.localIdentifier, snapshot: false) {
|
if let transitionView = controller.transitionView(for: result.localIdentifier, snapshot: false) {
|
||||||
let transitionOut: () -> (UIView, CGRect)? = {
|
let transitionOut: (Bool?) -> (UIView, CGRect)? = { isNew in
|
||||||
if let transitionView = controller.transitionView(for: result.localIdentifier, snapshot: false) {
|
if let isNew {
|
||||||
return (transitionView, transitionView.bounds)
|
if isNew {
|
||||||
|
controller.updateHiddenMediaId(nil)
|
||||||
|
if let transitionView = controller.defaultTransitionView() {
|
||||||
|
return (transitionView, transitionView.bounds)
|
||||||
|
}
|
||||||
|
} else if let transitionView = controller.transitionView(for: result.localIdentifier, snapshot: false) {
|
||||||
|
return (transitionView, transitionView.bounds)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -282,7 +282,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
private var genericReactionEffectDisposable: Disposable?
|
private var genericReactionEffectDisposable: Disposable?
|
||||||
private var genericReactionEffect: String?
|
private var genericReactionEffect: String?
|
||||||
|
|
||||||
private var isReactionSearchActive: Bool = false
|
public var isReactionSearchActive: Bool = false
|
||||||
|
|
||||||
public static func randomGenericReactionEffect(context: AccountContext) -> Signal<String?, NoError> {
|
public static func randomGenericReactionEffect(context: AccountContext) -> Signal<String?, NoError> {
|
||||||
return context.engine.stickers.loadedStickerPack(reference: .emojiGenericAnimations, forceActualized: false)
|
return context.engine.stickers.loadedStickerPack(reference: .emojiGenericAnimations, forceActualized: false)
|
||||||
@ -2016,17 +2016,15 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
strongSelf.hapticFeedback?.tap()
|
strongSelf.hapticFeedback?.tap()
|
||||||
|
|
||||||
guard let targetView = targetView as? ReactionIconView else {
|
if let targetView = targetView as? ReactionIconView {
|
||||||
return
|
if switchToInlineImmediately {
|
||||||
}
|
targetView.updateIsAnimationHidden(isAnimationHidden: false, transition: .immediate)
|
||||||
|
itemNode.isHidden = true
|
||||||
if switchToInlineImmediately {
|
} else {
|
||||||
targetView.updateIsAnimationHidden(isAnimationHidden: false, transition: .immediate)
|
targetView.updateIsAnimationHidden(isAnimationHidden: true, transition: .immediate)
|
||||||
itemNode.isHidden = true
|
targetView.addSubnode(itemNode)
|
||||||
} else {
|
itemNode.frame = selfTargetBounds
|
||||||
targetView.updateIsAnimationHidden(isAnimationHidden: true, transition: .immediate)
|
}
|
||||||
targetView.addSubnode(itemNode)
|
|
||||||
itemNode.frame = selfTargetBounds
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if switchToInlineImmediately {
|
if switchToInlineImmediately {
|
||||||
@ -2071,7 +2069,11 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
targetView: targetView,
|
targetView: targetView,
|
||||||
addStandaloneReactionAnimation: nil,
|
addStandaloneReactionAnimation: nil,
|
||||||
completion: { [weak standaloneReactionAnimation] in
|
completion: { [weak standaloneReactionAnimation] in
|
||||||
standaloneReactionAnimation?.removeFromSupernode()
|
if let _ = standaloneReactionAnimation?.supernode {
|
||||||
|
standaloneReactionAnimation?.removeFromSupernode()
|
||||||
|
} else {
|
||||||
|
standaloneReactionAnimation?.view.removeFromSuperview()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -2085,7 +2087,11 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
targetView.isHidden = false
|
targetView.isHidden = false
|
||||||
if let targetView = targetView as? ReactionIconView {
|
if let targetView = targetView as? ReactionIconView {
|
||||||
targetView.updateIsAnimationHidden(isAnimationHidden: false, transition: .immediate)
|
targetView.updateIsAnimationHidden(isAnimationHidden: false, transition: .immediate)
|
||||||
itemNode.removeFromSupernode()
|
if let _ = itemNode.supernode {
|
||||||
|
itemNode.removeFromSupernode()
|
||||||
|
} else {
|
||||||
|
itemNode.view.removeFromSuperview()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mainAnimationCompleted = true
|
mainAnimationCompleted = true
|
||||||
|
@ -4,7 +4,7 @@ import Postbox
|
|||||||
import TelegramApi
|
import TelegramApi
|
||||||
|
|
||||||
public enum EngineOutgoingMessageContent {
|
public enum EngineOutgoingMessageContent {
|
||||||
case text(String)
|
case text(String, [MessageTextEntity])
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class StoryPreloadInfo {
|
public final class StoryPreloadInfo {
|
||||||
@ -222,10 +222,14 @@ public extension TelegramEngine {
|
|||||||
content: EngineOutgoingMessageContent
|
content: EngineOutgoingMessageContent
|
||||||
) {
|
) {
|
||||||
switch content {
|
switch content {
|
||||||
case let .text(text):
|
case let .text(text, entities):
|
||||||
|
var attributes: [MessageAttribute] = []
|
||||||
|
if !entities.isEmpty {
|
||||||
|
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
||||||
|
}
|
||||||
let message: EnqueueMessage = .message(
|
let message: EnqueueMessage = .message(
|
||||||
text: text,
|
text: text,
|
||||||
attributes: [],
|
attributes: attributes,
|
||||||
inlineStickers: [:],
|
inlineStickers: [:],
|
||||||
mediaReference: nil,
|
mediaReference: nil,
|
||||||
replyToMessageId: replyToMessageId,
|
replyToMessageId: replyToMessageId,
|
||||||
|
@ -451,7 +451,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
.disappear(.default(scale: true))
|
.disappear(.default(scale: true))
|
||||||
)
|
)
|
||||||
|
|
||||||
if #available(iOS 13.0, *), !isTablet && !"".isEmpty {
|
if #available(iOS 13.0, *), !isTablet {
|
||||||
let dualButton = dualButton.update(
|
let dualButton = dualButton.update(
|
||||||
component: CameraButton(
|
component: CameraButton(
|
||||||
content: AnyComponentWithIdentity(
|
content: AnyComponentWithIdentity(
|
||||||
@ -1431,7 +1431,7 @@ public class CameraScreen: ViewController {
|
|||||||
|
|
||||||
let parentFrame = self.view.convert(self.bounds, to: nil)
|
let parentFrame = self.view.convert(self.bounds, to: nil)
|
||||||
let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0)
|
let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0)
|
||||||
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 25.0), size: CGSize())
|
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 4.0), size: CGSize())
|
||||||
|
|
||||||
let controller = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: "Draft Saved", location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _ in
|
let controller = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: "Draft Saved", location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _ in
|
||||||
return .ignore
|
return .ignore
|
||||||
@ -1682,13 +1682,13 @@ public class CameraScreen: ViewController {
|
|||||||
public weak var sourceView: UIView?
|
public weak var sourceView: UIView?
|
||||||
public let sourceRect: CGRect
|
public let sourceRect: CGRect
|
||||||
public let sourceImage: UIImage?
|
public let sourceImage: UIImage?
|
||||||
public let transitionOut: () -> (UIView, CGRect)?
|
public let transitionOut: (Bool?) -> (UIView, CGRect)?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
sourceView: UIView,
|
sourceView: UIView,
|
||||||
sourceRect: CGRect,
|
sourceRect: CGRect,
|
||||||
sourceImage: UIImage?,
|
sourceImage: UIImage?,
|
||||||
transitionOut: @escaping () -> (UIView, CGRect)?
|
transitionOut: @escaping (Bool?) -> (UIView, CGRect)?
|
||||||
) {
|
) {
|
||||||
self.sourceView = sourceView
|
self.sourceView = sourceView
|
||||||
self.sourceRect = sourceRect
|
self.sourceRect = sourceRect
|
||||||
@ -1775,10 +1775,17 @@ public class CameraScreen: ViewController {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
didStopCameraCapture = true
|
didStopCameraCapture = true
|
||||||
|
|
||||||
self.node.pauseCameraCapture()
|
self.node.pauseCameraCapture()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let resumeCameraCapture = { [weak self] in
|
||||||
|
guard didStopCameraCapture, let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
didStopCameraCapture = false
|
||||||
|
self.node.resumeCameraCapture()
|
||||||
|
}
|
||||||
|
|
||||||
let controller: ViewController
|
let controller: ViewController
|
||||||
if let current = self.galleryController {
|
if let current = self.galleryController {
|
||||||
controller = current
|
controller = current
|
||||||
@ -1809,10 +1816,8 @@ public class CameraScreen: ViewController {
|
|||||||
self.completion(.single(.draft(draft)), resultTransition, dismissed)
|
self.completion(.single(.draft(draft)), resultTransition, dismissed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, dismissed: { [weak self] in
|
}, dismissed: {
|
||||||
if let self {
|
resumeCameraCapture()
|
||||||
self.node.resumeCameraCapture()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
self.galleryController = controller
|
self.galleryController = controller
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,8 @@ public final class PaneSearchContainerNode: ASDisplayNode, EntitySearchContainer
|
|||||||
|
|
||||||
public var openGifContextMenu: ((MultiplexedVideoNodeFile, ASDisplayNode, CGRect, ContextGesture, Bool) -> Void)?
|
public var openGifContextMenu: ((MultiplexedVideoNodeFile, ASDisplayNode, CGRect, ContextGesture, Bool) -> Void)?
|
||||||
|
|
||||||
|
public var addOnSupernode: Bool = true
|
||||||
|
|
||||||
public var ready: Signal<Void, NoError> {
|
public var ready: Signal<Void, NoError> {
|
||||||
return self.contentNode.ready
|
return self.contentNode.ready
|
||||||
}
|
}
|
||||||
|
@ -1718,6 +1718,7 @@ public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate {
|
|||||||
let textFieldFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textFrame.minX, height: backgroundFrame.height))
|
let textFieldFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textFrame.minX, height: backgroundFrame.height))
|
||||||
|
|
||||||
let textField = EmojiSearchTextField(frame: textFieldFrame)
|
let textField = EmojiSearchTextField(frame: textFieldFrame)
|
||||||
|
textField.keyboardAppearance = params.theme.rootController.keyboardColor.keyboardAppearance
|
||||||
textField.autocorrectionType = .no
|
textField.autocorrectionType = .no
|
||||||
textField.returnKeyType = .search
|
textField.returnKeyType = .search
|
||||||
self.textField = textField
|
self.textField = textField
|
||||||
|
@ -57,6 +57,8 @@ public final class EmojiSearchContent: ASDisplayNode, EntitySearchContainerNode
|
|||||||
|
|
||||||
public var onCancel: (() -> Void)?
|
public var onCancel: (() -> Void)?
|
||||||
|
|
||||||
|
public var addOnSupernode: Bool = false
|
||||||
|
|
||||||
private let emojiSearchDisposable = MetaDisposable()
|
private let emojiSearchDisposable = MetaDisposable()
|
||||||
private let emojiSearchState = Promise<EmojiSearchState>(EmojiSearchState(result: nil, isSearching: false))
|
private let emojiSearchState = Promise<EmojiSearchState>(EmojiSearchState(result: nil, isSearching: false))
|
||||||
private var emojiSearchStateValue = EmojiSearchState(result: nil, isSearching: false) {
|
private var emojiSearchStateValue = EmojiSearchState(result: nil, isSearching: false) {
|
||||||
|
@ -803,7 +803,7 @@ public final class EntityKeyboardComponent: Component {
|
|||||||
searchView = ComponentHostView<EntitySearchContentEnvironment>()
|
searchView = ComponentHostView<EntitySearchContentEnvironment>()
|
||||||
self.searchView = searchView
|
self.searchView = searchView
|
||||||
self.addSubview(searchView)
|
self.addSubview(searchView)
|
||||||
|
|
||||||
animateIn = true
|
animateIn = true
|
||||||
component.topPanelExtensionUpdated(0.0, transition)
|
component.topPanelExtensionUpdated(0.0, transition)
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@ import AsyncDisplayKit
|
|||||||
import ComponentDisplayAdapters
|
import ComponentDisplayAdapters
|
||||||
|
|
||||||
public protocol EntitySearchContainerNode: ASDisplayNode {
|
public protocol EntitySearchContainerNode: ASDisplayNode {
|
||||||
|
var addOnSupernode: Bool { get set }
|
||||||
|
|
||||||
var onCancel: (() -> Void)? { get set }
|
var onCancel: (() -> Void)? { get set }
|
||||||
|
|
||||||
func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, inputHeight: CGFloat, deviceMetrics: DeviceMetrics, transition: ContainedViewLayoutTransition)
|
func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, inputHeight: CGFloat, deviceMetrics: DeviceMetrics, transition: ContainedViewLayoutTransition)
|
||||||
|
@ -288,7 +288,7 @@ public final class MediaEditor {
|
|||||||
let colors = mediaEditorGetGradientColors(from: image)
|
let colors = mediaEditorGetGradientColors(from: image)
|
||||||
textureSource = .single((ImageTextureSource(image: image, renderTarget: renderTarget), image, nil, colors.0, colors.1))
|
textureSource = .single((ImageTextureSource(image: image, renderTarget: renderTarget), image, nil, colors.0, colors.1))
|
||||||
case let .draft(draft):
|
case let .draft(draft):
|
||||||
guard let image = UIImage(contentsOfFile: draft.path) else {
|
guard let image = UIImage(contentsOfFile: draft.fullPath()) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let colors: (UIColor, UIColor)
|
let colors: (UIColor, UIColor)
|
||||||
@ -607,6 +607,11 @@ public final class MediaEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func requestRenderFrame() {
|
||||||
|
self.renderer.willRenderFrame()
|
||||||
|
self.renderer.renderFrame()
|
||||||
|
}
|
||||||
|
|
||||||
private func maybeGeneratePersonSegmentation(_ image: UIImage?) {
|
private func maybeGeneratePersonSegmentation(_ image: UIImage?) {
|
||||||
if #available(iOS 15.0, *), let cgImage = image?.cgImage {
|
if #available(iOS 15.0, *), let cgImage = image?.cgImage {
|
||||||
let faceRequest = VNDetectFaceRectanglesRequest { [weak self] request, _ in
|
let faceRequest = VNDetectFaceRectanglesRequest { [weak self] request, _ in
|
||||||
|
@ -5,10 +5,52 @@ import TelegramCore
|
|||||||
import TelegramUIPreferences
|
import TelegramUIPreferences
|
||||||
import PersistentStringHash
|
import PersistentStringHash
|
||||||
import Postbox
|
import Postbox
|
||||||
|
import AccountContext
|
||||||
|
|
||||||
public enum MediaEditorResultPrivacy: Equatable {
|
public enum MediaEditorResultPrivacy: Codable, Equatable {
|
||||||
case story(privacy: EngineStoryPrivacy, timeout: Int, archive: Bool)
|
case story(privacy: EngineStoryPrivacy, timeout: Int, archive: Bool)
|
||||||
case message(peers: [EnginePeer.Id], timeout: Int?)
|
case message(peers: [EnginePeer.Id], timeout: Int?)
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case type
|
||||||
|
case privacy
|
||||||
|
case peers
|
||||||
|
case timeout
|
||||||
|
case archive
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(from decoder: Decoder) throws {
|
||||||
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
|
if let privacy = try container.decodeIfPresent(EngineStoryPrivacy.self, forKey: .privacy) {
|
||||||
|
let timeout = try container.decode(Int32.self, forKey: .timeout)
|
||||||
|
let archive = try container.decode(Bool.self, forKey: .archive)
|
||||||
|
self = .story(privacy: privacy, timeout: Int(timeout), archive: archive)
|
||||||
|
} else if let peers = try container.decodeIfPresent([EnginePeer.Id].self, forKey: .peers) {
|
||||||
|
let timeout = try container.decodeIfPresent(Int32.self, forKey: .timeout)
|
||||||
|
self = .message(peers: peers, timeout: timeout.flatMap { Int($0) })
|
||||||
|
} else {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(to encoder: Encoder) throws {
|
||||||
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
|
switch self {
|
||||||
|
case let .story(privacy, timeout, archive):
|
||||||
|
try container.encode(privacy, forKey: .privacy)
|
||||||
|
try container.encode(Int32(timeout), forKey: .timeout)
|
||||||
|
try container.encode(archive, forKey: .archive)
|
||||||
|
case let .message(peers, timeout):
|
||||||
|
try container.encode(peers, forKey: .peers)
|
||||||
|
if let timeout {
|
||||||
|
try container.encode(Int32(timeout), forKey: .timeout)
|
||||||
|
} else {
|
||||||
|
try container.encodeNil(forKey: .timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class MediaEditorDraft: Codable, Equatable {
|
public final class MediaEditorDraft: Codable, Equatable {
|
||||||
@ -66,8 +108,9 @@ public final class MediaEditorDraft: Codable, Equatable {
|
|||||||
} else {
|
} else {
|
||||||
fatalError()
|
fatalError()
|
||||||
}
|
}
|
||||||
self.caption = NSAttributedString()
|
self.caption = ((try? container.decode(ChatTextInputStateText.self, forKey: .caption)) ?? ChatTextInputStateText()).attributedText()
|
||||||
self.privacy = nil
|
self.privacy = nil
|
||||||
|
//self.privacy = try container.decode(MediaEditorResultPrivacy.self, forKey: .values)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
@ -85,6 +128,10 @@ public final class MediaEditorDraft: Codable, Equatable {
|
|||||||
} else {
|
} else {
|
||||||
fatalError()
|
fatalError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let chatInputText = ChatTextInputStateText(attributedText: self.caption)
|
||||||
|
try container.encode(chatInputText, forKey: .caption)
|
||||||
|
//try container.encode(self.privacy, forKey: .privacy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +169,7 @@ public func addStoryDraft(engine: TelegramEngine, item: MediaEditorDraft) {
|
|||||||
|
|
||||||
public func removeStoryDraft(engine: TelegramEngine, path: String, delete: Bool) {
|
public func removeStoryDraft(engine: TelegramEngine, path: String, delete: Bool) {
|
||||||
if delete {
|
if delete {
|
||||||
try? FileManager.default.removeItem(atPath: path)
|
try? FileManager.default.removeItem(atPath: fullDraftPath(path))
|
||||||
}
|
}
|
||||||
let itemId = MediaEditorDraftItemId(path.persistentHashValue)
|
let itemId = MediaEditorDraftItemId(path.persistentHashValue)
|
||||||
let _ = engine.orderedLists.removeItem(collectionId: ApplicationSpecificOrderedItemListCollectionId.storyDrafts, id: itemId.rawValue).start()
|
let _ = engine.orderedLists.removeItem(collectionId: ApplicationSpecificOrderedItemListCollectionId.storyDrafts, id: itemId.rawValue).start()
|
||||||
@ -144,3 +191,13 @@ public func storyDrafts(engine: TelegramEngine) -> Signal<[MediaEditorDraft], No
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public extension MediaEditorDraft {
|
||||||
|
func fullPath() -> String {
|
||||||
|
return fullDraftPath(self.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func fullDraftPath(_ path: String) -> String {
|
||||||
|
return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/storyDrafts/" + path
|
||||||
|
}
|
||||||
|
@ -223,7 +223,7 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
|
|
||||||
private let fadeView = UIButton()
|
private let fadeView = UIButton()
|
||||||
|
|
||||||
private let inputPanel = ComponentView<Empty>()
|
fileprivate let inputPanel = ComponentView<Empty>()
|
||||||
private let inputPanelExternalState = MessageInputPanelComponent.ExternalState()
|
private let inputPanelExternalState = MessageInputPanelComponent.ExternalState()
|
||||||
private let inputPanelBackground = ComponentView<Empty>()
|
private let inputPanelBackground = ComponentView<Empty>()
|
||||||
|
|
||||||
@ -584,6 +584,18 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getInputText() -> NSAttributedString {
|
||||||
|
guard let inputPanelView = self.inputPanel.view as? MessageInputPanelComponent.View else {
|
||||||
|
return NSAttributedString()
|
||||||
|
}
|
||||||
|
var inputText = NSAttributedString()
|
||||||
|
switch inputPanelView.getSendMessageInput() {
|
||||||
|
case let .text(text):
|
||||||
|
inputText = text
|
||||||
|
}
|
||||||
|
return inputText
|
||||||
|
}
|
||||||
|
|
||||||
func update(component: MediaEditorScreenComponent, availableSize: CGSize, state: State, environment: Environment<ViewControllerComponentContainer.Environment>, transition: Transition) -> CGSize {
|
func update(component: MediaEditorScreenComponent, availableSize: CGSize, state: State, environment: Environment<ViewControllerComponentContainer.Environment>, transition: Transition) -> CGSize {
|
||||||
guard !self.isDismissed else {
|
guard !self.isDismissed else {
|
||||||
return availableSize
|
return availableSize
|
||||||
@ -1000,6 +1012,9 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
self.isEditingCaption = isEditingCaption
|
self.isEditingCaption = isEditingCaption
|
||||||
|
|
||||||
if isEditingCaption {
|
if isEditingCaption {
|
||||||
|
if let controller = environment.controller() as? MediaEditorScreen {
|
||||||
|
controller.dismissAllTooltips()
|
||||||
|
}
|
||||||
mediaEditor?.stop()
|
mediaEditor?.stop()
|
||||||
} else {
|
} else {
|
||||||
mediaEditor?.play()
|
mediaEditor?.play()
|
||||||
@ -1658,25 +1673,12 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
hasTrending: true,
|
hasTrending: true,
|
||||||
forceHasPremium: true
|
forceHasPremium: true
|
||||||
)
|
)
|
||||||
|
|
||||||
let maskItems = EmojiPagerContentComponent.stickerInputData(
|
|
||||||
context: controller.context,
|
|
||||||
animationCache: controller.context.animationCache,
|
|
||||||
animationRenderer: controller.context.animationRenderer,
|
|
||||||
stickerNamespaces: [Namespaces.ItemCollection.CloudMaskPacks],
|
|
||||||
stickerOrderedItemListCollectionIds: [],
|
|
||||||
chatPeerId: controller.context.account.peerId,
|
|
||||||
hasSearch: false,
|
|
||||||
hasTrending: false,
|
|
||||||
forceHasPremium: true
|
|
||||||
)
|
|
||||||
|
|
||||||
let signal = combineLatest(queue: .mainQueue(),
|
let signal = combineLatest(queue: .mainQueue(),
|
||||||
emojiItems,
|
emojiItems,
|
||||||
stickerItems,
|
stickerItems
|
||||||
maskItems
|
) |> map { emoji, stickers -> StickerPickerInputData in
|
||||||
) |> map { emoji, stickers, masks -> StickerPickerInputData in
|
return StickerPickerInputData(emoji: emoji, stickers: stickers, masks: nil)
|
||||||
return StickerPickerInputData(emoji: emoji, stickers: stickers, masks: masks)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stickerPickerInputData.set(signal)
|
stickerPickerInputData.set(signal)
|
||||||
@ -1752,7 +1754,6 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
let imageEntity = DrawingStickerEntity(content: .image(image ?? additionalImage))
|
let imageEntity = DrawingStickerEntity(content: .image(image ?? additionalImage))
|
||||||
imageEntity.referenceDrawingSize = storyDimensions
|
imageEntity.referenceDrawingSize = storyDimensions
|
||||||
imageEntity.scale = 1.49
|
imageEntity.scale = 1.49
|
||||||
imageEntity.mirrored = true
|
|
||||||
imageEntity.position = position.getPosition(storyDimensions)
|
imageEntity.position = position.getPosition(storyDimensions)
|
||||||
self.entitiesView.add(imageEntity, announce: false)
|
self.entitiesView.add(imageEntity, announce: false)
|
||||||
} else if case let .video(_, _, additionalVideoPath, additionalVideoImage, _, position) = subject, let additionalVideoPath {
|
} else if case let .video(_, _, additionalVideoPath, additionalVideoImage, _, position) = subject, let additionalVideoPath {
|
||||||
@ -2135,7 +2136,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
func animateOut(finished: Bool, completion: @escaping () -> Void) {
|
func animateOut(finished: Bool, saveDraft: Bool, completion: @escaping () -> Void) {
|
||||||
guard let controller = self.controller else {
|
guard let controller = self.controller else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -2147,6 +2148,16 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
self.backgroundDimView.alpha = 0.0
|
self.backgroundDimView.alpha = 0.0
|
||||||
self.backgroundDimView.layer.animateAlpha(from: previousDimAlpha, to: 0.0, duration: 0.15)
|
self.backgroundDimView.layer.animateAlpha(from: previousDimAlpha, to: 0.0, duration: 0.15)
|
||||||
|
|
||||||
|
var isNew: Bool? = false
|
||||||
|
if let subject = self.subject {
|
||||||
|
if saveDraft {
|
||||||
|
isNew = true
|
||||||
|
}
|
||||||
|
if case .draft = subject, !saveDraft {
|
||||||
|
isNew = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if finished, case .message = controller.state.privacy {
|
if finished, case .message = controller.state.privacy {
|
||||||
if let view = self.componentHost.view as? MediaEditorScreenComponent.View {
|
if let view = self.componentHost.view as? MediaEditorScreenComponent.View {
|
||||||
view.animateOut(to: .camera)
|
view.animateOut(to: .camera)
|
||||||
@ -2158,10 +2169,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
view.previewView = nil
|
view.previewView = nil
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else if let transitionOut = controller.transitionOut(finished), let destinationView = transitionOut.destinationView {
|
} else if let transitionOut = controller.transitionOut(finished, isNew), let destinationView = transitionOut.destinationView {
|
||||||
var destinationTransitionView: UIView?
|
var destinationTransitionView: UIView?
|
||||||
if !finished {
|
if !finished {
|
||||||
if let transitionIn = controller.transitionIn, case let .gallery(galleryTransitionIn) = transitionIn, let sourceImage = galleryTransitionIn.sourceImage {
|
if let transitionIn = controller.transitionIn, case let .gallery(galleryTransitionIn) = transitionIn, let sourceImage = galleryTransitionIn.sourceImage, isNew != true {
|
||||||
let sourceSuperView = galleryTransitionIn.sourceView?.superview?.superview
|
let sourceSuperView = galleryTransitionIn.sourceView?.superview?.superview
|
||||||
let destinationTransitionOutView = UIImageView(image: sourceImage)
|
let destinationTransitionOutView = UIImageView(image: sourceImage)
|
||||||
destinationTransitionOutView.clipsToBounds = true
|
destinationTransitionOutView.clipsToBounds = true
|
||||||
@ -2179,22 +2190,34 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
let destinationAspectRatio = destinationLocalFrame.height / destinationLocalFrame.width
|
let destinationAspectRatio = destinationLocalFrame.height / destinationLocalFrame.width
|
||||||
|
|
||||||
var destinationSnapshotView: UIView?
|
var destinationSnapshotView: UIView?
|
||||||
if let destinationNode = destinationView.asyncdisplaykit_node, destinationNode is AvatarNode, let snapshotView = destinationView.snapshotView(afterScreenUpdates: false) {
|
if let destinationNode = destinationView.asyncdisplaykit_node as? AvatarNode {
|
||||||
|
let destinationTransitionView: UIView?
|
||||||
|
if let image = destinationNode.unroundedImage {
|
||||||
|
destinationTransitionView = UIImageView(image: image)
|
||||||
|
destinationTransitionView?.bounds = destinationNode.bounds
|
||||||
|
destinationTransitionView?.layer.cornerRadius = destinationNode.bounds.width / 2.0
|
||||||
|
} else if let snapshotView = destinationView.snapshotView(afterScreenUpdates: false) {
|
||||||
|
destinationTransitionView = snapshotView
|
||||||
|
} else {
|
||||||
|
destinationTransitionView = nil
|
||||||
|
}
|
||||||
destinationView.isHidden = true
|
destinationView.isHidden = true
|
||||||
|
|
||||||
snapshotView.layer.anchorPoint = CGPoint(x: 0.0, y: 0.5)
|
if let destinationTransitionView {
|
||||||
let snapshotScale = self.previewContainerView.bounds.width / snapshotView.frame.width
|
destinationTransitionView.layer.anchorPoint = CGPoint(x: 0.0, y: 0.5)
|
||||||
snapshotView.center = CGPoint(x: 0.0, y: self.previewContainerView.bounds.height / 2.0)
|
let snapshotScale = self.previewContainerView.bounds.width / destinationTransitionView.frame.width
|
||||||
snapshotView.layer.transform = CATransform3DMakeScale(snapshotScale, snapshotScale, 1.0)
|
destinationTransitionView.center = CGPoint(x: 0.0, y: self.previewContainerView.bounds.height / 2.0)
|
||||||
|
destinationTransitionView.layer.transform = CATransform3DMakeScale(snapshotScale, snapshotScale, 1.0)
|
||||||
snapshotView.alpha = 0.0
|
|
||||||
Queue.mainQueue().after(0.15) {
|
destinationTransitionView.alpha = 0.0
|
||||||
snapshotView.alpha = 1.0
|
Queue.mainQueue().after(0.15) {
|
||||||
snapshotView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
destinationTransitionView.alpha = 1.0
|
||||||
|
destinationTransitionView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.previewContainerView.addSubview(destinationTransitionView)
|
||||||
|
destinationSnapshotView = destinationTransitionView
|
||||||
}
|
}
|
||||||
|
|
||||||
self.previewContainerView.addSubview(snapshotView)
|
|
||||||
destinationSnapshotView = snapshotView
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.previewContainerView.layer.animatePosition(from: self.previewContainerView.center, to: destinationLocalFrame.center, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
|
self.previewContainerView.layer.animatePosition(from: self.previewContainerView.center, to: destinationLocalFrame.center, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
|
||||||
@ -2258,7 +2281,11 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
completion()
|
completion()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
completion()
|
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.4, removeOnCompletion: false)
|
||||||
|
self.layer.animateScale(from: 1.0, to: 0.8, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||||
|
self.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: self.bounds.height), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true, completion: { _ in
|
||||||
|
completion()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2820,7 +2847,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
fileprivate let context: AccountContext
|
fileprivate let context: AccountContext
|
||||||
fileprivate let subject: Signal<Subject?, NoError>
|
fileprivate let subject: Signal<Subject?, NoError>
|
||||||
fileprivate let transitionIn: TransitionIn?
|
fileprivate let transitionIn: TransitionIn?
|
||||||
fileprivate let transitionOut: (Bool) -> TransitionOut?
|
fileprivate let transitionOut: (Bool, Bool?) -> TransitionOut?
|
||||||
|
|
||||||
public var cancelled: (Bool) -> Void = { _ in }
|
public var cancelled: (Bool) -> Void = { _ in }
|
||||||
public var completion: (Int64, MediaEditorScreen.Result, MediaEditorResultPrivacy, @escaping (@escaping () -> Void) -> Void) -> Void = { _, _, _, _ in }
|
public var completion: (Int64, MediaEditorScreen.Result, MediaEditorResultPrivacy, @escaping (@escaping () -> Void) -> Void) -> Void = { _, _, _, _ in }
|
||||||
@ -2832,7 +2859,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
subject: Signal<Subject?, NoError>,
|
subject: Signal<Subject?, NoError>,
|
||||||
transitionIn: TransitionIn?,
|
transitionIn: TransitionIn?,
|
||||||
transitionOut: @escaping (Bool) -> TransitionOut?,
|
transitionOut: @escaping (Bool, Bool?) -> TransitionOut?,
|
||||||
completion: @escaping (Int64, MediaEditorScreen.Result, MediaEditorResultPrivacy, @escaping (@escaping () -> Void) -> Void) -> Void
|
completion: @escaping (Int64, MediaEditorScreen.Result, MediaEditorResultPrivacy, @escaping (@escaping () -> Void) -> Void) -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
@ -3153,11 +3180,14 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
func maybePresentDiscardAlert() {
|
func maybePresentDiscardAlert() {
|
||||||
self.hapticFeedback.impact(.light)
|
guard let mediaEditor = self.node.mediaEditor else {
|
||||||
if "".isEmpty {
|
|
||||||
self.requestDismiss(saveDraft: false, animated: true)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let entities = self.node.entitiesView.entities.filter { !($0 is DrawingMediaEntity) }
|
||||||
|
let codableEntities = DrawingEntitiesView.encodeEntities(entities, entitiesView: self.node.entitiesView)
|
||||||
|
mediaEditor.setDrawingAndEntities(data: nil, image: mediaEditor.values.drawing, entities: codableEntities)
|
||||||
|
|
||||||
|
self.hapticFeedback.impact(.light)
|
||||||
if let subject = self.node.subject, case .asset = subject, self.node.mediaEditor?.values.hasChanges == false {
|
if let subject = self.node.subject, case .asset = subject, self.node.mediaEditor?.values.hasChanges == false {
|
||||||
self.requestDismiss(saveDraft: false, animated: true)
|
self.requestDismiss(saveDraft: false, animated: true)
|
||||||
return
|
return
|
||||||
@ -3203,9 +3233,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
if saveDraft {
|
if saveDraft {
|
||||||
self.saveDraft(id: nil)
|
self.saveDraft(id: nil)
|
||||||
} else {
|
} else {
|
||||||
// if case let .draft(draft, _) = self.node.subject {
|
if case let .draft(draft, _) = self.node.subject {
|
||||||
// removeStoryDraft(engine: self.context.engine, path: draft.path, delete: true)
|
removeStoryDraft(engine: self.context.engine, path: draft.path, delete: true)
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let mediaEditor = self.node.mediaEditor {
|
if let mediaEditor = self.node.mediaEditor {
|
||||||
@ -3215,7 +3245,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
|
|
||||||
self.cancelled(saveDraft)
|
self.cancelled(saveDraft)
|
||||||
|
|
||||||
self.node.animateOut(finished: false, completion: { [weak self] in
|
self.node.animateOut(finished: false, saveDraft: saveDraft, completion: { [weak self] in
|
||||||
self?.dismiss()
|
self?.dismiss()
|
||||||
self?.dismissed()
|
self?.dismissed()
|
||||||
})
|
})
|
||||||
@ -3228,6 +3258,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
try? FileManager.default.createDirectory(atPath: draftPath(), withIntermediateDirectories: true)
|
try? FileManager.default.createDirectory(atPath: draftPath(), withIntermediateDirectories: true)
|
||||||
|
|
||||||
let privacy = self.state.privacy
|
let privacy = self.state.privacy
|
||||||
|
let caption = (self.node.componentHost.view as? MediaEditorScreenComponent.View)?.getInputText() ?? NSAttributedString()
|
||||||
|
|
||||||
if let resultImage = self.node.mediaEditor?.resultImage {
|
if let resultImage = self.node.mediaEditor?.resultImage {
|
||||||
self.node.mediaEditor?.seek(0.0, andPlay: false)
|
self.node.mediaEditor?.seek(0.0, andPlay: false)
|
||||||
@ -3239,10 +3270,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
|
|
||||||
let saveImageDraft: (UIImage, PixelDimensions) -> Void = { image, dimensions in
|
let saveImageDraft: (UIImage, PixelDimensions) -> Void = { image, dimensions in
|
||||||
if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) {
|
if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) {
|
||||||
let path = draftPath() + "/\(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) {
|
||||||
try? data.write(to: URL(fileURLWithPath: path))
|
let draft = MediaEditorDraft(path: path, isVideo: false, thumbnail: thumbnailImage, dimensions: dimensions, values: values, caption: caption, privacy: privacy)
|
||||||
let draft = MediaEditorDraft(path: path, isVideo: false, thumbnail: thumbnailImage, dimensions: dimensions, values: values, caption: NSAttributedString(), privacy: privacy)
|
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)
|
||||||
} else {
|
} else {
|
||||||
@ -3254,9 +3285,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
|
|
||||||
let saveVideoDraft: (String, PixelDimensions) -> Void = { videoPath, dimensions in
|
let saveVideoDraft: (String, PixelDimensions) -> Void = { videoPath, dimensions in
|
||||||
if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) {
|
if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) {
|
||||||
let path = draftPath() + "/\(Int64.random(in: .min ... .max)).mp4"
|
let path = "\(Int64.random(in: .min ... .max)).mp4"
|
||||||
try? FileManager.default.moveItem(atPath: videoPath, toPath: path)
|
let draft = MediaEditorDraft(path: path, isVideo: true, thumbnail: thumbnailImage, dimensions: dimensions, values: values, caption: caption, privacy: privacy)
|
||||||
let draft = MediaEditorDraft(path: path, isVideo: true, thumbnail: thumbnailImage, dimensions: dimensions, values: values, caption: NSAttributedString(), privacy: privacy)
|
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)
|
||||||
} else {
|
} else {
|
||||||
@ -3288,15 +3319,11 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
case let .draft(draft, _):
|
case let .draft(draft, _):
|
||||||
if draft.isVideo {
|
if draft.isVideo {
|
||||||
saveVideoDraft(draft.path, draft.dimensions)
|
saveVideoDraft(draft.fullPath(), draft.dimensions)
|
||||||
} else if let image = UIImage(contentsOfFile: draft.path) {
|
} else if let image = UIImage(contentsOfFile: draft.fullPath()) {
|
||||||
saveImageDraft(image, draft.dimensions)
|
saveImageDraft(image, draft.dimensions)
|
||||||
}
|
}
|
||||||
// if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) {
|
removeStoryDraft(engine: self.context.engine, path: draft.path, delete: false)
|
||||||
// removeStoryDraft(engine: self.context.engine, path: draft.path, delete: false)
|
|
||||||
// let draft = MediaEditorDraft(path: draft.path, isVideo: draft.isVideo, thumbnail: thumbnailImage, dimensions: draft.dimensions, values: values)
|
|
||||||
// addStoryDraft(engine: self.context.engine, item: draft)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -3311,7 +3338,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
|
|
||||||
self.dismissAllTooltips()
|
self.dismissAllTooltips()
|
||||||
|
|
||||||
mediaEditor.seek(0.0, andPlay: false)
|
mediaEditor.seek(mediaEditor.values.videoTrimRange?.lowerBound ?? 0.0, andPlay: false)
|
||||||
|
mediaEditor.requestRenderFrame()
|
||||||
mediaEditor.invalidate()
|
mediaEditor.invalidate()
|
||||||
self.node.entitiesView.invalidate()
|
self.node.entitiesView.invalidate()
|
||||||
|
|
||||||
@ -3331,6 +3359,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
if mediaEditor.resultIsVideo {
|
if mediaEditor.resultIsVideo {
|
||||||
|
var firstFrame: Signal<UIImage?, NoError>
|
||||||
|
let firstFrameTime = CMTime(seconds: mediaEditor.values.videoTrimRange?.lowerBound ?? 0.0, preferredTimescale: CMTimeScale(60))
|
||||||
|
|
||||||
let videoResult: Result.VideoResult
|
let videoResult: Result.VideoResult
|
||||||
let duration: Double
|
let duration: Double
|
||||||
switch subject {
|
switch subject {
|
||||||
@ -3341,6 +3372,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
videoResult = .imageFile(path: tempImagePath)
|
videoResult = .imageFile(path: tempImagePath)
|
||||||
duration = 5.0
|
duration = 5.0
|
||||||
|
|
||||||
|
firstFrame = .single(image)
|
||||||
case let .video(path, _, _, _, _, _):
|
case let .video(path, _, _, _, _, _):
|
||||||
videoResult = .videoFile(path: path)
|
videoResult = .videoFile(path: path)
|
||||||
if let videoTrimRange = mediaEditor.values.videoTrimRange {
|
if let videoTrimRange = mediaEditor.values.videoTrimRange {
|
||||||
@ -3348,6 +3381,20 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
} else {
|
} else {
|
||||||
duration = 5.0
|
duration = 5.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
firstFrame = Signal<UIImage?, NoError> { subscriber in
|
||||||
|
let avAsset = AVURLAsset(url: URL(fileURLWithPath: path))
|
||||||
|
let avAssetGenerator = AVAssetImageGenerator(asset: avAsset)
|
||||||
|
avAssetGenerator.generateCGImagesAsynchronously(forTimes: [NSValue(time: firstFrameTime)], completionHandler: { _, cgImage, _, _, _ in
|
||||||
|
if let cgImage {
|
||||||
|
subscriber.putNext(UIImage(cgImage: cgImage))
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return ActionDisposable {
|
||||||
|
avAssetGenerator.cancelAllCGImageGeneration()
|
||||||
|
}
|
||||||
|
}
|
||||||
case let .asset(asset):
|
case let .asset(asset):
|
||||||
videoResult = .asset(localIdentifier: asset.localIdentifier)
|
videoResult = .asset(localIdentifier: asset.localIdentifier)
|
||||||
if asset.mediaType == .video {
|
if asset.mediaType == .video {
|
||||||
@ -3359,34 +3406,86 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
} else {
|
} else {
|
||||||
duration = 5.0
|
duration = 5.0
|
||||||
}
|
}
|
||||||
|
firstFrame = Signal<UIImage?, NoError> { subscriber in
|
||||||
|
if asset.mediaType == .video {
|
||||||
|
PHImageManager.default().requestAVAsset(forVideo: asset, options: nil) { avAsset, _, _ in
|
||||||
|
if let avAsset {
|
||||||
|
let avAssetGenerator = AVAssetImageGenerator(asset: avAsset)
|
||||||
|
avAssetGenerator.generateCGImagesAsynchronously(forTimes: [NSValue(time: firstFrameTime)], completionHandler: { _, cgImage, _, _, _ in
|
||||||
|
if let cgImage {
|
||||||
|
subscriber.putNext(UIImage(cgImage: cgImage))
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let options = PHImageRequestOptions()
|
||||||
|
options.deliveryMode = .highQualityFormat
|
||||||
|
PHImageManager.default().requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .default, options: options) { image, _ in
|
||||||
|
if let image {
|
||||||
|
subscriber.putNext(image)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
case let .draft(draft, _):
|
case let .draft(draft, _):
|
||||||
if draft.isVideo {
|
if draft.isVideo {
|
||||||
videoResult = .videoFile(path: draft.path)
|
videoResult = .videoFile(path: draft.fullPath())
|
||||||
if let videoTrimRange = mediaEditor.values.videoTrimRange {
|
if let videoTrimRange = mediaEditor.values.videoTrimRange {
|
||||||
duration = videoTrimRange.upperBound - videoTrimRange.lowerBound
|
duration = videoTrimRange.upperBound - videoTrimRange.lowerBound
|
||||||
} else {
|
} else {
|
||||||
duration = 5.0
|
duration = 5.0
|
||||||
}
|
}
|
||||||
|
firstFrame = Signal<UIImage?, NoError> { subscriber in
|
||||||
|
let avAsset = AVURLAsset(url: URL(fileURLWithPath: draft.fullPath()))
|
||||||
|
let avAssetGenerator = AVAssetImageGenerator(asset: avAsset)
|
||||||
|
avAssetGenerator.generateCGImagesAsynchronously(forTimes: [NSValue(time: firstFrameTime)], completionHandler: { _, cgImage, _, _, _ in
|
||||||
|
if let cgImage {
|
||||||
|
subscriber.putNext(UIImage(cgImage: cgImage))
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return ActionDisposable {
|
||||||
|
avAssetGenerator.cancelAllCGImageGeneration()
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
videoResult = .imageFile(path: draft.path)
|
videoResult = .imageFile(path: draft.fullPath())
|
||||||
duration = 5.0
|
duration = 5.0
|
||||||
|
|
||||||
|
if let image = UIImage(contentsOfFile: draft.fullPath()) {
|
||||||
|
firstFrame = .single(image)
|
||||||
|
} else {
|
||||||
|
firstFrame = .single(UIImage())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let resultImage = mediaEditor.resultImage {
|
||||||
|
firstFrame = .single(resultImage)
|
||||||
|
}
|
||||||
|
|
||||||
// makeEditorImageComposition(account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { [weak self] coverImage in
|
let _ = (firstFrame
|
||||||
// if let self {
|
|> deliverOnMainQueue).start(next: { [weak self] image in
|
||||||
self.completion(randomId, .video(video: videoResult, coverImage: nil, values: mediaEditor.values, duration: duration, dimensions: mediaEditor.values.resultDimensions, caption: caption), self.state.privacy, { [weak self] finished in
|
if let self {
|
||||||
self?.node.animateOut(finished: true, completion: { [weak self] in
|
makeEditorImageComposition(account: self.context.account, inputImage: image ?? UIImage(), dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { [weak self] coverImage in
|
||||||
self?.dismiss()
|
if let self {
|
||||||
Queue.mainQueue().justDispatch {
|
self.completion(randomId, .video(video: videoResult, coverImage: coverImage, values: mediaEditor.values, duration: duration, dimensions: mediaEditor.values.resultDimensions, caption: caption), self.state.privacy, { [weak self] finished in
|
||||||
finished()
|
self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in
|
||||||
}
|
self?.dismiss()
|
||||||
})
|
Queue.mainQueue().justDispatch {
|
||||||
|
finished()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
// }
|
}
|
||||||
// })
|
})
|
||||||
|
|
||||||
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: true)
|
removeStoryDraft(engine: self.context.engine, path: draft.path, delete: true)
|
||||||
}
|
}
|
||||||
@ -3397,7 +3496,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
makeEditorImageComposition(account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { [weak self] resultImage in
|
makeEditorImageComposition(account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { [weak self] resultImage in
|
||||||
if let self, let resultImage {
|
if let self, let resultImage {
|
||||||
self.completion(randomId, .image(image: resultImage, dimensions: PixelDimensions(resultImage.size), caption: caption), self.state.privacy, { [weak self] finished in
|
self.completion(randomId, .image(image: resultImage, dimensions: PixelDimensions(resultImage.size), caption: caption), self.state.privacy, { [weak self] finished in
|
||||||
self?.node.animateOut(finished: true, completion: { [weak self] in
|
self?.node.animateOut(finished: true, saveDraft: false, completion: { [weak self] in
|
||||||
self?.dismiss()
|
self?.dismiss()
|
||||||
Queue.mainQueue().justDispatch {
|
Queue.mainQueue().justDispatch {
|
||||||
finished()
|
finished()
|
||||||
@ -3491,10 +3590,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
}
|
}
|
||||||
case let .draft(draft, _):
|
case let .draft(draft, _):
|
||||||
if draft.isVideo {
|
if draft.isVideo {
|
||||||
let asset = AVURLAsset(url: NSURL(fileURLWithPath: draft.path) as URL)
|
let asset = AVURLAsset(url: NSURL(fileURLWithPath: draft.fullPath()) as URL)
|
||||||
exportSubject = .single(.video(asset))
|
exportSubject = .single(.video(asset))
|
||||||
} else {
|
} else {
|
||||||
if let image = UIImage(contentsOfFile: draft.path) {
|
if let image = UIImage(contentsOfFile: draft.fullPath()) {
|
||||||
exportSubject = .single(.image(image))
|
exportSubject = .single(.image(image))
|
||||||
} else {
|
} else {
|
||||||
fatalError()
|
fatalError()
|
||||||
@ -3577,7 +3676,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
self.node.updateEditProgress(progress)
|
self.node.updateEditProgress(progress)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func dismissAllTooltips() {
|
fileprivate func dismissAllTooltips() {
|
||||||
self.window?.forEachController({ controller in
|
self.window?.forEachController({ controller in
|
||||||
if let controller = controller as? TooltipScreen {
|
if let controller = controller as? TooltipScreen {
|
||||||
controller.dismiss()
|
controller.dismiss()
|
||||||
@ -3724,11 +3823,6 @@ private final class HeaderContextReferenceContentSource: ContextReferenceContent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func draftPath() -> String {
|
|
||||||
return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/storyDrafts"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private final class ToolValueComponent: Component {
|
private final class ToolValueComponent: Component {
|
||||||
typealias EnvironmentType = Empty
|
typealias EnvironmentType = Empty
|
||||||
|
|
||||||
@ -3927,3 +4021,7 @@ public final class BlurredGradientComponent: Component {
|
|||||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func draftPath() -> String {
|
||||||
|
return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/storyDrafts"
|
||||||
|
}
|
||||||
|
@ -1776,7 +1776,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
let reactionsAnchorRect = CGRect(origin: CGPoint(x: inputPanelFrame.maxX - 40.0, y: inputPanelFrame.minY + 9.0), size: CGSize(width: 32.0, height: 32.0)).insetBy(dx: -4.0, dy: -4.0)
|
let reactionsAnchorRect = CGRect(origin: CGPoint(x: inputPanelFrame.maxX - 40.0, y: inputPanelFrame.minY + 9.0), size: CGSize(width: 32.0, height: 32.0)).insetBy(dx: -4.0, dy: -4.0)
|
||||||
|
|
||||||
var effectiveDisplayReactions = false
|
var effectiveDisplayReactions = false
|
||||||
if self.inputPanelExternalState.isEditing && !self.inputPanelExternalState.hasText {
|
if self.inputPanelExternalState.isEditing && !self.inputPanelExternalState.hasText {
|
||||||
effectiveDisplayReactions = true
|
effectiveDisplayReactions = true
|
||||||
}
|
}
|
||||||
if self.sendMessageContext.audioRecorderValue != nil || self.sendMessageContext.videoRecorderValue != nil {
|
if self.sendMessageContext.audioRecorderValue != nil || self.sendMessageContext.videoRecorderValue != nil {
|
||||||
@ -1789,6 +1789,10 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
effectiveDisplayReactions = false
|
effectiveDisplayReactions = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let reactionContextNode = self.reactionContextNode, reactionContextNode.isReactionSearchActive {
|
||||||
|
effectiveDisplayReactions = true
|
||||||
|
}
|
||||||
|
|
||||||
if let reactionItems = self.reactionItems, effectiveDisplayReactions {
|
if let reactionItems = self.reactionItems, effectiveDisplayReactions {
|
||||||
let reactionContextNode: ReactionContextNode
|
let reactionContextNode: ReactionContextNode
|
||||||
var reactionContextNodeTransition = transition
|
var reactionContextNodeTransition = transition
|
||||||
@ -2157,7 +2161,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
context: context,
|
context: context,
|
||||||
subject: .single(.draft(source, Int64(id))),
|
subject: .single(.draft(source, Int64(id))),
|
||||||
transitionIn: nil,
|
transitionIn: nil,
|
||||||
transitionOut: { _ in return nil },
|
transitionOut: { _, _ in return nil },
|
||||||
completion: { [weak self] _, mediaResult, privacy, commit in
|
completion: { [weak self] _, mediaResult, privacy, commit in
|
||||||
switch mediaResult {
|
switch mediaResult {
|
||||||
case let .image(image, dimensions, caption):
|
case let .image(image, dimensions, caption):
|
||||||
|
@ -101,11 +101,12 @@ final class StoryItemSetContainerSendMessage {
|
|||||||
switch inputPanelView.getSendMessageInput() {
|
switch inputPanelView.getSendMessageInput() {
|
||||||
case let .text(text):
|
case let .text(text):
|
||||||
if !text.string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
if !text.string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||||
|
let entities = generateChatInputTextEntities(text)
|
||||||
component.context.engine.messages.enqueueOutgoingMessage(
|
component.context.engine.messages.enqueueOutgoingMessage(
|
||||||
to: peerId,
|
to: peerId,
|
||||||
replyTo: nil,
|
replyTo: nil,
|
||||||
storyId: StoryId(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id),
|
storyId: StoryId(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id),
|
||||||
content: .text(text.string)
|
content: .text(text.string, entities)
|
||||||
)
|
)
|
||||||
inputPanelView.clearSendMessageInput()
|
inputPanelView.clearSendMessageInput()
|
||||||
view.endEditing(true)
|
view.endEditing(true)
|
||||||
|
@ -696,7 +696,7 @@ public final class StoryPeerListItemComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var titleTransition = transition
|
var titleTransition = transition
|
||||||
if previousComponent?.ringAnimation != nil && component.ringAnimation == nil {
|
if let previousAnimation = previousComponent?.ringAnimation, case .progress = previousAnimation, component.ringAnimation == nil {
|
||||||
if let titleView = self.title.view, let snapshotView = titleView.snapshotView(afterScreenUpdates: false) {
|
if let titleView = self.title.view, let snapshotView = titleView.snapshotView(afterScreenUpdates: false) {
|
||||||
self.button.addSubview(snapshotView)
|
self.button.addSubview(snapshotView)
|
||||||
snapshotView.frame = titleView.frame
|
snapshotView.frame = titleView.frame
|
||||||
@ -706,6 +706,20 @@ public final class StoryPeerListItemComponent: Component {
|
|||||||
titleView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
titleView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||||
}
|
}
|
||||||
titleTransition = .immediate
|
titleTransition = .immediate
|
||||||
|
|
||||||
|
self.avatarContent.layer.transform = CATransform3DMakeScale(1.08, 1.08, 1.0)
|
||||||
|
self.avatarContent.layer.animateScale(from: 1.0, to: 1.08, duration: 0.2, completion: { [weak self] _ in
|
||||||
|
self?.avatarContent.layer.transform = CATransform3DMakeScale(1.0, 1.0, 1.0)
|
||||||
|
self?.avatarContent.layer.animateScale(from: 1.08, to: 1.0, duration: 0.15)
|
||||||
|
})
|
||||||
|
|
||||||
|
let initialLineWidth: CGFloat = 2.0
|
||||||
|
let targetLineWidth: CGFloat = 3.0
|
||||||
|
self.indicatorShapeLayer.lineWidth = targetLineWidth
|
||||||
|
self.indicatorShapeLayer.animateShapeLineWidth(from: initialLineWidth, to: targetLineWidth, duration: 0.2, completion: { [weak self] _ in
|
||||||
|
self?.indicatorShapeLayer.lineWidth = initialLineWidth
|
||||||
|
self?.indicatorShapeLayer.animateShapeLineWidth(from: targetLineWidth, to: initialLineWidth, duration: 0.15)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let titleSize = self.title.update(
|
let titleSize = self.title.update(
|
||||||
@ -746,7 +760,13 @@ public final class StoryPeerListItemComponent: Component {
|
|||||||
|
|
||||||
switch ringAnimation {
|
switch ringAnimation {
|
||||||
case let .progress(progress):
|
case let .progress(progress):
|
||||||
progressLayer.update(size: progressFrame.size, lineWidth: 4.0, value: .progress(progress), transition: transition)
|
let progressTransition: Transition
|
||||||
|
if abs(progress - 0.028) < 0.001 {
|
||||||
|
progressTransition = .immediate
|
||||||
|
} else {
|
||||||
|
progressTransition = .easeInOut(duration: 0.3)
|
||||||
|
}
|
||||||
|
progressLayer.update(size: progressFrame.size, lineWidth: 4.0, value: .progress(progress), transition: progressTransition)
|
||||||
case .loading:
|
case .loading:
|
||||||
progressLayer.update(size: progressFrame.size, lineWidth: 4.0, value: .indefinite, transition: transition)
|
progressLayer.update(size: progressFrame.size, lineWidth: 4.0, value: .indefinite, transition: transition)
|
||||||
}
|
}
|
||||||
|
@ -1834,7 +1834,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
return StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: mainStickerPack, stickerPacks: stickerPacks, loadedStickerPacks: loadedStickerPacks, parentNavigationController: parentNavigationController, sendSticker: sendSticker)
|
return StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: mainStickerPack, stickerPacks: stickerPacks, loadedStickerPacks: loadedStickerPacks, parentNavigationController: parentNavigationController, sendSticker: sendSticker)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping () -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController {
|
public func makeMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController {
|
||||||
return storyMediaPickerController(context: context, getSourceRect: getSourceRect, completion: completion, dismissed: dismissed)
|
return storyMediaPickerController(context: context, getSourceRect: getSourceRect, completion: completion, dismissed: dismissed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,14 +331,14 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
|||||||
context: context,
|
context: context,
|
||||||
subject: subject,
|
subject: subject,
|
||||||
transitionIn: transitionIn,
|
transitionIn: transitionIn,
|
||||||
transitionOut: { finished in
|
transitionOut: { finished, isNew in
|
||||||
if finished, let transitionOut = transitionOut(finished), let destinationView = transitionOut.destinationView {
|
if finished, let transitionOut = transitionOut(finished), let destinationView = transitionOut.destinationView {
|
||||||
return MediaEditorScreen.TransitionOut(
|
return MediaEditorScreen.TransitionOut(
|
||||||
destinationView: destinationView,
|
destinationView: destinationView,
|
||||||
destinationRect: transitionOut.destinationRect,
|
destinationRect: transitionOut.destinationRect,
|
||||||
destinationCornerRadius: transitionOut.destinationCornerRadius
|
destinationCornerRadius: transitionOut.destinationCornerRadius
|
||||||
)
|
)
|
||||||
} else if !finished, let resultTransition, let (destinationView, destinationRect) = resultTransition.transitionOut() {
|
} else if !finished, let resultTransition, let (destinationView, destinationRect) = resultTransition.transitionOut(isNew) {
|
||||||
return MediaEditorScreen.TransitionOut(
|
return MediaEditorScreen.TransitionOut(
|
||||||
destinationView: destinationView,
|
destinationView: destinationView,
|
||||||
destinationRect: destinationRect,
|
destinationRect: destinationRect,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user