mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-15 18:59:54 +00:00
UI fixes
This commit is contained in:
parent
1270d99fc2
commit
a43f219f13
@ -6,12 +6,12 @@
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "SearchBarIconLight@2x.png",
|
||||
"filename" : "IconSearch@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "SearchBarIconLight@3x.png",
|
||||
"filename" : "IconSearch@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
|
||||
BIN
Images.xcassets/Components/Search Bar/Loupe.imageset/IconSearch@2x.png
vendored
Normal file
BIN
Images.xcassets/Components/Search Bar/Loupe.imageset/IconSearch@2x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 964 B |
BIN
Images.xcassets/Components/Search Bar/Loupe.imageset/IconSearch@3x.png
vendored
Normal file
BIN
Images.xcassets/Components/Search Bar/Loupe.imageset/IconSearch@3x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 330 B |
Binary file not shown.
|
Before Width: | Height: | Size: 476 B |
@ -57,6 +57,8 @@
|
||||
096C98C021787C6700C211FF /* TGBridgeAudioEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 096C98BC21787C6600C211FF /* TGBridgeAudioEncoder.h */; };
|
||||
096C98C121787C6700C211FF /* TGBridgeAudioDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 096C98BD21787C6700C211FF /* TGBridgeAudioDecoder.h */; };
|
||||
096C98C221787C6700C211FF /* TGBridgeAudioDecoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 096C98BE21787C6700C211FF /* TGBridgeAudioDecoder.mm */; };
|
||||
09749BC321F0DFFD008FDDE9 /* StickersChatInputContextPanelNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09749BC221F0DFFD008FDDE9 /* StickersChatInputContextPanelNode.swift */; };
|
||||
09749BC521F0E024008FDDE9 /* StickersChatInputPanelItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09749BC421F0E024008FDDE9 /* StickersChatInputPanelItem.swift */; };
|
||||
09797873210633CD0077D77F /* InstantPageSettingsButtonItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09797872210633CD0077D77F /* InstantPageSettingsButtonItemNode.swift */; };
|
||||
0979787C210642CB0077D77F /* WebEmbedPlayerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0979787B210642CB0077D77F /* WebEmbedPlayerNode.swift */; };
|
||||
0979787E210646C00077D77F /* YoutubeEmbedImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0979787D210646C00077D77F /* YoutubeEmbedImplementation.swift */; };
|
||||
@ -1182,6 +1184,8 @@
|
||||
096C98BC21787C6600C211FF /* TGBridgeAudioEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGBridgeAudioEncoder.h; sourceTree = "<group>"; };
|
||||
096C98BD21787C6700C211FF /* TGBridgeAudioDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGBridgeAudioDecoder.h; sourceTree = "<group>"; };
|
||||
096C98BE21787C6700C211FF /* TGBridgeAudioDecoder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TGBridgeAudioDecoder.mm; sourceTree = "<group>"; };
|
||||
09749BC221F0DFFD008FDDE9 /* StickersChatInputContextPanelNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickersChatInputContextPanelNode.swift; sourceTree = "<group>"; };
|
||||
09749BC421F0E024008FDDE9 /* StickersChatInputPanelItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickersChatInputPanelItem.swift; sourceTree = "<group>"; };
|
||||
09797872210633CD0077D77F /* InstantPageSettingsButtonItemNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantPageSettingsButtonItemNode.swift; sourceTree = "<group>"; };
|
||||
0979787B210642CB0077D77F /* WebEmbedPlayerNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebEmbedPlayerNode.swift; sourceTree = "<group>"; };
|
||||
0979787D210646C00077D77F /* YoutubeEmbedImplementation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YoutubeEmbedImplementation.swift; sourceTree = "<group>"; };
|
||||
@ -2912,6 +2916,8 @@
|
||||
children = (
|
||||
D049EAE11E447AD500A2CD3A /* HorizontalStickersChatContextPanelNode.swift */,
|
||||
D049EAE31E44949F00A2CD3A /* HorizontalStickerGridItem.swift */,
|
||||
09749BC221F0DFFD008FDDE9 /* StickersChatInputContextPanelNode.swift */,
|
||||
09749BC421F0E024008FDDE9 /* StickersChatInputPanelItem.swift */,
|
||||
);
|
||||
name = Stickers;
|
||||
sourceTree = "<group>";
|
||||
@ -5168,6 +5174,7 @@
|
||||
092F36902157AB46001A9F49 /* ItemListCallListItem.swift in Sources */,
|
||||
D0EC6CC61EB9F58800EBF1C3 /* PresenceStrings.swift in Sources */,
|
||||
D0EC6CC71EB9F58800EBF1C3 /* PeerNotificationSoundStrings.swift in Sources */,
|
||||
09749BC321F0DFFD008FDDE9 /* StickersChatInputContextPanelNode.swift in Sources */,
|
||||
D01C06C01FBF118A001561AB /* MessageUtils.swift in Sources */,
|
||||
D0104F281F47171F004E4881 /* InstantPageGalleryController.swift in Sources */,
|
||||
D0EC6CC81EB9F58800EBF1C3 /* ProgressiveImage.swift in Sources */,
|
||||
@ -5724,6 +5731,7 @@
|
||||
D0EEE9A12165585F001292A6 /* DocumentPreviewController.swift in Sources */,
|
||||
D0EC6DCE1EB9F58900EBF1C3 /* HorizontalStickersChatContextPanelNode.swift in Sources */,
|
||||
D0BCC3D2203F0A6C008126C2 /* StringForMessageTimestampStatus.swift in Sources */,
|
||||
09749BC521F0E024008FDDE9 /* StickersChatInputPanelItem.swift in Sources */,
|
||||
D0EC6DCF1EB9F58900EBF1C3 /* HorizontalStickerGridItem.swift in Sources */,
|
||||
D0EC6DD01EB9F58900EBF1C3 /* HashtagChatInputContextPanelNode.swift in Sources */,
|
||||
09B4EE5621A8149C00847FA6 /* PermissionInfoItem.swift in Sources */,
|
||||
|
||||
@ -5350,6 +5350,8 @@ public final class ChatController: TelegramController, KeyShortcutResponder, Gal
|
||||
}
|
||||
|
||||
private func openUrl(_ url: String, concealed: Bool) {
|
||||
self.commitPurposefulAction()
|
||||
|
||||
let openImpl: () -> Void = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
||||
@ -181,9 +181,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.controller = controller
|
||||
|
||||
self.backgroundNode = ChatBackgroundNode()
|
||||
//self.backgroundNode.isLayerBacked = true
|
||||
self.backgroundNode.displaysAsynchronously = false
|
||||
//self.backgroundNode.clipsToBounds = true
|
||||
|
||||
self.titleAccessoryPanelContainer = ChatControllerTitlePanelNodeContainer()
|
||||
self.titleAccessoryPanelContainer.clipsToBounds = true
|
||||
|
||||
@ -996,7 +996,8 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
||||
markAllChatsAsReadInteractively(transaction: transaction, viewTracker: account.viewTracker)
|
||||
}
|
||||
}
|
||||
let _ = signal.start(completed: { [weak self] in
|
||||
let _ = (signal
|
||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||
self?.donePressed()
|
||||
})
|
||||
} else if !peerIds.isEmpty {
|
||||
@ -1034,7 +1035,8 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
||||
progressDisposable.dispose()
|
||||
}
|
||||
}
|
||||
let _ = signal.start(completed: {
|
||||
let _ = (signal
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
self?.donePressed()
|
||||
})
|
||||
}))
|
||||
|
||||
@ -507,7 +507,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
switch status {
|
||||
case let .fetchStatus(fetchStatus):
|
||||
switch fetchStatus {
|
||||
case let .Fetching(isActive, progress):
|
||||
case let .Fetching(_, progress):
|
||||
let adjustedProgress = max(progress, 0.027)
|
||||
state = .progress(color: bubbleTheme.mediaOverlayControlForegroundColor, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true)
|
||||
case .Local:
|
||||
|
||||
@ -641,6 +641,16 @@ private func preparedContactListNodeTransition(account: Account, from fromEntrie
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
outer: for entry in toEntries {
|
||||
switch entry {
|
||||
case .sort:
|
||||
shouldFixScroll = true
|
||||
break outer
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var scrollToItem: ListViewScrollToItem?
|
||||
|
||||
@ -103,18 +103,22 @@ func fetchPhotoLibraryResource(localIdentifier: String) -> Signal<MediaResourceD
|
||||
}
|
||||
}
|
||||
|
||||
func fetchPhotoLibraryImage(localIdentifier: String) -> Signal<(UIImage, Bool)?, NoError> {
|
||||
func fetchPhotoLibraryImage(localIdentifier: String, thumbnail: Bool) -> Signal<(UIImage, Bool)?, NoError> {
|
||||
return Signal { subscriber in
|
||||
let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [localIdentifier], options: nil)
|
||||
let requestId = Atomic<RequestId>(value: RequestId())
|
||||
if fetchResult.count != 0 {
|
||||
let asset = fetchResult.object(at: 0)
|
||||
let option = PHImageRequestOptions()
|
||||
option.deliveryMode = .opportunistic
|
||||
option.deliveryMode = .highQualityFormat
|
||||
if thumbnail {
|
||||
option.resizeMode = .fast
|
||||
}
|
||||
option.isNetworkAccessAllowed = true
|
||||
option.isSynchronous = false
|
||||
|
||||
let requestIdValue = PHImageManager.default().requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFill, options: option, resultHandler: { (image, info) -> Void in
|
||||
let targetSize: CGSize = thumbnail ? CGSize(width: 128.0, height: 128.0) : PHImageManagerMaximumSize
|
||||
let requestIdValue = PHImageManager.default().requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFill, options: option, resultHandler: { (image, info) -> Void in
|
||||
Queue.concurrentDefaultQueue().async {
|
||||
requestId.with { current -> Void in
|
||||
if !current.invalidated {
|
||||
@ -123,15 +127,8 @@ func fetchPhotoLibraryImage(localIdentifier: String) -> Signal<(UIImage, Bool)?,
|
||||
}
|
||||
}
|
||||
if let image = image {
|
||||
var isThumbnail = true
|
||||
if let info = info, let degraded = info[PHImageResultIsDegradedKey] {
|
||||
isThumbnail = (degraded as AnyObject).boolValue!
|
||||
}
|
||||
subscriber.putNext((image, isThumbnail))
|
||||
if !isThumbnail {
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
|
||||
subscriber.putNext((image, thumbnail))
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -8,7 +8,7 @@ class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGestureRecog
|
||||
var navigationBar: NavigationBar?
|
||||
let footerNode: GalleryFooterNode
|
||||
var currentThumbnailContainerNode: GalleryThumbnailContainerNode?
|
||||
var toolbarNode: ASDisplayNode?
|
||||
var overlayNode: ASDisplayNode?
|
||||
var transitionDataForCentralItem: (() -> ((ASDisplayNode, () -> UIView?)?, (UIView) -> Void)?)?
|
||||
var dismiss: (() -> Void)?
|
||||
|
||||
@ -255,10 +255,6 @@ class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGestureRecog
|
||||
self.currentThumbnailContainerNode?.alpha = 1.0
|
||||
})
|
||||
|
||||
if let toolbarNode = self.toolbarNode {
|
||||
toolbarNode.layer.animatePosition(from: CGPoint(x: 0.0, y: self.bounds.size.height), to: CGPoint(), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
}
|
||||
|
||||
if animateContent {
|
||||
self.scrollView.layer.animateBounds(from: self.scrollView.layer.bounds.offsetBy(dx: 0.0, dy: -self.scrollView.layer.bounds.size.height), to: self.scrollView.layer.bounds, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
}
|
||||
@ -292,10 +288,6 @@ class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGestureRecog
|
||||
intermediateCompletion()
|
||||
})
|
||||
|
||||
if let toolbarNode = self.toolbarNode {
|
||||
toolbarNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: self.bounds.size.height), duration: 0.25, timingFunction: kCAMediaTimingFunctionLinear, removeOnCompletion: false, additive: true)
|
||||
}
|
||||
|
||||
if animateContent {
|
||||
contentAnimationCompleted = false
|
||||
self.scrollView.layer.animateBounds(from: self.scrollView.layer.bounds, to: self.scrollView.layer.bounds.offsetBy(dx: 0.0, dy: -self.scrollView.layer.bounds.size.height), duration: 0.25, timingFunction: kCAMediaTimingFunctionLinear, removeOnCompletion: false, completion: { _ in
|
||||
@ -327,8 +319,8 @@ class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGestureRecog
|
||||
|
||||
self.updateDismissTransition(transition)
|
||||
|
||||
if let toolbarNode = toolbarNode {
|
||||
toolbarNode.alpha = transition
|
||||
if let overlayNode = self.overlayNode {
|
||||
overlayNode.alpha = transition
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -60,6 +60,7 @@ final class HorizontalStickerGridItemNode: GridItemNode {
|
||||
|
||||
super.init()
|
||||
|
||||
self.imageNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
|
||||
self.addSubnode(self.imageNode)
|
||||
}
|
||||
|
||||
|
||||
@ -8,6 +8,38 @@ import SwiftSignalKit
|
||||
final class HorizontalStickersChatContextPanelInteraction {
|
||||
var previewedStickerItem: StickerPackItem?
|
||||
}
|
||||
private func backgroundCenterImage(_ theme: PresentationTheme) -> UIImage? {
|
||||
return generateImage(CGSize(width: 30.0, height: 82.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setStrokeColor(theme.list.itemPlainSeparatorColor.cgColor)
|
||||
context.setFillColor(theme.list.plainBackgroundColor.cgColor)
|
||||
let lineWidth = UIScreenPixel
|
||||
context.setLineWidth(lineWidth)
|
||||
|
||||
context.translateBy(x: 460.5, y: 364)
|
||||
let _ = try? drawSvgPath(context, path: "M-490.476836,-365 L-394.167708,-365 L-394.167708,-291.918214 C-394.167708,-291.918214 -383.538396,-291.918214 -397.691655,-291.918214 C-402.778486,-291.918214 -424.555168,-291.918214 -434.037301,-291.918214 C-440.297129,-291.918214 -440.780682,-283.5 -445.999879,-283.5 C-450.393041,-283.5 -452.491241,-291.918214 -456.502636,-291.918214 C-465.083339,-291.918214 -476.209155,-291.918214 -483.779021,-291.918214 C-503.033963,-291.918214 -490.476836,-291.918214 -490.476836,-291.918214 L-490.476836,-365 ")
|
||||
context.fillPath()
|
||||
context.translateBy(x: 0.0, y: lineWidth / 2.0)
|
||||
let _ = try? drawSvgPath(context, path: "M-490.476836,-365 L-394.167708,-365 L-394.167708,-291.918214 C-394.167708,-291.918214 -383.538396,-291.918214 -397.691655,-291.918214 C-402.778486,-291.918214 -424.555168,-291.918214 -434.037301,-291.918214 C-440.297129,-291.918214 -440.780682,-283.5 -445.999879,-283.5 C-450.393041,-283.5 -452.491241,-291.918214 -456.502636,-291.918214 C-465.083339,-291.918214 -476.209155,-291.918214 -483.779021,-291.918214 C-503.033963,-291.918214 -490.476836,-291.918214 -490.476836,-291.918214 L-490.476836,-365 ")
|
||||
context.strokePath()
|
||||
context.translateBy(x: -460.5, y: -lineWidth / 2.0 - 364.0)
|
||||
context.move(to: CGPoint(x: 0.0, y: lineWidth / 2.0))
|
||||
context.addLine(to: CGPoint(x: size.width, y: lineWidth / 2.0))
|
||||
context.strokePath()
|
||||
})
|
||||
}
|
||||
private func backgroundLeftImage(_ theme: PresentationTheme) -> UIImage? {
|
||||
return generateImage(CGSize(width: 8.0, height: 16.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setStrokeColor(theme.list.itemPlainSeparatorColor.cgColor)
|
||||
context.setFillColor(theme.list.plainBackgroundColor.cgColor)
|
||||
let lineWidth = UIScreenPixel
|
||||
context.setLineWidth(lineWidth)
|
||||
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: size.height, height: size.height)))
|
||||
context.strokeEllipse(in: CGRect(origin: CGPoint(x: lineWidth / 2.0, y: lineWidth / 2.0), size: CGSize(width: size.height - lineWidth, height: size.height - lineWidth)))
|
||||
})?.stretchableImage(withLeftCapWidth: 8, topCapHeight: 8)
|
||||
}
|
||||
|
||||
private struct StickerEntry: Identifiable, Comparable {
|
||||
let index: Int
|
||||
@ -55,12 +87,15 @@ private func preparedGridEntryTransition(account: Account, from fromEntries: [St
|
||||
final class HorizontalStickersChatContextPanelNode: ChatInputContextPanelNode {
|
||||
private var strings: PresentationStrings
|
||||
|
||||
private let backgroundLeftNode: ASImageNode
|
||||
private let backgroundNode: ASImageNode
|
||||
private let backgroundRightNode: ASImageNode
|
||||
private let clippingNode: ASDisplayNode
|
||||
private let gridNode: GridNode
|
||||
private let backgroundNode: ASDisplayNode
|
||||
|
||||
private var validLayout: (CGSize, CGFloat, CGFloat, ChatPresentationInterfaceState)?
|
||||
private var currentEntries: [StickerEntry]?
|
||||
private var queuedTransitions: [(StickerEntryTransition, Bool)] = []
|
||||
private var currentEntries: [StickerEntry] = []
|
||||
private var queuedTransitions: [StickerEntryTransition] = []
|
||||
|
||||
public var controllerInteraction: ChatControllerInteraction?
|
||||
private let stickersInteraction: HorizontalStickersChatContextPanelInteraction
|
||||
@ -70,22 +105,41 @@ final class HorizontalStickersChatContextPanelNode: ChatInputContextPanelNode {
|
||||
override init(account: Account, theme: PresentationTheme, strings: PresentationStrings) {
|
||||
self.strings = strings
|
||||
|
||||
self.gridNode = GridNode()
|
||||
self.gridNode.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||
self.gridNode.scrollView.alwaysBounceVertical = true
|
||||
self.backgroundNode = ASImageNode()
|
||||
self.backgroundNode.displayWithoutProcessing = true
|
||||
self.backgroundNode.displaysAsynchronously = false
|
||||
self.backgroundNode.image = backgroundCenterImage(theme)
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.backgroundColor = theme.list.plainBackgroundColor
|
||||
self.backgroundLeftNode = ASImageNode()
|
||||
self.backgroundLeftNode.displayWithoutProcessing = true
|
||||
self.backgroundLeftNode.displaysAsynchronously = false
|
||||
self.backgroundLeftNode.image = backgroundLeftImage(theme)
|
||||
|
||||
self.backgroundRightNode = ASImageNode()
|
||||
self.backgroundRightNode.displayWithoutProcessing = true
|
||||
self.backgroundRightNode.displaysAsynchronously = false
|
||||
self.backgroundRightNode.image = backgroundLeftImage(theme)
|
||||
self.backgroundRightNode.transform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
|
||||
|
||||
self.clippingNode = ASDisplayNode()
|
||||
self.clippingNode.clipsToBounds = true
|
||||
self.gridNode = GridNode()
|
||||
self.gridNode.transform = CATransform3DMakeRotation(-CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
|
||||
self.gridNode.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||
|
||||
self.stickersInteraction = HorizontalStickersChatContextPanelInteraction()
|
||||
|
||||
super.init(account: account, theme: theme, strings: strings)
|
||||
|
||||
self.placement = .overTextInput
|
||||
self.isOpaque = false
|
||||
self.clipsToBounds = true
|
||||
|
||||
self.addSubnode(self.gridNode)
|
||||
self.gridNode.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.backgroundLeftNode)
|
||||
self.addSubnode(self.backgroundRightNode)
|
||||
|
||||
self.addSubnode(self.clippingNode)
|
||||
self.clippingNode.addSubnode(self.gridNode)
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
@ -99,82 +153,81 @@ final class HorizontalStickersChatContextPanelNode: ChatInputContextPanelNode {
|
||||
}
|
||||
|
||||
if let itemNode = strongSelf.gridNode.itemNodeAtPoint(strongSelf.view.convert(point, to: strongSelf.gridNode.view)) as? HorizontalStickerGridItemNode, let item = itemNode.stickerItem {
|
||||
return strongSelf.account.postbox.transaction { transaction -> Bool in
|
||||
return getIsStickerSaved(transaction: transaction, fileId: item.file.fileId)
|
||||
}
|
||||
|> deliverOnMainQueue
|
||||
|> map { isStarred -> (ASDisplayNode, PeekControllerContent)? in
|
||||
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
||||
var menuItems: [PeekControllerMenuItem] = []
|
||||
menuItems = [
|
||||
PeekControllerMenuItem(title: strongSelf.strings.StickerPack_Send, color: .accent, font: .bold, action: {
|
||||
controllerInteraction.sendSticker(.standalone(media: item.file), true)
|
||||
}),
|
||||
PeekControllerMenuItem(title: isStarred ? strongSelf.strings.Stickers_RemoveFromFavorites : strongSelf.strings.Stickers_AddToFavorites, color: isStarred ? .destructive : .accent, action: {
|
||||
if let strongSelf = self {
|
||||
if isStarred {
|
||||
let _ = removeSavedSticker(postbox: strongSelf.account.postbox, mediaId: item.file.fileId).start()
|
||||
} else {
|
||||
let _ = addSavedSticker(postbox: strongSelf.account.postbox, network: strongSelf.account.network, file: item.file).start()
|
||||
}
|
||||
return strongSelf.account.postbox.transaction { transaction -> Bool in
|
||||
return getIsStickerSaved(transaction: transaction, fileId: item.file.fileId)
|
||||
}
|
||||
|> deliverOnMainQueue
|
||||
|> map { isStarred -> (ASDisplayNode, PeekControllerContent)? in
|
||||
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
||||
var menuItems: [PeekControllerMenuItem] = []
|
||||
menuItems = [
|
||||
PeekControllerMenuItem(title: strongSelf.strings.StickerPack_Send, color: .accent, font: .bold, action: {
|
||||
controllerInteraction.sendSticker(.standalone(media: item.file), true)
|
||||
}),
|
||||
PeekControllerMenuItem(title: isStarred ? strongSelf.strings.Stickers_RemoveFromFavorites : strongSelf.strings.Stickers_AddToFavorites, color: isStarred ? .destructive : .accent, action: {
|
||||
if let strongSelf = self {
|
||||
if isStarred {
|
||||
let _ = removeSavedSticker(postbox: strongSelf.account.postbox, mediaId: item.file.fileId).start()
|
||||
} else {
|
||||
let _ = addSavedSticker(postbox: strongSelf.account.postbox, network: strongSelf.account.network, file: item.file).start()
|
||||
}
|
||||
}),
|
||||
PeekControllerMenuItem(title: strongSelf.strings.StickerPack_ViewPack, color: .accent, action: {
|
||||
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
||||
loop: for attribute in item.file.attributes {
|
||||
switch attribute {
|
||||
case let .Sticker(_, packReference, _):
|
||||
if let packReference = packReference {
|
||||
let controller = StickerPackPreviewController(account: strongSelf.account, stickerPack: packReference, parentNavigationController: controllerInteraction.navigationController())
|
||||
controller.sendSticker = { file in
|
||||
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
||||
controllerInteraction.sendSticker(file, true)
|
||||
}
|
||||
}
|
||||
}),
|
||||
PeekControllerMenuItem(title: strongSelf.strings.StickerPack_ViewPack, color: .accent, action: {
|
||||
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
||||
loop: for attribute in item.file.attributes {
|
||||
switch attribute {
|
||||
case let .Sticker(_, packReference, _):
|
||||
if let packReference = packReference {
|
||||
let controller = StickerPackPreviewController(account: strongSelf.account, stickerPack: packReference, parentNavigationController: controllerInteraction.navigationController())
|
||||
controller.sendSticker = { file in
|
||||
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
||||
controllerInteraction.sendSticker(file, true)
|
||||
}
|
||||
|
||||
controllerInteraction.navigationController()?.view.window?.endEditing(true)
|
||||
controllerInteraction.presentController(controller, nil)
|
||||
}
|
||||
break loop
|
||||
default:
|
||||
break
|
||||
|
||||
controllerInteraction.navigationController()?.view.window?.endEditing(true)
|
||||
controllerInteraction.presentController(controller, nil)
|
||||
}
|
||||
break loop
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}),
|
||||
PeekControllerMenuItem(title: strongSelf.strings.Common_Cancel, color: .accent, action: {})
|
||||
]
|
||||
return (itemNode, StickerPreviewPeekContent(account: strongSelf.account, item: .pack(item), menu: menuItems))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}),
|
||||
PeekControllerMenuItem(title: strongSelf.strings.Common_Cancel, color: .accent, action: {})
|
||||
]
|
||||
return (itemNode, StickerPreviewPeekContent(account: strongSelf.account, item: .pack(item), menu: menuItems))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}, present: { [weak self] content, sourceNode in
|
||||
if let strongSelf = self {
|
||||
let controller = PeekController(theme: PeekControllerTheme(presentationTheme: strongSelf.theme), content: content, sourceNode: {
|
||||
return sourceNode
|
||||
})
|
||||
strongSelf.interfaceInteraction?.presentGlobalOverlayController(controller, nil)
|
||||
return controller
|
||||
}
|
||||
return nil
|
||||
}, updateContent: { [weak self] content in
|
||||
if let strongSelf = self {
|
||||
var item: StickerPackItem?
|
||||
if let content = content as? StickerPreviewPeekContent, case let .pack(contentItem) = content.item {
|
||||
item = contentItem
|
||||
}, present: { [weak self] content, sourceNode in
|
||||
if let strongSelf = self {
|
||||
let controller = PeekController(theme: PeekControllerTheme(presentationTheme: strongSelf.theme), content: content, sourceNode: {
|
||||
return sourceNode
|
||||
})
|
||||
strongSelf.interfaceInteraction?.presentGlobalOverlayController(controller, nil)
|
||||
return controller
|
||||
}
|
||||
return nil
|
||||
}, updateContent: { [weak self] content in
|
||||
if let strongSelf = self {
|
||||
var item: StickerPackItem?
|
||||
if let content = content as? StickerPreviewPeekContent, case let .pack(contentItem) = content.item {
|
||||
item = contentItem
|
||||
}
|
||||
strongSelf.updatePreviewingItem(item: item, animated: true)
|
||||
}
|
||||
strongSelf.updatePreviewingItem(item: item, animated: true)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
func updateResults(_ results: [TelegramMediaFile]) {
|
||||
let firstTime = self.currentEntries == nil
|
||||
let previousEntries = self.currentEntries ?? []
|
||||
let previousEntries = self.currentEntries
|
||||
var entries: [StickerEntry] = []
|
||||
for i in 0 ..< results.count {
|
||||
entries.append(StickerEntry(index: i, file: results[i]))
|
||||
@ -186,11 +239,11 @@ final class HorizontalStickersChatContextPanelNode: ChatInputContextPanelNode {
|
||||
}
|
||||
|
||||
let transition = preparedGridEntryTransition(account: self.account, from: previousEntries, to: entries, stickersInteraction: self.stickersInteraction, interfaceInteraction: self.interfaceInteraction!)
|
||||
self.enqueueTransition(transition, firstTime: firstTime)
|
||||
self.enqueueTransition(transition)
|
||||
}
|
||||
|
||||
private func enqueueTransition(_ transition: StickerEntryTransition, firstTime: Bool) {
|
||||
self.queuedTransitions.append((transition, firstTime))
|
||||
private func enqueueTransition(_ transition: StickerEntryTransition) {
|
||||
self.queuedTransitions.append(transition)
|
||||
if self.validLayout != nil {
|
||||
self.dequeueTransitions()
|
||||
}
|
||||
@ -198,71 +251,71 @@ final class HorizontalStickersChatContextPanelNode: ChatInputContextPanelNode {
|
||||
|
||||
private func dequeueTransitions() {
|
||||
while !self.queuedTransitions.isEmpty {
|
||||
let (transition, firstTime) = self.queuedTransitions.removeFirst()
|
||||
self.gridNode.transaction(GridNodeTransaction(deleteItems: transition.deletions, insertItems: transition.insertions, updateItems: transition.updates, scrollToItem: transition.scrollToItem, updateLayout: nil, itemTransition: .immediate, stationaryItems: transition.stationaryItems, updateFirstIndexInSectionOffset: transition.updateFirstIndexInSectionOffset), completion: { [weak self] _ in
|
||||
|
||||
if let strongSelf = self {
|
||||
strongSelf.backgroundNode.frame = CGRect(x: 0.0, y: 0.0, width: strongSelf.bounds.width, height: strongSelf.gridNode.scrollView.contentSize.height + 500.0)
|
||||
|
||||
if firstTime {
|
||||
let position = strongSelf.gridNode.layer.position
|
||||
let offset = strongSelf.gridNode.frame.height + strongSelf.gridNode.scrollView.contentOffset.y
|
||||
strongSelf.gridNode.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + offset), to: position, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in })
|
||||
}
|
||||
}
|
||||
})
|
||||
let transition = self.queuedTransitions.removeFirst()
|
||||
self.gridNode.transaction(GridNodeTransaction(deleteItems: transition.deletions, insertItems: transition.insertions, updateItems: transition.updates, scrollToItem: transition.scrollToItem, updateLayout: nil, itemTransition: .immediate, stationaryItems: transition.stationaryItems, updateFirstIndexInSectionOffset: transition.updateFirstIndexInSectionOffset), completion: { _ in })
|
||||
}
|
||||
}
|
||||
|
||||
private func topInsetForLayout(size: CGSize) -> CGFloat {
|
||||
let minimumItemHeights: CGFloat = floor(66.0 * 1.5)
|
||||
|
||||
return max(size.height - minimumItemHeights, 0.0)
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) {
|
||||
let hadValidLayout = self.validLayout != nil
|
||||
let sideInsets: CGFloat = 10.0 + leftInset
|
||||
let contentWidth = min(size.width - sideInsets - sideInsets, max(24.0, CGFloat(self.currentEntries.count) * 66.0 + 6.0))
|
||||
|
||||
var contentLeftInset: CGFloat = 40.0
|
||||
var leftOffset: CGFloat = 0.0
|
||||
if sideInsets + floor(contentWidth / 2.0) < sideInsets + contentLeftInset + 15.0 {
|
||||
let updatedLeftInset = sideInsets + floor(contentWidth / 2.0) - 15.0 - sideInsets
|
||||
leftOffset = contentLeftInset - updatedLeftInset
|
||||
contentLeftInset = updatedLeftInset
|
||||
}
|
||||
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: sideInsets + leftOffset, y: size.height - 82.0 + 4.0), size: CGSize(width: contentWidth, height: 82.0))
|
||||
let backgroundLeftFrame = CGRect(origin: backgroundFrame.origin, size: CGSize(width: contentLeftInset, height: backgroundFrame.size.height - 10.0 + UIScreenPixel))
|
||||
let backgroundCenterFrame = CGRect(origin: CGPoint(x: backgroundLeftFrame.maxX, y: backgroundFrame.minY), size: CGSize(width: 30.0, height: 82.0))
|
||||
let backgroundRightFrame = CGRect(origin: CGPoint(x: backgroundCenterFrame.maxX, y: backgroundFrame.minY), size: CGSize(width: max(0.0, backgroundFrame.minX + backgroundFrame.size.width - backgroundCenterFrame.maxX), height: backgroundFrame.size.height - 10.0 + UIScreenPixel))
|
||||
transition.updateFrame(node: self.backgroundLeftNode, frame: backgroundLeftFrame)
|
||||
transition.updateFrame(node: self.backgroundNode, frame: backgroundCenterFrame)
|
||||
transition.updateFrame(node: self.backgroundRightNode, frame: backgroundRightFrame)
|
||||
|
||||
let gridFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY + 4.0), size: CGSize(width: backgroundFrame.size.width, height: 66.0))
|
||||
transition.updateFrame(node: self.clippingNode, frame: gridFrame)
|
||||
self.gridNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: gridFrame.size.height, height: gridFrame.size.width))
|
||||
|
||||
let gridBounds = self.gridNode.bounds
|
||||
self.gridNode.bounds = CGRect(x: gridBounds.minX, y: gridBounds.minY, width: gridFrame.size.height, height: gridFrame.size.width)
|
||||
self.gridNode.position = CGPoint(x: gridFrame.size.width / 2.0, y: gridFrame.size.height / 2.0)
|
||||
|
||||
self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: GridNodeUpdateLayout(layout: GridNodeLayout(size: CGSize(width: gridFrame.size.height, height: gridFrame.size.width), insets: UIEdgeInsets(top: 3.0, left: 0.0, bottom: 3.0, right: 0.0), preloadSize: 100.0, type: .fixed(itemSize: CGSize(width: 66.0, height: 66.0), fillWidth: nil, lineSpacing: 0.0, itemSpacing: nil)), transition: .immediate), itemTransition: .immediate, stationaryItems: .all, updateFirstIndexInSectionOffset: nil), completion: { _ in })
|
||||
|
||||
let dequeue = self.validLayout == nil
|
||||
self.validLayout = (size, leftInset, rightInset, interfaceState)
|
||||
|
||||
var insets = UIEdgeInsets()
|
||||
insets.top = self.topInsetForLayout(size: size)
|
||||
insets.left = leftInset
|
||||
insets.right = rightInset
|
||||
|
||||
transition.updateFrame(node: self.gridNode, frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
|
||||
|
||||
let updateSizeAndInsets = GridNodeUpdateLayout(layout: GridNodeLayout(size: size, insets: insets, preloadSize: 100.0, type: .fixed(itemSize: CGSize(width: 66.0, height: 66.0), fillWidth: nil, lineSpacing: 0.0, itemSpacing: nil)), transition: transition)
|
||||
|
||||
self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: nil, updateLayout: updateSizeAndInsets, itemTransition: .immediate, stationaryItems: .all, updateFirstIndexInSectionOffset: nil), completion: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.backgroundNode.frame = CGRect(x: 0.0, y: 0.0, width: size.width, height: strongSelf.gridNode.scrollView.contentSize.height + 500.0)
|
||||
}
|
||||
})
|
||||
|
||||
if !hadValidLayout {
|
||||
if dequeue {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.dequeueTransitions()
|
||||
}
|
||||
|
||||
if self.theme !== interfaceState.theme {
|
||||
self.theme = interfaceState.theme
|
||||
self.backgroundNode.image = backgroundCenterImage(theme)
|
||||
self.backgroundLeftNode.image = backgroundLeftImage(theme)
|
||||
self.backgroundRightNode.image = backgroundLeftImage(theme)
|
||||
// if let currentEntries = self.currentEntries {
|
||||
// self.updateToEntries(entries: currentEntries, forceUpdate: true)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
override func animateOut(completion: @escaping () -> Void) {
|
||||
let position = self.gridNode.layer.position
|
||||
let offset = self.gridNode.frame.height + self.gridNode.scrollView.contentOffset.y
|
||||
self.gridNode.layer.animatePosition(from: position, to: CGPoint(x: position.x, y: position.y + offset), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
|
||||
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
let convertedPoint = self.convert(point, to: self.gridNode)
|
||||
if convertedPoint.y > 0.0 {
|
||||
return super.hitTest(point, with: event)
|
||||
} else {
|
||||
if !self.clippingNode.frame.contains(point) {
|
||||
return nil
|
||||
}
|
||||
return super.hitTest(point, with: event)
|
||||
}
|
||||
|
||||
private func updatePreviewingItem(item: StickerPackItem?, animated: Bool) {
|
||||
|
||||
@ -2196,7 +2196,7 @@ func instantPageImageFile(account: Account, fileReference: FileMediaReference, f
|
||||
}
|
||||
}
|
||||
|
||||
private func avatarGalleryPhotoDatas(account: Account, fileReference: FileMediaReference? = nil, representations: [ImageRepresentationWithReference], autoFetchFullSize: Bool = false) -> Signal<(Data?, Data?, Bool), NoError> {
|
||||
private func avatarGalleryPhotoDatas(account: Account, fileReference: FileMediaReference? = nil, representations: [ImageRepresentationWithReference], alwaysShowThumbnailFirst: Bool = false, autoFetchFullSize: Bool = false) -> Signal<(Data?, Data?, Bool), NoError> {
|
||||
if let smallestRepresentation = smallestImageRepresentation(representations.map({ $0.representation })), let largestRepresentation = largestImageRepresentation(representations.map({ $0.representation })), let smallestIndex = representations.index(where: { $0.representation == smallestRepresentation }), let largestIndex = representations.index(where: { $0.representation == largestRepresentation }) {
|
||||
let maybeFullSize = account.postbox.mediaBox.resourceData(largestRepresentation.resource)
|
||||
let decodedThumbnailData = fileReference?.media.immediateThumbnailData.flatMap(decodeTinyThumbnail)
|
||||
@ -2206,7 +2206,12 @@ private func avatarGalleryPhotoDatas(account: Account, fileReference: FileMediaR
|
||||
|> mapToSignal { maybeData -> Signal<(Data?, Data?, Bool), NoError> in
|
||||
if maybeData.complete {
|
||||
let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: [])
|
||||
return .single((nil, loadedData, true))
|
||||
if alwaysShowThumbnailFirst, let decodedThumbnailData = decodedThumbnailData {
|
||||
return .single((decodedThumbnailData, nil, false))
|
||||
|> then(.single((nil, loadedData, true)))
|
||||
} else {
|
||||
return .single((nil, loadedData, true))
|
||||
}
|
||||
} else {
|
||||
let fetchedThumbnail: Signal<FetchResourceSourceType, FetchResourceError>
|
||||
if let _ = decodedThumbnailData {
|
||||
@ -2269,8 +2274,8 @@ private func avatarGalleryPhotoDatas(account: Account, fileReference: FileMediaR
|
||||
}
|
||||
}
|
||||
|
||||
func chatAvatarGalleryPhoto(account: Account, fileReference: FileMediaReference? = nil, representations: [ImageRepresentationWithReference], autoFetchFullSize: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
let signal = avatarGalleryPhotoDatas(account: account, fileReference: fileReference, representations: representations, autoFetchFullSize: autoFetchFullSize)
|
||||
func chatAvatarGalleryPhoto(account: Account, fileReference: FileMediaReference? = nil, representations: [ImageRepresentationWithReference], alwaysShowThumbnailFirst: Bool = false, autoFetchFullSize: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
let signal = avatarGalleryPhotoDatas(account: account, fileReference: fileReference, representations: representations, alwaysShowThumbnailFirst: alwaysShowThumbnailFirst, autoFetchFullSize: autoFetchFullSize)
|
||||
|
||||
return signal
|
||||
|> map { (thumbnailData, fullSizeData, fullSizeComplete) in
|
||||
@ -2794,7 +2799,10 @@ func playerAlbumArt(postbox: Postbox, fileReference: FileMediaReference?, albumA
|
||||
}
|
||||
|
||||
func photoWallpaper(postbox: Postbox, photoLibraryResource: PhotoLibraryMediaResource) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||
return fetchPhotoLibraryImage(localIdentifier: photoLibraryResource.localIdentifier)
|
||||
let thumbnail = fetchPhotoLibraryImage(localIdentifier: photoLibraryResource.localIdentifier, thumbnail: true)
|
||||
let fullSize = fetchPhotoLibraryImage(localIdentifier: photoLibraryResource.localIdentifier, thumbnail: false)
|
||||
|
||||
return (thumbnail |> then(fullSize))
|
||||
|> map { result in
|
||||
var sourceImage = result?.0
|
||||
let isThumbnail = result?.1 ?? false
|
||||
@ -2802,7 +2810,7 @@ func photoWallpaper(postbox: Postbox, photoLibraryResource: PhotoLibraryMediaRes
|
||||
return { arguments in
|
||||
let context = DrawingContext(size: arguments.drawingSize, scale: 1.0, clear: true)
|
||||
|
||||
var dimensions = sourceImage?.size
|
||||
let dimensions = sourceImage?.size
|
||||
|
||||
if let thumbnailImage = sourceImage?.cgImage, isThumbnail {
|
||||
var fittedSize = arguments.imageSize
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -373,14 +373,25 @@ func proxyServerSettingsController(theme: PresentationTheme, strings: Presentati
|
||||
let _ = controller?.dismiss()
|
||||
}
|
||||
|
||||
shareImpl = {
|
||||
shareImpl = { [weak controller] in
|
||||
let state = stateValue.with { $0 }
|
||||
guard let server = proxyServerSettings(with: state) else {
|
||||
guard let server = proxyServerSettings(with: state), let strongController = controller else {
|
||||
return
|
||||
}
|
||||
|
||||
let controller = ShareProxyServerActionSheetController(theme: theme, strings: strings, updatedPresentationData: updatedPresentationData, link: shareLink(for: server))
|
||||
presentImpl?(controller, nil)
|
||||
let link = shareLink(for: server)
|
||||
if #available(iOSApplicationExtension 9.0, *) {
|
||||
let controller = ShareProxyServerActionSheetController(theme: theme, strings: strings, updatedPresentationData: updatedPresentationData, link: link)
|
||||
presentImpl?(controller, nil)
|
||||
} else {
|
||||
let activityController = UIActivityViewController(activityItems: [link], applicationActivities: nil)
|
||||
|
||||
if let window = strongController.view.window, let rootViewController = window.rootViewController {
|
||||
activityController.popoverPresentationController?.sourceView = window
|
||||
activityController.popoverPresentationController?.sourceRect = CGRect(origin: CGPoint(x: window.bounds.width / 2.0, y: window.bounds.size.height - 1.0), size: CGSize(width: 1.0, height: 1.0))
|
||||
rootViewController.present(activityController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return controller
|
||||
|
||||
@ -76,7 +76,7 @@ func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = nil, sca
|
||||
c.setFillColor(color.cgColor)
|
||||
let _ = try? drawSvgPath(c, path: "M0.0,40 C0,20.3664202 20.1230605,0.0 32.5,0.0 C44.8769395,0.0 65,20.3664202 65,40 C65,47.217934 65,55.5505326 65,64.9977957 L32.5,79 L0.0,64.9977957 C0.0,55.0825772 0.0,46.7499786 0.0,40 Z")
|
||||
|
||||
if false, let backgroundColor = backgroundColor {
|
||||
if let backgroundColor = backgroundColor {
|
||||
c.setFillColor(backgroundColor.cgColor)
|
||||
} else {
|
||||
c.setBlendMode(.clear)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 498 KiB |
Binary file not shown.
@ -112,8 +112,7 @@ class SearchBarPlaceholderNode: ASDisplayNode {
|
||||
|
||||
var iconSize = CGSize()
|
||||
var totalWidth = labelLayoutResult.size.width
|
||||
let spacing: CGFloat = 7.0
|
||||
|
||||
let spacing: CGFloat = 8.0
|
||||
|
||||
if let iconImage = strongSelf.iconNode.image {
|
||||
iconSize = iconImage.size
|
||||
|
||||
@ -97,7 +97,7 @@ final class SettingsThemeWallpaperNode: ASDisplayNode {
|
||||
|
||||
var convertedRepresentations: [ImageRepresentationWithReference] = []
|
||||
for representation in file.file.previewRepresentations {
|
||||
convertedRepresentations.append(ImageRepresentationWithReference(representation: representation, reference: .standalone(resource: representation.resource)))
|
||||
convertedRepresentations.append(ImageRepresentationWithReference(representation: representation, reference: .wallpaper(resource: representation.resource)))
|
||||
}
|
||||
let dimensions = file.file.dimensions ?? CGSize(width: 100.0, height: 100.0)
|
||||
self.imageNode.setSignal(chatAvatarGalleryPhoto(account: account, fileReference: .standalone(media: file.file), representations: convertedRepresentations, autoFetchFullSize: true))
|
||||
|
||||
@ -33,7 +33,7 @@ public final class ShareProxyServerActionSheetController: ActionSheetController
|
||||
items.append(ProxyServerQRCodeItem(strings: strings, link: link, ready: { [weak self] in
|
||||
self?._ready.set(.single(true))
|
||||
}))
|
||||
items.append(ActionSheetButtonItem(title: "Share QR Code", action: { [weak self] in
|
||||
items.append(ActionSheetButtonItem(title: strings.SocksProxySetup_ShareQRCode, action: { [weak self] in
|
||||
self?.dismissAnimated()
|
||||
let _ = (qrCode(string: link, color: .black, backgroundColor: .white, scale: 1.0)
|
||||
|> map { generator -> UIImage? in
|
||||
@ -47,7 +47,7 @@ public final class ShareProxyServerActionSheetController: ActionSheetController
|
||||
}
|
||||
})
|
||||
}))
|
||||
items.append(ActionSheetButtonItem(title: "Share Link", action: { [weak self] in
|
||||
items.append(ActionSheetButtonItem(title: strings.SocksProxySetup_ShareLink, action: { [weak self] in
|
||||
self?.dismissAnimated()
|
||||
presentActivityController(link)
|
||||
}))
|
||||
@ -105,6 +105,9 @@ private final class ProxyServerQRCodeItemNode: ActionSheetItemNode {
|
||||
|
||||
private let ready: () -> Void
|
||||
|
||||
private var cachedHasLabel = true
|
||||
private var cachedHasImage = true
|
||||
|
||||
init(theme: ActionSheetControllerTheme, strings: PresentationStrings, link: String, ready: @escaping () -> Void = {}) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
@ -117,10 +120,10 @@ private final class ProxyServerQRCodeItemNode: ActionSheetItemNode {
|
||||
self.label.displaysAsynchronously = false
|
||||
self.label.truncationMode = .byTruncatingTail
|
||||
self.label.isUserInteractionEnabled = false
|
||||
self.label.attributedText = NSAttributedString(string: "Your friends can add this proxy by scanning this code with phone or in-app camera.", font: ActionSheetTextNode.defaultFont, textColor: self.theme.secondaryTextColor, paragraphAlignment: .center)
|
||||
self.label.attributedText = NSAttributedString(string: strings.SocksProxySetup_ShareQRCodeInfo, font: ActionSheetTextNode.defaultFont, textColor: self.theme.secondaryTextColor, paragraphAlignment: .center)
|
||||
|
||||
self.imageNode = TransformImageNode()
|
||||
self.imageNode.setSignal(qrCode(string: link, color: self.theme.primaryTextColor), attemptSynchronously: true)
|
||||
self.imageNode.setSignal(qrCode(string: link, color: .black, backgroundColor: .white), attemptSynchronously: true)
|
||||
|
||||
super.init(theme: theme)
|
||||
|
||||
@ -131,16 +134,26 @@ private final class ProxyServerQRCodeItemNode: ActionSheetItemNode {
|
||||
override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
||||
let imageInset: CGFloat = 44.0
|
||||
let side = constrainedSize.width - imageInset * 2.0
|
||||
let imageSize = CGSize(width: side, height: side)
|
||||
var imageSize = CGSize(width: side, height: side)
|
||||
|
||||
let makeLayout = self.imageNode.asyncLayout()
|
||||
let apply = makeLayout(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: nil))
|
||||
apply()
|
||||
|
||||
var labelSize = self.label.measure(CGSize(width: max(1.0, constrainedSize.width - 64.0), height: constrainedSize.height))
|
||||
|
||||
self.cachedHasImage = constrainedSize.width < constrainedSize.height
|
||||
if !self.cachedHasImage {
|
||||
imageSize = CGSize()
|
||||
}
|
||||
|
||||
self.ready()
|
||||
|
||||
let labelSize = self.label.measure(CGSize(width: max(1.0, constrainedSize.width - 64.0), height: constrainedSize.height))
|
||||
return CGSize(width: constrainedSize.width, height: 14.0 + labelSize.height + 14.0 + constrainedSize.width - 88.0 + 14.0)
|
||||
self.cachedHasLabel = constrainedSize.height > 480 || !self.cachedHasImage
|
||||
if !self.cachedHasLabel {
|
||||
labelSize = CGSize()
|
||||
}
|
||||
return CGSize(width: constrainedSize.width, height: 14.0 + (labelSize.height > 0.0 ? labelSize.height + 14.0 : 0.0) + (imageSize.height > 0.0 ? imageSize.height + 14.0 : 8.0))
|
||||
}
|
||||
|
||||
override func layout() {
|
||||
@ -151,10 +164,21 @@ private final class ProxyServerQRCodeItemNode: ActionSheetItemNode {
|
||||
let imageInset: CGFloat = 44.0
|
||||
let spacing: CGFloat = 18.0
|
||||
|
||||
let labelSize = self.label.measure(CGSize(width: max(1.0, size.width - inset * 2.0), height: size.height))
|
||||
self.label.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - labelSize.width) / 2.0), y: spacing), size: labelSize)
|
||||
let labelSize: CGSize
|
||||
if self.cachedHasLabel {
|
||||
labelSize = self.label.measure(CGSize(width: max(1.0, size.width - inset * 2.0), height: size.height))
|
||||
self.label.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - labelSize.width) / 2.0), y: spacing), size: labelSize)
|
||||
} else {
|
||||
labelSize = CGSize()
|
||||
}
|
||||
|
||||
let imageFrame = CGRect(x: imageInset, y: self.label.frame.maxY + spacing - 4.0, width: size.width - imageInset * 2.0, height: size.width - imageInset * 2.0)
|
||||
self.imageNode.frame = imageFrame
|
||||
let imageOrigin = CGPoint(x: imageInset, y: self.label.frame.maxY + spacing - 4.0)
|
||||
var imageSize: CGSize
|
||||
if !self.cachedHasImage {
|
||||
imageSize = CGSize()
|
||||
} else {
|
||||
imageSize = CGSize(width: size.width - imageInset * 2.0, height: size.width - imageInset * 2.0)
|
||||
}
|
||||
self.imageNode.frame = CGRect(origin: imageOrigin, size: imageSize)
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ final class StickerPaneSearchBarPlaceholderNode: GridItemNode {
|
||||
if self.currentState?.0 !== theme || self.currentState?.1 !== strings {
|
||||
self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 36.0, color: theme.chat.inputMediaPanel.stickersSearchBackgroundColor)
|
||||
self.iconNode.image = generateLoupeIcon(color: theme.chat.inputMediaPanel.stickersSearchControlColor)
|
||||
self.labelNode.attributedText = NSAttributedString(string: strings.Stickers_Search, font: Font.regular(14.0), textColor: theme.chat.inputMediaPanel.stickersSearchPlaceholderColor)
|
||||
self.labelNode.attributedText = NSAttributedString(string: strings.Stickers_Search, font: Font.regular(17.0), textColor: theme.chat.inputMediaPanel.stickersSearchPlaceholderColor)
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ final class StickerPaneSearchBarPlaceholderNode: GridItemNode {
|
||||
self.labelNode.frame = textFrame
|
||||
|
||||
if let iconImage = self.iconNode.image {
|
||||
self.iconNode.frame = CGRect(origin: CGPoint(x: textFrame.minX - iconImage.size.width - 5.0, y: floorToScreenPixels(textFrame.midY - iconImage.size.height / 2.0)), size: iconImage.size)
|
||||
self.iconNode.frame = CGRect(origin: CGPoint(x: textFrame.minX - iconImage.size.width - 6.0, y: floorToScreenPixels(textFrame.midY - iconImage.size.height / 2.0)), size: iconImage.size)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
239
TelegramUI/StickersChatInputContextPanelNode.swift
Normal file
239
TelegramUI/StickersChatInputContextPanelNode.swift
Normal file
@ -0,0 +1,239 @@
|
||||
import Foundation
|
||||
import AsyncDisplayKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import Display
|
||||
|
||||
private struct StickersChatInputContextPanelEntryStableId: Hashable {
|
||||
let text: String
|
||||
|
||||
var hashValue: Int {
|
||||
return self.text.hashValue
|
||||
}
|
||||
|
||||
static func ==(lhs: StickersChatInputContextPanelEntryStableId, rhs: StickersChatInputContextPanelEntryStableId) -> Bool {
|
||||
return lhs.text == rhs.text
|
||||
}
|
||||
}
|
||||
|
||||
private struct StickersChatInputContextPanelEntry: Comparable, Identifiable {
|
||||
let index: Int
|
||||
let theme: PresentationTheme
|
||||
let text: String
|
||||
|
||||
var stableId: StickersChatInputContextPanelEntryStableId {
|
||||
return StickersChatInputContextPanelEntryStableId(text: self.text)
|
||||
}
|
||||
|
||||
func withUpdatedTheme(_ theme: PresentationTheme) -> StickersChatInputContextPanelEntry {
|
||||
return StickersChatInputContextPanelEntry(index: self.index, theme: theme, text: self.text)
|
||||
}
|
||||
|
||||
static func ==(lhs: StickersChatInputContextPanelEntry, rhs: StickersChatInputContextPanelEntry) -> Bool {
|
||||
return lhs.index == rhs.index && lhs.text == rhs.text && lhs.theme === rhs.theme
|
||||
}
|
||||
|
||||
static func <(lhs: StickersChatInputContextPanelEntry, rhs: StickersChatInputContextPanelEntry) -> Bool {
|
||||
return lhs.index < rhs.index
|
||||
}
|
||||
|
||||
func item(account: Account, hashtagSelected: @escaping (String) -> Void) -> ListViewItem {
|
||||
return StickersChatInputPanelItem(theme: self.theme, text: self.text, hashtagSelected: hashtagSelected)
|
||||
}
|
||||
}
|
||||
|
||||
private struct StickersChatInputContextPanelTransition {
|
||||
let deletions: [ListViewDeleteItem]
|
||||
let insertions: [ListViewInsertItem]
|
||||
let updates: [ListViewUpdateItem]
|
||||
}
|
||||
|
||||
private func preparedTransition(from fromEntries: [StickersChatInputContextPanelEntry], to toEntries: [StickersChatInputContextPanelEntry], account: Account, hashtagSelected: @escaping (String) -> Void) -> StickersChatInputContextPanelTransition {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||
|
||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, hashtagSelected: hashtagSelected), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, hashtagSelected: hashtagSelected), directionHint: nil) }
|
||||
|
||||
return StickersChatInputContextPanelTransition(deletions: deletions, insertions: insertions, updates: updates)
|
||||
}
|
||||
|
||||
final class StickersChatInputContextPanelNode: ChatInputContextPanelNode {
|
||||
|
||||
private let listView: ListView
|
||||
private var currentEntries: [StickersChatInputContextPanelEntry]?
|
||||
|
||||
private var enqueuedTransitions: [(StickersChatInputContextPanelTransition, Bool)] = []
|
||||
private var validLayout: (CGSize, CGFloat, CGFloat)?
|
||||
|
||||
override init(account: Account, theme: PresentationTheme, strings: PresentationStrings) {
|
||||
self.listView = ListView()
|
||||
self.listView.isOpaque = false
|
||||
self.listView.stackFromBottom = true
|
||||
self.listView.keepBottomItemOverscrollBackground = theme.list.plainBackgroundColor
|
||||
self.listView.limitHitTestToNodes = true
|
||||
self.listView.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||
|
||||
super.init(account: account, theme: theme, strings: strings)
|
||||
|
||||
self.isOpaque = false
|
||||
self.clipsToBounds = true
|
||||
|
||||
self.addSubnode(self.listView)
|
||||
}
|
||||
|
||||
func updateResults(_ results: [String]) {
|
||||
var entries: [StickersChatInputContextPanelEntry] = []
|
||||
var index = 0
|
||||
var stableIds = Set<StickersChatInputContextPanelEntryStableId>()
|
||||
for text in results {
|
||||
let entry = StickersChatInputContextPanelEntry(index: index, theme: self.theme, text: text)
|
||||
if stableIds.contains(entry.stableId) {
|
||||
continue
|
||||
}
|
||||
stableIds.insert(entry.stableId)
|
||||
entries.append(entry)
|
||||
index += 1
|
||||
}
|
||||
self.prepareTransition(from: self.currentEntries ?? [], to: entries)
|
||||
}
|
||||
|
||||
private func prepareTransition(from: [StickersChatInputContextPanelEntry]? , to: [StickersChatInputContextPanelEntry]) {
|
||||
let firstTime = from == nil
|
||||
let transition = preparedTransition(from: from ?? [], to: to, account: self.account, hashtagSelected: { [weak self] text in
|
||||
|
||||
})
|
||||
self.currentEntries = to
|
||||
self.enqueueTransition(transition, firstTime: firstTime)
|
||||
}
|
||||
|
||||
private func enqueueTransition(_ transition: StickersChatInputContextPanelTransition, firstTime: Bool) {
|
||||
self.enqueuedTransitions.append((transition, firstTime))
|
||||
|
||||
if self.validLayout != nil {
|
||||
while !self.enqueuedTransitions.isEmpty {
|
||||
self.dequeueTransition()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func dequeueTransition() {
|
||||
if let validLayout = self.validLayout, let (transition, firstTime) = self.enqueuedTransitions.first {
|
||||
self.enqueuedTransitions.remove(at: 0)
|
||||
|
||||
var options = ListViewDeleteAndInsertOptions()
|
||||
if firstTime {
|
||||
//options.insert(.Synchronous)
|
||||
//options.insert(.LowLatency)
|
||||
} else {
|
||||
options.insert(.AnimateTopItemPosition)
|
||||
options.insert(.AnimateCrossfade)
|
||||
}
|
||||
|
||||
var insets = UIEdgeInsets()
|
||||
insets.top = topInsetForLayout(size: validLayout.0)
|
||||
insets.left = validLayout.1
|
||||
insets.right = validLayout.2
|
||||
|
||||
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: validLayout.0, insets: insets, duration: 0.0, curve: .Default(duration: nil))
|
||||
|
||||
self.listView.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateSizeAndInsets: updateSizeAndInsets, updateOpaqueState: nil, completion: { [weak self] _ in
|
||||
if let strongSelf = self, firstTime {
|
||||
var topItemOffset: CGFloat?
|
||||
strongSelf.listView.forEachItemNode { itemNode in
|
||||
if topItemOffset == nil {
|
||||
topItemOffset = itemNode.frame.minY
|
||||
}
|
||||
}
|
||||
|
||||
if let topItemOffset = topItemOffset {
|
||||
let position = strongSelf.listView.layer.position
|
||||
strongSelf.listView.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + (strongSelf.listView.bounds.size.height - topItemOffset)), to: position, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private func topInsetForLayout(size: CGSize) -> CGFloat {
|
||||
let minimumItemHeights: CGFloat = floor(MentionChatInputPanelItemNode.itemHeight * 3.5)
|
||||
|
||||
return max(size.height - minimumItemHeights, 0.0)
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) {
|
||||
let hadValidLayout = self.validLayout != nil
|
||||
self.validLayout = (size, leftInset, rightInset)
|
||||
|
||||
var insets = UIEdgeInsets()
|
||||
insets.top = self.topInsetForLayout(size: size)
|
||||
insets.left = leftInset
|
||||
insets.right = rightInset
|
||||
|
||||
transition.updateFrame(node: self.listView, frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
|
||||
|
||||
var duration: Double = 0.0
|
||||
var curve: UInt = 0
|
||||
switch transition {
|
||||
case .immediate:
|
||||
break
|
||||
case let .animated(animationDuration, animationCurve):
|
||||
duration = animationDuration
|
||||
switch animationCurve {
|
||||
case .easeInOut:
|
||||
break
|
||||
case .spring:
|
||||
curve = 7
|
||||
}
|
||||
}
|
||||
|
||||
let listViewCurve: ListViewAnimationCurve
|
||||
if curve == 7 {
|
||||
listViewCurve = .Spring(duration: duration)
|
||||
} else {
|
||||
listViewCurve = .Default(duration: duration)
|
||||
}
|
||||
|
||||
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: size, insets: insets, duration: duration, curve: listViewCurve)
|
||||
|
||||
self.listView.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
|
||||
if !hadValidLayout {
|
||||
while !self.enqueuedTransitions.isEmpty {
|
||||
self.dequeueTransition()
|
||||
}
|
||||
}
|
||||
|
||||
if self.theme !== interfaceState.theme {
|
||||
self.theme = interfaceState.theme
|
||||
self.listView.keepBottomItemOverscrollBackground = self.theme.list.plainBackgroundColor
|
||||
|
||||
let new = self.currentEntries?.map({$0.withUpdatedTheme(interfaceState.theme)}) ?? []
|
||||
self.prepareTransition(from: self.currentEntries, to: new)
|
||||
}
|
||||
}
|
||||
|
||||
override func animateOut(completion: @escaping () -> Void) {
|
||||
var topItemOffset: CGFloat?
|
||||
self.listView.forEachItemNode { itemNode in
|
||||
if topItemOffset == nil {
|
||||
topItemOffset = itemNode.frame.minY
|
||||
}
|
||||
}
|
||||
|
||||
if let topItemOffset = topItemOffset {
|
||||
let position = self.listView.layer.position
|
||||
self.listView.layer.animatePosition(from: position, to: CGPoint(x: position.x, y: position.y + (self.listView.bounds.size.height - topItemOffset)), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
} else {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
let listViewFrame = self.listView.frame
|
||||
return self.listView.hitTest(CGPoint(x: point.x - listViewFrame.minX, y: point.y - listViewFrame.minY), with: event)
|
||||
}
|
||||
}
|
||||
|
||||
134
TelegramUI/StickersChatInputPanelItem.swift
Normal file
134
TelegramUI/StickersChatInputPanelItem.swift
Normal file
@ -0,0 +1,134 @@
|
||||
import Foundation
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
|
||||
final class StickersChatInputPanelItem: ListViewItem {
|
||||
fileprivate let theme: PresentationTheme
|
||||
fileprivate let text: String
|
||||
private let hashtagSelected: (String) -> Void
|
||||
|
||||
let selectable: Bool = true
|
||||
|
||||
public init(theme: PresentationTheme, text: String, hashtagSelected: @escaping (String) -> Void) {
|
||||
self.theme = theme
|
||||
self.text = text
|
||||
self.hashtagSelected = hashtagSelected
|
||||
}
|
||||
|
||||
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
let configure = { () -> Void in
|
||||
let node = StickersChatInputPanelItemNode()
|
||||
|
||||
let nodeLayout = node.asyncLayout()
|
||||
let (top, bottom) = (previousItem != nil, nextItem != nil)
|
||||
let (layout, apply) = nodeLayout(self, params, top, bottom)
|
||||
|
||||
node.contentSize = layout.contentSize
|
||||
node.insets = layout.insets
|
||||
|
||||
Queue.mainQueue().async {
|
||||
completion(node, {
|
||||
return (nil, { _ in apply(.None) })
|
||||
})
|
||||
}
|
||||
}
|
||||
if Thread.isMainThread {
|
||||
async {
|
||||
configure()
|
||||
}
|
||||
} else {
|
||||
configure()
|
||||
}
|
||||
}
|
||||
|
||||
public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
|
||||
Queue.mainQueue().async {
|
||||
if let nodeValue = node() as? StickersChatInputPanelItemNode {
|
||||
let nodeLayout = nodeValue.asyncLayout()
|
||||
|
||||
async {
|
||||
let (top, bottom) = (previousItem != nil, nextItem != nil)
|
||||
|
||||
let (layout, apply) = nodeLayout(self, params, top, bottom)
|
||||
Queue.mainQueue().async {
|
||||
completion(layout, { _ in
|
||||
apply(animation)
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let textFont = Font.medium(14.0)
|
||||
|
||||
final class StickersChatInputPanelItemNode: ListViewItemNode {
|
||||
static let itemHeight: CGFloat = 42.0
|
||||
private let textNode: TextNode
|
||||
private let topSeparatorNode: ASDisplayNode
|
||||
private let highlightedBackgroundNode: ASDisplayNode
|
||||
|
||||
init() {
|
||||
self.textNode = TextNode()
|
||||
|
||||
self.topSeparatorNode = ASDisplayNode()
|
||||
self.topSeparatorNode.isLayerBacked = true
|
||||
|
||||
self.highlightedBackgroundNode = ASDisplayNode()
|
||||
self.highlightedBackgroundNode.isLayerBacked = true
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
|
||||
self.addSubnode(self.topSeparatorNode)
|
||||
self.addSubnode(self.textNode)
|
||||
}
|
||||
|
||||
override public func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) {
|
||||
if let item = item as? StickersChatInputPanelItem {
|
||||
let doLayout = self.asyncLayout()
|
||||
let merged = (top: previousItem != nil, bottom: nextItem != nil)
|
||||
let (layout, apply) = doLayout(item, params, merged.top, merged.bottom)
|
||||
self.contentSize = layout.contentSize
|
||||
self.insets = layout.insets
|
||||
apply(.None)
|
||||
}
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ item: StickersChatInputPanelItem, _ params: ListViewItemLayoutParams, _ mergedTop: Bool, _ mergedBottom: Bool) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation) -> Void) {
|
||||
let makeTextLayout = TextNode.asyncLayout(self.textNode)
|
||||
return { [weak self] item, params, mergedTop, mergedBottom in
|
||||
let baseWidth = params.width - params.leftInset - params.rightInset
|
||||
|
||||
let leftInset: CGFloat = 15.0 + params.leftInset
|
||||
let rightInset: CGFloat = 10.0 + params.rightInset
|
||||
|
||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "#\(item.text)", font: textFont, textColor: item.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: HashtagChatInputPanelItemNode.itemHeight), insets: UIEdgeInsets())
|
||||
|
||||
return (nodeLayout, { _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.topSeparatorNode.backgroundColor = item.theme.list.itemPlainSeparatorColor
|
||||
strongSelf.backgroundColor = item.theme.list.plainBackgroundColor
|
||||
strongSelf.highlightedBackgroundNode.backgroundColor = item.theme.list.itemHighlightedBackgroundColor
|
||||
|
||||
let _ = textApply()
|
||||
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: floor((nodeLayout.contentSize.height - textLayout.size.height) / 2.0)), size: textLayout.size)
|
||||
|
||||
strongSelf.topSeparatorNode.isHidden = mergedTop
|
||||
|
||||
|
||||
strongSelf.topSeparatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: UIScreenPixel))
|
||||
|
||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: nodeLayout.size.height + UIScreenPixel))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -82,7 +82,7 @@ public final class TelegramRootController: NavigationController {
|
||||
//
|
||||
// DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.15) {
|
||||
// //(controller.navigationController as? NavigationController)?.pushViewController(ThemeGridController(account: self.account))
|
||||
//
|
||||
//
|
||||
// let wrapperNode = ASDisplayNode()
|
||||
// let bounds = controller.displayNode.bounds
|
||||
// wrapperNode.frame = bounds
|
||||
|
||||
@ -45,7 +45,8 @@ private func availableColors() -> [Int32] {
|
||||
}
|
||||
|
||||
private func randomColor() -> Int32 {
|
||||
return availableColors().randomElement() ?? 0x000000
|
||||
let colors = availableColors()
|
||||
return colors[1 ..< colors.count - 1].randomElement() ?? 0x000000
|
||||
}
|
||||
|
||||
final class ThemeColorsGridController: ViewController {
|
||||
@ -116,9 +117,18 @@ final class ThemeColorsGridController: ViewController {
|
||||
override func loadDisplayNode() {
|
||||
self.displayNode = ThemeColorsGridControllerNode(account: self.account, presentationData: self.presentationData, colors: availableColors(), present: { [weak self] controller, arguments in
|
||||
self?.present(controller, in: .window(.root), with: arguments, blockInteraction: true)
|
||||
}, 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 controller = WallpaperListPreviewController(account: strongSelf.account, source: .customColor(randomColor()))
|
||||
controller.apply = { _, _, _ in
|
||||
if let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
let _ = navigationController.popViewController(animated: true)
|
||||
}
|
||||
}
|
||||
self?.present(controller, in: .window(.root), blockInteraction: true)
|
||||
}
|
||||
})
|
||||
|
||||
@ -79,7 +79,7 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
|
||||
|
||||
private var disposable: Disposable?
|
||||
|
||||
init(account: Account, presentationData: PresentationData, colors: [Int32], present: @escaping (ViewController, Any?) -> Void, presentColorPicker: @escaping () -> Void) {
|
||||
init(account: Account, presentationData: PresentationData, colors: [Int32], present: @escaping (ViewController, Any?) -> Void, pop: @escaping () -> Void, presentColorPicker: @escaping () -> Void) {
|
||||
self.account = account
|
||||
self.presentationData = presentationData
|
||||
self.present = present
|
||||
@ -119,7 +119,11 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
|
||||
let entries = previousEntries.with { $0 }
|
||||
if let entries = entries, !entries.isEmpty {
|
||||
let wallpapers = entries.map { $0.wallpaper }
|
||||
let controller = WallpaperListPreviewController(account: account, source: .list(wallpapers: wallpapers, central: wallpaper, type: .colors))
|
||||
let controller = WallpaperGalleryController(account: account, source: .list(wallpapers: wallpapers, central: wallpaper, type: .colors))
|
||||
controller.apply = { _, _, _ in
|
||||
pop()
|
||||
}
|
||||
//let controller = WallpaperListPreviewController(account: account, source: .list(wallpapers: wallpapers, central: wallpaper, type: .colors))
|
||||
strongSelf.present(controller, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,7 +206,7 @@ class ThemeGalleryController: ViewController {
|
||||
let toolbarNode = ThemeGalleryToolbarNode(theme: presentationData.theme, strings: presentationData.strings)
|
||||
self.toolbarNode = toolbarNode
|
||||
self.galleryNode.addSubnode(toolbarNode)
|
||||
self.galleryNode.toolbarNode = toolbarNode
|
||||
//self.galleryNode.toolbarNode = toolbarNode
|
||||
toolbarNode.cancel = { [weak self] in
|
||||
self?.dismiss(forceAway: true)
|
||||
}
|
||||
|
||||
@ -49,6 +49,11 @@ final class ThemeGalleryToolbarNode: ASDisplayNode {
|
||||
self.doneButton.addTarget(self, action: #selector(self.donePressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
func setDoneEnabled(_ enabled: Bool) {
|
||||
self.doneButton.alpha = enabled ? 1.0 : 0.3
|
||||
self.doneButton.isUserInteractionEnabled = enabled
|
||||
}
|
||||
|
||||
func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) {
|
||||
self.backgroundColor = theme.rootController.tabBar.backgroundColor
|
||||
self.separatorNode.backgroundColor = theme.rootController.tabBar.separatorColor
|
||||
|
||||
@ -64,10 +64,10 @@ final class ThemeGridController: ViewController {
|
||||
}
|
||||
})
|
||||
|
||||
self.searchContentNode = NavigationBarSearchContentNode(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Wallpaper_Search, activate: { [weak self] in
|
||||
self?.activateSearch()
|
||||
})
|
||||
self.navigationBar?.setContentNode(self.searchContentNode, animated: false)
|
||||
// self.searchContentNode = NavigationBarSearchContentNode(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Wallpaper_Search, activate: { [weak self] in
|
||||
// self?.activateSearch()
|
||||
// })
|
||||
// self.navigationBar?.setContentNode(self.searchContentNode, animated: false)
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
@ -84,11 +84,11 @@ final class ThemeGridController: ViewController {
|
||||
|
||||
if let isEmpty = self.isEmpty, isEmpty {
|
||||
} else {
|
||||
if self.editingMode {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.donePressed))
|
||||
} else {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed))
|
||||
}
|
||||
// if self.editingMode {
|
||||
// self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.donePressed))
|
||||
// } else {
|
||||
// self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed))
|
||||
// }
|
||||
}
|
||||
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBar.style.style
|
||||
@ -103,19 +103,19 @@ final class ThemeGridController: ViewController {
|
||||
override func loadDisplayNode() {
|
||||
self.displayNode = ThemeGridControllerNode(account: self.account, presentationData: self.presentationData, presentPreviewController: { [weak self] source in
|
||||
if let strongSelf = self {
|
||||
//let controller = WallpaperGalleryController(account: strongSelf.account, source: source)
|
||||
//self?.present(controller, in: .window(.root), with: nil, blockInteraction: true)
|
||||
let controller = WallpaperListPreviewController(account: strongSelf.account, source: source)
|
||||
controller.apply = { [weak self, weak controller] wallpaper, mode, cropRect in
|
||||
if let strongSelf = self {
|
||||
strongSelf.uploadCustomWallpaper(wallpaper, mode: mode, cropRect: cropRect)
|
||||
if case .wallpaper = wallpaper {
|
||||
} else if let controller = controller {
|
||||
controller.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
let controller = WallpaperGalleryController(account: strongSelf.account, source: source)
|
||||
self?.present(controller, in: .window(.root), with: nil, blockInteraction: true)
|
||||
// let controller = WallpaperListPreviewController(account: strongSelf.account, source: source)
|
||||
// controller.apply = { [weak self, weak controller] wallpaper, mode, cropRect in
|
||||
// if let strongSelf = self {
|
||||
// strongSelf.uploadCustomWallpaper(wallpaper, mode: mode, cropRect: cropRect)
|
||||
// if case .wallpaper = wallpaper {
|
||||
// } else if let controller = controller {
|
||||
// controller.dismiss()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// self?.present(controller, in: .window(.root), with: nil, blockInteraction: true)
|
||||
}
|
||||
}, presentGallery: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
@ -132,11 +132,12 @@ final class ThemeGridController: ViewController {
|
||||
let controller = WallpaperListPreviewController(account: strongSelf.account, source: .asset(asset.backingAsset, thumbnailImage))
|
||||
controller.apply = { [weak self, weak legacyController, weak controller] wallpaper, mode, cropRect in
|
||||
if let strongSelf = self, let legacyController = legacyController, let controller = controller {
|
||||
strongSelf.uploadCustomWallpaper(wallpaper, mode: mode, cropRect: cropRect)
|
||||
|
||||
let _ = (strongSelf.navigationController as? NavigationController)?.popViewController(animated: true)
|
||||
legacyController.dismiss()
|
||||
controller.dismiss()
|
||||
strongSelf.uploadCustomWallpaper(wallpaper, mode: mode, cropRect: cropRect, completion: { [weak legacyController, weak controller] in
|
||||
if let legacyController = legacyController, let controller = controller {
|
||||
legacyController.dismiss()
|
||||
controller.dismiss()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
strongSelf.present(controller, in: .window(.root), with: nil, blockInteraction: true)
|
||||
@ -164,11 +165,11 @@ final class ThemeGridController: ViewController {
|
||||
if empty {
|
||||
strongSelf.navigationItem.setRightBarButton(nil, animated: true)
|
||||
} else {
|
||||
if strongSelf.editingMode {
|
||||
strongSelf.navigationItem.rightBarButtonItem = UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Done, style: .done, target: strongSelf, action: #selector(strongSelf.donePressed))
|
||||
} else {
|
||||
strongSelf.navigationItem.rightBarButtonItem = UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Edit, style: .plain, target: strongSelf, action: #selector(strongSelf.editPressed))
|
||||
}
|
||||
// if strongSelf.editingMode {
|
||||
// strongSelf.navigationItem.rightBarButtonItem = UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Done, style: .done, target: strongSelf, action: #selector(strongSelf.donePressed))
|
||||
// } else {
|
||||
// strongSelf.navigationItem.rightBarButtonItem = UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Edit, style: .plain, target: strongSelf, action: #selector(strongSelf.editPressed))
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -238,13 +239,13 @@ final class ThemeGridController: ViewController {
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
private func uploadCustomWallpaper(_ wallpaper: WallpaperEntry, mode: WallpaperPresentationOptions, cropRect: CGRect?) {
|
||||
private func uploadCustomWallpaper(_ wallpaper: WallpaperEntry, mode: WallpaperPresentationOptions, cropRect: CGRect?, completion: @escaping () -> Void) {
|
||||
let imageSignal: Signal<UIImage, NoError>
|
||||
switch wallpaper {
|
||||
case .wallpaper:
|
||||
imageSignal = .complete()
|
||||
case let .asset(asset, _):
|
||||
imageSignal = fetchPhotoLibraryImage(localIdentifier: asset.localIdentifier)
|
||||
imageSignal = fetchPhotoLibraryImage(localIdentifier: asset.localIdentifier, thumbnail: false)
|
||||
|> filter { value in
|
||||
return !(value?.1 ?? true)
|
||||
}
|
||||
@ -311,29 +312,31 @@ final class ThemeGridController: ViewController {
|
||||
})).start()
|
||||
}
|
||||
|
||||
let completion: () -> Void = {
|
||||
let wallpaper: TelegramWallpaper = .image([TelegramMediaImageRepresentation(dimensions: image.size, resource: resource)])
|
||||
let apply: () -> Void = {
|
||||
let wallpaper: TelegramWallpaper = .image([TelegramMediaImageRepresentation(dimensions: croppedImage.size, resource: resource)])
|
||||
updateWallpaper(wallpaper)
|
||||
|
||||
let _ = uploadWallpaper(account: account, resource: resource).start(next: { status in
|
||||
if case let .complete(wallpaper) = status {
|
||||
if mode.contains(.blur), case let .file(_, _, _, _, _, file) = wallpaper {
|
||||
let _ = account.postbox.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true).start(completed: {
|
||||
updateWallpaper(wallpaper)
|
||||
})
|
||||
} else {
|
||||
updateWallpaper(wallpaper)
|
||||
}
|
||||
}
|
||||
})
|
||||
DispatchQueue.main.async {
|
||||
completion()
|
||||
}
|
||||
// let _ = uploadWallpaper(account: account, resource: resource).start(next: { status in
|
||||
// if case let .complete(wallpaper) = status {
|
||||
// if mode.contains(.blur), case let .file(_, _, _, _, _, file) = wallpaper {
|
||||
// let _ = account.postbox.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true).start(completed: {
|
||||
// updateWallpaper(wallpaper)
|
||||
// })
|
||||
// } else {
|
||||
// updateWallpaper(wallpaper)
|
||||
// }
|
||||
// }
|
||||
// }).start()
|
||||
}
|
||||
|
||||
if mode.contains(.blur) {
|
||||
let _ = account.postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true).start(completed: {
|
||||
completion()
|
||||
apply()
|
||||
})
|
||||
} else {
|
||||
completion()
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -233,7 +233,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
self.gridNode.addSubnode(self.separatorNode)
|
||||
self.gridNode.addSubnode(self.colorItemNode)
|
||||
self.gridNode.addSubnode(self.galleryItemNode)
|
||||
self.gridNode.addSubnode(self.descriptionItemNode)
|
||||
//self.gridNode.addSubnode(self.descriptionItemNode)
|
||||
self.addSubnode(self.gridNode)
|
||||
|
||||
let wallpapersPromise: Promise<[TelegramWallpaper]> = Promise()
|
||||
@ -471,7 +471,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
|
||||
let buttonTopInset: CGFloat = 32.0
|
||||
let buttonHeight: CGFloat = 44.0
|
||||
let buttonBottomInset: CGFloat = descriptionLayout.contentSize.height + 17.0
|
||||
let buttonBottomInset: CGFloat = 35.0 //descriptionLayout.contentSize.height + 17.0
|
||||
|
||||
let buttonInset: CGFloat = buttonTopInset + buttonHeight * 2.0 + buttonBottomInset
|
||||
let buttonOffset = buttonInset + 10.0
|
||||
|
||||
@ -244,6 +244,9 @@ final class WallpaperColorPickerNode: ASDisplayNode {
|
||||
let colorPanRecognizer = UIPanGestureRecognizer(target: self, action: #selector(WallpaperColorPickerNode.colorPan))
|
||||
self.colorNode.view.addGestureRecognizer(colorPanRecognizer)
|
||||
|
||||
let colorTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(WallpaperColorPickerNode.colorTap))
|
||||
self.colorNode.view.addGestureRecognizer(colorTapRecognizer)
|
||||
|
||||
let brightnessPanRecognizer = UIPanGestureRecognizer(target: self, action: #selector(WallpaperColorPickerNode.brightnessPan))
|
||||
self.brightnessNode.view.addGestureRecognizer(brightnessPanRecognizer)
|
||||
}
|
||||
@ -286,6 +289,23 @@ final class WallpaperColorPickerNode: ASDisplayNode {
|
||||
self.updateKnobLayout(size: size, panningColor: false, transition: transition)
|
||||
}
|
||||
|
||||
@objc private func colorTap(_ recognizer: UITapGestureRecognizer) {
|
||||
guard let size = self.validLayout, recognizer.state == .recognized else {
|
||||
return
|
||||
}
|
||||
|
||||
let location = recognizer.location(in: recognizer.view)
|
||||
let newHue = max(0.0, min(1.0, location.x / size.width))
|
||||
let newSaturation = max(0.0, min(1.0, (1.0 - location.y / (size.height - 66.0))))
|
||||
self.colorHSV.0 = newHue
|
||||
self.colorHSV.1 = newSaturation
|
||||
|
||||
self.updateKnobLayout(size: size, panningColor: false, transition: .immediate)
|
||||
|
||||
self.update()
|
||||
self.colorChanged?(self.color)
|
||||
}
|
||||
|
||||
@objc private func colorPan(_ recognizer: UIPanGestureRecognizer) {
|
||||
guard let size = self.validLayout else {
|
||||
return
|
||||
@ -307,7 +327,7 @@ final class WallpaperColorPickerNode: ASDisplayNode {
|
||||
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
self.updateKnobLayout(size: size, panningColor: true, transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||
self.updateKnobLayout(size: size, panningColor: true, transition: .immediate)
|
||||
case .changed:
|
||||
self.updateKnobLayout(size: size, panningColor: true, transition: .immediate)
|
||||
recognizer.setTranslation(CGPoint(), in: recognizer.view)
|
||||
|
||||
@ -50,6 +50,17 @@ enum WallpaperGalleryEntry: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
class WallpaperGalleryOverlayNode: ASDisplayNode {
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
let result = super.hitTest(point, with: event)
|
||||
if result != self.view {
|
||||
return result
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WallpaperGalleryController: ViewController {
|
||||
private var galleryNode: GalleryControllerNode {
|
||||
return self.displayNode as! GalleryControllerNode
|
||||
@ -57,7 +68,7 @@ class WallpaperGalleryController: ViewController {
|
||||
|
||||
private let account: Account
|
||||
private let source: WallpaperListSource
|
||||
var apply: ((WallpaperEntry, WallpaperPresentationOptions, CGRect?) -> Void)?
|
||||
var apply: ((WallpaperGalleryEntry, WallpaperPresentationOptions, CGRect?) -> Void)?
|
||||
|
||||
private let _ready = Promise<Bool>()
|
||||
override var ready: Promise<Bool> {
|
||||
@ -79,6 +90,8 @@ class WallpaperGalleryController: ViewController {
|
||||
|
||||
private var validLayout: (ContainerViewLayout, CGFloat)?
|
||||
|
||||
private var overlayNode: WallpaperGalleryOverlayNode?
|
||||
private var messageNodes: [ListViewItemNode]?
|
||||
private var toolbarNode: ThemeGalleryToolbarNode?
|
||||
|
||||
init(account: Account, source: WallpaperListSource) {
|
||||
@ -119,24 +132,6 @@ class WallpaperGalleryController: ViewController {
|
||||
self.centralEntryIndex = 0
|
||||
}
|
||||
|
||||
// let initialEntries: [ThemeGalleryEntry] = wallpapers.map { ThemeGalleryEntry.wallpaper($0) }
|
||||
// let entriesSignal: Signal<[ThemeGalleryEntry], NoError> = .single(initialEntries)
|
||||
//
|
||||
// self.disposable.set((entriesSignal |> deliverOnMainQueue).start(next: { [weak self] entries in
|
||||
// if let strongSelf = self {
|
||||
// strongSelf.entries = entries
|
||||
// strongSelf.centralEntryIndex = wallpapers.index(of: centralWallpaper)!
|
||||
// if strongSelf.isViewLoaded {
|
||||
// strongSelf.galleryNode.pager.replaceItems(strongSelf.entries.map({ ThemeGalleryItem(account: account, entry: $0) }), centralItemIndex: strongSelf.centralEntryIndex, keepFirst: true)
|
||||
//
|
||||
// let ready = strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak strongSelf] _ in
|
||||
// strongSelf?.didSetReady = true
|
||||
// }
|
||||
// strongSelf._ready.set(ready |> map { true })
|
||||
// }
|
||||
// }
|
||||
// }))
|
||||
|
||||
self.presentationDataDisposable = (account.telegramApplicationContext.presentationData
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||
if let strongSelf = self {
|
||||
@ -150,15 +145,18 @@ class WallpaperGalleryController: ViewController {
|
||||
}
|
||||
})
|
||||
|
||||
//self.centralItemAttributesDisposable.add(self.centralItemTitleView.get().start(next: { [weak self] titleView in
|
||||
// self?.navigationItem.titleView = titleView
|
||||
//}))
|
||||
|
||||
// self.centralItemAttributesDisposable.add(self.centralItemFooterContentNode.get().start(next: { [weak self] footerContentNode in
|
||||
// self?.galleryNode.updatePresentationState({
|
||||
// $0.withUpdatedFooterContentNode(footerContentNode)
|
||||
// }, transition: .immediate)
|
||||
// }))
|
||||
self.centralItemAttributesDisposable.add(self.centralItemStatus.get().start(next: { [weak self] status in
|
||||
if let strongSelf = self {
|
||||
let enabled: Bool
|
||||
switch status {
|
||||
case .Local:
|
||||
enabled = true
|
||||
default:
|
||||
enabled = false
|
||||
}
|
||||
strongSelf.toolbarNode?.setDoneEnabled(enabled)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
@ -177,16 +175,12 @@ class WallpaperGalleryController: ViewController {
|
||||
self.toolbarNode?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings)
|
||||
}
|
||||
|
||||
@objc func donePressed() {
|
||||
self.dismiss(forceAway: false)
|
||||
}
|
||||
|
||||
private func dismiss(forceAway: Bool) {
|
||||
let completion = { [weak self] in
|
||||
let completion: () -> Void = { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
}
|
||||
|
||||
//self.galleryNode.modalAnimateOut(completion: completion)
|
||||
self.galleryNode.modalAnimateOut(completion: completion)
|
||||
}
|
||||
|
||||
override func loadDisplayNode() {
|
||||
@ -203,27 +197,14 @@ class WallpaperGalleryController: ViewController {
|
||||
|
||||
self.galleryNode.statusBar = self.statusBar
|
||||
self.galleryNode.navigationBar = self.navigationBar
|
||||
|
||||
self.galleryNode.transitionDataForCentralItem = { [weak self] in
|
||||
// if let strongSelf = self {
|
||||
// if let centralItemNode = strongSelf.galleryNode.pager.centralItemNode(), let presentationArguments = strongSelf.presentationArguments as? ThemePreviewControllerPresentationArguments {
|
||||
// if let transitionArguments = presentationArguments.transitionArguments(strongSelf.entries[centralItemNode.index]) {
|
||||
// return (transitionArguments.transitionNode, transitionArguments.addToTransitionSurface)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return nil
|
||||
}
|
||||
self.galleryNode.dismiss = { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
}
|
||||
|
||||
self.galleryNode.pager.centralItemIndexUpdated = { [weak self] index in
|
||||
if let strongSelf = self {
|
||||
if let index = index {
|
||||
if let node = strongSelf.galleryNode.pager.centralItemNode() {
|
||||
//strongSelf.centralItemTitle.set(node.title())
|
||||
}
|
||||
if let node = strongSelf.galleryNode.pager.centralItemNode() as? WallpaperGalleryItemNode {
|
||||
strongSelf.centralItemStatus.set(node.status.get())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -234,29 +215,36 @@ class WallpaperGalleryController: ViewController {
|
||||
|
||||
let presentationData = self.account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||
let toolbarNode = ThemeGalleryToolbarNode(theme: presentationData.theme, strings: presentationData.strings)
|
||||
let overlayNode = WallpaperGalleryOverlayNode()
|
||||
self.overlayNode = overlayNode
|
||||
self.galleryNode.overlayNode = overlayNode
|
||||
self.galleryNode.addSubnode(overlayNode)
|
||||
|
||||
self.toolbarNode = toolbarNode
|
||||
self.galleryNode.addSubnode(toolbarNode)
|
||||
self.galleryNode.toolbarNode = toolbarNode
|
||||
overlayNode.addSubnode(toolbarNode)
|
||||
|
||||
toolbarNode.cancel = { [weak self] in
|
||||
//self?.dismiss(forceAway: true)
|
||||
self?.dismiss(forceAway: true)
|
||||
}
|
||||
toolbarNode.done = { [weak self] in
|
||||
// if let strongSelf = self {
|
||||
// if let centralItemNode = strongSelf.galleryNode.pager.centralItemNode() {
|
||||
// if !strongSelf.entries.isEmpty {
|
||||
// let wallpaper: TelegramWallpaper
|
||||
// switch strongSelf.entries[centralItemNode.index] {
|
||||
// case let .wallpaper(value):
|
||||
// wallpaper = value
|
||||
// }
|
||||
// let _ = (updatePresentationThemeSettingsInteractively(postbox: strongSelf.account.postbox, { current in
|
||||
// return PresentationThemeSettings(chatWallpaper: wallpaper, chatWallpaperOptions: [], theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations)
|
||||
// }) |> deliverOnMainQueue).start(completed: {
|
||||
// self?.dismiss(forceAway: true)
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
if let strongSelf = self {
|
||||
if let centralItemNode = strongSelf.galleryNode.pager.centralItemNode() {
|
||||
if !strongSelf.entries.isEmpty {
|
||||
let entry = strongSelf.entries[centralItemNode.index]
|
||||
switch entry {
|
||||
case let .wallpaper(wallpaper):
|
||||
let _ = (updatePresentationThemeSettingsInteractively(postbox: strongSelf.account.postbox, { current in
|
||||
return PresentationThemeSettings(chatWallpaper: wallpaper, chatWallpaperOptions: [], theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations)
|
||||
}) |> deliverOnMainQueue).start(completed: {
|
||||
self?.dismiss(forceAway: true)
|
||||
})
|
||||
default:
|
||||
break
|
||||
}
|
||||
strongSelf.apply?(entry, [], nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ready = self.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak self] _ in
|
||||
@ -276,10 +264,129 @@ class WallpaperGalleryController: ViewController {
|
||||
|
||||
self.galleryNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
self.galleryNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition)
|
||||
self.overlayNode?.frame = self.galleryNode.bounds
|
||||
|
||||
var items: [ChatMessageItem] = []
|
||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: 1)
|
||||
let otherPeerId = self.account.peerId
|
||||
var peers = SimpleDictionary<PeerId, Peer>()
|
||||
let messages = SimpleDictionary<MessageId, Message>()
|
||||
peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_PreviewReplyAuthor, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
|
||||
peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: self.presentationData.strings.Appearance_PreviewReplyAuthor, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
|
||||
let controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in
|
||||
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _ in }, navigateToMessage: { _, _ in }, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendMessage: { _ in }, sendSticker: { _, _ in }, sendGif: { _ in }, requestMessageActionCallback: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _ in }, openWallpaper: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||
}, presentController: { _, _ in }, navigationController: {
|
||||
return nil
|
||||
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _ in }, longTap: { _ in }, openCheckoutOrReceipt: { _ in }, openSearch: { }, setupReply: { _ in
|
||||
}, canSetupReply: { _ in
|
||||
return false
|
||||
}, navigateToFirstDateMessage: { _ in
|
||||
}, requestRedeliveryOfFailedMessages: { _ in
|
||||
}, addContact: { _ in
|
||||
}, rateCall: { _, _ in
|
||||
}, requestSelectMessagePollOption: { _, _ in
|
||||
}, openAppStorePage: {
|
||||
}, requestMessageUpdate: { _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, automaticMediaDownloadSettings: AutomaticMediaDownloadSettings.defaultSettings,
|
||||
pollActionState: ChatInterfacePollActionState())
|
||||
|
||||
let chatPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper), fontSize: self.presentationData.fontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: false)
|
||||
|
||||
let topMessageText: String
|
||||
let bottomMessageText: String
|
||||
switch self.source {
|
||||
case .wallpaper, .slug:
|
||||
topMessageText = presentationData.strings.WallpaperPreview_PreviewTopText
|
||||
bottomMessageText = presentationData.strings.WallpaperPreview_PreviewBottomText
|
||||
case let .list(_, _, type):
|
||||
switch type {
|
||||
case .wallpapers:
|
||||
topMessageText = presentationData.strings.WallpaperPreview_SwipeTopText
|
||||
bottomMessageText = presentationData.strings.WallpaperPreview_SwipeBottomText
|
||||
case .colors:
|
||||
topMessageText = presentationData.strings.WallpaperPreview_SwipeColorsTopText
|
||||
bottomMessageText = presentationData.strings.WallpaperPreview_SwipeColorsBottomText
|
||||
}
|
||||
case .asset, .contextResult:
|
||||
topMessageText = presentationData.strings.WallpaperPreview_CropTopText
|
||||
bottomMessageText = presentationData.strings.WallpaperPreview_CropBottomText
|
||||
case .customColor:
|
||||
topMessageText = presentationData.strings.WallpaperPreview_CustomColorTopText
|
||||
bottomMessageText = presentationData.strings.WallpaperPreview_CustomColorBottomText
|
||||
}
|
||||
|
||||
items.append(ChatMessageItem(presentationData: chatPresentationData, account: self.account, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: bottomMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, isAdmin: false), disableDate: true))
|
||||
|
||||
items.append(ChatMessageItem(presentationData: chatPresentationData, account: self.account, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: topMessageText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, isAdmin: false), disableDate: true))
|
||||
|
||||
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
|
||||
if let messageNodes = self.messageNodes {
|
||||
for i in 0 ..< items.count {
|
||||
let itemNode = messageNodes[i]
|
||||
items[i].updateNode(async: { $0() }, node: {
|
||||
return itemNode
|
||||
}, params: params, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], animation: .None, completion: { (layout, apply) in
|
||||
let nodeFrame = CGRect(origin: itemNode.frame.origin, size: CGSize(width: layout.size.width, height: layout.size.height))
|
||||
|
||||
itemNode.contentSize = layout.contentSize
|
||||
itemNode.insets = layout.insets
|
||||
itemNode.frame = nodeFrame
|
||||
itemNode.isUserInteractionEnabled = false
|
||||
|
||||
apply(ListViewItemApply(isOnScreen: true))
|
||||
})
|
||||
}
|
||||
} else {
|
||||
var messageNodes: [ListViewItemNode] = []
|
||||
for i in 0 ..< items.count {
|
||||
var itemNode: ListViewItemNode?
|
||||
items[i].nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: false, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], completion: { node, apply in
|
||||
itemNode = node
|
||||
apply().1(ListViewItemApply(isOnScreen: true))
|
||||
})
|
||||
itemNode!.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
|
||||
itemNode!.isUserInteractionEnabled = false
|
||||
messageNodes.append(itemNode!)
|
||||
self.overlayNode?.addSubnode(itemNode!)
|
||||
}
|
||||
self.messageNodes = messageNodes
|
||||
}
|
||||
|
||||
var bottomInset = layout.intrinsicInsets.bottom + 49.0
|
||||
|
||||
var optionsAvailable = true
|
||||
if let centralItemNode = self.galleryNode.pager.centralItemNode() {
|
||||
if !self.entries.isEmpty {
|
||||
let entry = self.entries[centralItemNode.index]
|
||||
switch entry {
|
||||
case let .wallpaper(wallpaper):
|
||||
switch wallpaper {
|
||||
case .color:
|
||||
optionsAvailable = false
|
||||
default:
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.toolbarNode!, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - 49.0 - layout.intrinsicInsets.bottom), size: CGSize(width: layout.size.width, height: 49.0 + layout.intrinsicInsets.bottom)))
|
||||
self.toolbarNode!.updateLayout(size: CGSize(width: layout.size.width, height: 49.0), layout: layout, transition: transition)
|
||||
|
||||
if let messageNodes = self.messageNodes {
|
||||
var bottomOffset: CGFloat = layout.size.height - bottomInset - 9.0
|
||||
// if optionsAvailable {
|
||||
// bottomOffset -= segmentedControlSize.height + 37.0
|
||||
// }
|
||||
for itemNode in messageNodes {
|
||||
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: bottomOffset - itemNode.frame.height), size: itemNode.frame.size))
|
||||
bottomOffset -= itemNode.frame.height
|
||||
}
|
||||
}
|
||||
|
||||
let replace = self.validLayout == nil
|
||||
self.validLayout = (layout, 0.0)
|
||||
|
||||
|
||||
@ -116,7 +116,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
case let .wallpaper(wallpaper):
|
||||
switch wallpaper {
|
||||
case .builtin:
|
||||
displaySize = CGSize(width: 640.0, height: 1136.0)
|
||||
displaySize = CGSize(width: 1308.0, height: 2688.0).fitted(CGSize(width: 1280.0, height: 1280.0)).dividedByScreenScale().integralFloor
|
||||
contentSize = displaySize
|
||||
signal = settingsBuiltinWallpaperImage(account: account)
|
||||
fetchSignal = .complete()
|
||||
@ -135,10 +135,10 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
|
||||
var convertedRepresentations: [ImageRepresentationWithReference] = []
|
||||
for representation in file.file.previewRepresentations {
|
||||
convertedRepresentations.append(ImageRepresentationWithReference(representation: representation, reference: .standalone(resource: representation.resource)))
|
||||
convertedRepresentations.append(ImageRepresentationWithReference(representation: representation, reference: .wallpaper(resource: representation.resource)))
|
||||
}
|
||||
convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.file.resource), reference: .standalone(resource: file.file.resource)))
|
||||
signal = chatMessageImageFile(account: account, fileReference: .standalone(media: file.file), thumbnail: false)
|
||||
convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.file.resource), reference: .wallpaper(resource: file.file.resource)))
|
||||
signal = chatAvatarGalleryPhoto(account: account, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: true, autoFetchFullSize: false)
|
||||
fetchSignal = fetchedMediaResource(postbox: account.postbox, reference: convertedRepresentations[convertedRepresentations.count - 1].reference)
|
||||
statusSignal = account.postbox.mediaBox.resourceStatus(file.file.resource)
|
||||
case let .image(representations):
|
||||
@ -276,7 +276,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
||||
controlsColorSignal = backgroundContrastColor(for: imagePromise.get())
|
||||
}
|
||||
self.controlsColor.set(.single(.white) |> then(controlsColorSignal))
|
||||
self.status.set(statusSignal)
|
||||
self.status.set(statusSignal |> deliverOnMainQueue)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -55,7 +55,7 @@ final class WallpaperListPreviewController: ViewController {
|
||||
})
|
||||
|
||||
self.title = self.presentationData.strings.WallpaperPreview_Title
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: self.presentationData.theme.rootController.navigationBar.accentTextColor), style: .plain, target: self, action: #selector(self.sharePressed))
|
||||
//self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: self.presentationData.theme.rootController.navigationBar.accentTextColor), style: .plain, target: self, action: #selector(self.sharePressed))
|
||||
}
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
@ -166,7 +166,7 @@ final class WallpaperListPreviewController: ViewController {
|
||||
} else {
|
||||
strongSelf.wallpaper = nil
|
||||
}
|
||||
strongSelf.navigationItem.rightBarButtonItem = barButtonItem
|
||||
//strongSelf.navigationItem.rightBarButtonItem = barButtonItem
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -719,23 +719,23 @@ final class WallpaperListPreviewControllerNode: ViewControllerTracingNode {
|
||||
self.colorPanelNode.updateLayout(size: colorPanelFrame.size, keyboardHeight: layout.inputHeight ?? 0.0, transition: transition)
|
||||
|
||||
bottomInset += height
|
||||
}
|
||||
|
||||
var optionsAvailable = true
|
||||
if let centralWallpaper = self.centralWallpaper {
|
||||
switch centralWallpaper {
|
||||
case let .wallpaper(wallpaper):
|
||||
switch wallpaper {
|
||||
case .color:
|
||||
optionsAvailable = false
|
||||
default:
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let optionsAvailable = false //true
|
||||
// if let centralWallpaper = self.centralWallpaper {
|
||||
// switch centralWallpaper {
|
||||
// case let .wallpaper(wallpaper):
|
||||
// switch wallpaper {
|
||||
// case .color:
|
||||
// optionsAvailable = false
|
||||
// default:
|
||||
// break
|
||||
// }
|
||||
// default:
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
|
||||
var segmentedControlSize = self.segmentedControl.sizeThatFits(layout.size)
|
||||
segmentedControlSize.width = max(270.0, segmentedControlSize.width)
|
||||
|
||||
@ -875,6 +875,7 @@ final class WallpaperListPreviewControllerNode: ViewControllerTracingNode {
|
||||
break
|
||||
}
|
||||
self.apply(wallpaper, options, self.centralNode()?.cropRect)
|
||||
self.isUserInteractionEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user