mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit '34062b0a06a4172597284b423349b806b53120f2'
This commit is contained in:
commit
a74350b5cd
@ -9151,3 +9151,10 @@ Sorry for the inconvenience.";
|
||||
"Conversation.Theme.SetPhotoWallpaper" = "Choose Background from Photos";
|
||||
"Conversation.Theme.SetColorWallpaper" = "Choose Color as a Background";
|
||||
"Conversation.Theme.OtherOptions" = "Other Options...";
|
||||
|
||||
"Conversation.Theme.ChooseWallpaperTitle" = "Choose Background";
|
||||
"Conversation.Theme.ResetWallpaper" = "Reset to Default Background";
|
||||
"Conversation.Theme.ChooseColorTitle" = "Set a Color";
|
||||
"Conversation.Theme.SetCustomColor" = "Set Custom";
|
||||
|
||||
"Appearance.ShowNextMediaOnTap" = "Show Next Media on Tap";
|
||||
|
@ -177,7 +177,7 @@ private func generateMaskImage() -> UIImage? {
|
||||
public class AttachmentController: ViewController {
|
||||
private let context: AccountContext
|
||||
private let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
|
||||
private let chatLocation: ChatLocation
|
||||
private let chatLocation: ChatLocation?
|
||||
private let buttons: [AttachmentButtonType]
|
||||
private let initialButton: AttachmentButtonType
|
||||
private let fromMenu: Bool
|
||||
@ -888,7 +888,7 @@ public class AttachmentController: ViewController {
|
||||
|
||||
public var getSourceRect: (() -> CGRect?)?
|
||||
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, chatLocation: ChatLocation, buttons: [AttachmentButtonType], initialButton: AttachmentButtonType = .gallery, fromMenu: Bool = false, hasTextInput: Bool = true, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView? = { return nil}) {
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, chatLocation: ChatLocation?, buttons: [AttachmentButtonType], initialButton: AttachmentButtonType = .gallery, fromMenu: Bool = false, hasTextInput: Bool = true, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView? = { return nil}) {
|
||||
self.context = context
|
||||
self.updatedPresentationData = updatedPresentationData
|
||||
self.chatLocation = chatLocation
|
||||
|
@ -387,7 +387,13 @@ public struct AttachmentMainButtonState {
|
||||
case center
|
||||
}
|
||||
|
||||
public enum Font: Equatable {
|
||||
case regular
|
||||
case bold
|
||||
}
|
||||
|
||||
public let text: String?
|
||||
public let font: Font
|
||||
public let background: Background
|
||||
public let textColor: UIColor
|
||||
public let isVisible: Bool
|
||||
@ -396,6 +402,7 @@ public struct AttachmentMainButtonState {
|
||||
|
||||
public init(
|
||||
text: String?,
|
||||
font: Font,
|
||||
background: Background,
|
||||
textColor: UIColor,
|
||||
isVisible: Bool,
|
||||
@ -403,6 +410,7 @@ public struct AttachmentMainButtonState {
|
||||
isEnabled: Bool
|
||||
) {
|
||||
self.text = text
|
||||
self.font = font
|
||||
self.background = background
|
||||
self.textColor = textColor
|
||||
self.isVisible = isVisible
|
||||
@ -411,7 +419,7 @@ public struct AttachmentMainButtonState {
|
||||
}
|
||||
|
||||
static var initial: AttachmentMainButtonState {
|
||||
return AttachmentMainButtonState(text: nil, background: .color(.clear), textColor: .clear, isVisible: false, progress: .none, isEnabled: false)
|
||||
return AttachmentMainButtonState(text: nil, font: .bold, background: .color(.clear), textColor: .clear, isVisible: false, progress: .none, isEnabled: false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -643,7 +651,14 @@ private final class MainButtonNode: HighlightTrackingButtonNode {
|
||||
self.setupShimmering()
|
||||
|
||||
if let text = state.text {
|
||||
self.textNode.attributedText = NSAttributedString(string: text, font: Font.semibold(17.0), textColor: state.textColor)
|
||||
let font: UIFont
|
||||
switch state.font {
|
||||
case .regular:
|
||||
font = Font.regular(17.0)
|
||||
case .bold:
|
||||
font = Font.semibold(17.0)
|
||||
}
|
||||
self.textNode.attributedText = NSAttributedString(string: text, font: font, textColor: state.textColor)
|
||||
|
||||
let textSize = self.textNode.updateLayout(size)
|
||||
self.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize)
|
||||
@ -758,13 +773,13 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
var mainButtonPressed: () -> Void = { }
|
||||
|
||||
init(context: AccountContext, chatLocation: ChatLocation, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView?) {
|
||||
init(context: AccountContext, chatLocation: ChatLocation?, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView?) {
|
||||
self.context = context
|
||||
self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
self.makeEntityInputView = makeEntityInputView
|
||||
|
||||
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: chatLocation, subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil)
|
||||
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: chatLocation ?? .peer(id: context.account.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil)
|
||||
|
||||
self.containerNode = ASDisplayNode()
|
||||
self.containerNode.clipsToBounds = true
|
||||
@ -936,7 +951,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
||||
return
|
||||
}
|
||||
textInputPanelNode.loadTextInputNodeIfNeeded()
|
||||
guard let textInputNode = textInputPanelNode.textInputNode, let peerId = chatLocation.peerId else {
|
||||
guard let textInputNode = textInputPanelNode.textInputNode, let peerId = chatLocation?.peerId else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -1267,7 +1282,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
||||
func updateMainButtonState(_ mainButtonState: AttachmentMainButtonState?) {
|
||||
var currentButtonState = self.mainButtonState
|
||||
if mainButtonState == nil {
|
||||
currentButtonState = AttachmentMainButtonState(text: currentButtonState.text, background: currentButtonState.background, textColor: currentButtonState.textColor, isVisible: false, progress: .none, isEnabled: currentButtonState.isEnabled)
|
||||
currentButtonState = AttachmentMainButtonState(text: currentButtonState.text, font: currentButtonState.font, background: currentButtonState.background, textColor: currentButtonState.textColor, isVisible: false, progress: .none, isEnabled: currentButtonState.isEnabled)
|
||||
}
|
||||
self.mainButtonState = mainButtonState ?? currentButtonState
|
||||
}
|
||||
@ -1417,6 +1432,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.scrollNode.isUserInteractionEnabled = !isSelecting
|
||||
|
||||
let isButtonVisible = self.mainButtonState.isVisible
|
||||
let isNarrowButton = isButtonVisible && self.mainButtonState.font == .regular
|
||||
|
||||
var insets = layout.insets(options: [])
|
||||
if let inputHeight = layout.inputHeight, inputHeight > 0.0 && (isSelecting || isButtonVisible) {
|
||||
@ -1457,7 +1473,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
||||
if isButtonVisible {
|
||||
var height: CGFloat
|
||||
if layout.intrinsicInsets.bottom > 0.0 && (layout.inputHeight ?? 0.0).isZero {
|
||||
height = bounds.height + 9.0
|
||||
height = bounds.height
|
||||
if case .regular = layout.metrics.widthClass {
|
||||
if self.isStandalone {
|
||||
height -= 3.0
|
||||
@ -1466,7 +1482,10 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
height = bounds.height + 9.0 + 8.0
|
||||
height = bounds.height + 8.0
|
||||
}
|
||||
if !isNarrowButton {
|
||||
height += 9.0
|
||||
}
|
||||
containerFrame = CGRect(origin: CGPoint(), size: CGSize(width: bounds.width, height: height))
|
||||
} else if isSelecting {
|
||||
@ -1532,11 +1551,13 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
let buttonSize = CGSize(width: layout.size.width - (sideInset + layout.safeInsets.left) * 2.0, height: 50.0)
|
||||
let buttonTopInset: CGFloat = isNarrowButton ? 2.0 : 8.0
|
||||
|
||||
if !self.dismissed {
|
||||
self.mainButtonNode.updateLayout(size: buttonSize, state: self.mainButtonState, transition: transition)
|
||||
}
|
||||
if !self.animatingTransition {
|
||||
transition.updateFrame(node: self.mainButtonNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + sideInset, y: isButtonVisible || self.fromMenu ? 8.0 : containerFrame.height), size: buttonSize))
|
||||
transition.updateFrame(node: self.mainButtonNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + sideInset, y: isButtonVisible || self.fromMenu ? buttonTopInset : containerFrame.height), size: buttonSize))
|
||||
}
|
||||
|
||||
return containerFrame.height
|
||||
|
@ -5,12 +5,16 @@ import Photos
|
||||
import AVFoundation
|
||||
|
||||
class MediaAssetsContext: NSObject, PHPhotoLibraryChangeObserver {
|
||||
private let assetType: PHAssetMediaType?
|
||||
|
||||
private var registeredChangeObserver = false
|
||||
private let changeSink = ValuePipe<PHChange>()
|
||||
private let mediaAccessSink = ValuePipe<PHAuthorizationStatus>()
|
||||
private let cameraAccessSink = ValuePipe<AVAuthorizationStatus?>()
|
||||
|
||||
override init() {
|
||||
init(assetType: PHAssetMediaType?) {
|
||||
self.assetType = assetType
|
||||
|
||||
super.init()
|
||||
|
||||
if PHPhotoLibrary.authorizationStatus() == .authorized {
|
||||
@ -30,7 +34,12 @@ class MediaAssetsContext: NSObject, PHPhotoLibraryChangeObserver {
|
||||
}
|
||||
|
||||
func fetchAssets(_ collection: PHAssetCollection) -> Signal<PHFetchResult<PHAsset>, NoError> {
|
||||
let initialFetchResult = PHAsset.fetchAssets(in: collection, options: nil)
|
||||
let options = PHFetchOptions()
|
||||
if let assetType = self.assetType {
|
||||
options.predicate = NSPredicate(format: "mediaType = %d", assetType.rawValue)
|
||||
}
|
||||
|
||||
let initialFetchResult = PHAsset.fetchAssets(in: collection, options: options)
|
||||
let fetchResult = Atomic<PHFetchResult<PHAsset>>(value: initialFetchResult)
|
||||
return .single(initialFetchResult)
|
||||
|> then(
|
||||
|
@ -128,7 +128,12 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
}
|
||||
}
|
||||
|
||||
case assets(PHAssetCollection?, Bool)
|
||||
public enum AssetsMode: Equatable {
|
||||
case `default`
|
||||
case wallpaper
|
||||
}
|
||||
|
||||
case assets(PHAssetCollection?, AssetsMode)
|
||||
case media([Media])
|
||||
}
|
||||
|
||||
@ -238,7 +243,11 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
self.controller = controller
|
||||
self.presentationData = controller.presentationData
|
||||
|
||||
let mediaAssetsContext = MediaAssetsContext()
|
||||
var assetType: PHAssetMediaType?
|
||||
if case let .assets(_, mode) = controller.subject, case .wallpaper = mode {
|
||||
assetType = .image
|
||||
}
|
||||
let mediaAssetsContext = MediaAssetsContext(assetType: assetType)
|
||||
self.mediaAssetsContext = mediaAssetsContext
|
||||
|
||||
self.containerNode = ASDisplayNode()
|
||||
@ -253,7 +262,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
|
||||
super.init()
|
||||
|
||||
if case .assets(nil, false) = controller.subject {
|
||||
if case .assets(nil, .default) = controller.subject {
|
||||
} else {
|
||||
self.preloadPromise.set(false)
|
||||
}
|
||||
@ -454,7 +463,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
}
|
||||
})
|
||||
|
||||
if let controller = self.controller, case .assets(nil, false) = controller.subject {
|
||||
if let controller = self.controller, case .assets(nil, .default) = controller.subject {
|
||||
let enableAnimations = self.controller?.context.sharedContext.energyUsageSettings.fullTranslucency ?? true
|
||||
|
||||
let cameraView = TGAttachmentCameraView(forSelfPortrait: false, videoModeByDefault: controller.bannedSendPhotos != nil && controller.bannedSendVideos == nil)!
|
||||
@ -556,7 +565,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
var updateLayout = false
|
||||
|
||||
var selectable = true
|
||||
if case let .assets(_, isStandalone) = controller.subject, isStandalone {
|
||||
if case let .assets(_, mode) = controller.subject, mode != .default {
|
||||
selectable = false
|
||||
}
|
||||
|
||||
@ -1229,7 +1238,24 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
|
||||
private var isDismissing = false
|
||||
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer?, threadTitle: String?, chatLocation: ChatLocation?, bannedSendPhotos: (Int32, Bool)?, bannedSendVideos: (Int32, Bool)?, subject: Subject, editingContext: TGMediaEditingContext? = nil, selectionContext: TGMediaSelectionContext? = nil, saveEditedPhotos: Bool = false) {
|
||||
fileprivate let mainButtonState: AttachmentMainButtonState?
|
||||
private let mainButtonAction: (() -> Void)?
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
|
||||
peer: EnginePeer?,
|
||||
threadTitle: String?,
|
||||
chatLocation: ChatLocation?,
|
||||
bannedSendPhotos: (Int32, Bool)?,
|
||||
bannedSendVideos: (Int32, Bool)?,
|
||||
subject: Subject,
|
||||
editingContext: TGMediaEditingContext? = nil,
|
||||
selectionContext: TGMediaSelectionContext? = nil,
|
||||
saveEditedPhotos: Bool = false,
|
||||
mainButtonState: AttachmentMainButtonState? = nil,
|
||||
mainButtonAction: (() -> Void)? = nil
|
||||
) {
|
||||
self.context = context
|
||||
|
||||
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||
@ -1242,13 +1268,24 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
self.bannedSendVideos = bannedSendVideos
|
||||
self.subject = subject
|
||||
self.saveEditedPhotos = saveEditedPhotos
|
||||
self.mainButtonState = mainButtonState
|
||||
self.mainButtonAction = mainButtonAction
|
||||
|
||||
let selectionContext = selectionContext ?? TGMediaSelectionContext()
|
||||
|
||||
self.titleView = MediaPickerTitleView(theme: self.presentationData.theme, segments: [self.presentationData.strings.Attachment_AllMedia, self.presentationData.strings.Attachment_SelectedMedia(1)], selectedIndex: 0)
|
||||
|
||||
if case let .assets(collection, _) = subject, let collection = collection {
|
||||
self.titleView.title = collection.localizedTitle ?? presentationData.strings.Attachment_Gallery
|
||||
if case let .assets(collection, mode) = subject {
|
||||
if let collection = collection {
|
||||
self.titleView.title = collection.localizedTitle ?? presentationData.strings.Attachment_Gallery
|
||||
} else {
|
||||
switch mode {
|
||||
case .default:
|
||||
self.titleView.title = presentationData.strings.Attachment_Gallery
|
||||
case .wallpaper:
|
||||
self.titleView.title = presentationData.strings.Conversation_Theme_ChooseWallpaperTitle
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.titleView.title = presentationData.strings.Attachment_Gallery
|
||||
}
|
||||
@ -1316,7 +1353,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
|
||||
self.navigationItem.titleView = self.titleView
|
||||
|
||||
if case let .assets(_, isStandalone) = self.subject, isStandalone {
|
||||
if case let .assets(_, mode) = self.subject, mode != .default {
|
||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed))
|
||||
} else {
|
||||
if case let .assets(collection, _) = self.subject, collection != nil {
|
||||
@ -1609,6 +1646,10 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
}
|
||||
}
|
||||
|
||||
func mainButtonPressed() {
|
||||
self.mainButtonAction?()
|
||||
}
|
||||
|
||||
func dismissAllTooltips() {
|
||||
self.undoOverlayController?.dismissWithCommitAction()
|
||||
}
|
||||
@ -1674,13 +1715,13 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
}
|
||||
|
||||
private func presentSearch(activateOnDisplay: Bool) {
|
||||
guard self.moreButtonNode.iconNode.iconState == .search else {
|
||||
guard self.moreButtonNode.iconNode.iconState == .search, case let .assets(_, mode) = self.subject else {
|
||||
return
|
||||
}
|
||||
self.requestAttachmentMenuExpansion()
|
||||
self.presentWebSearch(MediaGroupsScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, mediaAssetsContext: self.controllerNode.mediaAssetsContext, 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, bannedSendPhotos: strongSelf.bannedSendPhotos, bannedSendVideos: strongSelf.bannedSendVideos, subject: .assets(collection, false), editingContext: strongSelf.interaction?.editingState, selectionContext: strongSelf.interaction?.selectionState)
|
||||
let mediaPicker = MediaPickerScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: strongSelf.peer, threadTitle: strongSelf.threadTitle, chatLocation: strongSelf.chatLocation, 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
|
||||
@ -1793,21 +1834,17 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
}
|
||||
|
||||
public var mediaPickerContext: AttachmentMediaPickerContext? {
|
||||
if let interaction = self.interaction {
|
||||
return MediaPickerContext(interaction: interaction)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
return MediaPickerContext(controller: self)
|
||||
}
|
||||
}
|
||||
|
||||
final class MediaPickerContext: AttachmentMediaPickerContext {
|
||||
private weak var interaction: MediaPickerInteraction?
|
||||
private weak var controller: MediaPickerScreen?
|
||||
|
||||
var selectionCount: Signal<Int, NoError> {
|
||||
return Signal { [weak self] subscriber in
|
||||
let disposable = self?.interaction?.selectionState?.selectionChangedSignal().start(next: { [weak self] value in
|
||||
subscriber.putNext(Int(self?.interaction?.selectionState?.count() ?? 0))
|
||||
let disposable = self?.controller?.interaction?.selectionState?.selectionChangedSignal().start(next: { [weak self] value in
|
||||
subscriber.putNext(Int(self?.controller?.interaction?.selectionState?.count() ?? 0))
|
||||
}, error: { _ in }, completed: { })
|
||||
return ActionDisposable {
|
||||
disposable?.dispose()
|
||||
@ -1817,7 +1854,7 @@ final class MediaPickerContext: AttachmentMediaPickerContext {
|
||||
|
||||
var caption: Signal<NSAttributedString?, NoError> {
|
||||
return Signal { [weak self] subscriber in
|
||||
let disposable = self?.interaction?.editingState.forcedCaption().start(next: { caption in
|
||||
let disposable = self?.controller?.interaction?.editingState.forcedCaption().start(next: { caption in
|
||||
if let caption = caption as? NSAttributedString {
|
||||
subscriber.putNext(caption)
|
||||
} else {
|
||||
@ -1835,27 +1872,27 @@ final class MediaPickerContext: AttachmentMediaPickerContext {
|
||||
}
|
||||
|
||||
public var mainButtonState: Signal<AttachmentMainButtonState?, NoError> {
|
||||
return .single(nil)
|
||||
return .single(self.controller?.mainButtonState)
|
||||
}
|
||||
|
||||
init(interaction: MediaPickerInteraction) {
|
||||
self.interaction = interaction
|
||||
init(controller: MediaPickerScreen) {
|
||||
self.controller = controller
|
||||
}
|
||||
|
||||
func setCaption(_ caption: NSAttributedString) {
|
||||
self.interaction?.editingState.setForcedCaption(caption, skipUpdate: true)
|
||||
self.controller?.interaction?.editingState.setForcedCaption(caption, skipUpdate: true)
|
||||
}
|
||||
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode) {
|
||||
self.interaction?.sendSelected(nil, mode == .silently, mode == .whenOnline ? scheduleWhenOnlineTimestamp : nil, true, {})
|
||||
self.controller?.interaction?.sendSelected(nil, mode == .silently, mode == .whenOnline ? scheduleWhenOnlineTimestamp : nil, true, {})
|
||||
}
|
||||
|
||||
func schedule() {
|
||||
self.interaction?.schedule()
|
||||
self.controller?.interaction?.schedule()
|
||||
}
|
||||
|
||||
func mainButtonAction() {
|
||||
|
||||
self.controller?.mainButtonPressed()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1968,3 +2005,25 @@ public class MediaPickerGridSelectionGesture<T> : UIPanGestureRecognizer {
|
||||
self.updateIsScrollEnabled(true)
|
||||
}
|
||||
}
|
||||
|
||||
public func wallpaperMediaPickerController(
|
||||
context: AccountContext,
|
||||
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
|
||||
peer: EnginePeer,
|
||||
canDelete: Bool,
|
||||
completion: @escaping (PHAsset) -> Void = { _ in }
|
||||
) -> ViewController {
|
||||
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: nil, buttons: [.standalone], initialButton: .standalone, fromMenu: false, hasTextInput: false, makeEntityInputView: {
|
||||
return nil
|
||||
})
|
||||
controller.requestController = { [weak controller] _, present in
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let mediaPickerController = MediaPickerScreen(context: context, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .wallpaper), mainButtonState: canDelete ? AttachmentMainButtonState(text: presentationData.strings.Conversation_Theme_ResetWallpaper, font: .regular, background: .color(.clear), textColor: presentationData.theme.actionSheet.destructiveActionTextColor, isVisible: true, progress: .none, isEnabled: true) : nil, mainButtonAction: canDelete ? {
|
||||
let _ = context.engine.themes.setChatWallpaper(peerId: peer.id, wallpaper: nil).start()
|
||||
controller?.dismiss(animated: true)
|
||||
} : nil)
|
||||
mediaPickerController.customSelection = completion
|
||||
present(mediaPickerController, mediaPickerController.mediaPickerContext)
|
||||
}
|
||||
return controller
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ final class MediaPickerTitleView: UIView {
|
||||
let controlSize = self.segmentedControlNode.updateLayout(.stretchToFill(width: min(300.0, size.width - 36.0)), transition: .immediate)
|
||||
self.segmentedControlNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - controlSize.width) / 2.0), y: floorToScreenPixels((size.height - controlSize.height) / 2.0)), size: controlSize)
|
||||
|
||||
let titleSize = self.titleNode.updateLayout(CGSize(width: 160.0, height: 44.0))
|
||||
let titleSize = self.titleNode.updateLayout(CGSize(width: 210.0, height: 44.0))
|
||||
self.titleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: floorToScreenPixels((size.height - titleSize.height) / 2.0)), size: titleSize)
|
||||
}
|
||||
}
|
||||
|
@ -645,7 +645,7 @@ private final class PremiumGiftScreenComponent: CombinedComponent {
|
||||
price = nil
|
||||
}
|
||||
let buttonText = presentationData.strings.Premium_Gift_GiftSubscription(price ?? "—").string
|
||||
self.buttonStatePromise.set(.single(AttachmentMainButtonState(text: buttonText, background: .premium, textColor: .white, isVisible: true, progress: self.inProgress ? .center : .none, isEnabled: true)))
|
||||
self.buttonStatePromise.set(.single(AttachmentMainButtonState(text: buttonText, font: .bold, background: .premium, textColor: .white, isVisible: true, progress: self.inProgress ? .center : .none, isEnabled: true)))
|
||||
}
|
||||
|
||||
func buy() {
|
||||
|
@ -112,6 +112,7 @@ swift_library(
|
||||
"//submodules/FeaturedStickersScreen:FeaturedStickersScreen",
|
||||
"//submodules/MediaPickerUI:MediaPickerUI",
|
||||
"//submodules/ImageBlur:ImageBlur",
|
||||
"//submodules/AttachmentUI:AttachmentUI",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -309,6 +309,9 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
|
||||
if let node = node {
|
||||
contentSize.height += node.frame.size.height
|
||||
}
|
||||
if item.reaction == nil {
|
||||
contentSize.height += 34.0
|
||||
}
|
||||
insets = itemListNeighborsGroupedInsets(neighbors, params)
|
||||
|
||||
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
||||
@ -333,7 +336,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
|
||||
|
||||
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize)
|
||||
|
||||
var topOffset: CGFloat = 16.0
|
||||
var topOffset: CGFloat = 16.0 + 17.0
|
||||
if let node = node {
|
||||
strongSelf.messageNode = node
|
||||
if node.supernode == nil {
|
||||
|
@ -9,6 +9,7 @@ import LegacyComponents
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import AccountContext
|
||||
import AttachmentUI
|
||||
|
||||
private func availableGradients(theme: PresentationTheme) -> [[UInt32]] {
|
||||
if theme.overallDarkAppearance {
|
||||
@ -102,7 +103,7 @@ private func availableColors(theme: PresentationTheme) -> [UInt32] {
|
||||
}
|
||||
}
|
||||
|
||||
public final class ThemeColorsGridController: ViewController {
|
||||
public final class ThemeColorsGridController: ViewController, AttachmentContainable {
|
||||
public enum Mode {
|
||||
case `default`
|
||||
case peer(EnginePeer)
|
||||
@ -145,14 +146,17 @@ public final class ThemeColorsGridController: ViewController {
|
||||
|
||||
private var previousContentOffset: GridNodeVisibleContentOffset?
|
||||
|
||||
public init(context: AccountContext, mode: Mode = .default) {
|
||||
fileprivate let mainButtonStatePromise = Promise<AttachmentMainButtonState?>(nil)
|
||||
|
||||
var pushController: (ViewController) -> Void = { _ in }
|
||||
|
||||
public init(context: AccountContext, mode: Mode = .default, canDelete: Bool = false) {
|
||||
self.context = context
|
||||
self.mode = mode
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
||||
|
||||
self.title = self.presentationData.strings.WallpaperColors_Title
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
|
||||
self.scrollToTop = { [weak self] in
|
||||
@ -174,6 +178,22 @@ public final class ThemeColorsGridController: ViewController {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if case .peer = mode {
|
||||
self.title = self.presentationData.strings.Conversation_Theme_ChooseColorTitle
|
||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed))
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Conversation_Theme_SetCustomColor, style: .plain, target: self, action: #selector(self.customPressed))
|
||||
} else {
|
||||
self.title = self.presentationData.strings.WallpaperColors_Title
|
||||
}
|
||||
|
||||
self.pushController = { [weak self] controller in
|
||||
self?.push(controller)
|
||||
}
|
||||
|
||||
if canDelete {
|
||||
self.mainButtonStatePromise.set(.single(AttachmentMainButtonState(text: self.presentationData.strings.Conversation_Theme_ResetWallpaper, font: .regular, background: .color(.clear), textColor: self.presentationData.theme.actionSheet.destructiveActionTextColor, isVisible: true, progress: .none, isEnabled: true)))
|
||||
}
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
@ -184,6 +204,14 @@ public final class ThemeColorsGridController: ViewController {
|
||||
self.presentationDataDisposable?.dispose()
|
||||
}
|
||||
|
||||
@objc private func cancelPressed() {
|
||||
self.dismiss()
|
||||
}
|
||||
|
||||
@objc private func customPressed() {
|
||||
self.presentColorPicker()
|
||||
}
|
||||
|
||||
private func updateThemeAndStrings() {
|
||||
self.title = self.presentationData.strings.WallpaperColors_Title
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
@ -194,56 +222,68 @@ public final class ThemeColorsGridController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
private func presentColorPicker() {
|
||||
let _ = (self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings])
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] sharedData in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings]?.get(PresentationThemeSettings.self) ?? PresentationThemeSettings.defaultSettings
|
||||
|
||||
let autoNightModeTriggered = strongSelf.presentationData.autoNightModeTriggered
|
||||
let themeReference: PresentationThemeReference
|
||||
if autoNightModeTriggered {
|
||||
themeReference = settings.automaticThemeSwitchSetting.theme
|
||||
} else {
|
||||
themeReference = settings.theme
|
||||
}
|
||||
|
||||
let controller = ThemeAccentColorController(context: strongSelf.context, mode: .background(themeReference: themeReference), resultMode: strongSelf.mode.colorPickerMode)
|
||||
controller.completion = { [weak self] in
|
||||
if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
var controllers = navigationController.viewControllers
|
||||
controllers = controllers.filter { controller in
|
||||
if controller is ThemeColorsGridController {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
navigationController.setViewControllers(controllers, animated: false)
|
||||
controllers = controllers.filter { controller in
|
||||
if controller is ThemeAccentColorController {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
}
|
||||
}
|
||||
strongSelf.pushController(controller)
|
||||
})
|
||||
}
|
||||
|
||||
public override func loadDisplayNode() {
|
||||
self.displayNode = ThemeColorsGridControllerNode(context: self.context, presentationData: self.presentationData, controller: self, gradients: availableGradients(theme: self.presentationData.theme), colors: availableColors(theme: self.presentationData.theme), push: { [weak self] controller in
|
||||
self?.push(controller)
|
||||
self?.pushController(controller)
|
||||
}, pop: { [weak self] in
|
||||
if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
let _ = navigationController.popViewController(animated: true)
|
||||
}
|
||||
}, presentColorPicker: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
let _ = (strongSelf.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings])
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] sharedData in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings]?.get(PresentationThemeSettings.self) ?? PresentationThemeSettings.defaultSettings
|
||||
|
||||
let autoNightModeTriggered = strongSelf.presentationData.autoNightModeTriggered
|
||||
let themeReference: PresentationThemeReference
|
||||
if autoNightModeTriggered {
|
||||
themeReference = settings.automaticThemeSwitchSetting.theme
|
||||
} else {
|
||||
themeReference = settings.theme
|
||||
}
|
||||
|
||||
let controller = ThemeAccentColorController(context: strongSelf.context, mode: .background(themeReference: themeReference), resultMode: strongSelf.mode.colorPickerMode)
|
||||
controller.completion = { [weak self] in
|
||||
if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
var controllers = navigationController.viewControllers
|
||||
controllers = controllers.filter { controller in
|
||||
if controller is ThemeColorsGridController {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
navigationController.setViewControllers(controllers, animated: false)
|
||||
controllers = controllers.filter { controller in
|
||||
if controller is ThemeAccentColorController {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
}
|
||||
}
|
||||
strongSelf.push(controller)
|
||||
})
|
||||
strongSelf.presentColorPicker()
|
||||
}
|
||||
})
|
||||
|
||||
let transitionOffset: CGFloat
|
||||
switch self.mode {
|
||||
case .default:
|
||||
transitionOffset = 30.0
|
||||
case .peer:
|
||||
transitionOffset = 2.0
|
||||
}
|
||||
|
||||
self.controllerNode.gridNode.visibleContentOffsetChanged = { [weak self] offset in
|
||||
if let strongSelf = self {
|
||||
var previousContentOffsetValue: CGFloat?
|
||||
@ -253,12 +293,12 @@ public final class ThemeColorsGridController: ViewController {
|
||||
switch offset {
|
||||
case let .known(value):
|
||||
let transition: ContainedViewLayoutTransition
|
||||
if let previousContentOffsetValue = previousContentOffsetValue, value <= 0.0, previousContentOffsetValue > 30.0 {
|
||||
if let previousContentOffsetValue = previousContentOffsetValue, value <= 0.0, previousContentOffsetValue > transitionOffset {
|
||||
transition = .animated(duration: 0.2, curve: .easeInOut)
|
||||
} else {
|
||||
transition = .immediate
|
||||
}
|
||||
strongSelf.navigationBar?.updateBackgroundAlpha(min(30.0, value) / 30.0, transition: transition)
|
||||
strongSelf.navigationBar?.updateBackgroundAlpha(min(transitionOffset, value) / transitionOffset, transition: transition)
|
||||
case .unknown, .none:
|
||||
strongSelf.navigationBar?.updateBackgroundAlpha(1.0, transition: .immediate)
|
||||
}
|
||||
@ -279,4 +319,76 @@ public final class ThemeColorsGridController: ViewController {
|
||||
|
||||
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
||||
}
|
||||
|
||||
@objc fileprivate func mainButtonPressed() {
|
||||
guard case let .peer(peer) = self.mode else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = self.context.engine.themes.setChatWallpaper(peerId: peer.id, wallpaper: nil).start()
|
||||
self.dismiss(animated: true)
|
||||
}
|
||||
|
||||
public var requestAttachmentMenuExpansion: () -> Void = {}
|
||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||
public var cancelPanGesture: () -> Void = { }
|
||||
public var isContainerPanning: () -> Bool = { return false }
|
||||
public var isContainerExpanded: () -> Bool = { return false }
|
||||
|
||||
public var mediaPickerContext: AttachmentMediaPickerContext? {
|
||||
return ThemeColorsGridContext(controller: self)
|
||||
}
|
||||
}
|
||||
|
||||
private final class ThemeColorsGridContext: AttachmentMediaPickerContext {
|
||||
private weak var controller: ThemeColorsGridController?
|
||||
|
||||
var selectionCount: Signal<Int, NoError> {
|
||||
return .single(0)
|
||||
}
|
||||
|
||||
var caption: Signal<NSAttributedString?, NoError> {
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
public var loadingProgress: Signal<CGFloat?, NoError> {
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
public var mainButtonState: Signal<AttachmentMainButtonState?, NoError> {
|
||||
return self.controller?.mainButtonStatePromise.get() ?? .single(nil)
|
||||
}
|
||||
|
||||
init(controller: ThemeColorsGridController) {
|
||||
self.controller = controller
|
||||
}
|
||||
|
||||
func setCaption(_ caption: NSAttributedString) {
|
||||
}
|
||||
|
||||
func send(mode: AttachmentMediaPickerSendMode, attachmentMode: AttachmentMediaPickerAttachmentMode) {
|
||||
}
|
||||
|
||||
func schedule() {
|
||||
}
|
||||
|
||||
func mainButtonAction() {
|
||||
self.controller?.mainButtonPressed()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func standaloneColorPickerController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer, canDelete: Bool, push: @escaping (ViewController) -> Void) -> ViewController {
|
||||
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: nil, buttons: [.standalone], initialButton: .standalone, fromMenu: false, hasTextInput: false, makeEntityInputView: {
|
||||
return nil
|
||||
})
|
||||
controller.requestController = { _, present in
|
||||
let colorPickerController = ThemeColorsGridController(context: context, mode: .peer(peer), canDelete: canDelete)
|
||||
colorPickerController.pushController = { controller in
|
||||
push(controller)
|
||||
}
|
||||
present(colorPickerController, colorPickerController.mediaPickerContext)
|
||||
}
|
||||
return controller
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
|
||||
|
||||
let ready = ValuePromise<Bool>()
|
||||
|
||||
private var backgroundNode: ASDisplayNode
|
||||
private var topBackgroundNode: ASDisplayNode
|
||||
private var separatorNode: ASDisplayNode
|
||||
|
||||
private let customColorItemNode: ItemListActionItemNode
|
||||
@ -102,8 +102,8 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
|
||||
self.rightOverlayNode = ASDisplayNode()
|
||||
self.rightOverlayNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
self.topBackgroundNode = ASDisplayNode()
|
||||
self.topBackgroundNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||
@ -121,9 +121,14 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
|
||||
|
||||
self.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor
|
||||
|
||||
self.gridNode.addSubnode(self.backgroundNode)
|
||||
self.gridNode.addSubnode(self.separatorNode)
|
||||
self.gridNode.addSubnode(self.customColorItemNode)
|
||||
if case .default = controller.mode {
|
||||
self.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor
|
||||
self.gridNode.addSubnode(self.topBackgroundNode)
|
||||
self.gridNode.addSubnode(self.separatorNode)
|
||||
self.gridNode.addSubnode(self.customColorItemNode)
|
||||
} else {
|
||||
self.backgroundColor = presentationData.theme.list.plainBackgroundColor
|
||||
}
|
||||
self.addSubnode(self.gridNode)
|
||||
|
||||
let previousEntries = Atomic<[ThemeColorsGridControllerEntry]?>(value: nil)
|
||||
@ -240,9 +245,13 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
|
||||
func updatePresentationData(_ presentationData: PresentationData) {
|
||||
self.presentationData = presentationData
|
||||
|
||||
self.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor
|
||||
self.leftOverlayNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
self.rightOverlayNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
if let controller = self.controller, case .default = controller.mode {
|
||||
self.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor
|
||||
self.leftOverlayNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
self.rightOverlayNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor
|
||||
} else {
|
||||
self.backgroundColor = presentationData.theme.list.plainBackgroundColor
|
||||
}
|
||||
|
||||
self.customColorItem = ItemListActionItem(presentationData: ItemListPresentationData(presentationData), title: presentationData.strings.WallpaperColors_SetCustomColor, kind: .generic, alignment: .natural, sectionId: 0, style: .blocks, action: { [weak self] in
|
||||
self?.presentColorPicker()
|
||||
@ -272,6 +281,9 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
guard let controller = self.controller else {
|
||||
return
|
||||
}
|
||||
let hadValidLayout = self.validLayout != nil
|
||||
|
||||
var insets = layout.insets(options: [.input])
|
||||
@ -280,7 +292,18 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
|
||||
insets.right = layout.safeInsets.right
|
||||
let scrollIndicatorInsets = insets
|
||||
|
||||
let minSpacing: CGFloat = 8.0
|
||||
let itemsPerRow: Int
|
||||
if case .compact = layout.metrics.widthClass {
|
||||
switch layout.orientation {
|
||||
case .portrait:
|
||||
itemsPerRow = 3
|
||||
case .landscape:
|
||||
itemsPerRow = 5
|
||||
}
|
||||
} else {
|
||||
itemsPerRow = 3
|
||||
}
|
||||
|
||||
let referenceImageSize: CGSize
|
||||
let screenWidth = min(layout.size.width, layout.size.height)
|
||||
if screenWidth >= 375.0 {
|
||||
@ -288,29 +311,56 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
|
||||
} else {
|
||||
referenceImageSize = CGSize(width: 91.0, height: 91.0)
|
||||
}
|
||||
let imageCount = Int((layout.size.width - insets.left - insets.right - minSpacing * 2.0) / (referenceImageSize.width + minSpacing))
|
||||
let imageSize = referenceImageSize.aspectFilled(CGSize(width: floor((layout.size.width - CGFloat(imageCount + 1) * minSpacing) / CGFloat(imageCount)), height: referenceImageSize.height))
|
||||
let spacing = floor((layout.size.width - CGFloat(imageCount) * imageSize.width) / CGFloat(imageCount + 1))
|
||||
|
||||
let width = layout.size.width - layout.safeInsets.left - layout.safeInsets.right
|
||||
let imageSize: CGSize
|
||||
let spacing: CGFloat
|
||||
var fillWidth: Bool?
|
||||
if case .peer = controller.mode {
|
||||
spacing = 1.0
|
||||
|
||||
let itemWidth = floorToScreenPixels((width - spacing * CGFloat(itemsPerRow - 1)) / CGFloat(itemsPerRow))
|
||||
imageSize = CGSize(width: itemWidth, height: itemWidth)
|
||||
fillWidth = true
|
||||
} else {
|
||||
let minSpacing = 8.0
|
||||
|
||||
imageSize = referenceImageSize.aspectFilled(CGSize(width: floor((width - CGFloat(itemsPerRow + 1) * minSpacing) / CGFloat(itemsPerRow)), height: referenceImageSize.height))
|
||||
spacing = floor((width - CGFloat(itemsPerRow) * imageSize.width) / CGFloat(itemsPerRow + 1))
|
||||
}
|
||||
|
||||
let buttonTopInset: CGFloat = 32.0
|
||||
let buttonHeight: CGFloat = 44.0
|
||||
let buttonBottomInset: CGFloat = 35.0
|
||||
|
||||
var buttonInset: CGFloat = buttonTopInset + buttonHeight + buttonBottomInset
|
||||
var buttonOffset = buttonInset + 10.0
|
||||
|
||||
var listInsets = insets
|
||||
if layout.size.width >= 375.0 {
|
||||
let inset = max(16.0, floor((layout.size.width - 674.0) / 2.0))
|
||||
listInsets.left += inset
|
||||
listInsets.right += inset
|
||||
|
||||
if self.leftOverlayNode.supernode == nil {
|
||||
self.gridNode.addSubnode(self.leftOverlayNode)
|
||||
}
|
||||
if self.rightOverlayNode.supernode == nil {
|
||||
self.gridNode.addSubnode(self.rightOverlayNode)
|
||||
if case .default = controller.mode {
|
||||
if layout.size.width >= 375.0 {
|
||||
let inset = max(16.0, floor((layout.size.width - 674.0) / 2.0))
|
||||
listInsets.left += inset
|
||||
listInsets.right += inset
|
||||
|
||||
if self.leftOverlayNode.supernode == nil {
|
||||
self.gridNode.addSubnode(self.leftOverlayNode)
|
||||
}
|
||||
if self.rightOverlayNode.supernode == nil {
|
||||
self.gridNode.addSubnode(self.rightOverlayNode)
|
||||
}
|
||||
} else {
|
||||
if self.leftOverlayNode.supernode != nil {
|
||||
self.leftOverlayNode.removeFromSupernode()
|
||||
}
|
||||
if self.rightOverlayNode.supernode != nil {
|
||||
self.rightOverlayNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if self.leftOverlayNode.supernode != nil {
|
||||
self.leftOverlayNode.removeFromSupernode()
|
||||
}
|
||||
if self.rightOverlayNode.supernode != nil {
|
||||
self.rightOverlayNode.removeFromSupernode()
|
||||
}
|
||||
self.customColorItemNode.isHidden = true
|
||||
buttonOffset = 0.0
|
||||
buttonInset = 0.0
|
||||
}
|
||||
|
||||
let makeColorLayout = self.customColorItemNode.asyncLayout()
|
||||
@ -318,14 +368,7 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
|
||||
let (colorLayout, colorApply) = makeColorLayout(self.customColorItem, params, ItemListNeighbors(top: .none, bottom: .none))
|
||||
colorApply()
|
||||
|
||||
let buttonTopInset: CGFloat = 32.0
|
||||
let buttonHeight: CGFloat = 44.0
|
||||
let buttonBottomInset: CGFloat = 35.0
|
||||
|
||||
let buttonInset: CGFloat = buttonTopInset + buttonHeight + buttonBottomInset
|
||||
let buttonOffset = buttonInset + 10.0
|
||||
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -buttonOffset - 500.0), size: CGSize(width: layout.size.width, height: buttonInset + 500.0)))
|
||||
transition.updateFrame(node: self.topBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -buttonOffset - 500.0), size: CGSize(width: layout.size.width, height: buttonInset + 500.0)))
|
||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -buttonOffset + buttonInset - UIScreenPixel), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
|
||||
transition.updateFrame(node: self.customColorItemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -buttonOffset + buttonTopInset), size: colorLayout.contentSize))
|
||||
|
||||
@ -334,7 +377,7 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
|
||||
|
||||
insets.top += spacing + buttonInset
|
||||
|
||||
self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: layout.size, insets: insets, scrollIndicatorInsets: scrollIndicatorInsets, preloadSize: 300.0, type: .fixed(itemSize: imageSize, fillWidth: nil, lineSpacing: spacing, itemSpacing: nil)), transition: transition), itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil), completion: { _ in })
|
||||
self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: layout.size, insets: insets, scrollIndicatorInsets: scrollIndicatorInsets, preloadSize: 300.0, type: .fixed(itemSize: imageSize, fillWidth: fillWidth, lineSpacing: spacing, itemSpacing: fillWidth != nil ? spacing : nil)), transition: transition), itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil), completion: { _ in })
|
||||
|
||||
self.gridNode.frame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)
|
||||
|
||||
@ -350,7 +393,7 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
|
||||
|
||||
self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: GridNodeScrollToItem(index: 0, position: .top(0.0), transition: .animated(duration: 0.25, curve: .easeInOut), directionHint: .up, adjustForSection: true, adjustForTopInset: true), updateLayout: nil, itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil), completion: { _ in })
|
||||
|
||||
self.backgroundNode.layer.animatePosition(from: self.backgroundNode.layer.position.offsetBy(dx: 0.0, dy: -offset), to: self.backgroundNode.layer.position, duration: duration)
|
||||
self.topBackgroundNode.layer.animatePosition(from: self.topBackgroundNode.layer.position.offsetBy(dx: 0.0, dy: -offset), to: self.topBackgroundNode.layer.position, duration: duration)
|
||||
self.separatorNode.layer.animatePosition(from: self.separatorNode.layer.position.offsetBy(dx: 0.0, dy: -offset), to: self.separatorNode.layer.position, duration: duration)
|
||||
self.customColorItemNode.layer.animatePosition(from: self.customColorItemNode.layer.position.offsetBy(dx: 0.0, dy: -offset), to: self.customColorItemNode.layer.position, duration: duration)
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ public final class ThemeGridController: ViewController {
|
||||
}
|
||||
}, presentGallery: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
let controller = MediaPickerScreen(context: strongSelf.context, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, true))
|
||||
let controller = MediaPickerScreen(context: strongSelf.context, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .wallpaper))
|
||||
controller.customSelection = { [weak self] asset in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
@ -3405,6 +3405,22 @@ func replayFinalState(
|
||||
for (space, _) in holesAtHistoryStart {
|
||||
transaction.removeHole(peerId: chatPeerId, threadId: nil, namespace: Namespaces.Message.Cloud, space: space, range: 1 ... id.id)
|
||||
}
|
||||
case let .setChatWallpaper(wallpaper):
|
||||
if chatPeerId == accountPeerId {
|
||||
transaction.updatePeerCachedData(peerIds: [message.id.peerId], update: { peerId, current in
|
||||
var current = current
|
||||
if current == nil {
|
||||
if peerId.namespace == Namespaces.Peer.CloudUser {
|
||||
current = CachedUserData()
|
||||
}
|
||||
}
|
||||
if let cachedData = current as? CachedUserData {
|
||||
return cachedData.withUpdatedWallpaper(wallpaper)
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
})
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
@ -13826,7 +13826,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.present(actionSheet, in: .window(.root))
|
||||
}
|
||||
|
||||
private func presentMediaPicker(subject: MediaPickerScreen.Subject = .assets(nil, false), saveEditedPhotos: Bool, bannedSendPhotos: (Int32, Bool)?, bannedSendVideos: (Int32, Bool)?, present: @escaping (MediaPickerScreen, AttachmentMediaPickerContext?) -> Void, updateMediaPickerContext: @escaping (AttachmentMediaPickerContext?) -> Void, completion: @escaping ([Any], Bool, Int32?, @escaping (String) -> UIView?, @escaping () -> Void) -> Void) {
|
||||
private func presentMediaPicker(subject: MediaPickerScreen.Subject = .assets(nil, .default), saveEditedPhotos: Bool, bannedSendPhotos: (Int32, Bool)?, bannedSendVideos: (Int32, Bool)?, present: @escaping (MediaPickerScreen, AttachmentMediaPickerContext?) -> Void, updateMediaPickerContext: @escaping (AttachmentMediaPickerContext?) -> Void, completion: @escaping ([Any], Bool, Int32?, @escaping (String) -> UIView?, @escaping () -> Void) -> Void) {
|
||||
guard let peer = self.presentationInterfaceState.renderedPeer?.peer else {
|
||||
return
|
||||
}
|
||||
@ -18511,7 +18511,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
let dismissControllers = { [weak self] in
|
||||
if let self, let navigationController = self.navigationController as? NavigationController {
|
||||
let controllers = navigationController.viewControllers.filter({ controller in
|
||||
if controller is WallpaperGalleryController || controller is MediaPickerScreen {
|
||||
if controller is WallpaperGalleryController || controller is AttachmentController {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@ -18520,23 +18520,32 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
let controller = MediaPickerScreen(context: strongSelf.context, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, true))
|
||||
controller.navigationPresentation = .modal
|
||||
controller.customSelection = { [weak self] asset in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let controller = WallpaperGalleryController(context: strongSelf.context, source: .asset(asset), mode: .peer(EnginePeer(peer), false))
|
||||
controller.navigationPresentation = .modal
|
||||
controller.apply = { [weak self] wallpaper, options, cropRect in
|
||||
if let strongSelf = self {
|
||||
uploadCustomPeerWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, cropRect: cropRect, peerId: peerId, completion: {
|
||||
dismissControllers()
|
||||
})
|
||||
}
|
||||
}
|
||||
strongSelf.push(controller)
|
||||
var canDelete = false
|
||||
if let cachedUserData = strongSelf.peerView?.cachedData as? CachedUserData {
|
||||
canDelete = cachedUserData.wallpaper != nil
|
||||
}
|
||||
let controller = wallpaperMediaPickerController(
|
||||
context: strongSelf.context,
|
||||
updatedPresentationData: strongSelf.updatedPresentationData,
|
||||
peer: EnginePeer(peer),
|
||||
canDelete: canDelete,
|
||||
completion: { asset in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let controller = WallpaperGalleryController(context: strongSelf.context, source: .asset(asset), mode: .peer(EnginePeer(peer), false))
|
||||
controller.navigationPresentation = .modal
|
||||
controller.apply = { [weak self] wallpaper, options, cropRect in
|
||||
if let strongSelf = self {
|
||||
uploadCustomPeerWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, cropRect: cropRect, peerId: peerId, completion: {
|
||||
dismissControllers()
|
||||
})
|
||||
}
|
||||
}
|
||||
strongSelf.push(controller)
|
||||
}
|
||||
)
|
||||
controller.navigationPresentation = .flatModal
|
||||
strongSelf.push(controller)
|
||||
},
|
||||
changeColor: {
|
||||
@ -18547,8 +18556,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.themeScreen = nil
|
||||
themeController.dimTapped()
|
||||
}
|
||||
let controller = ThemeColorsGridController(context: context, mode: .peer(EnginePeer(peer)))
|
||||
controller.navigationPresentation = .modal
|
||||
|
||||
var canDelete = false
|
||||
if let cachedUserData = strongSelf.peerView?.cachedData as? CachedUserData {
|
||||
canDelete = cachedUserData.wallpaper != nil
|
||||
}
|
||||
let controller = standaloneColorPickerController(context: strongSelf.context, peer: EnginePeer(peer), canDelete: canDelete, push: { [weak self] controller in
|
||||
if let strongSelf = self {
|
||||
strongSelf.push(controller)
|
||||
}
|
||||
})
|
||||
controller.navigationPresentation = .flatModal
|
||||
strongSelf.push(controller)
|
||||
},
|
||||
completion: { [weak self] emoticon in
|
||||
|
@ -675,7 +675,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
|
||||
let isLoading = json["is_progress_visible"] as? Bool
|
||||
let isEnabled = json["is_active"] as? Bool
|
||||
let state = AttachmentMainButtonState(text: text, background: .color(backgroundColor), textColor: textColor, isVisible: isVisible, progress: (isLoading ?? false) ? .side : .none, isEnabled: isEnabled ?? true)
|
||||
let state = AttachmentMainButtonState(text: text, font: .bold, background: .color(backgroundColor), textColor: textColor, isVisible: isVisible, progress: (isLoading ?? false) ? .side : .none, isEnabled: isEnabled ?? true)
|
||||
self.mainButtonState = state
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user