mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit '3e8e152ca55533b24bc4fe4d11244c64599d3bf3'
This commit is contained in:
commit
29bbcf1bd7
@ -197,6 +197,7 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
private let avatarNode: AvatarNode
|
||||
private let titleNode: TextNode
|
||||
private var credibilityIconNode: ASImageNode?
|
||||
private let statusNode: TextNode
|
||||
private let dateNode: TextNode
|
||||
private let typeIconNode: ASImageNode
|
||||
@ -475,7 +476,26 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
let (dateLayout, dateApply) = makeDateLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: dateText, font: dateFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(0.0, params.width - leftInset - rightInset), height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(0.0, params.width - leftInset - dateRightInset - dateLayout.size.width - (item.editing ? -30.0 : 10.0)), height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.context.currentAppConfiguration.with { $0 })
|
||||
var currentCredibilityIconImage: UIImage?
|
||||
if let peer = item.topMessage.peers[item.topMessage.id.peerId], peer.id != item.context.account.peerId {
|
||||
if peer.isScam {
|
||||
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular)
|
||||
} else if peer.isFake {
|
||||
currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular)
|
||||
} else if peer.isVerified {
|
||||
currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme)
|
||||
} else if peer.isPremium && !premiumConfiguration.isPremiumDisabled {
|
||||
currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(item.presentationData.theme)
|
||||
}
|
||||
}
|
||||
|
||||
var additionalTitleInset: CGFloat = 0.0
|
||||
if let currentCredibilityIconImage = currentCredibilityIconImage {
|
||||
additionalTitleInset += 3.0 + currentCredibilityIconImage.size.width
|
||||
}
|
||||
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(0.0, params.width - additionalTitleInset - leftInset - dateRightInset - dateLayout.size.width - (item.editing ? -30.0 : 10.0)), height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let (statusLayout, statusApply) = makeStatusLayout(TextNodeLayoutArguments(attributedString: statusAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(0.0, params.width - leftInset - dateRightInset - dateLayout.size.width - (item.editing ? -30.0 : 10.0)), height: CGFloat.infinity), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
@ -614,11 +634,31 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
||||
transition.updateFrameAdditive(node: strongSelf.avatarNode, frame: CGRect(origin: CGPoint(x: revealOffset + leftInset - 52.0, y: floor((contentSize.height - avatarDiameter) / 2.0)), size: CGSize(width: avatarDiameter, height: avatarDiameter)))
|
||||
|
||||
let _ = titleApply()
|
||||
transition.updateFrameAdditive(node: strongSelf.titleNode, frame: CGRect(origin: CGPoint(x: revealOffset + leftInset, y: verticalInset), size: titleLayout.size))
|
||||
let titleFrame = CGRect(origin: CGPoint(x: revealOffset + leftInset, y: verticalInset), size: titleLayout.size)
|
||||
transition.updateFrameAdditive(node: strongSelf.titleNode, frame: titleFrame)
|
||||
|
||||
let _ = statusApply()
|
||||
transition.updateFrameAdditive(node: strongSelf.statusNode, frame: CGRect(origin: CGPoint(x: revealOffset + leftInset, y: strongSelf.titleNode.frame.maxY + titleSpacing), size: statusLayout.size))
|
||||
|
||||
if let currentCredibilityIconImage = currentCredibilityIconImage {
|
||||
let iconNode: ASImageNode
|
||||
if let current = strongSelf.credibilityIconNode {
|
||||
iconNode = current
|
||||
} else {
|
||||
iconNode = ASImageNode()
|
||||
iconNode.isLayerBacked = true
|
||||
iconNode.displaysAsynchronously = false
|
||||
iconNode.displayWithoutProcessing = true
|
||||
strongSelf.containerNode.addSubnode(iconNode)
|
||||
strongSelf.credibilityIconNode = iconNode
|
||||
}
|
||||
iconNode.image = currentCredibilityIconImage
|
||||
transition.updateFrame(node: iconNode, frame: CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: floorToScreenPixels(titleFrame.midY - currentCredibilityIconImage.size.height / 2.0) - UIScreenPixel), size: currentCredibilityIconImage.size))
|
||||
} else if let credibilityIconNode = strongSelf.credibilityIconNode {
|
||||
strongSelf.credibilityIconNode = nil
|
||||
credibilityIconNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
let _ = dateApply()
|
||||
transition.updateFrameAdditive(node: strongSelf.dateNode, frame: CGRect(origin: CGPoint(x: editingOffset + revealOffset + params.width - dateRightInset - dateLayout.size.width, y: floor((nodeLayout.contentSize.height - dateLayout.size.height) / 2.0) + 2.0), size: dateLayout.size))
|
||||
|
||||
|
@ -267,22 +267,15 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
let isFirstFilter = strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter == strongSelf.chatListDisplayNode.containerNode.availableFilters.first?.filter
|
||||
|
||||
if offset <= navigationBarSearchContentHeight + 1.0 && !isFirstFilter {
|
||||
let _ = (strongSelf.context.engine.peers.currentChatListFilters()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] filters in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let targetTab: ChatListFilterTabEntryId
|
||||
let firstFilter = filters.first ?? .allChats
|
||||
switch firstFilter {
|
||||
case .allChats:
|
||||
targetTab = .all
|
||||
case let .filter(id, _, _, _):
|
||||
targetTab = .filter(id)
|
||||
}
|
||||
|
||||
strongSelf.selectTab(id: targetTab)
|
||||
})
|
||||
let firstFilter = strongSelf.chatListDisplayNode.containerNode.availableFilters.first ?? .all
|
||||
let targetTab: ChatListFilterTabEntryId
|
||||
switch firstFilter {
|
||||
case .all:
|
||||
targetTab = .all
|
||||
case let .filter(filter):
|
||||
targetTab = .filter(filter.id)
|
||||
}
|
||||
strongSelf.selectTab(id: targetTab)
|
||||
} else {
|
||||
if let searchContentNode = strongSelf.searchContentNode {
|
||||
searchContentNode.updateExpansionProgress(1.0, animated: true)
|
||||
@ -1262,11 +1255,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
strongSelf.setToolbar(toolbar, transition: transition)
|
||||
}))
|
||||
|
||||
self.tabContainerNode.tabSelected = { [weak self] id, disabled in
|
||||
self.tabContainerNode.tabSelected = { [weak self] id, isDisabled in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if disabled {
|
||||
if isDisabled {
|
||||
let context = strongSelf.context
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let controller = PremiumLimitScreen(context: context, subject: .folders, count: strongSelf.tabContainerNode.filtersCount, action: {
|
||||
@ -1296,7 +1289,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}
|
||||
}
|
||||
|
||||
let tabContextGesture: (Int32?, ContextExtractedContentContainingNode, ContextGesture, Bool) -> Void = { [weak self] id, sourceNode, gesture, keepInPlace in
|
||||
let tabContextGesture: (Int32?, ContextExtractedContentContainingNode, ContextGesture, Bool, Bool) -> Void = { [weak self] id, sourceNode, gesture, keepInPlace, isDisabled in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@ -1319,24 +1312,37 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let _ = (strongSelf.context.engine.peers.currentChatListFilters()
|
||||
|> deliverOnMainQueue).start(next: { presetList in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
if isDisabled {
|
||||
let context = strongSelf.context
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let controller = PremiumLimitScreen(context: context, subject: .folders, count: strongSelf.tabContainerNode.filtersCount, action: {
|
||||
let controller = PremiumIntroScreen(context: context, source: .folders)
|
||||
replaceImpl?(controller)
|
||||
})
|
||||
replaceImpl = { [weak controller] c in
|
||||
controller?.replace(with: c)
|
||||
}
|
||||
var found = false
|
||||
for filter in presetList {
|
||||
if filter.id == id {
|
||||
strongSelf.push(chatListFilterPresetController(context: strongSelf.context, currentPreset: filter, updated: { _ in }))
|
||||
f(.dismissWithoutContent)
|
||||
found = true
|
||||
break
|
||||
strongSelf.push(controller)
|
||||
} else {
|
||||
let _ = (strongSelf.context.engine.peers.currentChatListFilters()
|
||||
|> deliverOnMainQueue).start(next: { presetList in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
f(.default)
|
||||
}
|
||||
})
|
||||
var found = false
|
||||
for filter in presetList {
|
||||
if filter.id == id {
|
||||
strongSelf.push(chatListFilterPresetController(context: strongSelf.context, currentPreset: filter, updated: { _ in }))
|
||||
f(.dismissWithoutContent)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
f(.default)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})))
|
||||
|
||||
@ -1349,62 +1355,75 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
return
|
||||
}
|
||||
|
||||
let _ = combineLatest(
|
||||
queue: Queue.mainQueue(),
|
||||
context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId),
|
||||
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false),
|
||||
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true)
|
||||
),
|
||||
strongSelf.context.engine.peers.currentChatListFilters()
|
||||
).start(next: { result, presetList in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
if isDisabled {
|
||||
let context = strongSelf.context
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let controller = PremiumLimitScreen(context: context, subject: .folders, count: strongSelf.tabContainerNode.filtersCount, action: {
|
||||
let controller = PremiumIntroScreen(context: context, source: .folders)
|
||||
replaceImpl?(controller)
|
||||
})
|
||||
replaceImpl = { [weak controller] c in
|
||||
controller?.replace(with: c)
|
||||
}
|
||||
var found = false
|
||||
for filter in presetList {
|
||||
if filter.id == id, case let .filter(_, _, _, data) = filter {
|
||||
let (accountPeer, limits, premiumLimits) = result
|
||||
let isPremium = accountPeer?.isPremium ?? false
|
||||
|
||||
let limit = limits.maxFolderChatsCount
|
||||
let premiumLimit = premiumLimits.maxFolderChatsCount
|
||||
|
||||
if data.includePeers.peers.count >= premiumLimit {
|
||||
let controller = PremiumLimitScreen(context: context, subject: .chatsPerFolder, count: Int32(data.includePeers.peers.count), action: {})
|
||||
strongSelf.push(controller)
|
||||
f(.dismissWithoutContent)
|
||||
return
|
||||
} else if data.includePeers.peers.count >= limit && !isPremium {
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let controller = PremiumLimitScreen(context: context, subject: .chatsPerFolder, count: Int32(data.includePeers.peers.count), action: {
|
||||
let controller = PremiumIntroScreen(context: context, source: .chatsPerFolder)
|
||||
replaceImpl?(controller)
|
||||
})
|
||||
replaceImpl = { [weak controller] c in
|
||||
controller?.replace(with: c)
|
||||
}
|
||||
strongSelf.push(controller)
|
||||
f(.dismissWithoutContent)
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (strongSelf.context.engine.peers.currentChatListFilters()
|
||||
|> deliverOnMainQueue).start(next: { filters in
|
||||
guard let strongSelf = self else {
|
||||
strongSelf.push(controller)
|
||||
} else {
|
||||
let _ = combineLatest(
|
||||
queue: Queue.mainQueue(),
|
||||
context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId),
|
||||
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false),
|
||||
TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true)
|
||||
),
|
||||
strongSelf.context.engine.peers.currentChatListFilters()
|
||||
).start(next: { result, presetList in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
var found = false
|
||||
for filter in presetList {
|
||||
if filter.id == id, case let .filter(_, _, _, data) = filter {
|
||||
let (accountPeer, limits, premiumLimits) = result
|
||||
let isPremium = accountPeer?.isPremium ?? false
|
||||
|
||||
let limit = limits.maxFolderChatsCount
|
||||
let premiumLimit = premiumLimits.maxFolderChatsCount
|
||||
|
||||
if data.includePeers.peers.count >= premiumLimit {
|
||||
let controller = PremiumLimitScreen(context: context, subject: .chatsPerFolder, count: Int32(data.includePeers.peers.count), action: {})
|
||||
strongSelf.push(controller)
|
||||
f(.dismissWithoutContent)
|
||||
return
|
||||
} else if data.includePeers.peers.count >= limit && !isPremium {
|
||||
var replaceImpl: ((ViewController) -> Void)?
|
||||
let controller = PremiumLimitScreen(context: context, subject: .chatsPerFolder, count: Int32(data.includePeers.peers.count), action: {
|
||||
let controller = PremiumIntroScreen(context: context, source: .chatsPerFolder)
|
||||
replaceImpl?(controller)
|
||||
})
|
||||
replaceImpl = { [weak controller] c in
|
||||
controller?.replace(with: c)
|
||||
}
|
||||
strongSelf.push(controller)
|
||||
f(.dismissWithoutContent)
|
||||
return
|
||||
}
|
||||
strongSelf.push(chatListFilterAddChatsController(context: strongSelf.context, filter: filter, allFilters: filters, limit: limits.maxFolderChatsCount, premiumLimit: premiumLimits.maxFolderChatsCount, isPremium: isPremium))
|
||||
f(.dismissWithoutContent)
|
||||
})
|
||||
found = true
|
||||
break
|
||||
|
||||
let _ = (strongSelf.context.engine.peers.currentChatListFilters()
|
||||
|> deliverOnMainQueue).start(next: { filters in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.push(chatListFilterAddChatsController(context: strongSelf.context, filter: filter, allFilters: filters, limit: limits.maxFolderChatsCount, premiumLimit: premiumLimits.maxFolderChatsCount, isPremium: isPremium))
|
||||
f(.dismissWithoutContent)
|
||||
})
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
f(.default)
|
||||
}
|
||||
})
|
||||
if !found {
|
||||
f(.default)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})))
|
||||
|
||||
@ -1472,11 +1491,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
strongSelf.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
|
||||
})
|
||||
}
|
||||
self.tabContainerNode.contextGesture = { id, sourceNode, gesture in
|
||||
tabContextGesture(id, sourceNode, gesture, false)
|
||||
self.tabContainerNode.contextGesture = { id, sourceNode, gesture, isDisabled in
|
||||
tabContextGesture(id, sourceNode, gesture, false, isDisabled)
|
||||
}
|
||||
self.chatListDisplayNode.inlineTabContainerNode.contextGesture = { id, sourceNode, gesture in
|
||||
tabContextGesture(id, sourceNode, gesture, true)
|
||||
self.chatListDisplayNode.inlineTabContainerNode.contextGesture = { id, sourceNode, gesture, isDisabled in
|
||||
tabContextGesture(id, sourceNode, gesture, true, isDisabled)
|
||||
}
|
||||
|
||||
if case .group = self.groupId {
|
||||
|
@ -88,7 +88,7 @@ private final class ItemNode: ASDisplayNode {
|
||||
|
||||
private var theme: PresentationTheme?
|
||||
|
||||
init(pressed: @escaping (Bool) -> Void, requestedDeletion: @escaping () -> Void, contextGesture: @escaping (ContextExtractedContentContainingNode, ContextGesture) -> Void) {
|
||||
init(pressed: @escaping (Bool) -> Void, requestedDeletion: @escaping () -> Void, contextGesture: @escaping (ContextExtractedContentContainingNode, ContextGesture, Bool) -> Void) {
|
||||
self.pressed = pressed
|
||||
self.requestedDeletion = requestedDeletion
|
||||
|
||||
@ -170,7 +170,7 @@ private final class ItemNode: ASDisplayNode {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
contextGesture(strongSelf.extractedContainerNode, gesture)
|
||||
contextGesture(strongSelf.extractedContainerNode, gesture, strongSelf.isDisabled)
|
||||
}
|
||||
|
||||
self.extractedContainerNode.willUpdateIsExtractedToContextPreview = { [weak self] isExtracted, transition in
|
||||
@ -467,7 +467,7 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
|
||||
var tabSelected: ((ChatListFilterTabEntryId, Bool) -> Void)?
|
||||
var tabRequestedDeletion: ((ChatListFilterTabEntryId) -> Void)?
|
||||
var addFilter: (() -> Void)?
|
||||
var contextGesture: ((Int32?, ContextExtractedContentContainingNode, ContextGesture) -> Void)?
|
||||
var contextGesture: ((Int32?, ContextExtractedContentContainingNode, ContextGesture, Bool) -> Void)?
|
||||
|
||||
private var reorderingGesture: ReorderingGestureRecognizer?
|
||||
private var reorderingItem: ChatListFilterTabEntryId?
|
||||
@ -739,7 +739,7 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
|
||||
self?.tabSelected?(filter.id, disabled)
|
||||
}, requestedDeletion: { [weak self] in
|
||||
self?.tabRequestedDeletion?(filter.id)
|
||||
}, contextGesture: { [weak self] sourceNode, gesture in
|
||||
}, contextGesture: { [weak self] sourceNode, gesture, isDisabled in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@ -748,9 +748,9 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
|
||||
strongSelf.scrollNode.view.setContentOffset(strongSelf.scrollNode.view.contentOffset, animated: false)
|
||||
switch filter {
|
||||
case let .filter(id, _, _):
|
||||
strongSelf.contextGesture?(id, sourceNode, gesture)
|
||||
strongSelf.contextGesture?(id, sourceNode, gesture, isDisabled)
|
||||
default:
|
||||
strongSelf.contextGesture?(nil, sourceNode, gesture)
|
||||
strongSelf.contextGesture?(nil, sourceNode, gesture, isDisabled)
|
||||
}
|
||||
})
|
||||
self.itemNodes[filter.id] = itemNode
|
||||
|
@ -374,7 +374,7 @@ final class ChatListFilterTabInlineContainerNode: ASDisplayNode {
|
||||
var tabSelected: ((ChatListFilterTabEntryId) -> Void)?
|
||||
var tabRequestedDeletion: ((ChatListFilterTabEntryId) -> Void)?
|
||||
var addFilter: (() -> Void)?
|
||||
var contextGesture: ((Int32?, ContextExtractedContentContainingNode, ContextGesture) -> Void)?
|
||||
var contextGesture: ((Int32?, ContextExtractedContentContainingNode, ContextGesture, Bool) -> Void)?
|
||||
|
||||
private var reorderingGesture: ReorderingGestureRecognizer?
|
||||
private var reorderingItem: ChatListFilterTabEntryId?
|
||||
@ -653,9 +653,9 @@ final class ChatListFilterTabInlineContainerNode: ASDisplayNode {
|
||||
strongSelf.scrollNode.view.setContentOffset(strongSelf.scrollNode.view.contentOffset, animated: false)
|
||||
switch filter {
|
||||
case let .filter(id, _, _):
|
||||
strongSelf.contextGesture?(id, sourceNode, gesture)
|
||||
strongSelf.contextGesture?(id, sourceNode, gesture, false)
|
||||
default:
|
||||
strongSelf.contextGesture?(nil, sourceNode, gesture)
|
||||
strongSelf.contextGesture?(nil, sourceNode, gesture, false)
|
||||
}
|
||||
}), highlighted: ItemNode(pressed: { [weak self] in
|
||||
self?.tabSelected?(filter.id)
|
||||
@ -670,9 +670,9 @@ final class ChatListFilterTabInlineContainerNode: ASDisplayNode {
|
||||
strongSelf.scrollNode.view.panGestureRecognizer.isEnabled = false
|
||||
strongSelf.scrollNode.view.panGestureRecognizer.isEnabled = true
|
||||
strongSelf.scrollNode.view.setContentOffset(strongSelf.scrollNode.view.contentOffset, animated: false)
|
||||
strongSelf.contextGesture?(id, sourceNode, gesture)
|
||||
strongSelf.contextGesture?(id, sourceNode, gesture, false)
|
||||
default:
|
||||
strongSelf.contextGesture?(nil, sourceNode, gesture)
|
||||
strongSelf.contextGesture?(nil, sourceNode, gesture, false)
|
||||
}
|
||||
}))
|
||||
self.itemNodePairs[filter.id] = itemNodePair
|
||||
|
@ -290,6 +290,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
let highlightBackgroundNode: ASDisplayNode
|
||||
let avatarNode: AvatarNode
|
||||
let titleLabelNode: ImmediateTextNode
|
||||
var credibilityIconNode: ASImageNode?
|
||||
let separatorNode: ASDisplayNode
|
||||
var reactionIconNode: ReactionImageNode?
|
||||
let action: () -> Void
|
||||
@ -368,6 +369,23 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
self.accessibilityLabel = "\(item.peer.debugDisplayTitle) \(item.reaction ?? "")"
|
||||
}
|
||||
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
||||
var currentCredibilityIconImage: UIImage?
|
||||
if item.peer.isScam {
|
||||
currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(presentationData.theme, strings: presentationData.strings, type: .regular)
|
||||
} else if item.peer.isFake {
|
||||
currentCredibilityIconImage = PresentationResourcesChatList.fakeIcon(presentationData.theme, strings: presentationData.strings, type: .regular)
|
||||
} else if item.peer.isVerified {
|
||||
currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(presentationData.theme)
|
||||
} else if item.peer.isPremium && !premiumConfiguration.isPremiumDisabled {
|
||||
currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(presentationData.theme)
|
||||
}
|
||||
|
||||
var additionalTitleInset: CGFloat = 0.0
|
||||
if let currentCredibilityIconImage = currentCredibilityIconImage {
|
||||
additionalTitleInset += 3.0 + currentCredibilityIconImage.size.width
|
||||
}
|
||||
|
||||
self.highlightBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
|
||||
self.separatorNode.backgroundColor = presentationData.theme.contextMenu.itemSeparatorColor
|
||||
|
||||
@ -377,12 +395,32 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
self.avatarNode.setPeer(context: self.context, theme: presentationData.theme, peer: item.peer, synchronousLoad: true)
|
||||
|
||||
self.titleLabelNode.attributedText = NSAttributedString(string: item.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), font: Font.regular(17.0), textColor: presentationData.theme.contextMenu.primaryColor)
|
||||
var maxTextWidth: CGFloat = size.width - avatarInset - avatarSize - avatarSpacing - sideInset
|
||||
var maxTextWidth: CGFloat = size.width - avatarInset - avatarSize - avatarSpacing - sideInset - additionalTitleInset
|
||||
if reactionIconNode != nil {
|
||||
maxTextWidth -= 32.0
|
||||
}
|
||||
let titleSize = self.titleLabelNode.updateLayout(CGSize(width: maxTextWidth, height: 100.0))
|
||||
self.titleLabelNode.frame = CGRect(origin: CGPoint(x: avatarInset + avatarSize + avatarSpacing, y: floor((size.height - titleSize.height) / 2.0)), size: titleSize)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: avatarInset + avatarSize + avatarSpacing, y: floor((size.height - titleSize.height) / 2.0)), size: titleSize)
|
||||
self.titleLabelNode.frame = titleFrame
|
||||
|
||||
if let currentCredibilityIconImage = currentCredibilityIconImage {
|
||||
let iconNode: ASImageNode
|
||||
if let current = self.credibilityIconNode {
|
||||
iconNode = current
|
||||
} else {
|
||||
iconNode = ASImageNode()
|
||||
iconNode.isLayerBacked = true
|
||||
iconNode.displaysAsynchronously = false
|
||||
iconNode.displayWithoutProcessing = true
|
||||
self.addSubnode(iconNode)
|
||||
self.credibilityIconNode = iconNode
|
||||
}
|
||||
iconNode.image = currentCredibilityIconImage
|
||||
iconNode.frame = CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: floorToScreenPixels(titleFrame.midY - currentCredibilityIconImage.size.height / 2.0) + 1.0 - UIScreenPixel), size: currentCredibilityIconImage.size)
|
||||
} else if let credibilityIconNode = self.credibilityIconNode {
|
||||
self.credibilityIconNode = nil
|
||||
credibilityIconNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
if let reactionIconNode = self.reactionIconNode {
|
||||
let reactionSize = CGSize(width: 22.0, height: 22.0)
|
||||
|
@ -334,7 +334,6 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
||||
private let avatarNode: AvatarNode
|
||||
private let titleNode: TextNode
|
||||
private var credibilityIconNode: ASImageNode?
|
||||
private var verificationIconNode: ASImageNode?
|
||||
private let statusNode: TextNode
|
||||
private var badgeBackgroundNode: ASImageNode?
|
||||
private var badgeTextNode: TextNode?
|
||||
|
@ -32,6 +32,11 @@ public final class InAppPurchaseManager: NSObject {
|
||||
case notAllowed
|
||||
}
|
||||
|
||||
public enum RestoreState {
|
||||
case succeed
|
||||
case failed
|
||||
}
|
||||
|
||||
private final class PaymentTransactionContext {
|
||||
var state: SKPaymentTransactionState?
|
||||
let subscriber: (TransactionState) -> Void
|
||||
@ -59,6 +64,8 @@ public final class InAppPurchaseManager: NSObject {
|
||||
private let stateQueue = Queue()
|
||||
private var paymentContexts: [String: PaymentTransactionContext] = [:]
|
||||
|
||||
private var onRestoreCompletion: ((RestoreState) -> Void)?
|
||||
|
||||
private let disposableSet = DisposableDict<String>()
|
||||
|
||||
public init(engine: TelegramEngine, premiumProductId: String) {
|
||||
@ -79,6 +86,7 @@ public final class InAppPurchaseManager: NSObject {
|
||||
guard !self.premiumProductId.isEmpty else {
|
||||
return
|
||||
}
|
||||
Logger.shared.log("InAppPurchaseManager", "Requesting products")
|
||||
let productRequest = SKProductsRequest(productIdentifiers: Set([self.premiumProductId]))
|
||||
productRequest.delegate = self
|
||||
productRequest.start()
|
||||
@ -93,7 +101,17 @@ public final class InAppPurchaseManager: NSObject {
|
||||
return self.productsPromise.get()
|
||||
}
|
||||
|
||||
public func restorePurchases(completion: @escaping (RestoreState) -> Void) {
|
||||
Logger.shared.log("InAppPurchaseManager", "Restoring purchases")
|
||||
self.onRestoreCompletion = completion
|
||||
|
||||
let paymentQueue = SKPaymentQueue.default()
|
||||
paymentQueue.restoreCompletedTransactions()
|
||||
}
|
||||
|
||||
public func finishAllTransactions() {
|
||||
Logger.shared.log("InAppPurchaseManager", "Finishing all transactions")
|
||||
|
||||
let paymentQueue = SKPaymentQueue.default()
|
||||
let transactions = paymentQueue.transactions
|
||||
for transaction in transactions {
|
||||
@ -102,6 +120,8 @@ public final class InAppPurchaseManager: NSObject {
|
||||
}
|
||||
|
||||
public func buyProduct(_ product: Product, account: Account) -> Signal<PurchaseState, PurchaseError> {
|
||||
Logger.shared.log("InAppPurchaseManager", "Buying product: \(product.skProduct.productIdentifier), price \(product.price)")
|
||||
|
||||
let payment = SKPayment(product: product.skProduct)
|
||||
SKPaymentQueue.default().add(payment)
|
||||
|
||||
@ -162,7 +182,10 @@ extension InAppPurchaseManager: SKProductsRequestDelegate {
|
||||
self.productRequest = nil
|
||||
|
||||
Queue.mainQueue().async {
|
||||
self.productsPromise.set(.single(response.products.map { Product(skProduct: $0) }))
|
||||
let products = response.products.map { Product(skProduct: $0) }
|
||||
|
||||
Logger.shared.log("InAppPurchaseManager", "Received products \(products.map({ $0.skProduct.productIdentifier }).joined(separator: ", "))")
|
||||
self.productsPromise.set(.single(products))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -187,37 +210,46 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
||||
let transactionState: TransactionState?
|
||||
switch transaction.transactionState {
|
||||
case .purchased:
|
||||
let transactionIdentifier = transaction.original?.transactionIdentifier ?? transaction.transactionIdentifier
|
||||
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "none") purchased")
|
||||
let transactionIdentifier = transaction.transactionIdentifier
|
||||
transactionState = .purchased(transactionId: transactionIdentifier)
|
||||
if let transactionIdentifier = transactionIdentifier {
|
||||
self.disposableSet.set(
|
||||
self.engine.payments.assignAppStoreTransaction(transactionId: transactionIdentifier, receipt: getReceiptData() ?? Data(), restore: false).start(error: { _ in
|
||||
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") failed to assign AppStore transaction")
|
||||
queue.finishTransaction(transaction)
|
||||
}, completed: {
|
||||
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") successfully assigned AppStore transaction")
|
||||
queue.finishTransaction(transaction)
|
||||
}),
|
||||
forKey: transaction.transactionIdentifier ?? ""
|
||||
forKey: transactionIdentifier
|
||||
)
|
||||
}
|
||||
case .restored:
|
||||
let transactionIdentifier = transaction.original?.transactionIdentifier ?? transaction.transactionIdentifier
|
||||
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? ""), original transaction \(transaction.original?.transactionIdentifier ?? "") restroring")
|
||||
let transactionIdentifier = transaction.transactionIdentifier
|
||||
transactionState = .restored(transactionId: transactionIdentifier)
|
||||
if let transactionIdentifier = transactionIdentifier {
|
||||
self.disposableSet.set(
|
||||
self.engine.payments.assignAppStoreTransaction(transactionId: transactionIdentifier, receipt: getReceiptData() ?? Data(), restore: true).start(error: { _ in
|
||||
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") failed to assign AppStore transaction")
|
||||
queue.finishTransaction(transaction)
|
||||
}, completed: {
|
||||
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") successfully assigned AppStore transaction")
|
||||
queue.finishTransaction(transaction)
|
||||
}),
|
||||
forKey: transaction.transactionIdentifier ?? ""
|
||||
forKey: transactionIdentifier
|
||||
)
|
||||
}
|
||||
case .failed:
|
||||
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") failed \((transaction.error as? SKError)?.localizedDescription ?? "")")
|
||||
transactionState = .failed(error: transaction.error as? SKError)
|
||||
queue.finishTransaction(transaction)
|
||||
case .purchasing:
|
||||
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") purchasing")
|
||||
transactionState = .purchasing
|
||||
case .deferred:
|
||||
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") deferred")
|
||||
transactionState = .deferred
|
||||
default:
|
||||
transactionState = nil
|
||||
@ -230,4 +262,20 @@ extension InAppPurchaseManager: SKPaymentTransactionObserver {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
|
||||
if let onRestoreCompletion = self.onRestoreCompletion {
|
||||
Logger.shared.log("InAppPurchaseManager", "Transactions restoration finished")
|
||||
onRestoreCompletion(.succeed)
|
||||
self.onRestoreCompletion = nil
|
||||
}
|
||||
}
|
||||
|
||||
public func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
|
||||
if let onRestoreCompletion = self.onRestoreCompletion {
|
||||
Logger.shared.log("InAppPurchaseManager", "Transactions restoration failed with error \((error as? SKError)?.localizedDescription ?? "")")
|
||||
onRestoreCompletion(.failed)
|
||||
self.onRestoreCompletion = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -382,6 +382,8 @@ private final class SectionGroupComponent: Component {
|
||||
buttonView = current
|
||||
} else {
|
||||
buttonView = HighlightTrackingButton()
|
||||
buttonView.isMultipleTouchEnabled = false
|
||||
buttonView.isExclusiveTouch = true
|
||||
buttonView.addTarget(self, action: #selector(self.buttonPressed(_:)), for: .touchUpInside)
|
||||
self.buttonViews[item.content.id] = buttonView
|
||||
self.addSubview(buttonView)
|
||||
|
@ -751,6 +751,8 @@ public final class SolidRoundedButtonView: UIView {
|
||||
}
|
||||
|
||||
self.buttonNode = HighlightTrackingButton()
|
||||
self.buttonNode.isMultipleTouchEnabled = false
|
||||
self.buttonNode.isExclusiveTouch = true
|
||||
|
||||
self.titleNode = ImmediateTextView()
|
||||
self.titleNode.isUserInteractionEnabled = false
|
||||
|
@ -917,7 +917,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[415997816] = { return Api.help.InviteText.parse_inviteText($0) }
|
||||
dict[-1600596305] = { return Api.help.PassportConfig.parse_passportConfig($0) }
|
||||
dict[-1078332329] = { return Api.help.PassportConfig.parse_passportConfigNotModified($0) }
|
||||
dict[-533328101] = { return Api.help.PremiumPromo.parse_premiumPromo($0) }
|
||||
dict[-1974518743] = { return Api.help.PremiumPromo.parse_premiumPromo($0) }
|
||||
dict[-1942390465] = { return Api.help.PromoData.parse_promoData($0) }
|
||||
dict[-1728664459] = { return Api.help.PromoData.parse_promoDataEmpty($0) }
|
||||
dict[235081943] = { return Api.help.RecentMeUrls.parse_recentMeUrls($0) }
|
||||
|
@ -212,13 +212,13 @@ public extension Api.help {
|
||||
}
|
||||
public extension Api.help {
|
||||
enum PremiumPromo: TypeConstructorDescription {
|
||||
case premiumPromo(statusText: String, statusEntities: [Api.MessageEntity], videoSections: [String], videos: [Api.Document], currency: String, monthlyAmount: Int64)
|
||||
case premiumPromo(statusText: String, statusEntities: [Api.MessageEntity], videoSections: [String], videos: [Api.Document], currency: String, monthlyAmount: Int64, users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let currency, let monthlyAmount):
|
||||
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let currency, let monthlyAmount, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-533328101)
|
||||
buffer.appendInt32(-1974518743)
|
||||
}
|
||||
serializeString(statusText, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
@ -238,14 +238,19 @@ public extension Api.help {
|
||||
}
|
||||
serializeString(currency, buffer: buffer, boxed: false)
|
||||
serializeInt64(monthlyAmount, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(users.count))
|
||||
for item in users {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let currency, let monthlyAmount):
|
||||
return ("premiumPromo", [("statusText", String(describing: statusText)), ("statusEntities", String(describing: statusEntities)), ("videoSections", String(describing: videoSections)), ("videos", String(describing: videos)), ("currency", String(describing: currency)), ("monthlyAmount", String(describing: monthlyAmount))])
|
||||
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let currency, let monthlyAmount, let users):
|
||||
return ("premiumPromo", [("statusText", String(describing: statusText)), ("statusEntities", String(describing: statusEntities)), ("videoSections", String(describing: videoSections)), ("videos", String(describing: videos)), ("currency", String(describing: currency)), ("monthlyAmount", String(describing: monthlyAmount)), ("users", String(describing: users))])
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,14 +273,19 @@ public extension Api.help {
|
||||
_5 = parseString(reader)
|
||||
var _6: Int64?
|
||||
_6 = reader.readInt64()
|
||||
var _7: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.help.PremiumPromo.premiumPromo(statusText: _1!, statusEntities: _2!, videoSections: _3!, videos: _4!, currency: _5!, monthlyAmount: _6!)
|
||||
let _c7 = _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.help.PremiumPromo.premiumPromo(statusText: _1!, statusEntities: _2!, videoSections: _3!, videos: _4!, currency: _5!, monthlyAmount: _6!, users: _7!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -19,6 +19,13 @@ func updatePremiumPromoConfigurationOnce(postbox: Postbox, network: Network) ->
|
||||
return .complete()
|
||||
}
|
||||
return postbox.transaction { transaction -> Void in
|
||||
if case let .premiumPromo(_, _, _, _, _, _, apiUsers) = result {
|
||||
let users = apiUsers.map { TelegramUser(user: $0) }
|
||||
updatePeers(transaction: transaction, peers: users, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
}
|
||||
|
||||
updatePremiumPromoConfiguration(transaction: transaction, { configuration -> PremiumPromoConfiguration in
|
||||
return PremiumPromoConfiguration(apiPremiumPromo: result)
|
||||
})
|
||||
@ -54,7 +61,7 @@ private func updatePremiumPromoConfiguration(transaction: Transaction, _ f: (Pre
|
||||
private extension PremiumPromoConfiguration {
|
||||
init(apiPremiumPromo: Api.help.PremiumPromo) {
|
||||
switch apiPremiumPromo {
|
||||
case let .premiumPromo(statusText, statusEntities, videoSections, videoFiles, currency, monthlyAmount):
|
||||
case let .premiumPromo(statusText, statusEntities, videoSections, videoFiles, currency, monthlyAmount, _):
|
||||
self.status = statusText
|
||||
self.statusEntities = messageTextEntitiesFromApiEntities(statusEntities)
|
||||
self.currency = currency
|
||||
|
@ -156,7 +156,9 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
}
|
||||
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings {
|
||||
if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
|
||||
titleRightIcon = .mute
|
||||
if titleCredibilityIcon != .verified {
|
||||
titleRightIcon = .mute
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2609,7 +2609,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
let _ = nextPanelSubtitleNodeLayout[TitleNodeStateRegular]!.size
|
||||
let usernameSize = usernameNodeLayout[TitleNodeStateRegular]!.size
|
||||
|
||||
var titleHorizontalOffset: CGFloat = 0.0
|
||||
if let image = self.titleCredibilityIconNode.image {
|
||||
titleHorizontalOffset = -(image.size.width + 4.0) / 2.0
|
||||
transition.updateFrame(node: self.titleCredibilityIconNode, frame: CGRect(origin: CGPoint(x: titleSize.width + 4.0, y: floor((titleSize.height - image.size.height) / 2.0) + 1.0), size: image.size))
|
||||
transition.updateFrame(node: self.titleExpandedCredibilityIconNode, frame: CGRect(origin: CGPoint(x: titleExpandedSize.width + 4.0, y: floor((titleExpandedSize.height - image.size.height) / 2.0) + 1.0), size: image.size))
|
||||
}
|
||||
@ -2629,14 +2631,14 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
subtitleFrame = CGRect(origin: CGPoint(x: 16.0, y: minTitleFrame.maxY + 2.0), size: subtitleSize)
|
||||
usernameFrame = CGRect(origin: CGPoint(x: width - usernameSize.width - 16.0, y: minTitleFrame.midY - usernameSize.height / 2.0), size: usernameSize)
|
||||
} else {
|
||||
titleFrame = CGRect(origin: CGPoint(x: floor((width - titleSize.width) / 2.0), y: avatarFrame.maxY + 7.0 + (subtitleSize.height.isZero ? 11.0 : 0.0) + 11.0), size: titleSize)
|
||||
titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - titleSize.width) / 2.0), y: avatarFrame.maxY + 7.0 + (subtitleSize.height.isZero ? 11.0 : 0.0) + 11.0), size: titleSize)
|
||||
|
||||
let totalSubtitleWidth = subtitleSize.width + usernameSpacing + usernameSize.width
|
||||
if usernameSize.width == 0.0 {
|
||||
subtitleFrame = CGRect(origin: CGPoint(x: floor((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
||||
usernameFrame = CGRect(origin: CGPoint(x: floor((width - usernameSize.width) / 2.0), y: subtitleFrame.maxY + 1.0), size: usernameSize)
|
||||
subtitleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
||||
usernameFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - usernameSize.width) / 2.0), y: subtitleFrame.maxY + 1.0), size: usernameSize)
|
||||
} else {
|
||||
subtitleFrame = CGRect(origin: CGPoint(x: floor((width - totalSubtitleWidth) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
||||
subtitleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - totalSubtitleWidth) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
||||
usernameFrame = CGRect(origin: CGPoint(x: subtitleFrame.maxX + usernameSpacing, y: titleFrame.maxY + 1.0), size: usernameSize)
|
||||
}
|
||||
}
|
||||
@ -2851,10 +2853,15 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
neutralTitleScale = 0.7
|
||||
neutralSubtitleScale = 1.0
|
||||
}
|
||||
|
||||
|
||||
let titleScale = (transitionFraction * transitionSourceTitleFrame.height + (1.0 - transitionFraction) * titleFrame.height * neutralTitleScale) / (titleFrame.height)
|
||||
let subtitleScale = max(0.01, min(10.0, (transitionFraction * transitionSourceSubtitleFrame.height + (1.0 - transitionFraction) * subtitleFrame.height * neutralSubtitleScale) / (subtitleFrame.height)))
|
||||
|
||||
var titleFrame = titleFrame
|
||||
if !self.isAvatarExpanded {
|
||||
titleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? 0.0 : titleHorizontalOffset * titleScale, dy: 0.0)
|
||||
}
|
||||
|
||||
let titleCenter = CGPoint(x: transitionFraction * transitionSourceTitleFrame.midX + (1.0 - transitionFraction) * titleFrame.midX, y: transitionFraction * transitionSourceTitleFrame.midY + (1.0 - transitionFraction) * titleFrame.midY)
|
||||
let subtitleCenter = CGPoint(x: transitionFraction * transitionSourceSubtitleFrame.midX + (1.0 - transitionFraction) * subtitleFrame.midX, y: transitionFraction * transitionSourceSubtitleFrame.midY + (1.0 - transitionFraction) * subtitleFrame.midY)
|
||||
|
||||
@ -2885,7 +2892,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
subtitleOffset = titleCollapseFraction * -2.0
|
||||
}
|
||||
|
||||
let rawTitleFrame = titleFrame
|
||||
let rawTitleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? 0.0 : titleHorizontalOffset * titleScale, dy: 0.0)
|
||||
self.titleNodeRawContainer.frame = rawTitleFrame
|
||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
|
||||
let rawSubtitleFrame = subtitleFrame
|
||||
@ -3090,7 +3097,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
if !self.backgroundNode.frame.contains(point) {
|
||||
return nil
|
||||
}
|
||||
if self.currentCredibilityIcon == .premium {
|
||||
|
||||
if self.currentCredibilityIcon == .premium && !(self.state?.isEditing ?? false) {
|
||||
let iconFrame = self.titleCredibilityIconNode.view.convert(self.titleCredibilityIconNode.bounds, to: self.view)
|
||||
let expandedIconFrame = self.titleExpandedCredibilityIconNode.view.convert(self.titleExpandedCredibilityIconNode.bounds, to: self.view)
|
||||
if expandedIconFrame.contains(point) && self.isAvatarExpanded {
|
||||
|
Loading…
x
Reference in New Issue
Block a user