[WIP] Story collage

This commit is contained in:
Ilya Laktyushin
2024-11-27 18:41:03 +04:00
parent 0794812bae
commit d2b5476293
78 changed files with 6086 additions and 1224 deletions

View File

@@ -27,6 +27,7 @@ import MediaEditor
import ImageObjectSeparation
import ChatSendMessageActionUI
import AnimatedCountLabelNode
import MediaAssetsContext
final class MediaPickerInteraction {
let downloadManager: AssetDownloadManager
@@ -40,16 +41,7 @@ final class MediaPickerInteraction {
let selectionState: TGMediaSelectionContext?
let editingState: TGMediaEditingContext
var hiddenMediaId: String?
var captionIsAboveMedia: Bool = false {
didSet {
if self.captionIsAboveMedia != oldValue {
self.captionIsAboveMediaValue.set(self.captionIsAboveMedia)
}
}
}
let captionIsAboveMediaValue = ValuePromise<Bool>(false)
init(downloadManager: AssetDownloadManager, openMedia: @escaping (PHFetchResult<PHAsset>, Int, UIImage?) -> Void, openSelectedMedia: @escaping (TGMediaSelectableItem, UIImage?) -> Void, openDraft: @escaping (MediaEditorDraft, UIImage?) -> Void, toggleSelection: @escaping (TGMediaSelectableItem, Bool, Bool) -> Bool, sendSelected: @escaping (TGMediaSelectableItem?, Bool, Int32?, Bool, ChatSendMessageActionSheetController.SendParameters?, @escaping () -> Void) -> Void, schedule: @escaping (ChatSendMessageActionSheetController.SendParameters?) -> Void, dismissInput: @escaping () -> Void, selectionState: TGMediaSelectionContext?, editingState: TGMediaEditingContext) {
self.downloadManager = downloadManager
self.openMedia = openMedia
@@ -138,7 +130,7 @@ struct Month: Equatable {
private var savedStoriesContentOffset: CGFloat?
public final class MediaPickerScreen: ViewController, AttachmentContainable {
public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, AttachmentContainable {
public enum Subject {
public enum Media: Equatable {
case image(UIImage)
@@ -207,7 +199,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
public var getCaptionPanelView: () -> TGCaptionPanelView? = { return nil }
public var openBoost: () -> Void = { }
public var customSelection: ((MediaPickerScreen, Any) -> Void)? = nil
public var customSelection: ((MediaPickerScreenImpl, Any) -> Void)? = nil
public var createFromScratch: () -> Void = {}
public var presentFilePicker: () -> Void = {}
@@ -250,7 +242,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
case media([Subject.Media])
}
private weak var controller: MediaPickerScreen?
private weak var controller: MediaPickerScreenImpl?
private var presentationData: PresentationData
fileprivate let mediaAssetsContext: MediaAssetsContext
@@ -307,7 +299,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
private var validLayout: (ContainerViewLayout, CGFloat)?
init(controller: MediaPickerScreen) {
init(controller: MediaPickerScreenImpl) {
self.controller = controller
self.presentationData = controller.presentationData
@@ -1255,7 +1247,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
if parameters == nil {
var textIsAboveMedia = false
if let interaction = controller.interaction {
textIsAboveMedia = interaction.captionIsAboveMedia
textIsAboveMedia = interaction.editingState.isCaptionAbove()
}
parameters = ChatSendMessageActionSheetController.SendParameters(
effect: nil,
@@ -2311,7 +2303,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
} else {
self.updateNavigationStack { current in
var mediaPickerContext: AttachmentMediaPickerContext?
if let first = current.first as? MediaPickerScreen {
if let first = current.first as? MediaPickerScreenImpl {
mediaPickerContext = first.webSearchController?.mediaPickerContext ?? first.mediaPickerContext
}
return (current.filter { $0 !== self }, mediaPickerContext)
@@ -2417,7 +2409,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
var updateNavigationStackImpl: ((AttachmentContainable) -> Void)?
let groupsController = MediaGroupsScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, mediaAssetsContext: self.controllerNode.mediaAssetsContext, embedded: embedded, openGroup: { [weak self] collection in
if let strongSelf = self {
let mediaPicker = MediaPickerScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: strongSelf.peer, threadTitle: strongSelf.threadTitle, chatLocation: strongSelf.chatLocation, isScheduledMessages: strongSelf.isScheduledMessages, bannedSendPhotos: strongSelf.bannedSendPhotos, bannedSendVideos: strongSelf.bannedSendVideos, subject: .assets(collection, mode), editingContext: strongSelf.interaction?.editingState, selectionContext: strongSelf.interaction?.selectionState)
let mediaPicker = MediaPickerScreenImpl(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: strongSelf.peer, threadTitle: strongSelf.threadTitle, chatLocation: strongSelf.chatLocation, isScheduledMessages: strongSelf.isScheduledMessages, bannedSendPhotos: strongSelf.bannedSendPhotos, bannedSendVideos: strongSelf.bannedSendVideos, subject: .assets(collection, mode), editingContext: strongSelf.interaction?.editingState, selectionContext: strongSelf.interaction?.selectionState)
mediaPicker.presentSchedulePicker = strongSelf.presentSchedulePicker
mediaPicker.presentTimerPicker = strongSelf.presentTimerPicker
@@ -2576,7 +2568,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
if isCaptionAboveMediaAvailable {
var mediaCaptionIsAbove = false
if let interaction = self.interaction {
mediaCaptionIsAbove = interaction.captionIsAboveMedia
mediaCaptionIsAbove = interaction.editingState.isCaptionAbove()
}
items.append(.action(ContextMenuActionItem(text: mediaCaptionIsAbove ? strings.Chat_SendMessageMenu_MoveCaptionDown : strings.Chat_SendMessageMenu_MoveCaptionUp, icon: { _ in return nil }, iconAnimation: ContextMenuActionItem.IconAnimation(
@@ -2588,7 +2580,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
}
if let interaction = strongSelf.interaction {
interaction.captionIsAboveMedia = !interaction.captionIsAboveMedia
interaction.editingState.setCaptionAbove(!interaction.editingState.isCaptionAbove())
}
})))
}
@@ -2684,13 +2676,17 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self.selectedButtonNode.frame = CGRect(origin: CGPoint(x: self.view.bounds.width - 54.0 - self.selectedButtonNode.frame.width - safeInset, y: floorToScreenPixels((navigationHeight - self.selectedButtonNode.frame.height) / 2.0) + 1.0), size: self.selectedButtonNode.frame.size)
}
public func dismissAnimated() {
self.requestDismiss(completion: {})
}
public var mediaPickerContext: AttachmentMediaPickerContext? {
return MediaPickerContext(controller: self)
}
}
final class MediaPickerContext: AttachmentMediaPickerContext {
private weak var controller: MediaPickerScreen?
private weak var controller: MediaPickerScreenImpl?
var selectionCount: Signal<Int, NoError> {
return Signal { [weak self] subscriber in
@@ -2791,23 +2787,32 @@ final class MediaPickerContext: AttachmentMediaPickerContext {
var captionIsAboveMedia: Signal<Bool, NoError> {
return Signal { [weak self] subscriber in
guard let interaction = self?.controller?.interaction else {
guard let self else {
subscriber.putNext(false)
subscriber.putCompletion()
return EmptyDisposable
}
let disposable = interaction.captionIsAboveMediaValue.get().start(next: { value in
subscriber.putNext(value)
guard let captionAbove = self.controller?.interaction?.editingState.captionAbove() else {
subscriber.putNext(false)
subscriber.putCompletion()
return EmptyDisposable
}
let disposable = captionAbove.start(next: { caption in
if let caption = caption as? NSNumber {
subscriber.putNext(caption.boolValue)
} else {
subscriber.putNext(false)
}
}, error: { _ in }, completed: { })
return ActionDisposable {
disposable.dispose()
disposable?.dispose()
}
}
}
func setCaptionIsAboveMedia(_ captionIsAboveMedia: Bool) -> Void {
self.controller?.interaction?.captionIsAboveMedia = captionIsAboveMedia
self.controller?.interaction?.editingState.setCaptionAbove(captionIsAboveMedia)
}
public var loadingProgress: Signal<CGFloat?, NoError> {
@@ -2818,7 +2823,7 @@ final class MediaPickerContext: AttachmentMediaPickerContext {
return .single(self.controller?.mainButtonState)
}
init(controller: MediaPickerScreen) {
init(controller: MediaPickerScreenImpl) {
self.controller = controller
}
@@ -2954,7 +2959,7 @@ public func wallpaperMediaPickerController(
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
peer: EnginePeer,
animateAppearance: Bool,
completion: @escaping (MediaPickerScreen, Any) -> Void = { _, _ in },
completion: @escaping (MediaPickerScreenImpl, Any) -> Void = { _, _ in },
openColors: @escaping () -> Void
) -> ViewController {
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: nil, buttons: [.standalone], initialButton: .standalone, fromMenu: false, hasTextInput: false, makeEntityInputView: {
@@ -2963,7 +2968,7 @@ public func wallpaperMediaPickerController(
controller.animateAppearance = animateAppearance
controller.requestController = { [weak controller] _, present in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let mediaPickerController = MediaPickerScreen(context: context, updatedPresentationData: updatedPresentationData, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .wallpaper), mainButtonState: AttachmentMainButtonState(text: presentationData.strings.Conversation_Theme_SetColorWallpaper, font: .regular, background: .color(.clear), textColor: presentationData.theme.actionSheet.controlAccentColor, isVisible: true, progress: .none, isEnabled: true, hasShimmer: false), mainButtonAction: {
let mediaPickerController = MediaPickerScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .wallpaper), mainButtonState: AttachmentMainButtonState(text: presentationData.strings.Conversation_Theme_SetColorWallpaper, font: .regular, background: .color(.clear), textColor: presentationData.theme.actionSheet.controlAccentColor, isVisible: true, progress: .none, isEnabled: true, hasShimmer: false), mainButtonAction: {
controller?.dismiss(animated: true)
openColors()
})
@@ -2985,7 +2990,7 @@ public func mediaPickerController(
return nil
})
controller.requestController = { _, present in
let mediaPickerController = MediaPickerScreen(context: context, updatedPresentationData: updatedPresentationData, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .addImage), mainButtonState: nil, mainButtonAction: nil)
let mediaPickerController = MediaPickerScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .addImage), mainButtonState: nil, mainButtonAction: nil)
mediaPickerController.customSelection = { controller, result in
completion(result)
controller.dismiss(animated: true)
@@ -3020,6 +3025,7 @@ public func mediaPickerController(
public func storyMediaPickerController(
context: AccountContext,
isDark: Bool,
forCollage: Bool,
getSourceRect: @escaping () -> CGRect,
completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void,
dismissed: @escaping () -> Void,
@@ -3036,7 +3042,7 @@ public func storyMediaPickerController(
controller.forceSourceRect = true
controller.getSourceRect = getSourceRect
controller.requestController = { _, present in
let mediaPickerController = MediaPickerScreen(context: context, updatedPresentationData: updatedPresentationData, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .story), mainButtonState: nil, mainButtonAction: nil)
let mediaPickerController = MediaPickerScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .story), mainButtonState: nil, mainButtonAction: nil)
mediaPickerController.groupsPresented = groupsPresented
mediaPickerController.customSelection = { controller, result in
if let result = result as? MediaEditorDraft {
@@ -3062,7 +3068,9 @@ public func storyMediaPickerController(
})
}
} else if let result = result as? PHAsset {
controller.updateHiddenMediaId(result.localIdentifier)
if !forCollage {
controller.updateHiddenMediaId(result.localIdentifier)
}
if let transitionView = controller.transitionView(for: result.localIdentifier, snapshot: false) {
let transitionOut: (Bool?) -> (UIView, CGRect)? = { isNew in
if let isNew {
@@ -3107,7 +3115,7 @@ public func stickerMediaPickerController(
controller.forceSourceRect = true
controller.getSourceRect = getSourceRect
controller.requestController = { [weak controller] _, present in
let mediaPickerController = MediaPickerScreen(context: context, updatedPresentationData: updatedPresentationData, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .createSticker), mainButtonState: nil, mainButtonAction: nil)
let mediaPickerController = MediaPickerScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .createSticker), mainButtonState: nil, mainButtonAction: nil)
mediaPickerController.customSelection = { controller, result in
if let result = result as? PHAsset {
controller.updateHiddenMediaId(result.localIdentifier)
@@ -3175,17 +3183,17 @@ public func stickerMediaPickerController(
}
var returnToCameraImpl: (() -> Void)?
let cameraScreen = CameraScreen(
let cameraScreen = CameraScreenImpl(
context: context,
mode: .sticker,
holder: cameraHolder,
transitionIn: CameraScreen.TransitionIn(
transitionIn: CameraScreenImpl.TransitionIn(
sourceView: cameraHolder.parentView,
sourceRect: cameraHolder.parentView.bounds,
sourceCornerRadius: 0.0
),
transitionOut: { _ in
return CameraScreen.TransitionOut(
return CameraScreenImpl.TransitionOut(
destinationView: cameraHolder.parentView,
destinationRect: cameraHolder.parentView.bounds,
destinationCornerRadius: 0.0