mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-16 19:30:29 +00:00
UI fixes
This commit is contained in:
parent
1270d99fc2
commit
a43f219f13
@ -6,12 +6,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"filename" : "SearchBarIconLight@2x.png",
|
"filename" : "IconSearch@2x.png",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"filename" : "SearchBarIconLight@3x.png",
|
"filename" : "IconSearch@3x.png",
|
||||||
"scale" : "3x"
|
"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 */; };
|
096C98C021787C6700C211FF /* TGBridgeAudioEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 096C98BC21787C6600C211FF /* TGBridgeAudioEncoder.h */; };
|
||||||
096C98C121787C6700C211FF /* TGBridgeAudioDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 096C98BD21787C6700C211FF /* TGBridgeAudioDecoder.h */; };
|
096C98C121787C6700C211FF /* TGBridgeAudioDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 096C98BD21787C6700C211FF /* TGBridgeAudioDecoder.h */; };
|
||||||
096C98C221787C6700C211FF /* TGBridgeAudioDecoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 096C98BE21787C6700C211FF /* TGBridgeAudioDecoder.mm */; };
|
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 */; };
|
09797873210633CD0077D77F /* InstantPageSettingsButtonItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09797872210633CD0077D77F /* InstantPageSettingsButtonItemNode.swift */; };
|
||||||
0979787C210642CB0077D77F /* WebEmbedPlayerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0979787B210642CB0077D77F /* WebEmbedPlayerNode.swift */; };
|
0979787C210642CB0077D77F /* WebEmbedPlayerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0979787B210642CB0077D77F /* WebEmbedPlayerNode.swift */; };
|
||||||
0979787E210646C00077D77F /* YoutubeEmbedImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0979787D210646C00077D77F /* YoutubeEmbedImplementation.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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
0979787D210646C00077D77F /* YoutubeEmbedImplementation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YoutubeEmbedImplementation.swift; sourceTree = "<group>"; };
|
||||||
@ -2912,6 +2916,8 @@
|
|||||||
children = (
|
children = (
|
||||||
D049EAE11E447AD500A2CD3A /* HorizontalStickersChatContextPanelNode.swift */,
|
D049EAE11E447AD500A2CD3A /* HorizontalStickersChatContextPanelNode.swift */,
|
||||||
D049EAE31E44949F00A2CD3A /* HorizontalStickerGridItem.swift */,
|
D049EAE31E44949F00A2CD3A /* HorizontalStickerGridItem.swift */,
|
||||||
|
09749BC221F0DFFD008FDDE9 /* StickersChatInputContextPanelNode.swift */,
|
||||||
|
09749BC421F0E024008FDDE9 /* StickersChatInputPanelItem.swift */,
|
||||||
);
|
);
|
||||||
name = Stickers;
|
name = Stickers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -5168,6 +5174,7 @@
|
|||||||
092F36902157AB46001A9F49 /* ItemListCallListItem.swift in Sources */,
|
092F36902157AB46001A9F49 /* ItemListCallListItem.swift in Sources */,
|
||||||
D0EC6CC61EB9F58800EBF1C3 /* PresenceStrings.swift in Sources */,
|
D0EC6CC61EB9F58800EBF1C3 /* PresenceStrings.swift in Sources */,
|
||||||
D0EC6CC71EB9F58800EBF1C3 /* PeerNotificationSoundStrings.swift in Sources */,
|
D0EC6CC71EB9F58800EBF1C3 /* PeerNotificationSoundStrings.swift in Sources */,
|
||||||
|
09749BC321F0DFFD008FDDE9 /* StickersChatInputContextPanelNode.swift in Sources */,
|
||||||
D01C06C01FBF118A001561AB /* MessageUtils.swift in Sources */,
|
D01C06C01FBF118A001561AB /* MessageUtils.swift in Sources */,
|
||||||
D0104F281F47171F004E4881 /* InstantPageGalleryController.swift in Sources */,
|
D0104F281F47171F004E4881 /* InstantPageGalleryController.swift in Sources */,
|
||||||
D0EC6CC81EB9F58800EBF1C3 /* ProgressiveImage.swift in Sources */,
|
D0EC6CC81EB9F58800EBF1C3 /* ProgressiveImage.swift in Sources */,
|
||||||
@ -5724,6 +5731,7 @@
|
|||||||
D0EEE9A12165585F001292A6 /* DocumentPreviewController.swift in Sources */,
|
D0EEE9A12165585F001292A6 /* DocumentPreviewController.swift in Sources */,
|
||||||
D0EC6DCE1EB9F58900EBF1C3 /* HorizontalStickersChatContextPanelNode.swift in Sources */,
|
D0EC6DCE1EB9F58900EBF1C3 /* HorizontalStickersChatContextPanelNode.swift in Sources */,
|
||||||
D0BCC3D2203F0A6C008126C2 /* StringForMessageTimestampStatus.swift in Sources */,
|
D0BCC3D2203F0A6C008126C2 /* StringForMessageTimestampStatus.swift in Sources */,
|
||||||
|
09749BC521F0E024008FDDE9 /* StickersChatInputPanelItem.swift in Sources */,
|
||||||
D0EC6DCF1EB9F58900EBF1C3 /* HorizontalStickerGridItem.swift in Sources */,
|
D0EC6DCF1EB9F58900EBF1C3 /* HorizontalStickerGridItem.swift in Sources */,
|
||||||
D0EC6DD01EB9F58900EBF1C3 /* HashtagChatInputContextPanelNode.swift in Sources */,
|
D0EC6DD01EB9F58900EBF1C3 /* HashtagChatInputContextPanelNode.swift in Sources */,
|
||||||
09B4EE5621A8149C00847FA6 /* PermissionInfoItem.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) {
|
private func openUrl(_ url: String, concealed: Bool) {
|
||||||
|
self.commitPurposefulAction()
|
||||||
|
|
||||||
let openImpl: () -> Void = { [weak self] in
|
let openImpl: () -> Void = { [weak self] in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
|
|||||||
@ -181,9 +181,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
self.controller = controller
|
self.controller = controller
|
||||||
|
|
||||||
self.backgroundNode = ChatBackgroundNode()
|
self.backgroundNode = ChatBackgroundNode()
|
||||||
//self.backgroundNode.isLayerBacked = true
|
|
||||||
self.backgroundNode.displaysAsynchronously = false
|
self.backgroundNode.displaysAsynchronously = false
|
||||||
//self.backgroundNode.clipsToBounds = true
|
|
||||||
|
|
||||||
self.titleAccessoryPanelContainer = ChatControllerTitlePanelNodeContainer()
|
self.titleAccessoryPanelContainer = ChatControllerTitlePanelNodeContainer()
|
||||||
self.titleAccessoryPanelContainer.clipsToBounds = true
|
self.titleAccessoryPanelContainer.clipsToBounds = true
|
||||||
|
|||||||
@ -996,7 +996,8 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
|||||||
markAllChatsAsReadInteractively(transaction: transaction, viewTracker: account.viewTracker)
|
markAllChatsAsReadInteractively(transaction: transaction, viewTracker: account.viewTracker)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let _ = signal.start(completed: { [weak self] in
|
let _ = (signal
|
||||||
|
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||||
self?.donePressed()
|
self?.donePressed()
|
||||||
})
|
})
|
||||||
} else if !peerIds.isEmpty {
|
} else if !peerIds.isEmpty {
|
||||||
@ -1034,7 +1035,8 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
|||||||
progressDisposable.dispose()
|
progressDisposable.dispose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let _ = signal.start(completed: {
|
let _ = (signal
|
||||||
|
|> deliverOnMainQueue).start(completed: {
|
||||||
self?.donePressed()
|
self?.donePressed()
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -507,7 +507,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
|||||||
switch status {
|
switch status {
|
||||||
case let .fetchStatus(fetchStatus):
|
case let .fetchStatus(fetchStatus):
|
||||||
switch fetchStatus {
|
switch fetchStatus {
|
||||||
case let .Fetching(isActive, progress):
|
case let .Fetching(_, progress):
|
||||||
let adjustedProgress = max(progress, 0.027)
|
let adjustedProgress = max(progress, 0.027)
|
||||||
state = .progress(color: bubbleTheme.mediaOverlayControlForegroundColor, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true)
|
state = .progress(color: bubbleTheme.mediaOverlayControlForegroundColor, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true)
|
||||||
case .Local:
|
case .Local:
|
||||||
|
|||||||
@ -641,6 +641,16 @@ private func preparedContactListNodeTransition(account: Account, from fromEntrie
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
outer: for entry in toEntries {
|
||||||
|
switch entry {
|
||||||
|
case .sort:
|
||||||
|
shouldFixScroll = true
|
||||||
|
break outer
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var scrollToItem: ListViewScrollToItem?
|
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
|
return Signal { subscriber in
|
||||||
let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [localIdentifier], options: nil)
|
let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [localIdentifier], options: nil)
|
||||||
let requestId = Atomic<RequestId>(value: RequestId())
|
let requestId = Atomic<RequestId>(value: RequestId())
|
||||||
if fetchResult.count != 0 {
|
if fetchResult.count != 0 {
|
||||||
let asset = fetchResult.object(at: 0)
|
let asset = fetchResult.object(at: 0)
|
||||||
let option = PHImageRequestOptions()
|
let option = PHImageRequestOptions()
|
||||||
option.deliveryMode = .opportunistic
|
option.deliveryMode = .highQualityFormat
|
||||||
|
if thumbnail {
|
||||||
|
option.resizeMode = .fast
|
||||||
|
}
|
||||||
option.isNetworkAccessAllowed = true
|
option.isNetworkAccessAllowed = true
|
||||||
option.isSynchronous = false
|
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 {
|
Queue.concurrentDefaultQueue().async {
|
||||||
requestId.with { current -> Void in
|
requestId.with { current -> Void in
|
||||||
if !current.invalidated {
|
if !current.invalidated {
|
||||||
@ -123,15 +127,8 @@ func fetchPhotoLibraryImage(localIdentifier: String) -> Signal<(UIImage, Bool)?,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let image = image {
|
if let image = image {
|
||||||
var isThumbnail = true
|
subscriber.putNext((image, thumbnail))
|
||||||
if let info = info, let degraded = info[PHImageResultIsDegradedKey] {
|
subscriber.putCompletion()
|
||||||
isThumbnail = (degraded as AnyObject).boolValue!
|
|
||||||
}
|
|
||||||
subscriber.putNext((image, isThumbnail))
|
|
||||||
if !isThumbnail {
|
|
||||||
subscriber.putCompletion()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -8,7 +8,7 @@ class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGestureRecog
|
|||||||
var navigationBar: NavigationBar?
|
var navigationBar: NavigationBar?
|
||||||
let footerNode: GalleryFooterNode
|
let footerNode: GalleryFooterNode
|
||||||
var currentThumbnailContainerNode: GalleryThumbnailContainerNode?
|
var currentThumbnailContainerNode: GalleryThumbnailContainerNode?
|
||||||
var toolbarNode: ASDisplayNode?
|
var overlayNode: ASDisplayNode?
|
||||||
var transitionDataForCentralItem: (() -> ((ASDisplayNode, () -> UIView?)?, (UIView) -> Void)?)?
|
var transitionDataForCentralItem: (() -> ((ASDisplayNode, () -> UIView?)?, (UIView) -> Void)?)?
|
||||||
var dismiss: (() -> Void)?
|
var dismiss: (() -> Void)?
|
||||||
|
|
||||||
@ -255,10 +255,6 @@ class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGestureRecog
|
|||||||
self.currentThumbnailContainerNode?.alpha = 1.0
|
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 {
|
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)
|
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()
|
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 {
|
if animateContent {
|
||||||
contentAnimationCompleted = false
|
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
|
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)
|
self.updateDismissTransition(transition)
|
||||||
|
|
||||||
if let toolbarNode = toolbarNode {
|
if let overlayNode = self.overlayNode {
|
||||||
toolbarNode.alpha = transition
|
overlayNode.alpha = transition
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -60,6 +60,7 @@ final class HorizontalStickerGridItemNode: GridItemNode {
|
|||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
self.imageNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
|
||||||
self.addSubnode(self.imageNode)
|
self.addSubnode(self.imageNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,38 @@ import SwiftSignalKit
|
|||||||
final class HorizontalStickersChatContextPanelInteraction {
|
final class HorizontalStickersChatContextPanelInteraction {
|
||||||
var previewedStickerItem: StickerPackItem?
|
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 {
|
private struct StickerEntry: Identifiable, Comparable {
|
||||||
let index: Int
|
let index: Int
|
||||||
@ -55,12 +87,15 @@ private func preparedGridEntryTransition(account: Account, from fromEntries: [St
|
|||||||
final class HorizontalStickersChatContextPanelNode: ChatInputContextPanelNode {
|
final class HorizontalStickersChatContextPanelNode: ChatInputContextPanelNode {
|
||||||
private var strings: PresentationStrings
|
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 gridNode: GridNode
|
||||||
private let backgroundNode: ASDisplayNode
|
|
||||||
|
|
||||||
private var validLayout: (CGSize, CGFloat, CGFloat, ChatPresentationInterfaceState)?
|
private var validLayout: (CGSize, CGFloat, CGFloat, ChatPresentationInterfaceState)?
|
||||||
private var currentEntries: [StickerEntry]?
|
private var currentEntries: [StickerEntry] = []
|
||||||
private var queuedTransitions: [(StickerEntryTransition, Bool)] = []
|
private var queuedTransitions: [StickerEntryTransition] = []
|
||||||
|
|
||||||
public var controllerInteraction: ChatControllerInteraction?
|
public var controllerInteraction: ChatControllerInteraction?
|
||||||
private let stickersInteraction: HorizontalStickersChatContextPanelInteraction
|
private let stickersInteraction: HorizontalStickersChatContextPanelInteraction
|
||||||
@ -70,22 +105,41 @@ final class HorizontalStickersChatContextPanelNode: ChatInputContextPanelNode {
|
|||||||
override init(account: Account, theme: PresentationTheme, strings: PresentationStrings) {
|
override init(account: Account, theme: PresentationTheme, strings: PresentationStrings) {
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
|
|
||||||
self.gridNode = GridNode()
|
self.backgroundNode = ASImageNode()
|
||||||
self.gridNode.view.disablesInteractiveTransitionGestureRecognizer = true
|
self.backgroundNode.displayWithoutProcessing = true
|
||||||
self.gridNode.scrollView.alwaysBounceVertical = true
|
self.backgroundNode.displaysAsynchronously = false
|
||||||
|
self.backgroundNode.image = backgroundCenterImage(theme)
|
||||||
|
|
||||||
self.backgroundNode = ASDisplayNode()
|
self.backgroundLeftNode = ASImageNode()
|
||||||
self.backgroundNode.backgroundColor = theme.list.plainBackgroundColor
|
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()
|
self.stickersInteraction = HorizontalStickersChatContextPanelInteraction()
|
||||||
|
|
||||||
super.init(account: account, theme: theme, strings: strings)
|
super.init(account: account, theme: theme, strings: strings)
|
||||||
|
|
||||||
|
self.placement = .overTextInput
|
||||||
self.isOpaque = false
|
self.isOpaque = false
|
||||||
self.clipsToBounds = true
|
|
||||||
|
|
||||||
self.addSubnode(self.gridNode)
|
self.addSubnode(self.backgroundNode)
|
||||||
self.gridNode.addSubnode(self.backgroundNode)
|
self.addSubnode(self.backgroundLeftNode)
|
||||||
|
self.addSubnode(self.backgroundRightNode)
|
||||||
|
|
||||||
|
self.addSubnode(self.clippingNode)
|
||||||
|
self.clippingNode.addSubnode(self.gridNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func didLoad() {
|
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 {
|
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 strongSelf.account.postbox.transaction { transaction -> Bool in
|
||||||
return getIsStickerSaved(transaction: transaction, fileId: item.file.fileId)
|
return getIsStickerSaved(transaction: transaction, fileId: item.file.fileId)
|
||||||
}
|
}
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> map { isStarred -> (ASDisplayNode, PeekControllerContent)? in
|
|> map { isStarred -> (ASDisplayNode, PeekControllerContent)? in
|
||||||
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
||||||
var menuItems: [PeekControllerMenuItem] = []
|
var menuItems: [PeekControllerMenuItem] = []
|
||||||
menuItems = [
|
menuItems = [
|
||||||
PeekControllerMenuItem(title: strongSelf.strings.StickerPack_Send, color: .accent, font: .bold, action: {
|
PeekControllerMenuItem(title: strongSelf.strings.StickerPack_Send, color: .accent, font: .bold, action: {
|
||||||
controllerInteraction.sendSticker(.standalone(media: item.file), true)
|
controllerInteraction.sendSticker(.standalone(media: item.file), true)
|
||||||
}),
|
}),
|
||||||
PeekControllerMenuItem(title: isStarred ? strongSelf.strings.Stickers_RemoveFromFavorites : strongSelf.strings.Stickers_AddToFavorites, color: isStarred ? .destructive : .accent, action: {
|
PeekControllerMenuItem(title: isStarred ? strongSelf.strings.Stickers_RemoveFromFavorites : strongSelf.strings.Stickers_AddToFavorites, color: isStarred ? .destructive : .accent, action: {
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if isStarred {
|
if isStarred {
|
||||||
let _ = removeSavedSticker(postbox: strongSelf.account.postbox, mediaId: item.file.fileId).start()
|
let _ = removeSavedSticker(postbox: strongSelf.account.postbox, mediaId: item.file.fileId).start()
|
||||||
} else {
|
} else {
|
||||||
let _ = addSavedSticker(postbox: strongSelf.account.postbox, network: strongSelf.account.network, file: item.file).start()
|
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 {
|
PeekControllerMenuItem(title: strongSelf.strings.StickerPack_ViewPack, color: .accent, action: {
|
||||||
loop: for attribute in item.file.attributes {
|
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
||||||
switch attribute {
|
loop: for attribute in item.file.attributes {
|
||||||
case let .Sticker(_, packReference, _):
|
switch attribute {
|
||||||
if let packReference = packReference {
|
case let .Sticker(_, packReference, _):
|
||||||
let controller = StickerPackPreviewController(account: strongSelf.account, stickerPack: packReference, parentNavigationController: controllerInteraction.navigationController())
|
if let packReference = packReference {
|
||||||
controller.sendSticker = { file in
|
let controller = StickerPackPreviewController(account: strongSelf.account, stickerPack: packReference, parentNavigationController: controllerInteraction.navigationController())
|
||||||
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
controller.sendSticker = { file in
|
||||||
controllerInteraction.sendSticker(file, true)
|
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:
|
controllerInteraction.navigationController()?.view.window?.endEditing(true)
|
||||||
break
|
controllerInteraction.presentController(controller, nil)
|
||||||
}
|
}
|
||||||
|
break loop
|
||||||
|
default:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}
|
||||||
PeekControllerMenuItem(title: strongSelf.strings.Common_Cancel, color: .accent, action: {})
|
}),
|
||||||
]
|
PeekControllerMenuItem(title: strongSelf.strings.Common_Cancel, color: .accent, action: {})
|
||||||
return (itemNode, StickerPreviewPeekContent(account: strongSelf.account, item: .pack(item), menu: menuItems))
|
]
|
||||||
} else {
|
return (itemNode, StickerPreviewPeekContent(account: strongSelf.account, item: .pack(item), menu: menuItems))
|
||||||
return nil
|
} else {
|
||||||
}
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}, present: { [weak self] content, sourceNode in
|
}, present: { [weak self] content, sourceNode in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let controller = PeekController(theme: PeekControllerTheme(presentationTheme: strongSelf.theme), content: content, sourceNode: {
|
let controller = PeekController(theme: PeekControllerTheme(presentationTheme: strongSelf.theme), content: content, sourceNode: {
|
||||||
return sourceNode
|
return sourceNode
|
||||||
})
|
})
|
||||||
strongSelf.interfaceInteraction?.presentGlobalOverlayController(controller, nil)
|
strongSelf.interfaceInteraction?.presentGlobalOverlayController(controller, nil)
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}, updateContent: { [weak self] content in
|
}, updateContent: { [weak self] content in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
var item: StickerPackItem?
|
var item: StickerPackItem?
|
||||||
if let content = content as? StickerPreviewPeekContent, case let .pack(contentItem) = content.item {
|
if let content = content as? StickerPreviewPeekContent, case let .pack(contentItem) = content.item {
|
||||||
item = contentItem
|
item = contentItem
|
||||||
|
}
|
||||||
|
strongSelf.updatePreviewingItem(item: item, animated: true)
|
||||||
}
|
}
|
||||||
strongSelf.updatePreviewingItem(item: item, animated: true)
|
|
||||||
}
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateResults(_ results: [TelegramMediaFile]) {
|
func updateResults(_ results: [TelegramMediaFile]) {
|
||||||
let firstTime = self.currentEntries == nil
|
let previousEntries = self.currentEntries
|
||||||
let previousEntries = self.currentEntries ?? []
|
|
||||||
var entries: [StickerEntry] = []
|
var entries: [StickerEntry] = []
|
||||||
for i in 0 ..< results.count {
|
for i in 0 ..< results.count {
|
||||||
entries.append(StickerEntry(index: i, file: results[i]))
|
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!)
|
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) {
|
private func enqueueTransition(_ transition: StickerEntryTransition) {
|
||||||
self.queuedTransitions.append((transition, firstTime))
|
self.queuedTransitions.append(transition)
|
||||||
if self.validLayout != nil {
|
if self.validLayout != nil {
|
||||||
self.dequeueTransitions()
|
self.dequeueTransitions()
|
||||||
}
|
}
|
||||||
@ -198,71 +251,71 @@ final class HorizontalStickersChatContextPanelNode: ChatInputContextPanelNode {
|
|||||||
|
|
||||||
private func dequeueTransitions() {
|
private func dequeueTransitions() {
|
||||||
while !self.queuedTransitions.isEmpty {
|
while !self.queuedTransitions.isEmpty {
|
||||||
let (transition, firstTime) = self.queuedTransitions.removeFirst()
|
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: { [weak self] _ in
|
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 })
|
||||||
|
|
||||||
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 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
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)
|
self.validLayout = (size, leftInset, rightInset, interfaceState)
|
||||||
|
|
||||||
var insets = UIEdgeInsets()
|
if dequeue {
|
||||||
insets.top = self.topInsetForLayout(size: size)
|
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
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 {
|
|
||||||
self.dequeueTransitions()
|
self.dequeueTransitions()
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.theme !== interfaceState.theme {
|
if self.theme !== interfaceState.theme {
|
||||||
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) {
|
override func animateOut(completion: @escaping () -> Void) {
|
||||||
let position = self.gridNode.layer.position
|
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in
|
||||||
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
|
|
||||||
completion()
|
completion()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
let convertedPoint = self.convert(point, to: self.gridNode)
|
if !self.clippingNode.frame.contains(point) {
|
||||||
if convertedPoint.y > 0.0 {
|
|
||||||
return super.hitTest(point, with: event)
|
|
||||||
} else {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
return super.hitTest(point, with: event)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updatePreviewingItem(item: StickerPackItem?, animated: Bool) {
|
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 }) {
|
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 maybeFullSize = account.postbox.mediaBox.resourceData(largestRepresentation.resource)
|
||||||
let decodedThumbnailData = fileReference?.media.immediateThumbnailData.flatMap(decodeTinyThumbnail)
|
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
|
|> mapToSignal { maybeData -> Signal<(Data?, Data?, Bool), NoError> in
|
||||||
if maybeData.complete {
|
if maybeData.complete {
|
||||||
let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: [])
|
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 {
|
} else {
|
||||||
let fetchedThumbnail: Signal<FetchResourceSourceType, FetchResourceError>
|
let fetchedThumbnail: Signal<FetchResourceSourceType, FetchResourceError>
|
||||||
if let _ = decodedThumbnailData {
|
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> {
|
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, autoFetchFullSize: autoFetchFullSize)
|
let signal = avatarGalleryPhotoDatas(account: account, fileReference: fileReference, representations: representations, alwaysShowThumbnailFirst: alwaysShowThumbnailFirst, autoFetchFullSize: autoFetchFullSize)
|
||||||
|
|
||||||
return signal
|
return signal
|
||||||
|> map { (thumbnailData, fullSizeData, fullSizeComplete) in
|
|> 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> {
|
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
|
|> map { result in
|
||||||
var sourceImage = result?.0
|
var sourceImage = result?.0
|
||||||
let isThumbnail = result?.1 ?? false
|
let isThumbnail = result?.1 ?? false
|
||||||
@ -2802,7 +2810,7 @@ func photoWallpaper(postbox: Postbox, photoLibraryResource: PhotoLibraryMediaRes
|
|||||||
return { arguments in
|
return { arguments in
|
||||||
let context = DrawingContext(size: arguments.drawingSize, scale: 1.0, clear: true)
|
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 {
|
if let thumbnailImage = sourceImage?.cgImage, isThumbnail {
|
||||||
var fittedSize = arguments.imageSize
|
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()
|
let _ = controller?.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
shareImpl = {
|
shareImpl = { [weak controller] in
|
||||||
let state = stateValue.with { $0 }
|
let state = stateValue.with { $0 }
|
||||||
guard let server = proxyServerSettings(with: state) else {
|
guard let server = proxyServerSettings(with: state), let strongController = controller else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = ShareProxyServerActionSheetController(theme: theme, strings: strings, updatedPresentationData: updatedPresentationData, link: shareLink(for: server))
|
let link = shareLink(for: server)
|
||||||
presentImpl?(controller, nil)
|
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
|
return controller
|
||||||
|
|||||||
@ -76,7 +76,7 @@ func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = nil, sca
|
|||||||
c.setFillColor(color.cgColor)
|
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")
|
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)
|
c.setFillColor(backgroundColor.cgColor)
|
||||||
} else {
|
} else {
|
||||||
c.setBlendMode(.clear)
|
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 iconSize = CGSize()
|
||||||
var totalWidth = labelLayoutResult.size.width
|
var totalWidth = labelLayoutResult.size.width
|
||||||
let spacing: CGFloat = 7.0
|
let spacing: CGFloat = 8.0
|
||||||
|
|
||||||
|
|
||||||
if let iconImage = strongSelf.iconNode.image {
|
if let iconImage = strongSelf.iconNode.image {
|
||||||
iconSize = iconImage.size
|
iconSize = iconImage.size
|
||||||
|
|||||||
@ -97,7 +97,7 @@ final class SettingsThemeWallpaperNode: ASDisplayNode {
|
|||||||
|
|
||||||
var convertedRepresentations: [ImageRepresentationWithReference] = []
|
var convertedRepresentations: [ImageRepresentationWithReference] = []
|
||||||
for representation in file.file.previewRepresentations {
|
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)
|
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))
|
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
|
items.append(ProxyServerQRCodeItem(strings: strings, link: link, ready: { [weak self] in
|
||||||
self?._ready.set(.single(true))
|
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()
|
self?.dismissAnimated()
|
||||||
let _ = (qrCode(string: link, color: .black, backgroundColor: .white, scale: 1.0)
|
let _ = (qrCode(string: link, color: .black, backgroundColor: .white, scale: 1.0)
|
||||||
|> map { generator -> UIImage? in
|
|> 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()
|
self?.dismissAnimated()
|
||||||
presentActivityController(link)
|
presentActivityController(link)
|
||||||
}))
|
}))
|
||||||
@ -105,6 +105,9 @@ private final class ProxyServerQRCodeItemNode: ActionSheetItemNode {
|
|||||||
|
|
||||||
private let ready: () -> Void
|
private let ready: () -> Void
|
||||||
|
|
||||||
|
private var cachedHasLabel = true
|
||||||
|
private var cachedHasImage = true
|
||||||
|
|
||||||
init(theme: ActionSheetControllerTheme, strings: PresentationStrings, link: String, ready: @escaping () -> Void = {}) {
|
init(theme: ActionSheetControllerTheme, strings: PresentationStrings, link: String, ready: @escaping () -> Void = {}) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
@ -117,10 +120,10 @@ private final class ProxyServerQRCodeItemNode: ActionSheetItemNode {
|
|||||||
self.label.displaysAsynchronously = false
|
self.label.displaysAsynchronously = false
|
||||||
self.label.truncationMode = .byTruncatingTail
|
self.label.truncationMode = .byTruncatingTail
|
||||||
self.label.isUserInteractionEnabled = false
|
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 = 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)
|
super.init(theme: theme)
|
||||||
|
|
||||||
@ -131,16 +134,26 @@ private final class ProxyServerQRCodeItemNode: ActionSheetItemNode {
|
|||||||
override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
||||||
let imageInset: CGFloat = 44.0
|
let imageInset: CGFloat = 44.0
|
||||||
let side = constrainedSize.width - imageInset * 2.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 makeLayout = self.imageNode.asyncLayout()
|
||||||
let apply = makeLayout(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: nil))
|
let apply = makeLayout(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: nil))
|
||||||
apply()
|
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()
|
self.ready()
|
||||||
|
|
||||||
let labelSize = self.label.measure(CGSize(width: max(1.0, constrainedSize.width - 64.0), height: constrainedSize.height))
|
self.cachedHasLabel = constrainedSize.height > 480 || !self.cachedHasImage
|
||||||
return CGSize(width: constrainedSize.width, height: 14.0 + labelSize.height + 14.0 + constrainedSize.width - 88.0 + 14.0)
|
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() {
|
override func layout() {
|
||||||
@ -151,10 +164,21 @@ private final class ProxyServerQRCodeItemNode: ActionSheetItemNode {
|
|||||||
let imageInset: CGFloat = 44.0
|
let imageInset: CGFloat = 44.0
|
||||||
let spacing: CGFloat = 18.0
|
let spacing: CGFloat = 18.0
|
||||||
|
|
||||||
let labelSize = self.label.measure(CGSize(width: max(1.0, size.width - inset * 2.0), height: size.height))
|
let labelSize: CGSize
|
||||||
self.label.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - labelSize.width) / 2.0), y: spacing), size: labelSize)
|
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)
|
let imageOrigin = CGPoint(x: imageInset, y: self.label.frame.maxY + spacing - 4.0)
|
||||||
self.imageNode.frame = imageFrame
|
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 {
|
if self.currentState?.0 !== theme || self.currentState?.1 !== strings {
|
||||||
self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 36.0, color: theme.chat.inputMediaPanel.stickersSearchBackgroundColor)
|
self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 36.0, color: theme.chat.inputMediaPanel.stickersSearchBackgroundColor)
|
||||||
self.iconNode.image = generateLoupeIcon(color: theme.chat.inputMediaPanel.stickersSearchControlColor)
|
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
|
self.labelNode.frame = textFrame
|
||||||
|
|
||||||
if let iconImage = self.iconNode.image {
|
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) {
|
// DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.15) {
|
||||||
// //(controller.navigationController as? NavigationController)?.pushViewController(ThemeGridController(account: self.account))
|
// //(controller.navigationController as? NavigationController)?.pushViewController(ThemeGridController(account: self.account))
|
||||||
//
|
//
|
||||||
// let wrapperNode = ASDisplayNode()
|
// let wrapperNode = ASDisplayNode()
|
||||||
// let bounds = controller.displayNode.bounds
|
// let bounds = controller.displayNode.bounds
|
||||||
// wrapperNode.frame = bounds
|
// wrapperNode.frame = bounds
|
||||||
|
|||||||
@ -45,7 +45,8 @@ private func availableColors() -> [Int32] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func randomColor() -> Int32 {
|
private func randomColor() -> Int32 {
|
||||||
return availableColors().randomElement() ?? 0x000000
|
let colors = availableColors()
|
||||||
|
return colors[1 ..< colors.count - 1].randomElement() ?? 0x000000
|
||||||
}
|
}
|
||||||
|
|
||||||
final class ThemeColorsGridController: ViewController {
|
final class ThemeColorsGridController: ViewController {
|
||||||
@ -116,9 +117,18 @@ final class ThemeColorsGridController: ViewController {
|
|||||||
override func loadDisplayNode() {
|
override func loadDisplayNode() {
|
||||||
self.displayNode = ThemeColorsGridControllerNode(account: self.account, presentationData: self.presentationData, colors: availableColors(), present: { [weak self] controller, arguments in
|
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)
|
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
|
}, presentColorPicker: { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let controller = WallpaperListPreviewController(account: strongSelf.account, source: .customColor(randomColor()))
|
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)
|
self?.present(controller, in: .window(.root), blockInteraction: true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -79,7 +79,7 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
private var disposable: Disposable?
|
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.account = account
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.present = present
|
self.present = present
|
||||||
@ -119,7 +119,11 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
|
|||||||
let entries = previousEntries.with { $0 }
|
let entries = previousEntries.with { $0 }
|
||||||
if let entries = entries, !entries.isEmpty {
|
if let entries = entries, !entries.isEmpty {
|
||||||
let wallpapers = entries.map { $0.wallpaper }
|
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)
|
strongSelf.present(controller, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -206,7 +206,7 @@ class ThemeGalleryController: ViewController {
|
|||||||
let toolbarNode = ThemeGalleryToolbarNode(theme: presentationData.theme, strings: presentationData.strings)
|
let toolbarNode = ThemeGalleryToolbarNode(theme: presentationData.theme, strings: presentationData.strings)
|
||||||
self.toolbarNode = toolbarNode
|
self.toolbarNode = toolbarNode
|
||||||
self.galleryNode.addSubnode(toolbarNode)
|
self.galleryNode.addSubnode(toolbarNode)
|
||||||
self.galleryNode.toolbarNode = toolbarNode
|
//self.galleryNode.toolbarNode = toolbarNode
|
||||||
toolbarNode.cancel = { [weak self] in
|
toolbarNode.cancel = { [weak self] in
|
||||||
self?.dismiss(forceAway: true)
|
self?.dismiss(forceAway: true)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,6 +49,11 @@ final class ThemeGalleryToolbarNode: ASDisplayNode {
|
|||||||
self.doneButton.addTarget(self, action: #selector(self.donePressed), forControlEvents: .touchUpInside)
|
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) {
|
func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) {
|
||||||
self.backgroundColor = theme.rootController.tabBar.backgroundColor
|
self.backgroundColor = theme.rootController.tabBar.backgroundColor
|
||||||
self.separatorNode.backgroundColor = theme.rootController.tabBar.separatorColor
|
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.searchContentNode = NavigationBarSearchContentNode(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Wallpaper_Search, activate: { [weak self] in
|
||||||
self?.activateSearch()
|
// self?.activateSearch()
|
||||||
})
|
// })
|
||||||
self.navigationBar?.setContentNode(self.searchContentNode, animated: false)
|
// self.navigationBar?.setContentNode(self.searchContentNode, animated: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
required public init(coder aDecoder: NSCoder) {
|
required public init(coder aDecoder: NSCoder) {
|
||||||
@ -84,11 +84,11 @@ final class ThemeGridController: ViewController {
|
|||||||
|
|
||||||
if let isEmpty = self.isEmpty, isEmpty {
|
if let isEmpty = self.isEmpty, isEmpty {
|
||||||
} else {
|
} else {
|
||||||
if self.editingMode {
|
// if self.editingMode {
|
||||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.donePressed))
|
// self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.donePressed))
|
||||||
} else {
|
// } else {
|
||||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed))
|
// 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
|
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBar.style.style
|
||||||
@ -103,19 +103,19 @@ final class ThemeGridController: ViewController {
|
|||||||
override func loadDisplayNode() {
|
override func loadDisplayNode() {
|
||||||
self.displayNode = ThemeGridControllerNode(account: self.account, presentationData: self.presentationData, presentPreviewController: { [weak self] source in
|
self.displayNode = ThemeGridControllerNode(account: self.account, presentationData: self.presentationData, presentPreviewController: { [weak self] source in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
//let controller = WallpaperGalleryController(account: strongSelf.account, source: source)
|
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)
|
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
|
}, presentGallery: { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
@ -132,11 +132,12 @@ final class ThemeGridController: ViewController {
|
|||||||
let controller = WallpaperListPreviewController(account: strongSelf.account, source: .asset(asset.backingAsset, thumbnailImage))
|
let controller = WallpaperListPreviewController(account: strongSelf.account, source: .asset(asset.backingAsset, thumbnailImage))
|
||||||
controller.apply = { [weak self, weak legacyController, weak controller] wallpaper, mode, cropRect in
|
controller.apply = { [weak self, weak legacyController, weak controller] wallpaper, mode, cropRect in
|
||||||
if let strongSelf = self, let legacyController = legacyController, let controller = controller {
|
if let strongSelf = self, let legacyController = legacyController, let controller = controller {
|
||||||
strongSelf.uploadCustomWallpaper(wallpaper, mode: mode, cropRect: cropRect)
|
strongSelf.uploadCustomWallpaper(wallpaper, mode: mode, cropRect: cropRect, completion: { [weak legacyController, weak controller] in
|
||||||
|
if let legacyController = legacyController, let controller = controller {
|
||||||
let _ = (strongSelf.navigationController as? NavigationController)?.popViewController(animated: true)
|
legacyController.dismiss()
|
||||||
legacyController.dismiss()
|
controller.dismiss()
|
||||||
controller.dismiss()
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
strongSelf.present(controller, in: .window(.root), with: nil, blockInteraction: true)
|
strongSelf.present(controller, in: .window(.root), with: nil, blockInteraction: true)
|
||||||
@ -164,11 +165,11 @@ final class ThemeGridController: ViewController {
|
|||||||
if empty {
|
if empty {
|
||||||
strongSelf.navigationItem.setRightBarButton(nil, animated: true)
|
strongSelf.navigationItem.setRightBarButton(nil, animated: true)
|
||||||
} else {
|
} else {
|
||||||
if strongSelf.editingMode {
|
// if strongSelf.editingMode {
|
||||||
strongSelf.navigationItem.rightBarButtonItem = UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Done, style: .done, target: strongSelf, action: #selector(strongSelf.donePressed))
|
// strongSelf.navigationItem.rightBarButtonItem = UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Done, style: .done, target: strongSelf, action: #selector(strongSelf.donePressed))
|
||||||
} else {
|
// } else {
|
||||||
strongSelf.navigationItem.rightBarButtonItem = UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Edit, style: .plain, target: strongSelf, action: #selector(strongSelf.editPressed))
|
// 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()
|
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>
|
let imageSignal: Signal<UIImage, NoError>
|
||||||
switch wallpaper {
|
switch wallpaper {
|
||||||
case .wallpaper:
|
case .wallpaper:
|
||||||
imageSignal = .complete()
|
imageSignal = .complete()
|
||||||
case let .asset(asset, _):
|
case let .asset(asset, _):
|
||||||
imageSignal = fetchPhotoLibraryImage(localIdentifier: asset.localIdentifier)
|
imageSignal = fetchPhotoLibraryImage(localIdentifier: asset.localIdentifier, thumbnail: false)
|
||||||
|> filter { value in
|
|> filter { value in
|
||||||
return !(value?.1 ?? true)
|
return !(value?.1 ?? true)
|
||||||
}
|
}
|
||||||
@ -311,29 +312,31 @@ final class ThemeGridController: ViewController {
|
|||||||
})).start()
|
})).start()
|
||||||
}
|
}
|
||||||
|
|
||||||
let completion: () -> Void = {
|
let apply: () -> Void = {
|
||||||
let wallpaper: TelegramWallpaper = .image([TelegramMediaImageRepresentation(dimensions: image.size, resource: resource)])
|
let wallpaper: TelegramWallpaper = .image([TelegramMediaImageRepresentation(dimensions: croppedImage.size, resource: resource)])
|
||||||
updateWallpaper(wallpaper)
|
updateWallpaper(wallpaper)
|
||||||
|
DispatchQueue.main.async {
|
||||||
let _ = uploadWallpaper(account: account, resource: resource).start(next: { status in
|
completion()
|
||||||
if case let .complete(wallpaper) = status {
|
}
|
||||||
if mode.contains(.blur), case let .file(_, _, _, _, _, file) = wallpaper {
|
// let _ = uploadWallpaper(account: account, resource: resource).start(next: { status in
|
||||||
let _ = account.postbox.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true).start(completed: {
|
// if case let .complete(wallpaper) = status {
|
||||||
updateWallpaper(wallpaper)
|
// if mode.contains(.blur), case let .file(_, _, _, _, _, file) = wallpaper {
|
||||||
})
|
// let _ = account.postbox.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true).start(completed: {
|
||||||
} else {
|
// updateWallpaper(wallpaper)
|
||||||
updateWallpaper(wallpaper)
|
// })
|
||||||
}
|
// } else {
|
||||||
}
|
// updateWallpaper(wallpaper)
|
||||||
})
|
// }
|
||||||
|
// }
|
||||||
|
// }).start()
|
||||||
}
|
}
|
||||||
|
|
||||||
if mode.contains(.blur) {
|
if mode.contains(.blur) {
|
||||||
let _ = account.postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true).start(completed: {
|
let _ = account.postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true).start(completed: {
|
||||||
completion()
|
apply()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
completion()
|
apply()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -233,7 +233,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
|||||||
self.gridNode.addSubnode(self.separatorNode)
|
self.gridNode.addSubnode(self.separatorNode)
|
||||||
self.gridNode.addSubnode(self.colorItemNode)
|
self.gridNode.addSubnode(self.colorItemNode)
|
||||||
self.gridNode.addSubnode(self.galleryItemNode)
|
self.gridNode.addSubnode(self.galleryItemNode)
|
||||||
self.gridNode.addSubnode(self.descriptionItemNode)
|
//self.gridNode.addSubnode(self.descriptionItemNode)
|
||||||
self.addSubnode(self.gridNode)
|
self.addSubnode(self.gridNode)
|
||||||
|
|
||||||
let wallpapersPromise: Promise<[TelegramWallpaper]> = Promise()
|
let wallpapersPromise: Promise<[TelegramWallpaper]> = Promise()
|
||||||
@ -471,7 +471,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
let buttonTopInset: CGFloat = 32.0
|
let buttonTopInset: CGFloat = 32.0
|
||||||
let buttonHeight: CGFloat = 44.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 buttonInset: CGFloat = buttonTopInset + buttonHeight * 2.0 + buttonBottomInset
|
||||||
let buttonOffset = buttonInset + 10.0
|
let buttonOffset = buttonInset + 10.0
|
||||||
|
|||||||
@ -244,6 +244,9 @@ final class WallpaperColorPickerNode: ASDisplayNode {
|
|||||||
let colorPanRecognizer = UIPanGestureRecognizer(target: self, action: #selector(WallpaperColorPickerNode.colorPan))
|
let colorPanRecognizer = UIPanGestureRecognizer(target: self, action: #selector(WallpaperColorPickerNode.colorPan))
|
||||||
self.colorNode.view.addGestureRecognizer(colorPanRecognizer)
|
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))
|
let brightnessPanRecognizer = UIPanGestureRecognizer(target: self, action: #selector(WallpaperColorPickerNode.brightnessPan))
|
||||||
self.brightnessNode.view.addGestureRecognizer(brightnessPanRecognizer)
|
self.brightnessNode.view.addGestureRecognizer(brightnessPanRecognizer)
|
||||||
}
|
}
|
||||||
@ -286,6 +289,23 @@ final class WallpaperColorPickerNode: ASDisplayNode {
|
|||||||
self.updateKnobLayout(size: size, panningColor: false, transition: transition)
|
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) {
|
@objc private func colorPan(_ recognizer: UIPanGestureRecognizer) {
|
||||||
guard let size = self.validLayout else {
|
guard let size = self.validLayout else {
|
||||||
return
|
return
|
||||||
@ -307,7 +327,7 @@ final class WallpaperColorPickerNode: ASDisplayNode {
|
|||||||
|
|
||||||
switch recognizer.state {
|
switch recognizer.state {
|
||||||
case .began:
|
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:
|
case .changed:
|
||||||
self.updateKnobLayout(size: size, panningColor: true, transition: .immediate)
|
self.updateKnobLayout(size: size, panningColor: true, transition: .immediate)
|
||||||
recognizer.setTranslation(CGPoint(), in: recognizer.view)
|
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 {
|
class WallpaperGalleryController: ViewController {
|
||||||
private var galleryNode: GalleryControllerNode {
|
private var galleryNode: GalleryControllerNode {
|
||||||
return self.displayNode as! GalleryControllerNode
|
return self.displayNode as! GalleryControllerNode
|
||||||
@ -57,7 +68,7 @@ class WallpaperGalleryController: ViewController {
|
|||||||
|
|
||||||
private let account: Account
|
private let account: Account
|
||||||
private let source: WallpaperListSource
|
private let source: WallpaperListSource
|
||||||
var apply: ((WallpaperEntry, WallpaperPresentationOptions, CGRect?) -> Void)?
|
var apply: ((WallpaperGalleryEntry, WallpaperPresentationOptions, CGRect?) -> Void)?
|
||||||
|
|
||||||
private let _ready = Promise<Bool>()
|
private let _ready = Promise<Bool>()
|
||||||
override var ready: Promise<Bool> {
|
override var ready: Promise<Bool> {
|
||||||
@ -79,6 +90,8 @@ class WallpaperGalleryController: ViewController {
|
|||||||
|
|
||||||
private var validLayout: (ContainerViewLayout, CGFloat)?
|
private var validLayout: (ContainerViewLayout, CGFloat)?
|
||||||
|
|
||||||
|
private var overlayNode: WallpaperGalleryOverlayNode?
|
||||||
|
private var messageNodes: [ListViewItemNode]?
|
||||||
private var toolbarNode: ThemeGalleryToolbarNode?
|
private var toolbarNode: ThemeGalleryToolbarNode?
|
||||||
|
|
||||||
init(account: Account, source: WallpaperListSource) {
|
init(account: Account, source: WallpaperListSource) {
|
||||||
@ -119,24 +132,6 @@ class WallpaperGalleryController: ViewController {
|
|||||||
self.centralEntryIndex = 0
|
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
|
self.presentationDataDisposable = (account.telegramApplicationContext.presentationData
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
@ -150,15 +145,18 @@ class WallpaperGalleryController: ViewController {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
//self.centralItemAttributesDisposable.add(self.centralItemTitleView.get().start(next: { [weak self] titleView in
|
self.centralItemAttributesDisposable.add(self.centralItemStatus.get().start(next: { [weak self] status in
|
||||||
// self?.navigationItem.titleView = titleView
|
if let strongSelf = self {
|
||||||
//}))
|
let enabled: Bool
|
||||||
|
switch status {
|
||||||
// self.centralItemAttributesDisposable.add(self.centralItemFooterContentNode.get().start(next: { [weak self] footerContentNode in
|
case .Local:
|
||||||
// self?.galleryNode.updatePresentationState({
|
enabled = true
|
||||||
// $0.withUpdatedFooterContentNode(footerContentNode)
|
default:
|
||||||
// }, transition: .immediate)
|
enabled = false
|
||||||
// }))
|
}
|
||||||
|
strongSelf.toolbarNode?.setDoneEnabled(enabled)
|
||||||
|
}
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
required init(coder aDecoder: NSCoder) {
|
required init(coder aDecoder: NSCoder) {
|
||||||
@ -177,16 +175,12 @@ class WallpaperGalleryController: ViewController {
|
|||||||
self.toolbarNode?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings)
|
self.toolbarNode?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func donePressed() {
|
|
||||||
self.dismiss(forceAway: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func dismiss(forceAway: Bool) {
|
private func dismiss(forceAway: Bool) {
|
||||||
let completion = { [weak self] in
|
let completion: () -> Void = { [weak self] in
|
||||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
//self.galleryNode.modalAnimateOut(completion: completion)
|
self.galleryNode.modalAnimateOut(completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func loadDisplayNode() {
|
override func loadDisplayNode() {
|
||||||
@ -203,27 +197,14 @@ class WallpaperGalleryController: ViewController {
|
|||||||
|
|
||||||
self.galleryNode.statusBar = self.statusBar
|
self.galleryNode.statusBar = self.statusBar
|
||||||
self.galleryNode.navigationBar = self.navigationBar
|
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.galleryNode.dismiss = { [weak self] in
|
||||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.galleryNode.pager.centralItemIndexUpdated = { [weak self] index in
|
self.galleryNode.pager.centralItemIndexUpdated = { [weak self] index in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if let index = index {
|
if let node = strongSelf.galleryNode.pager.centralItemNode() as? WallpaperGalleryItemNode {
|
||||||
if let node = strongSelf.galleryNode.pager.centralItemNode() {
|
strongSelf.centralItemStatus.set(node.status.get())
|
||||||
//strongSelf.centralItemTitle.set(node.title())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -234,29 +215,36 @@ class WallpaperGalleryController: ViewController {
|
|||||||
|
|
||||||
let presentationData = self.account.telegramApplicationContext.currentPresentationData.with { $0 }
|
let presentationData = self.account.telegramApplicationContext.currentPresentationData.with { $0 }
|
||||||
let toolbarNode = ThemeGalleryToolbarNode(theme: presentationData.theme, strings: presentationData.strings)
|
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.toolbarNode = toolbarNode
|
||||||
self.galleryNode.addSubnode(toolbarNode)
|
overlayNode.addSubnode(toolbarNode)
|
||||||
self.galleryNode.toolbarNode = toolbarNode
|
|
||||||
toolbarNode.cancel = { [weak self] in
|
toolbarNode.cancel = { [weak self] in
|
||||||
//self?.dismiss(forceAway: true)
|
self?.dismiss(forceAway: true)
|
||||||
}
|
}
|
||||||
toolbarNode.done = { [weak self] in
|
toolbarNode.done = { [weak self] in
|
||||||
// if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
// if let centralItemNode = strongSelf.galleryNode.pager.centralItemNode() {
|
if let centralItemNode = strongSelf.galleryNode.pager.centralItemNode() {
|
||||||
// if !strongSelf.entries.isEmpty {
|
if !strongSelf.entries.isEmpty {
|
||||||
// let wallpaper: TelegramWallpaper
|
let entry = strongSelf.entries[centralItemNode.index]
|
||||||
// switch strongSelf.entries[centralItemNode.index] {
|
switch entry {
|
||||||
// case let .wallpaper(value):
|
case let .wallpaper(wallpaper):
|
||||||
// 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)
|
||||||
// let _ = (updatePresentationThemeSettingsInteractively(postbox: strongSelf.account.postbox, { current in
|
}) |> deliverOnMainQueue).start(completed: {
|
||||||
// return PresentationThemeSettings(chatWallpaper: wallpaper, chatWallpaperOptions: [], theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations)
|
self?.dismiss(forceAway: true)
|
||||||
// }) |> 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
|
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.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||||
self.galleryNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition)
|
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)))
|
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)
|
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
|
let replace = self.validLayout == nil
|
||||||
self.validLayout = (layout, 0.0)
|
self.validLayout = (layout, 0.0)
|
||||||
|
|
||||||
|
|||||||
@ -116,7 +116,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
case let .wallpaper(wallpaper):
|
case let .wallpaper(wallpaper):
|
||||||
switch wallpaper {
|
switch wallpaper {
|
||||||
case .builtin:
|
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
|
contentSize = displaySize
|
||||||
signal = settingsBuiltinWallpaperImage(account: account)
|
signal = settingsBuiltinWallpaperImage(account: account)
|
||||||
fetchSignal = .complete()
|
fetchSignal = .complete()
|
||||||
@ -135,10 +135,10 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
|
|
||||||
var convertedRepresentations: [ImageRepresentationWithReference] = []
|
var convertedRepresentations: [ImageRepresentationWithReference] = []
|
||||||
for representation in file.file.previewRepresentations {
|
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)))
|
convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.file.resource), reference: .wallpaper(resource: file.file.resource)))
|
||||||
signal = chatMessageImageFile(account: account, fileReference: .standalone(media: file.file), thumbnail: false)
|
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)
|
fetchSignal = fetchedMediaResource(postbox: account.postbox, reference: convertedRepresentations[convertedRepresentations.count - 1].reference)
|
||||||
statusSignal = account.postbox.mediaBox.resourceStatus(file.file.resource)
|
statusSignal = account.postbox.mediaBox.resourceStatus(file.file.resource)
|
||||||
case let .image(representations):
|
case let .image(representations):
|
||||||
@ -276,7 +276,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
controlsColorSignal = backgroundContrastColor(for: imagePromise.get())
|
controlsColorSignal = backgroundContrastColor(for: imagePromise.get())
|
||||||
}
|
}
|
||||||
self.controlsColor.set(.single(.white) |> then(controlsColorSignal))
|
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.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) {
|
required init(coder aDecoder: NSCoder) {
|
||||||
@ -166,7 +166,7 @@ final class WallpaperListPreviewController: ViewController {
|
|||||||
} else {
|
} else {
|
||||||
strongSelf.wallpaper = nil
|
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)
|
self.colorPanelNode.updateLayout(size: colorPanelFrame.size, keyboardHeight: layout.inputHeight ?? 0.0, transition: transition)
|
||||||
|
|
||||||
bottomInset += height
|
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)
|
var segmentedControlSize = self.segmentedControl.sizeThatFits(layout.size)
|
||||||
segmentedControlSize.width = max(270.0, segmentedControlSize.width)
|
segmentedControlSize.width = max(270.0, segmentedControlSize.width)
|
||||||
|
|
||||||
@ -875,6 +875,7 @@ final class WallpaperListPreviewControllerNode: ViewControllerTracingNode {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
self.apply(wallpaper, options, self.centralNode()?.cropRect)
|
self.apply(wallpaper, options, self.centralNode()?.cropRect)
|
||||||
|
self.isUserInteractionEnabled = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user