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 avatarNode: AvatarNode
|
||||||
private let titleNode: TextNode
|
private let titleNode: TextNode
|
||||||
|
private var credibilityIconNode: ASImageNode?
|
||||||
private let statusNode: TextNode
|
private let statusNode: TextNode
|
||||||
private let dateNode: TextNode
|
private let dateNode: TextNode
|
||||||
private let typeIconNode: ASImageNode
|
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 (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()))
|
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)))
|
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()
|
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()
|
let _ = statusApply()
|
||||||
transition.updateFrameAdditive(node: strongSelf.statusNode, frame: CGRect(origin: CGPoint(x: revealOffset + leftInset, y: strongSelf.titleNode.frame.maxY + titleSpacing), size: statusLayout.size))
|
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()
|
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))
|
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
|
let isFirstFilter = strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter == strongSelf.chatListDisplayNode.containerNode.availableFilters.first?.filter
|
||||||
|
|
||||||
if offset <= navigationBarSearchContentHeight + 1.0 && !isFirstFilter {
|
if offset <= navigationBarSearchContentHeight + 1.0 && !isFirstFilter {
|
||||||
let _ = (strongSelf.context.engine.peers.currentChatListFilters()
|
let firstFilter = strongSelf.chatListDisplayNode.containerNode.availableFilters.first ?? .all
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] filters in
|
|
||||||
guard let strongSelf = self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let targetTab: ChatListFilterTabEntryId
|
let targetTab: ChatListFilterTabEntryId
|
||||||
let firstFilter = filters.first ?? .allChats
|
|
||||||
switch firstFilter {
|
switch firstFilter {
|
||||||
case .allChats:
|
case .all:
|
||||||
targetTab = .all
|
targetTab = .all
|
||||||
case let .filter(id, _, _, _):
|
case let .filter(filter):
|
||||||
targetTab = .filter(id)
|
targetTab = .filter(filter.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.selectTab(id: targetTab)
|
strongSelf.selectTab(id: targetTab)
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
if let searchContentNode = strongSelf.searchContentNode {
|
if let searchContentNode = strongSelf.searchContentNode {
|
||||||
searchContentNode.updateExpansionProgress(1.0, animated: true)
|
searchContentNode.updateExpansionProgress(1.0, animated: true)
|
||||||
@ -1262,11 +1255,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
strongSelf.setToolbar(toolbar, transition: transition)
|
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 {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if disabled {
|
if isDisabled {
|
||||||
let context = strongSelf.context
|
let context = strongSelf.context
|
||||||
var replaceImpl: ((ViewController) -> Void)?
|
var replaceImpl: ((ViewController) -> Void)?
|
||||||
let controller = PremiumLimitScreen(context: context, subject: .folders, count: strongSelf.tabContainerNode.filtersCount, action: {
|
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 {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1319,6 +1312,18 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
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)
|
||||||
|
}
|
||||||
|
strongSelf.push(controller)
|
||||||
|
} else {
|
||||||
let _ = (strongSelf.context.engine.peers.currentChatListFilters()
|
let _ = (strongSelf.context.engine.peers.currentChatListFilters()
|
||||||
|> deliverOnMainQueue).start(next: { presetList in
|
|> deliverOnMainQueue).start(next: { presetList in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
@ -1337,6 +1342,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
f(.default)
|
f(.default)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})))
|
})))
|
||||||
|
|
||||||
@ -1349,6 +1355,18 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
return
|
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)
|
||||||
|
}
|
||||||
|
strongSelf.push(controller)
|
||||||
|
} else {
|
||||||
let _ = combineLatest(
|
let _ = combineLatest(
|
||||||
queue: Queue.mainQueue(),
|
queue: Queue.mainQueue(),
|
||||||
context.engine.data.get(
|
context.engine.data.get(
|
||||||
@ -1405,6 +1423,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
f(.default)
|
f(.default)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})))
|
})))
|
||||||
|
|
||||||
@ -1472,11 +1491,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
strongSelf.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
|
strongSelf.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
self.tabContainerNode.contextGesture = { id, sourceNode, gesture in
|
self.tabContainerNode.contextGesture = { id, sourceNode, gesture, isDisabled in
|
||||||
tabContextGesture(id, sourceNode, gesture, false)
|
tabContextGesture(id, sourceNode, gesture, false, isDisabled)
|
||||||
}
|
}
|
||||||
self.chatListDisplayNode.inlineTabContainerNode.contextGesture = { id, sourceNode, gesture in
|
self.chatListDisplayNode.inlineTabContainerNode.contextGesture = { id, sourceNode, gesture, isDisabled in
|
||||||
tabContextGesture(id, sourceNode, gesture, true)
|
tabContextGesture(id, sourceNode, gesture, true, isDisabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
if case .group = self.groupId {
|
if case .group = self.groupId {
|
||||||
|
@ -88,7 +88,7 @@ private final class ItemNode: ASDisplayNode {
|
|||||||
|
|
||||||
private var theme: PresentationTheme?
|
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.pressed = pressed
|
||||||
self.requestedDeletion = requestedDeletion
|
self.requestedDeletion = requestedDeletion
|
||||||
|
|
||||||
@ -170,7 +170,7 @@ private final class ItemNode: ASDisplayNode {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
contextGesture(strongSelf.extractedContainerNode, gesture)
|
contextGesture(strongSelf.extractedContainerNode, gesture, strongSelf.isDisabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.extractedContainerNode.willUpdateIsExtractedToContextPreview = { [weak self] isExtracted, transition in
|
self.extractedContainerNode.willUpdateIsExtractedToContextPreview = { [weak self] isExtracted, transition in
|
||||||
@ -467,7 +467,7 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
|
|||||||
var tabSelected: ((ChatListFilterTabEntryId, Bool) -> Void)?
|
var tabSelected: ((ChatListFilterTabEntryId, Bool) -> Void)?
|
||||||
var tabRequestedDeletion: ((ChatListFilterTabEntryId) -> Void)?
|
var tabRequestedDeletion: ((ChatListFilterTabEntryId) -> Void)?
|
||||||
var addFilter: (() -> Void)?
|
var addFilter: (() -> Void)?
|
||||||
var contextGesture: ((Int32?, ContextExtractedContentContainingNode, ContextGesture) -> Void)?
|
var contextGesture: ((Int32?, ContextExtractedContentContainingNode, ContextGesture, Bool) -> Void)?
|
||||||
|
|
||||||
private var reorderingGesture: ReorderingGestureRecognizer?
|
private var reorderingGesture: ReorderingGestureRecognizer?
|
||||||
private var reorderingItem: ChatListFilterTabEntryId?
|
private var reorderingItem: ChatListFilterTabEntryId?
|
||||||
@ -739,7 +739,7 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
|
|||||||
self?.tabSelected?(filter.id, disabled)
|
self?.tabSelected?(filter.id, disabled)
|
||||||
}, requestedDeletion: { [weak self] in
|
}, requestedDeletion: { [weak self] in
|
||||||
self?.tabRequestedDeletion?(filter.id)
|
self?.tabRequestedDeletion?(filter.id)
|
||||||
}, contextGesture: { [weak self] sourceNode, gesture in
|
}, contextGesture: { [weak self] sourceNode, gesture, isDisabled in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -748,9 +748,9 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
|
|||||||
strongSelf.scrollNode.view.setContentOffset(strongSelf.scrollNode.view.contentOffset, animated: false)
|
strongSelf.scrollNode.view.setContentOffset(strongSelf.scrollNode.view.contentOffset, animated: false)
|
||||||
switch filter {
|
switch filter {
|
||||||
case let .filter(id, _, _):
|
case let .filter(id, _, _):
|
||||||
strongSelf.contextGesture?(id, sourceNode, gesture)
|
strongSelf.contextGesture?(id, sourceNode, gesture, isDisabled)
|
||||||
default:
|
default:
|
||||||
strongSelf.contextGesture?(nil, sourceNode, gesture)
|
strongSelf.contextGesture?(nil, sourceNode, gesture, isDisabled)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
self.itemNodes[filter.id] = itemNode
|
self.itemNodes[filter.id] = itemNode
|
||||||
|
@ -374,7 +374,7 @@ final class ChatListFilterTabInlineContainerNode: ASDisplayNode {
|
|||||||
var tabSelected: ((ChatListFilterTabEntryId) -> Void)?
|
var tabSelected: ((ChatListFilterTabEntryId) -> Void)?
|
||||||
var tabRequestedDeletion: ((ChatListFilterTabEntryId) -> Void)?
|
var tabRequestedDeletion: ((ChatListFilterTabEntryId) -> Void)?
|
||||||
var addFilter: (() -> Void)?
|
var addFilter: (() -> Void)?
|
||||||
var contextGesture: ((Int32?, ContextExtractedContentContainingNode, ContextGesture) -> Void)?
|
var contextGesture: ((Int32?, ContextExtractedContentContainingNode, ContextGesture, Bool) -> Void)?
|
||||||
|
|
||||||
private var reorderingGesture: ReorderingGestureRecognizer?
|
private var reorderingGesture: ReorderingGestureRecognizer?
|
||||||
private var reorderingItem: ChatListFilterTabEntryId?
|
private var reorderingItem: ChatListFilterTabEntryId?
|
||||||
@ -653,9 +653,9 @@ final class ChatListFilterTabInlineContainerNode: ASDisplayNode {
|
|||||||
strongSelf.scrollNode.view.setContentOffset(strongSelf.scrollNode.view.contentOffset, animated: false)
|
strongSelf.scrollNode.view.setContentOffset(strongSelf.scrollNode.view.contentOffset, animated: false)
|
||||||
switch filter {
|
switch filter {
|
||||||
case let .filter(id, _, _):
|
case let .filter(id, _, _):
|
||||||
strongSelf.contextGesture?(id, sourceNode, gesture)
|
strongSelf.contextGesture?(id, sourceNode, gesture, false)
|
||||||
default:
|
default:
|
||||||
strongSelf.contextGesture?(nil, sourceNode, gesture)
|
strongSelf.contextGesture?(nil, sourceNode, gesture, false)
|
||||||
}
|
}
|
||||||
}), highlighted: ItemNode(pressed: { [weak self] in
|
}), highlighted: ItemNode(pressed: { [weak self] in
|
||||||
self?.tabSelected?(filter.id)
|
self?.tabSelected?(filter.id)
|
||||||
@ -670,9 +670,9 @@ final class ChatListFilterTabInlineContainerNode: ASDisplayNode {
|
|||||||
strongSelf.scrollNode.view.panGestureRecognizer.isEnabled = false
|
strongSelf.scrollNode.view.panGestureRecognizer.isEnabled = false
|
||||||
strongSelf.scrollNode.view.panGestureRecognizer.isEnabled = true
|
strongSelf.scrollNode.view.panGestureRecognizer.isEnabled = true
|
||||||
strongSelf.scrollNode.view.setContentOffset(strongSelf.scrollNode.view.contentOffset, animated: false)
|
strongSelf.scrollNode.view.setContentOffset(strongSelf.scrollNode.view.contentOffset, animated: false)
|
||||||
strongSelf.contextGesture?(id, sourceNode, gesture)
|
strongSelf.contextGesture?(id, sourceNode, gesture, false)
|
||||||
default:
|
default:
|
||||||
strongSelf.contextGesture?(nil, sourceNode, gesture)
|
strongSelf.contextGesture?(nil, sourceNode, gesture, false)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
self.itemNodePairs[filter.id] = itemNodePair
|
self.itemNodePairs[filter.id] = itemNodePair
|
||||||
|
@ -290,6 +290,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
let highlightBackgroundNode: ASDisplayNode
|
let highlightBackgroundNode: ASDisplayNode
|
||||||
let avatarNode: AvatarNode
|
let avatarNode: AvatarNode
|
||||||
let titleLabelNode: ImmediateTextNode
|
let titleLabelNode: ImmediateTextNode
|
||||||
|
var credibilityIconNode: ASImageNode?
|
||||||
let separatorNode: ASDisplayNode
|
let separatorNode: ASDisplayNode
|
||||||
var reactionIconNode: ReactionImageNode?
|
var reactionIconNode: ReactionImageNode?
|
||||||
let action: () -> Void
|
let action: () -> Void
|
||||||
@ -368,6 +369,23 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
self.accessibilityLabel = "\(item.peer.debugDisplayTitle) \(item.reaction ?? "")"
|
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.highlightBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor
|
||||||
self.separatorNode.backgroundColor = presentationData.theme.contextMenu.itemSeparatorColor
|
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.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)
|
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 {
|
if reactionIconNode != nil {
|
||||||
maxTextWidth -= 32.0
|
maxTextWidth -= 32.0
|
||||||
}
|
}
|
||||||
let titleSize = self.titleLabelNode.updateLayout(CGSize(width: maxTextWidth, height: 100.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 {
|
if let reactionIconNode = self.reactionIconNode {
|
||||||
let reactionSize = CGSize(width: 22.0, height: 22.0)
|
let reactionSize = CGSize(width: 22.0, height: 22.0)
|
||||||
|
@ -334,7 +334,6 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
|||||||
private let avatarNode: AvatarNode
|
private let avatarNode: AvatarNode
|
||||||
private let titleNode: TextNode
|
private let titleNode: TextNode
|
||||||
private var credibilityIconNode: ASImageNode?
|
private var credibilityIconNode: ASImageNode?
|
||||||
private var verificationIconNode: ASImageNode?
|
|
||||||
private let statusNode: TextNode
|
private let statusNode: TextNode
|
||||||
private var badgeBackgroundNode: ASImageNode?
|
private var badgeBackgroundNode: ASImageNode?
|
||||||
private var badgeTextNode: TextNode?
|
private var badgeTextNode: TextNode?
|
||||||
|
@ -32,6 +32,11 @@ public final class InAppPurchaseManager: NSObject {
|
|||||||
case notAllowed
|
case notAllowed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum RestoreState {
|
||||||
|
case succeed
|
||||||
|
case failed
|
||||||
|
}
|
||||||
|
|
||||||
private final class PaymentTransactionContext {
|
private final class PaymentTransactionContext {
|
||||||
var state: SKPaymentTransactionState?
|
var state: SKPaymentTransactionState?
|
||||||
let subscriber: (TransactionState) -> Void
|
let subscriber: (TransactionState) -> Void
|
||||||
@ -59,6 +64,8 @@ public final class InAppPurchaseManager: NSObject {
|
|||||||
private let stateQueue = Queue()
|
private let stateQueue = Queue()
|
||||||
private var paymentContexts: [String: PaymentTransactionContext] = [:]
|
private var paymentContexts: [String: PaymentTransactionContext] = [:]
|
||||||
|
|
||||||
|
private var onRestoreCompletion: ((RestoreState) -> Void)?
|
||||||
|
|
||||||
private let disposableSet = DisposableDict<String>()
|
private let disposableSet = DisposableDict<String>()
|
||||||
|
|
||||||
public init(engine: TelegramEngine, premiumProductId: String) {
|
public init(engine: TelegramEngine, premiumProductId: String) {
|
||||||
@ -79,6 +86,7 @@ public final class InAppPurchaseManager: NSObject {
|
|||||||
guard !self.premiumProductId.isEmpty else {
|
guard !self.premiumProductId.isEmpty else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Requesting products")
|
||||||
let productRequest = SKProductsRequest(productIdentifiers: Set([self.premiumProductId]))
|
let productRequest = SKProductsRequest(productIdentifiers: Set([self.premiumProductId]))
|
||||||
productRequest.delegate = self
|
productRequest.delegate = self
|
||||||
productRequest.start()
|
productRequest.start()
|
||||||
@ -93,7 +101,17 @@ public final class InAppPurchaseManager: NSObject {
|
|||||||
return self.productsPromise.get()
|
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() {
|
public func finishAllTransactions() {
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Finishing all transactions")
|
||||||
|
|
||||||
let paymentQueue = SKPaymentQueue.default()
|
let paymentQueue = SKPaymentQueue.default()
|
||||||
let transactions = paymentQueue.transactions
|
let transactions = paymentQueue.transactions
|
||||||
for transaction in transactions {
|
for transaction in transactions {
|
||||||
@ -102,6 +120,8 @@ public final class InAppPurchaseManager: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func buyProduct(_ product: Product, account: Account) -> Signal<PurchaseState, PurchaseError> {
|
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)
|
let payment = SKPayment(product: product.skProduct)
|
||||||
SKPaymentQueue.default().add(payment)
|
SKPaymentQueue.default().add(payment)
|
||||||
|
|
||||||
@ -162,7 +182,10 @@ extension InAppPurchaseManager: SKProductsRequestDelegate {
|
|||||||
self.productRequest = nil
|
self.productRequest = nil
|
||||||
|
|
||||||
Queue.mainQueue().async {
|
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?
|
let transactionState: TransactionState?
|
||||||
switch transaction.transactionState {
|
switch transaction.transactionState {
|
||||||
case .purchased:
|
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)
|
transactionState = .purchased(transactionId: transactionIdentifier)
|
||||||
if let transactionIdentifier = transactionIdentifier {
|
if let transactionIdentifier = transactionIdentifier {
|
||||||
self.disposableSet.set(
|
self.disposableSet.set(
|
||||||
self.engine.payments.assignAppStoreTransaction(transactionId: transactionIdentifier, receipt: getReceiptData() ?? Data(), restore: false).start(error: { _ in
|
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)
|
queue.finishTransaction(transaction)
|
||||||
}, completed: {
|
}, completed: {
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") successfully assigned AppStore transaction")
|
||||||
queue.finishTransaction(transaction)
|
queue.finishTransaction(transaction)
|
||||||
}),
|
}),
|
||||||
forKey: transaction.transactionIdentifier ?? ""
|
forKey: transactionIdentifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case .restored:
|
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)
|
transactionState = .restored(transactionId: transactionIdentifier)
|
||||||
if let transactionIdentifier = transactionIdentifier {
|
if let transactionIdentifier = transactionIdentifier {
|
||||||
self.disposableSet.set(
|
self.disposableSet.set(
|
||||||
self.engine.payments.assignAppStoreTransaction(transactionId: transactionIdentifier, receipt: getReceiptData() ?? Data(), restore: true).start(error: { _ in
|
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)
|
queue.finishTransaction(transaction)
|
||||||
}, completed: {
|
}, completed: {
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") successfully assigned AppStore transaction")
|
||||||
queue.finishTransaction(transaction)
|
queue.finishTransaction(transaction)
|
||||||
}),
|
}),
|
||||||
forKey: transaction.transactionIdentifier ?? ""
|
forKey: transactionIdentifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case .failed:
|
case .failed:
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") failed \((transaction.error as? SKError)?.localizedDescription ?? "")")
|
||||||
transactionState = .failed(error: transaction.error as? SKError)
|
transactionState = .failed(error: transaction.error as? SKError)
|
||||||
queue.finishTransaction(transaction)
|
queue.finishTransaction(transaction)
|
||||||
case .purchasing:
|
case .purchasing:
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") purchasing")
|
||||||
transactionState = .purchasing
|
transactionState = .purchasing
|
||||||
case .deferred:
|
case .deferred:
|
||||||
|
Logger.shared.log("InAppPurchaseManager", "Transaction \(transaction.transactionIdentifier ?? "") deferred")
|
||||||
transactionState = .deferred
|
transactionState = .deferred
|
||||||
default:
|
default:
|
||||||
transactionState = nil
|
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
|
buttonView = current
|
||||||
} else {
|
} else {
|
||||||
buttonView = HighlightTrackingButton()
|
buttonView = HighlightTrackingButton()
|
||||||
|
buttonView.isMultipleTouchEnabled = false
|
||||||
|
buttonView.isExclusiveTouch = true
|
||||||
buttonView.addTarget(self, action: #selector(self.buttonPressed(_:)), for: .touchUpInside)
|
buttonView.addTarget(self, action: #selector(self.buttonPressed(_:)), for: .touchUpInside)
|
||||||
self.buttonViews[item.content.id] = buttonView
|
self.buttonViews[item.content.id] = buttonView
|
||||||
self.addSubview(buttonView)
|
self.addSubview(buttonView)
|
||||||
|
@ -751,6 +751,8 @@ public final class SolidRoundedButtonView: UIView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.buttonNode = HighlightTrackingButton()
|
self.buttonNode = HighlightTrackingButton()
|
||||||
|
self.buttonNode.isMultipleTouchEnabled = false
|
||||||
|
self.buttonNode.isExclusiveTouch = true
|
||||||
|
|
||||||
self.titleNode = ImmediateTextView()
|
self.titleNode = ImmediateTextView()
|
||||||
self.titleNode.isUserInteractionEnabled = false
|
self.titleNode.isUserInteractionEnabled = false
|
||||||
|
@ -917,7 +917,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[415997816] = { return Api.help.InviteText.parse_inviteText($0) }
|
dict[415997816] = { return Api.help.InviteText.parse_inviteText($0) }
|
||||||
dict[-1600596305] = { return Api.help.PassportConfig.parse_passportConfig($0) }
|
dict[-1600596305] = { return Api.help.PassportConfig.parse_passportConfig($0) }
|
||||||
dict[-1078332329] = { return Api.help.PassportConfig.parse_passportConfigNotModified($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[-1942390465] = { return Api.help.PromoData.parse_promoData($0) }
|
||||||
dict[-1728664459] = { return Api.help.PromoData.parse_promoDataEmpty($0) }
|
dict[-1728664459] = { return Api.help.PromoData.parse_promoDataEmpty($0) }
|
||||||
dict[235081943] = { return Api.help.RecentMeUrls.parse_recentMeUrls($0) }
|
dict[235081943] = { return Api.help.RecentMeUrls.parse_recentMeUrls($0) }
|
||||||
|
@ -212,13 +212,13 @@ public extension Api.help {
|
|||||||
}
|
}
|
||||||
public extension Api.help {
|
public extension Api.help {
|
||||||
enum PremiumPromo: TypeConstructorDescription {
|
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) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
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 {
|
if boxed {
|
||||||
buffer.appendInt32(-533328101)
|
buffer.appendInt32(-1974518743)
|
||||||
}
|
}
|
||||||
serializeString(statusText, buffer: buffer, boxed: false)
|
serializeString(statusText, buffer: buffer, boxed: false)
|
||||||
buffer.appendInt32(481674261)
|
buffer.appendInt32(481674261)
|
||||||
@ -238,14 +238,19 @@ public extension Api.help {
|
|||||||
}
|
}
|
||||||
serializeString(currency, buffer: buffer, boxed: false)
|
serializeString(currency, buffer: buffer, boxed: false)
|
||||||
serializeInt64(monthlyAmount, 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
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||||
switch self {
|
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):
|
||||||
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))])
|
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)
|
_5 = parseString(reader)
|
||||||
var _6: Int64?
|
var _6: Int64?
|
||||||
_6 = reader.readInt64()
|
_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 _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = _3 != nil
|
let _c3 = _3 != nil
|
||||||
let _c4 = _4 != nil
|
let _c4 = _4 != nil
|
||||||
let _c5 = _5 != nil
|
let _c5 = _5 != nil
|
||||||
let _c6 = _6 != nil
|
let _c6 = _6 != nil
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
let _c7 = _7 != nil
|
||||||
return Api.help.PremiumPromo.premiumPromo(statusText: _1!, statusEntities: _2!, videoSections: _3!, videos: _4!, currency: _5!, monthlyAmount: _6!)
|
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 {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -19,6 +19,13 @@ func updatePremiumPromoConfigurationOnce(postbox: Postbox, network: Network) ->
|
|||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
return postbox.transaction { transaction -> Void in
|
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
|
updatePremiumPromoConfiguration(transaction: transaction, { configuration -> PremiumPromoConfiguration in
|
||||||
return PremiumPromoConfiguration(apiPremiumPromo: result)
|
return PremiumPromoConfiguration(apiPremiumPromo: result)
|
||||||
})
|
})
|
||||||
@ -54,7 +61,7 @@ private func updatePremiumPromoConfiguration(transaction: Transaction, _ f: (Pre
|
|||||||
private extension PremiumPromoConfiguration {
|
private extension PremiumPromoConfiguration {
|
||||||
init(apiPremiumPromo: Api.help.PremiumPromo) {
|
init(apiPremiumPromo: Api.help.PremiumPromo) {
|
||||||
switch apiPremiumPromo {
|
switch apiPremiumPromo {
|
||||||
case let .premiumPromo(statusText, statusEntities, videoSections, videoFiles, currency, monthlyAmount):
|
case let .premiumPromo(statusText, statusEntities, videoSections, videoFiles, currency, monthlyAmount, _):
|
||||||
self.status = statusText
|
self.status = statusText
|
||||||
self.statusEntities = messageTextEntitiesFromApiEntities(statusEntities)
|
self.statusEntities = messageTextEntitiesFromApiEntities(statusEntities)
|
||||||
self.currency = currency
|
self.currency = currency
|
||||||
|
@ -156,10 +156,12 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
|||||||
}
|
}
|
||||||
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings {
|
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings {
|
||||||
if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
|
if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
|
||||||
|
if titleCredibilityIcon != .verified {
|
||||||
titleRightIcon = .mute
|
titleRightIcon = .mute
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
case let .replyThread(type, count):
|
case let .replyThread(type, count):
|
||||||
let textFont = titleFont
|
let textFont = titleFont
|
||||||
let textColor = titleTheme.rootController.navigationBar.primaryTextColor
|
let textColor = titleTheme.rootController.navigationBar.primaryTextColor
|
||||||
|
@ -2609,7 +2609,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
let _ = nextPanelSubtitleNodeLayout[TitleNodeStateRegular]!.size
|
let _ = nextPanelSubtitleNodeLayout[TitleNodeStateRegular]!.size
|
||||||
let usernameSize = usernameNodeLayout[TitleNodeStateRegular]!.size
|
let usernameSize = usernameNodeLayout[TitleNodeStateRegular]!.size
|
||||||
|
|
||||||
|
var titleHorizontalOffset: CGFloat = 0.0
|
||||||
if let image = self.titleCredibilityIconNode.image {
|
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.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))
|
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)
|
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)
|
usernameFrame = CGRect(origin: CGPoint(x: width - usernameSize.width - 16.0, y: minTitleFrame.midY - usernameSize.height / 2.0), size: usernameSize)
|
||||||
} else {
|
} 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
|
let totalSubtitleWidth = subtitleSize.width + usernameSpacing + usernameSize.width
|
||||||
if usernameSize.width == 0.0 {
|
if usernameSize.width == 0.0 {
|
||||||
subtitleFrame = CGRect(origin: CGPoint(x: floor((width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
subtitleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((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)
|
usernameFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - usernameSize.width) / 2.0), y: subtitleFrame.maxY + 1.0), size: usernameSize)
|
||||||
} else {
|
} 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)
|
usernameFrame = CGRect(origin: CGPoint(x: subtitleFrame.maxX + usernameSpacing, y: titleFrame.maxY + 1.0), size: usernameSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2855,6 +2857,11 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
let titleScale = (transitionFraction * transitionSourceTitleFrame.height + (1.0 - transitionFraction) * titleFrame.height * neutralTitleScale) / (titleFrame.height)
|
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)))
|
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 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)
|
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
|
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
|
self.titleNodeRawContainer.frame = rawTitleFrame
|
||||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
|
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(), size: CGSize()))
|
||||||
let rawSubtitleFrame = subtitleFrame
|
let rawSubtitleFrame = subtitleFrame
|
||||||
@ -3090,7 +3097,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
if !self.backgroundNode.frame.contains(point) {
|
if !self.backgroundNode.frame.contains(point) {
|
||||||
return nil
|
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 iconFrame = self.titleCredibilityIconNode.view.convert(self.titleCredibilityIconNode.bounds, to: self.view)
|
||||||
let expandedIconFrame = self.titleExpandedCredibilityIconNode.view.convert(self.titleExpandedCredibilityIconNode.bounds, to: self.view)
|
let expandedIconFrame = self.titleExpandedCredibilityIconNode.view.convert(self.titleExpandedCredibilityIconNode.bounds, to: self.view)
|
||||||
if expandedIconFrame.contains(point) && self.isAvatarExpanded {
|
if expandedIconFrame.contains(point) && self.isAvatarExpanded {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user