mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
5d25b32ac4
commit
e3498e99a1
@ -8182,7 +8182,7 @@ Sorry for the inconvenience.";
|
|||||||
"ChatList.SelectedTopics_any" = "%@ Topics Selected";
|
"ChatList.SelectedTopics_any" = "%@ Topics Selected";
|
||||||
|
|
||||||
"ChatList.EmptyTopicsTitle" = "No topics here yet";
|
"ChatList.EmptyTopicsTitle" = "No topics here yet";
|
||||||
"ChatList.EmptyTopicsCreate" = "Create New Topic";
|
"ChatList.EmptyTopicsCreate" = "Create Topic";
|
||||||
"ChatList.EmptyTopicsShowAsMessages" = "Show as Messages";
|
"ChatList.EmptyTopicsShowAsMessages" = "Show as Messages";
|
||||||
|
|
||||||
"Message.AudioTranscription.SubscribeToPremium" = "Subscribe to **Telegram Premium** to convert voice to text.";
|
"Message.AudioTranscription.SubscribeToPremium" = "Subscribe to **Telegram Premium** to convert voice to text.";
|
||||||
@ -8301,7 +8301,11 @@ Sorry for the inconvenience.";
|
|||||||
"Notification.OverviewTopicUnhidden" = "%1$@ unhid %2$@ %3$@";
|
"Notification.OverviewTopicUnhidden" = "%1$@ unhid %2$@ %3$@";
|
||||||
|
|
||||||
"CreateTopic.ShowGeneral" = "Show in Topics";
|
"CreateTopic.ShowGeneral" = "Show in Topics";
|
||||||
"CreateTopic.ShowGeneralInfo" = "If the 'General' topic is hidden, group members can pull down in the topic list to view it.";
|
"CreateTopic.ShowGeneralInfo" = "If the \"General\" topic is hidden, group members can pull down in the topic list to view it.";
|
||||||
|
|
||||||
"Conversation.ContextMenuReportFalsePositive" = "Report False Positive";
|
"Conversation.ContextMenuReportFalsePositive" = "Report False Positive";
|
||||||
"Group.AdminLog.AntiSpamFalsePositiveReportedText" = "Telegram moderators will review your report. Thank you!";
|
"Group.AdminLog.AntiSpamFalsePositiveReportedText" = "Telegram moderators will review your report. Thank you!";
|
||||||
|
|
||||||
|
"ChatList.EmptyTopicsDescription" = "Older messages from this group have been moved to \"General\".";
|
||||||
|
|
||||||
|
"Stickers.EmojiPackInfoText" = "This message contains **%@** emoji.";
|
||||||
|
@ -776,7 +776,7 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func openCustomMute(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64, baseController: ViewController) {
|
private func openCustomMute(context: AccountContext, peerId: EnginePeer.Id, threadId: Int64, baseController: ViewController) {
|
||||||
let controller = ChatTimerScreen(context: context, updatedPresentationData: nil, peerId: peerId, style: .default, mode: .mute, currentTime: nil, dismissByTapOutside: true, completion: { [weak baseController] value in
|
let controller = ChatTimerScreen(context: context, updatedPresentationData: nil, style: .default, mode: .mute, currentTime: nil, dismissByTapOutside: true, completion: { [weak baseController] value in
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
if value <= 0 {
|
if value <= 0 {
|
||||||
|
@ -1173,14 +1173,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.chatListDisplayNode.emptyListAction = { [weak self] in
|
self.chatListDisplayNode.emptyListAction = { [weak self] forumPeerId in
|
||||||
guard let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController else {
|
guard let strongSelf = self, let navigationController = strongSelf.navigationController as? NavigationController else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let filter = strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter {
|
if let filter = strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter {
|
||||||
strongSelf.push(chatListFilterPresetController(context: strongSelf.context, currentPreset: filter, updated: { _ in }))
|
strongSelf.push(chatListFilterPresetController(context: strongSelf.context, currentPreset: filter, updated: { _ in }))
|
||||||
} else {
|
} else {
|
||||||
if case let .forum(peerId) = strongSelf.location {
|
if let peerId = forumPeerId {
|
||||||
let context = strongSelf.context
|
let context = strongSelf.context
|
||||||
let controller = ForumCreateTopicScreen(context: context, peerId: peerId, mode: .create)
|
let controller = ForumCreateTopicScreen(context: context, peerId: peerId, mode: .create)
|
||||||
controller.navigationPresentation = .modal
|
controller.navigationPresentation = .modal
|
||||||
|
@ -333,68 +333,75 @@ private final class ChatListContainerItemNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
var needsShimmerNode = false
|
var needsShimmerNode = false
|
||||||
var shimmerNodeOffset: CGFloat = 0.0
|
var shimmerNodeOffset: CGFloat = 0.0
|
||||||
|
|
||||||
|
var needsEmptyNode = false
|
||||||
|
var hasOnlyGeneralThread = false
|
||||||
|
var isLoading = false
|
||||||
|
|
||||||
switch isEmptyState {
|
switch isEmptyState {
|
||||||
case let .empty(isLoading, hasArchiveInfo):
|
case let .empty(isLoadingValue, hasArchiveInfo):
|
||||||
if hasArchiveInfo {
|
if hasArchiveInfo {
|
||||||
shimmerNodeOffset = 253.0
|
shimmerNodeOffset = 253.0
|
||||||
}
|
}
|
||||||
if isLoading {
|
if isLoadingValue {
|
||||||
needsShimmerNode = true
|
needsShimmerNode = true
|
||||||
|
needsEmptyNode = false
|
||||||
if let emptyNode = strongSelf.emptyNode {
|
isLoading = isLoadingValue
|
||||||
strongSelf.emptyNode = nil
|
|
||||||
transition.updateAlpha(node: emptyNode, alpha: 0.0, completion: { [weak emptyNode] _ in
|
|
||||||
emptyNode?.removeFromSupernode()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if let currentNode = strongSelf.emptyNode {
|
needsEmptyNode = true
|
||||||
currentNode.updateIsLoading(isLoading)
|
|
||||||
} else {
|
|
||||||
let subject: ChatListEmptyNode.Subject
|
|
||||||
if let filter = filter {
|
|
||||||
var showEdit = true
|
|
||||||
if case let .filter(_, _, _, data) = filter {
|
|
||||||
if data.excludeRead && data.includePeers.peers.isEmpty && data.includePeers.pinnedPeers.isEmpty {
|
|
||||||
showEdit = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subject = .filter(showEdit: showEdit)
|
|
||||||
} else {
|
|
||||||
if case .forum = location {
|
|
||||||
subject = .forum
|
|
||||||
} else {
|
|
||||||
subject = .chats
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let emptyNode = ChatListEmptyNode(context: context, subject: subject, isLoading: isLoading, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, action: {
|
|
||||||
self?.emptyAction(filter)
|
|
||||||
}, secondaryAction: {
|
|
||||||
self?.secondaryEmptyAction()
|
|
||||||
})
|
|
||||||
strongSelf.emptyNode = emptyNode
|
|
||||||
strongSelf.addSubnode(emptyNode)
|
|
||||||
if let (size, insets, _, _, _, _) = strongSelf.validLayout {
|
|
||||||
let emptyNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: size.width, height: size.height - insets.top - insets.bottom))
|
|
||||||
emptyNode.frame = emptyNodeFrame
|
|
||||||
emptyNode.updateLayout(size: emptyNodeFrame.size, transition: .immediate)
|
|
||||||
}
|
|
||||||
emptyNode.alpha = 0.0
|
|
||||||
transition.updateAlpha(node: emptyNode, alpha: 1.0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !isLoading {
|
if !isLoadingValue {
|
||||||
strongSelf.becameEmpty(filter)
|
strongSelf.becameEmpty(filter)
|
||||||
}
|
}
|
||||||
case .notEmpty:
|
case let .notEmpty(_, onlyGeneralThreadValue):
|
||||||
if let emptyNode = strongSelf.emptyNode {
|
needsEmptyNode = onlyGeneralThreadValue
|
||||||
strongSelf.emptyNode = nil
|
hasOnlyGeneralThread = onlyGeneralThreadValue
|
||||||
transition.updateAlpha(node: emptyNode, alpha: 0.0, completion: { [weak emptyNode] _ in
|
|
||||||
emptyNode?.removeFromSupernode()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if needsEmptyNode {
|
||||||
|
if let currentNode = strongSelf.emptyNode {
|
||||||
|
currentNode.updateIsLoading(isLoading)
|
||||||
|
} else {
|
||||||
|
let subject: ChatListEmptyNode.Subject
|
||||||
|
if let filter = filter {
|
||||||
|
var showEdit = true
|
||||||
|
if case let .filter(_, _, _, data) = filter {
|
||||||
|
if data.excludeRead && data.includePeers.peers.isEmpty && data.includePeers.pinnedPeers.isEmpty {
|
||||||
|
showEdit = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subject = .filter(showEdit: showEdit)
|
||||||
|
} else {
|
||||||
|
if case .forum = location {
|
||||||
|
subject = .forum(hasGeneral: hasOnlyGeneralThread)
|
||||||
|
} else {
|
||||||
|
subject = .chats
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let emptyNode = ChatListEmptyNode(context: context, subject: subject, isLoading: isLoading, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, action: {
|
||||||
|
self?.emptyAction(filter)
|
||||||
|
}, secondaryAction: {
|
||||||
|
self?.secondaryEmptyAction()
|
||||||
|
})
|
||||||
|
strongSelf.emptyNode = emptyNode
|
||||||
|
strongSelf.addSubnode(emptyNode)
|
||||||
|
if let (size, insets, _, _, _, _) = strongSelf.validLayout {
|
||||||
|
let emptyNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: size.width, height: size.height - insets.top - insets.bottom))
|
||||||
|
emptyNode.frame = emptyNodeFrame
|
||||||
|
emptyNode.updateLayout(size: emptyNodeFrame.size, transition: .immediate)
|
||||||
|
}
|
||||||
|
emptyNode.alpha = 0.0
|
||||||
|
transition.updateAlpha(node: emptyNode, alpha: 1.0)
|
||||||
|
}
|
||||||
|
} else if let emptyNode = strongSelf.emptyNode {
|
||||||
|
strongSelf.emptyNode = nil
|
||||||
|
transition.updateAlpha(node: emptyNode, alpha: 0.0, completion: { [weak emptyNode] _ in
|
||||||
|
emptyNode?.removeFromSupernode()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if needsShimmerNode {
|
if needsShimmerNode {
|
||||||
strongSelf.shimmerNodeOffset = shimmerNodeOffset
|
strongSelf.shimmerNodeOffset = shimmerNodeOffset
|
||||||
if strongSelf.emptyShimmerEffectNode == nil {
|
if strongSelf.emptyShimmerEffectNode == nil {
|
||||||
@ -1191,7 +1198,7 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||||||
var peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
var peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||||
var dismissSelfIfCompletedPresentation: (() -> Void)?
|
var dismissSelfIfCompletedPresentation: (() -> Void)?
|
||||||
var isEmptyUpdated: ((Bool) -> Void)?
|
var isEmptyUpdated: ((Bool) -> Void)?
|
||||||
var emptyListAction: (() -> Void)?
|
var emptyListAction: ((EnginePeer.Id?) -> Void)?
|
||||||
var cancelEditing: (() -> Void)?
|
var cancelEditing: (() -> Void)?
|
||||||
|
|
||||||
let debugListView = ListView()
|
let debugListView = ListView()
|
||||||
@ -1243,11 +1250,11 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||||||
strongSelf.dismissSelfIfCompletedPresentation?()
|
strongSelf.dismissSelfIfCompletedPresentation?()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filterEmptyAction = { [weak self] filter in
|
filterEmptyAction = { [weak self] _ in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.emptyListAction?()
|
strongSelf.emptyListAction?(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
secondaryEmptyAction = { [weak self] in
|
secondaryEmptyAction = { [weak self] in
|
||||||
@ -1629,7 +1636,12 @@ final class ChatListControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||||||
func setInlineChatList(location: ChatListControllerLocation?) {
|
func setInlineChatList(location: ChatListControllerLocation?) {
|
||||||
if let location = location {
|
if let location = location {
|
||||||
if self.inlineStackContainerNode?.location != location {
|
if self.inlineStackContainerNode?.location != location {
|
||||||
let inlineStackContainerNode = ChatListContainerNode(context: self.context, location: location, previewing: false, controlsHistoryPreload: false, isInlineMode: true, presentationData: self.presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, filterBecameEmpty: { _ in }, filterEmptyAction: { _ in }, secondaryEmptyAction: {})
|
var forumPeerId: EnginePeer.Id?
|
||||||
|
if case let .forum(peerId) = location {
|
||||||
|
forumPeerId = peerId
|
||||||
|
}
|
||||||
|
|
||||||
|
let inlineStackContainerNode = ChatListContainerNode(context: self.context, location: location, previewing: false, controlsHistoryPreload: false, isInlineMode: true, presentationData: self.presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, filterBecameEmpty: { _ in }, filterEmptyAction: { [weak self] _ in self?.emptyListAction?(forumPeerId) }, secondaryEmptyAction: {})
|
||||||
|
|
||||||
inlineStackContainerNode.leftSeparatorLayer.isHidden = false
|
inlineStackContainerNode.leftSeparatorLayer.isHidden = false
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ final class ChatListEmptyNode: ASDisplayNode {
|
|||||||
enum Subject {
|
enum Subject {
|
||||||
case chats
|
case chats
|
||||||
case filter(showEdit: Bool)
|
case filter(showEdit: Bool)
|
||||||
case forum
|
case forum(hasGeneral: Bool)
|
||||||
}
|
}
|
||||||
private let action: () -> Void
|
private let action: () -> Void
|
||||||
private let secondaryAction: () -> Void
|
private let secondaryAction: () -> Void
|
||||||
@ -131,7 +131,6 @@ final class ChatListEmptyNode: ASDisplayNode {
|
|||||||
let text: String
|
let text: String
|
||||||
var descriptionText = ""
|
var descriptionText = ""
|
||||||
let buttonText: String
|
let buttonText: String
|
||||||
var secondaryButtonText = ""
|
|
||||||
switch self.subject {
|
switch self.subject {
|
||||||
case .chats:
|
case .chats:
|
||||||
text = strings.ChatList_EmptyChatList
|
text = strings.ChatList_EmptyChatList
|
||||||
@ -143,7 +142,7 @@ final class ChatListEmptyNode: ASDisplayNode {
|
|||||||
case .forum:
|
case .forum:
|
||||||
text = strings.ChatList_EmptyTopicsTitle
|
text = strings.ChatList_EmptyTopicsTitle
|
||||||
buttonText = strings.ChatList_EmptyTopicsCreate
|
buttonText = strings.ChatList_EmptyTopicsCreate
|
||||||
secondaryButtonText = strings.ChatList_EmptyTopicsShowAsMessages
|
descriptionText = strings.ChatList_EmptyTopicsDescription
|
||||||
}
|
}
|
||||||
let string = NSMutableAttributedString(string: text, font: Font.medium(17.0), textColor: theme.list.itemPrimaryTextColor)
|
let string = NSMutableAttributedString(string: text, font: Font.medium(17.0), textColor: theme.list.itemPrimaryTextColor)
|
||||||
let descriptionString = NSAttributedString(string: descriptionText, font: Font.regular(14.0), textColor: theme.list.itemSecondaryTextColor)
|
let descriptionString = NSAttributedString(string: descriptionText, font: Font.regular(14.0), textColor: theme.list.itemSecondaryTextColor)
|
||||||
@ -152,8 +151,6 @@ final class ChatListEmptyNode: ASDisplayNode {
|
|||||||
self.descriptionNode.attributedText = descriptionString
|
self.descriptionNode.attributedText = descriptionString
|
||||||
|
|
||||||
self.buttonNode.title = buttonText
|
self.buttonNode.title = buttonText
|
||||||
self.secondaryButtonNode.setAttributedTitle(NSAttributedString(string: secondaryButtonText, font: Font.regular(17.0), textColor: theme.list.itemAccentColor), for: .normal)
|
|
||||||
self.secondaryButtonNode.isHidden = secondaryButtonText.isEmpty
|
|
||||||
|
|
||||||
self.activityIndicator.type = .custom(theme.list.itemAccentColor, 22.0, 1.0, false)
|
self.activityIndicator.type = .custom(theme.list.itemAccentColor, 22.0, 1.0, false)
|
||||||
|
|
||||||
@ -186,16 +183,21 @@ final class ChatListEmptyNode: ASDisplayNode {
|
|||||||
let textSize = self.textNode.updateLayout(CGSize(width: size.width - 40.0, height: size.height))
|
let textSize = self.textNode.updateLayout(CGSize(width: size.width - 40.0, height: size.height))
|
||||||
let descriptionSize = self.descriptionNode.updateLayout(CGSize(width: size.width - 40.0, height: size.height))
|
let descriptionSize = self.descriptionNode.updateLayout(CGSize(width: size.width - 40.0, height: size.height))
|
||||||
|
|
||||||
let buttonSideInset: CGFloat = 16.0
|
let buttonSideInset: CGFloat = 32.0
|
||||||
let buttonWidth = size.width - buttonSideInset * 2.0
|
let buttonWidth = min(270.0, size.width - buttonSideInset * 2.0)
|
||||||
let buttonHeight = self.buttonNode.updateLayout(width: buttonWidth, transition: transition)
|
let buttonHeight = self.buttonNode.updateLayout(width: buttonWidth, transition: transition)
|
||||||
let buttonSize = CGSize(width: buttonWidth, height: buttonHeight)
|
let buttonSize = CGSize(width: buttonWidth, height: buttonHeight)
|
||||||
|
|
||||||
let secondaryButtonSize = self.secondaryButtonNode.measure(CGSize(width: buttonWidth, height: .greatestFiniteMagnitude))
|
let secondaryButtonSize = self.secondaryButtonNode.measure(CGSize(width: buttonWidth, height: .greatestFiniteMagnitude))
|
||||||
|
|
||||||
|
var threshold: CGFloat = 0.0
|
||||||
|
if case .forum = self.subject {
|
||||||
|
threshold = 80.0
|
||||||
|
}
|
||||||
|
|
||||||
let contentHeight = self.animationSize.height + animationSpacing + textSize.height + buttonSize.height
|
let contentHeight = self.animationSize.height + animationSpacing + textSize.height + buttonSize.height
|
||||||
var contentOffset: CGFloat = 0.0
|
var contentOffset: CGFloat = 0.0
|
||||||
if size.height < contentHeight {
|
if size.height < contentHeight + threshold {
|
||||||
contentOffset = -self.animationSize.height - animationSpacing + 44.0
|
contentOffset = -self.animationSize.height - animationSpacing + 44.0
|
||||||
transition.updateAlpha(node: self.animationNode, alpha: 0.0)
|
transition.updateAlpha(node: self.animationNode, alpha: 0.0)
|
||||||
} else {
|
} else {
|
||||||
@ -224,7 +226,12 @@ final class ChatListEmptyNode: ASDisplayNode {
|
|||||||
bottomInset += secondaryButtonSize.height + 23.0
|
bottomInset += secondaryButtonSize.height + 23.0
|
||||||
}
|
}
|
||||||
|
|
||||||
let buttonFrame = CGRect(origin: CGPoint(x: floor((size.width - buttonSize.width) / 2.0), y: size.height - buttonHeight - bottomInset), size: buttonSize)
|
let buttonFrame: CGRect
|
||||||
|
if case .forum = self.subject {
|
||||||
|
buttonFrame = CGRect(origin: CGPoint(x: floor((size.width - buttonSize.width) / 2.0), y: descriptionFrame.maxY + 20.0), size: buttonSize)
|
||||||
|
} else {
|
||||||
|
buttonFrame = CGRect(origin: CGPoint(x: floor((size.width - buttonSize.width) / 2.0), y: size.height - buttonHeight - bottomInset), size: buttonSize)
|
||||||
|
}
|
||||||
transition.updateFrame(node: self.buttonNode, frame: buttonFrame)
|
transition.updateFrame(node: self.buttonNode, frame: buttonFrame)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -759,7 +759,7 @@ public enum ChatListNodeScrollPosition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum ChatListNodeEmptyState: Equatable {
|
public enum ChatListNodeEmptyState: Equatable {
|
||||||
case notEmpty(containsChats: Bool)
|
case notEmpty(containsChats: Bool, onlyGeneralThread: Bool)
|
||||||
case empty(isLoading: Bool, hasArchiveInfo: Bool)
|
case empty(isLoading: Bool, hasArchiveInfo: Bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2244,16 +2244,30 @@ public final class ChatListNode: ListView {
|
|||||||
isEmptyState = .empty(isLoading: isLoading, hasArchiveInfo: false)
|
isEmptyState = .empty(isLoading: isLoading, hasArchiveInfo: false)
|
||||||
} else {
|
} else {
|
||||||
var containsChats = false
|
var containsChats = false
|
||||||
|
var threadCount = 0
|
||||||
|
var hasGeneral = false
|
||||||
loop: for entry in transition.chatListView.filteredEntries {
|
loop: for entry in transition.chatListView.filteredEntries {
|
||||||
switch entry {
|
switch entry {
|
||||||
case .GroupReferenceEntry, .HoleEntry, .PeerEntry:
|
case .GroupReferenceEntry, .HoleEntry, .PeerEntry:
|
||||||
containsChats = true
|
containsChats = true
|
||||||
break loop
|
if case .forum = strongSelf.location {
|
||||||
|
if case let .PeerEntry(_, _, _, _, _, _, _, threadInfo, _, _, _, _, _, _, _, _, _, _, _, _, _) = entry, let threadInfo {
|
||||||
|
if threadInfo.id == 1 {
|
||||||
|
hasGeneral = true
|
||||||
|
}
|
||||||
|
threadCount += 1
|
||||||
|
if threadCount > 1 {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
case .ArchiveIntro, .HeaderEntry, .AdditionalCategory:
|
case .ArchiveIntro, .HeaderEntry, .AdditionalCategory:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isEmptyState = .notEmpty(containsChats: containsChats)
|
isEmptyState = .notEmpty(containsChats: containsChats, onlyGeneralThread: hasGeneral && threadCount == 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
var insertedPeerIds: [EnginePeer.Id] = []
|
var insertedPeerIds: [EnginePeer.Id] = []
|
||||||
|
@ -334,6 +334,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem {
|
|||||||
let enabled: Bool
|
let enabled: Bool
|
||||||
let highlighted: Bool
|
let highlighted: Bool
|
||||||
public let selectable: Bool
|
public let selectable: Bool
|
||||||
|
let animateFirstAvatarTransition: Bool
|
||||||
public let sectionId: ItemListSectionId
|
public let sectionId: ItemListSectionId
|
||||||
let action: (() -> Void)?
|
let action: (() -> Void)?
|
||||||
let setPeerIdWithRevealedOptions: (EnginePeer.Id?, EnginePeer.Id?) -> Void
|
let setPeerIdWithRevealedOptions: (EnginePeer.Id?, EnginePeer.Id?) -> Void
|
||||||
@ -350,7 +351,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem {
|
|||||||
let displayDecorations: Bool
|
let displayDecorations: Bool
|
||||||
let disableInteractiveTransitionIfNecessary: Bool
|
let disableInteractiveTransitionIfNecessary: Bool
|
||||||
|
|
||||||
public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, peer: EnginePeer, threadInfo: EngineMessageHistoryThread.Info? = nil, height: ItemListPeerItemHeight = .peerList, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, nameStyle: ItemListPeerItemNameStyle = .distinctBold, presence: EnginePeer.Presence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, highlighted: Bool = false, selectable: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, removePeer: @escaping (EnginePeer.Id) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil, hasTopStripe: Bool = true, hasTopGroupInset: Bool = true, noInsets: Bool = false, noCorners: Bool = false, tag: ItemListItemTag? = nil, header: ListViewItemHeader? = nil, shimmering: ItemListPeerItemShimmering? = nil, displayDecorations: Bool = true, disableInteractiveTransitionIfNecessary: Bool = false) {
|
public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, peer: EnginePeer, threadInfo: EngineMessageHistoryThread.Info? = nil, height: ItemListPeerItemHeight = .peerList, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, nameStyle: ItemListPeerItemNameStyle = .distinctBold, presence: EnginePeer.Presence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, highlighted: Bool = false, selectable: Bool, animateFirstAvatarTransition: Bool = true, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, removePeer: @escaping (EnginePeer.Id) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil, hasTopStripe: Bool = true, hasTopGroupInset: Bool = true, noInsets: Bool = false, noCorners: Bool = false, tag: ItemListItemTag? = nil, header: ListViewItemHeader? = nil, shimmering: ItemListPeerItemShimmering? = nil, displayDecorations: Bool = true, disableInteractiveTransitionIfNecessary: Bool = false) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.dateTimeFormat = dateTimeFormat
|
self.dateTimeFormat = dateTimeFormat
|
||||||
self.nameDisplayOrder = nameDisplayOrder
|
self.nameDisplayOrder = nameDisplayOrder
|
||||||
@ -370,6 +371,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem {
|
|||||||
self.enabled = enabled
|
self.enabled = enabled
|
||||||
self.highlighted = highlighted
|
self.highlighted = highlighted
|
||||||
self.selectable = selectable
|
self.selectable = selectable
|
||||||
|
self.animateFirstAvatarTransition = animateFirstAvatarTransition
|
||||||
self.sectionId = sectionId
|
self.sectionId = sectionId
|
||||||
self.action = action
|
self.action = action
|
||||||
self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions
|
self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions
|
||||||
@ -1289,6 +1291,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
if item.peer.isDeleted {
|
if item.peer.isDeleted {
|
||||||
overrideImage = .deletedIcon
|
overrideImage = .deletedIcon
|
||||||
}
|
}
|
||||||
|
strongSelf.avatarNode.imageNode.animateFirstTransition = item.animateFirstAvatarTransition
|
||||||
strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad)
|
strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,6 +107,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/EntityKeyboard:EntityKeyboard",
|
"//submodules/TelegramUI/Components/EntityKeyboard:EntityKeyboard",
|
||||||
"//submodules/PersistentStringHash:PersistentStringHash",
|
"//submodules/PersistentStringHash:PersistentStringHash",
|
||||||
"//submodules/TelegramUI/Components/NotificationPeerExceptionController",
|
"//submodules/TelegramUI/Components/NotificationPeerExceptionController",
|
||||||
|
"//submodules/TelegramUI/Components/ChatTimerScreen",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -30,7 +30,6 @@ public final class ChatTimerScreen: ViewController {
|
|||||||
private var animatedIn = false
|
private var animatedIn = false
|
||||||
|
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let peerId: PeerId
|
|
||||||
private let style: ChatTimerScreenStyle
|
private let style: ChatTimerScreenStyle
|
||||||
private let mode: ChatTimerScreenMode
|
private let mode: ChatTimerScreenMode
|
||||||
private let currentTime: Int32?
|
private let currentTime: Int32?
|
||||||
@ -40,9 +39,8 @@ public final class ChatTimerScreen: ViewController {
|
|||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private var presentationDataDisposable: Disposable?
|
private var presentationDataDisposable: Disposable?
|
||||||
|
|
||||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: PeerId, style: ChatTimerScreenStyle, mode: ChatTimerScreenMode = .sendTimer, currentTime: Int32? = nil, dismissByTapOutside: Bool = true, completion: @escaping (Int32) -> Void) {
|
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, style: ChatTimerScreenStyle, mode: ChatTimerScreenMode = .sendTimer, currentTime: Int32? = nil, dismissByTapOutside: Bool = true, completion: @escaping (Int32) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.peerId = peerId
|
|
||||||
self.style = style
|
self.style = style
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.currentTime = currentTime
|
self.currentTime = currentTime
|
||||||
|
@ -1,112 +0,0 @@
|
|||||||
%PDF-1.7
|
|
||||||
|
|
||||||
1 0 obj
|
|
||||||
<< >>
|
|
||||||
endobj
|
|
||||||
|
|
||||||
2 0 obj
|
|
||||||
<< /Length 3 0 R >>
|
|
||||||
stream
|
|
||||||
/DeviceRGB CS
|
|
||||||
/DeviceRGB cs
|
|
||||||
q
|
|
||||||
1.000000 0.000000 -0.000000 1.000000 4.199997 1.907497 cm
|
|
||||||
1.000000 1.000000 1.000000 scn
|
|
||||||
0.000000 12.592503 m
|
|
||||||
0.000000 18.498425 l
|
|
||||||
0.000000 19.400753 0.000000 19.851917 0.142046 20.246223 c
|
|
||||||
0.267605 20.594761 0.472146 20.909500 0.739650 21.165794 c
|
|
||||||
1.042280 21.455738 1.454996 21.639168 2.280427 22.006027 c
|
|
||||||
7.680857 24.406218 l
|
|
||||||
8.829472 24.916712 9.403779 25.171961 10.000750 25.272770 c
|
|
||||||
10.529839 25.362116 11.070163 25.362116 11.599251 25.272770 c
|
|
||||||
12.196222 25.171961 12.770529 24.916714 13.919143 24.406218 c
|
|
||||||
19.319572 22.006027 l
|
|
||||||
20.145004 21.639168 20.557720 21.455738 20.860350 21.165794 c
|
|
||||||
21.127855 20.909500 21.332396 20.594761 21.457954 20.246223 c
|
|
||||||
21.600000 19.851917 21.600000 19.400753 21.600000 18.498423 c
|
|
||||||
21.600000 12.592503 l
|
|
||||||
21.600000 4.965950 14.783872 1.126137 11.981524 -0.130262 c
|
|
||||||
11.981509 -0.130268 l
|
|
||||||
11.662810 -0.273155 11.503456 -0.344597 11.202699 -0.395788 c
|
|
||||||
10.999416 -0.430387 10.600584 -0.430387 10.397303 -0.395788 c
|
|
||||||
10.096541 -0.344597 9.937186 -0.273151 9.618476 -0.130262 c
|
|
||||||
6.816126 1.126139 0.000000 4.965952 0.000000 12.592503 c
|
|
||||||
h
|
|
||||||
f
|
|
||||||
n
|
|
||||||
Q
|
|
||||||
q
|
|
||||||
1.000000 0.000000 -0.000000 1.000000 7.199997 8.199517 cm
|
|
||||||
0.274510 0.294118 0.298039 scn
|
|
||||||
0.989925 6.863103 m
|
|
||||||
4.855387 8.547224 7.432969 9.657499 8.722668 10.193930 c
|
|
||||||
12.405018 11.725546 13.170178 11.991605 13.668900 12.000390 c
|
|
||||||
13.778588 12.002322 14.023846 11.975138 14.182714 11.846229 c
|
|
||||||
14.316857 11.737380 14.353765 11.590341 14.371427 11.487141 c
|
|
||||||
14.389089 11.383940 14.411081 11.148846 14.393599 10.965151 c
|
|
||||||
14.194051 8.868484 13.330610 3.780426 12.891341 1.432123 c
|
|
||||||
12.705469 0.438469 12.339483 0.105302 11.985166 0.072697 c
|
|
||||||
11.215152 0.001839 10.630439 0.581574 9.884643 1.070453 c
|
|
||||||
8.717619 1.835451 8.058326 2.311666 6.925527 3.058163 c
|
|
||||||
5.616383 3.920870 6.465046 4.395029 7.211124 5.169937 c
|
|
||||||
7.406376 5.372734 10.799074 8.458654 10.864739 8.738596 c
|
|
||||||
10.872952 8.773607 10.880574 8.904113 10.803043 8.973024 c
|
|
||||||
10.725513 9.041937 10.611083 9.018372 10.528507 8.999630 c
|
|
||||||
10.411459 8.973064 8.547124 7.740808 4.935502 5.302861 c
|
|
||||||
4.406317 4.939481 3.926997 4.762432 3.497544 4.771710 c
|
|
||||||
3.024105 4.781938 2.113399 5.039400 1.436383 5.259471 c
|
|
||||||
0.605995 5.529397 -0.053981 5.672108 0.003489 6.130527 c
|
|
||||||
0.033422 6.369299 0.362234 6.613492 0.989925 6.863103 c
|
|
||||||
h
|
|
||||||
f*
|
|
||||||
n
|
|
||||||
Q
|
|
||||||
|
|
||||||
endstream
|
|
||||||
endobj
|
|
||||||
|
|
||||||
3 0 obj
|
|
||||||
2275
|
|
||||||
endobj
|
|
||||||
|
|
||||||
4 0 obj
|
|
||||||
<< /Annots []
|
|
||||||
/Type /Page
|
|
||||||
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
|
||||||
/Resources 1 0 R
|
|
||||||
/Contents 2 0 R
|
|
||||||
/Parent 5 0 R
|
|
||||||
>>
|
|
||||||
endobj
|
|
||||||
|
|
||||||
5 0 obj
|
|
||||||
<< /Kids [ 4 0 R ]
|
|
||||||
/Count 1
|
|
||||||
/Type /Pages
|
|
||||||
>>
|
|
||||||
endobj
|
|
||||||
|
|
||||||
6 0 obj
|
|
||||||
<< /Pages 5 0 R
|
|
||||||
/Type /Catalog
|
|
||||||
>>
|
|
||||||
endobj
|
|
||||||
|
|
||||||
xref
|
|
||||||
0 7
|
|
||||||
0000000000 65535 f
|
|
||||||
0000000010 00000 n
|
|
||||||
0000000034 00000 n
|
|
||||||
0000002365 00000 n
|
|
||||||
0000002388 00000 n
|
|
||||||
0000002561 00000 n
|
|
||||||
0000002635 00000 n
|
|
||||||
trailer
|
|
||||||
<< /ID [ (some) (id) ]
|
|
||||||
/Root 6 0 R
|
|
||||||
/Size 7
|
|
||||||
>>
|
|
||||||
startxref
|
|
||||||
2694
|
|
||||||
%%EOF
|
|
@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "AntiSpamTooltip.pdf",
|
|
||||||
"idiom" : "universal"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
@ -171,7 +171,7 @@ final class ChatBotInfoItemNode: ListViewItemNode {
|
|||||||
break
|
break
|
||||||
case .ignore:
|
case .ignore:
|
||||||
return .fail
|
return .fail
|
||||||
case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji:
|
case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji, .customEmoji:
|
||||||
return .waitForSingleTap
|
return .waitForSingleTap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3590,6 +3590,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}, displayPremiumStickerTooltip: { [weak self] file, message in
|
}, displayPremiumStickerTooltip: { [weak self] file, message in
|
||||||
self?.displayPremiumStickerTooltip(file: file, message: message)
|
self?.displayPremiumStickerTooltip(file: file, message: message)
|
||||||
|
}, displayEmojiPackTooltip: { [weak self] file, message in
|
||||||
|
self?.displayEmojiPackTooltip(file: file, message: message)
|
||||||
}, openPeerContextMenu: { [weak self] peer, messageId, node, rect, gesture in
|
}, openPeerContextMenu: { [weak self] peer, messageId, node, rect, gesture in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -13581,6 +13583,56 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func displayEmojiPackTooltip(file: TelegramMediaFile, message: Message) {
|
||||||
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
||||||
|
guard !premiumConfiguration.isPremiumDisabled else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentOverlayController: UndoOverlayController?
|
||||||
|
|
||||||
|
self.window?.forEachController({ controller in
|
||||||
|
if let controller = controller as? UndoOverlayController {
|
||||||
|
currentOverlayController = controller
|
||||||
|
}
|
||||||
|
})
|
||||||
|
self.forEachController({ controller in
|
||||||
|
if let controller = controller as? UndoOverlayController {
|
||||||
|
currentOverlayController = controller
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
if let currentOverlayController = currentOverlayController {
|
||||||
|
if case .sticker = currentOverlayController.content {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currentOverlayController.dismissWithCommitAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
var stickerPackReference: StickerPackReference?
|
||||||
|
for attribute in file.attributes {
|
||||||
|
if case let .CustomEmoji(_, _, packReference) = attribute {
|
||||||
|
stickerPackReference = packReference
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let stickerPackReference = stickerPackReference {
|
||||||
|
let _ = (self.context.engine.stickers.loadedStickerPack(reference: stickerPackReference, forceActualized: false)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] stickerPack in
|
||||||
|
if let strongSelf = self, case let .result(info, _, _) = stickerPack {
|
||||||
|
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: file, title: nil, text: strongSelf.presentationData.strings.Stickers_EmojiPackInfoText(info.title).string, undoText: strongSelf.presentationData.strings.Stickers_PremiumPackView, customAction: nil), elevatedLayout: false, action: { [weak self] action in
|
||||||
|
if let strongSelf = self, action == .undo {
|
||||||
|
strongSelf.presentEmojiList(references: [stickerPackReference])
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}), in: .current)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func displayDiceTooltip(dice: TelegramMediaDice) {
|
private func displayDiceTooltip(dice: TelegramMediaDice) {
|
||||||
guard let _ = dice.value else {
|
guard let _ = dice.value else {
|
||||||
return
|
return
|
||||||
@ -17171,10 +17223,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func presentTimerPicker(style: ChatTimerScreenStyle = .default, selectedTime: Int32? = nil, dismissByTapOutside: Bool = true, completion: @escaping (Int32) -> Void) {
|
private func presentTimerPicker(style: ChatTimerScreenStyle = .default, selectedTime: Int32? = nil, dismissByTapOutside: Bool = true, completion: @escaping (Int32) -> Void) {
|
||||||
guard case let .peer(peerId) = self.chatLocation else {
|
let controller = ChatTimerScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, style: style, currentTime: selectedTime, dismissByTapOutside: dismissByTapOutside, completion: { time in
|
||||||
return
|
|
||||||
}
|
|
||||||
let controller = ChatTimerScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, peerId: peerId, style: style, currentTime: selectedTime, dismissByTapOutside: dismissByTapOutside, completion: { time in
|
|
||||||
completion(time)
|
completion(time)
|
||||||
})
|
})
|
||||||
self.chatDisplayNode.dismissInput()
|
self.chatDisplayNode.dismissInput()
|
||||||
@ -17231,7 +17280,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = ChatTimerScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, peerId: peer.id, style: .default, mode: .autoremove, currentTime: self.presentationInterfaceState.autoremoveTimeout, dismissByTapOutside: true, completion: { [weak self] value in
|
let controller = ChatTimerScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, style: .default, mode: .autoremove, currentTime: self.presentationInterfaceState.autoremoveTimeout, dismissByTapOutside: true, completion: { [weak self] value in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -134,6 +134,7 @@ public final class ChatControllerInteraction {
|
|||||||
let displayDiceTooltip: (TelegramMediaDice) -> Void
|
let displayDiceTooltip: (TelegramMediaDice) -> Void
|
||||||
let animateDiceSuccess: (Bool, Bool) -> Void
|
let animateDiceSuccess: (Bool, Bool) -> Void
|
||||||
let displayPremiumStickerTooltip: (TelegramMediaFile, Message) -> Void
|
let displayPremiumStickerTooltip: (TelegramMediaFile, Message) -> Void
|
||||||
|
let displayEmojiPackTooltip: (TelegramMediaFile, Message) -> Void
|
||||||
let openPeerContextMenu: (Peer, MessageId?, ASDisplayNode, CGRect, ContextGesture?) -> Void
|
let openPeerContextMenu: (Peer, MessageId?, ASDisplayNode, CGRect, ContextGesture?) -> Void
|
||||||
let openMessageReplies: (MessageId, Bool, Bool) -> Void
|
let openMessageReplies: (MessageId, Bool, Bool) -> Void
|
||||||
let openReplyThreadOriginalMessage: (Message) -> Void
|
let openReplyThreadOriginalMessage: (Message) -> Void
|
||||||
@ -243,6 +244,7 @@ public final class ChatControllerInteraction {
|
|||||||
displayDiceTooltip: @escaping (TelegramMediaDice) -> Void,
|
displayDiceTooltip: @escaping (TelegramMediaDice) -> Void,
|
||||||
animateDiceSuccess: @escaping (Bool, Bool) -> Void,
|
animateDiceSuccess: @escaping (Bool, Bool) -> Void,
|
||||||
displayPremiumStickerTooltip: @escaping (TelegramMediaFile, Message) -> Void,
|
displayPremiumStickerTooltip: @escaping (TelegramMediaFile, Message) -> Void,
|
||||||
|
displayEmojiPackTooltip: @escaping (TelegramMediaFile, Message) -> Void,
|
||||||
openPeerContextMenu: @escaping (Peer, MessageId?, ASDisplayNode, CGRect, ContextGesture?) -> Void,
|
openPeerContextMenu: @escaping (Peer, MessageId?, ASDisplayNode, CGRect, ContextGesture?) -> Void,
|
||||||
openMessageReplies: @escaping (MessageId, Bool, Bool) -> Void,
|
openMessageReplies: @escaping (MessageId, Bool, Bool) -> Void,
|
||||||
openReplyThreadOriginalMessage: @escaping (Message) -> Void,
|
openReplyThreadOriginalMessage: @escaping (Message) -> Void,
|
||||||
@ -335,6 +337,7 @@ public final class ChatControllerInteraction {
|
|||||||
self.displayDiceTooltip = displayDiceTooltip
|
self.displayDiceTooltip = displayDiceTooltip
|
||||||
self.animateDiceSuccess = animateDiceSuccess
|
self.animateDiceSuccess = animateDiceSuccess
|
||||||
self.displayPremiumStickerTooltip = displayPremiumStickerTooltip
|
self.displayPremiumStickerTooltip = displayPremiumStickerTooltip
|
||||||
|
self.displayEmojiPackTooltip = displayEmojiPackTooltip
|
||||||
self.openPeerContextMenu = openPeerContextMenu
|
self.openPeerContextMenu = openPeerContextMenu
|
||||||
self.openMessageReplies = openMessageReplies
|
self.openMessageReplies = openMessageReplies
|
||||||
self.openReplyThreadOriginalMessage = openReplyThreadOriginalMessage
|
self.openReplyThreadOriginalMessage = openReplyThreadOriginalMessage
|
||||||
|
@ -2126,7 +2126,17 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let item = self.item, self.imageNode.frame.contains(location) {
|
if let item = self.item, let emojiString = self.emojiString, emojiString.emojis.count > 1 {
|
||||||
|
if let (_, attributes) = self.textNode.textNode.attributesAtPoint(self.view.convert(location, to: self.textNode.textNode.view)) {
|
||||||
|
for (_, attribute) in attributes {
|
||||||
|
if let attribute = attribute as? ChatTextInputTextCustomEmojiAttribute, let file = attribute.file {
|
||||||
|
return .optionalAction({
|
||||||
|
item.controllerInteraction.displayEmojiPackTooltip(file, item.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let item = self.item, self.imageNode.frame.contains(location) {
|
||||||
let emojiTapAction: (Bool) -> InternalBubbleTapAction? = { shouldPlay in
|
let emojiTapAction: (Bool) -> InternalBubbleTapAction? = { shouldPlay in
|
||||||
let beatingHearts: [UInt32] = [0x2764, 0x1F90E, 0x1F9E1, 0x1F499, 0x1F49A, 0x1F49C, 0x1F49B, 0x1F5A4, 0x1F90D]
|
let beatingHearts: [UInt32] = [0x2764, 0x1F90E, 0x1F9E1, 0x1F499, 0x1F49A, 0x1F49C, 0x1F49B, 0x1F5A4, 0x1F90D]
|
||||||
let heart = 0x2764
|
let heart = 0x2764
|
||||||
|
@ -123,6 +123,7 @@ enum ChatMessageBubbleContentTapAction {
|
|||||||
case openPollResults(Data)
|
case openPollResults(Data)
|
||||||
case copy(String)
|
case copy(String)
|
||||||
case largeEmoji(String, String?, TelegramMediaFile)
|
case largeEmoji(String, String?, TelegramMediaFile)
|
||||||
|
case customEmoji(TelegramMediaFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
final class ChatMessageBubbleContentItem {
|
final class ChatMessageBubbleContentItem {
|
||||||
|
@ -964,7 +964,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
|||||||
break
|
break
|
||||||
case .ignore:
|
case .ignore:
|
||||||
return .fail
|
return .fail
|
||||||
case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji:
|
case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji, .customEmoji:
|
||||||
return .waitForSingleTap
|
return .waitForSingleTap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3742,6 +3742,12 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
|||||||
item.controllerInteraction.openLargeEmojiInfo(emoji, fitz, file)
|
item.controllerInteraction.openLargeEmojiInfo(emoji, fitz, file)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
case let .customEmoji(file):
|
||||||
|
if let item = self.item {
|
||||||
|
return .optionalAction({
|
||||||
|
item.controllerInteraction.displayEmojiPackTooltip(file, item.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -3826,6 +3832,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
|||||||
break
|
break
|
||||||
case .largeEmoji:
|
case .largeEmoji:
|
||||||
break
|
break
|
||||||
|
case .customEmoji:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let tapMessage = tapMessage {
|
if let tapMessage = tapMessage {
|
||||||
|
@ -580,6 +580,8 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
return .bankCard(bankCard)
|
return .bankCard(bankCard)
|
||||||
} else if let pre = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Pre)] as? String {
|
} else if let pre = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Pre)] as? String {
|
||||||
return .copy(pre)
|
return .copy(pre)
|
||||||
|
} else if let emoji = attributes[NSAttributedString.Key(rawValue: ChatTextInputAttributes.customEmoji.rawValue)] as? ChatTextInputTextCustomEmojiAttribute, let file = emoji.file {
|
||||||
|
return .customEmoji(file)
|
||||||
} else {
|
} else {
|
||||||
if let item = self.item, item.message.text.count == 1, !item.presentationData.largeEmoji {
|
if let item = self.item, item.message.text.count == 1, !item.presentationData.largeEmoji {
|
||||||
let (emoji, fitz) = item.message.text.basicEmoji
|
let (emoji, fitz) = item.message.text.basicEmoji
|
||||||
|
@ -535,6 +535,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
}, displayDiceTooltip: { _ in
|
}, displayDiceTooltip: { _ in
|
||||||
}, animateDiceSuccess: { _, _ in
|
}, animateDiceSuccess: { _, _ in
|
||||||
}, displayPremiumStickerTooltip: { _, _ in
|
}, displayPremiumStickerTooltip: { _, _ in
|
||||||
|
}, displayEmojiPackTooltip: { _, _ in
|
||||||
}, openPeerContextMenu: { _, _, _, _, _ in
|
}, openPeerContextMenu: { _, _, _, _, _ in
|
||||||
}, openMessageReplies: { _, _, _ in
|
}, openMessageReplies: { _, _, _ in
|
||||||
}, openReplyThreadOriginalMessage: { _ in
|
}, openReplyThreadOriginalMessage: { _ in
|
||||||
@ -789,7 +790,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
if peer.id == antiSpamBotConfiguration.antiSpamBotId {
|
if peer.id == antiSpamBotConfiguration.antiSpamBotId {
|
||||||
self.dismissAllTooltips()
|
self.dismissAllTooltips()
|
||||||
|
|
||||||
self.presentController(UndoOverlayController(presentationData: self.presentationData, content: .image(image: UIImage(bundleImageName: "Chat/AntiSpamTooltipIcon")!, title: self.presentationData.strings.Group_AdminLog_AntiSpamTitle, text: self.presentationData.strings.Group_AdminLog_AntiSpamText, undo: false), elevatedLayout: true, action: { [weak self] action in
|
self.presentController(UndoOverlayController(presentationData: self.presentationData, content: .universal(animation: "anim_antispam", scale: 0.066, colors: [:], title: self.presentationData.strings.Group_AdminLog_AntiSpamTitle, text: self.presentationData.strings.Group_AdminLog_AntiSpamText, customUndoText: nil), elevatedLayout: true, action: { [weak self] action in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if case .info = action {
|
if case .info = action {
|
||||||
let _ = strongSelf.getNavigationController()?.popViewController(animated: true)
|
let _ = strongSelf.getNavigationController()?.popViewController(animated: true)
|
||||||
@ -923,8 +924,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
|> deliverOnMainQueue).start(), forKey: message.id)
|
|> deliverOnMainQueue).start(), forKey: message.id)
|
||||||
|
|
||||||
Queue.mainQueue().after(0.2, {
|
Queue.mainQueue().after(0.2, {
|
||||||
let content: UndoOverlayContent = .image(image: UIImage(bundleImageName: "Chat/AntiSpamTooltipIcon")!, title: nil, text: strongSelf.presentationData.strings.Group_AdminLog_AntiSpamFalsePositiveReportedText, undo: false)
|
strongSelf.presentController(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_antispam", scale: 0.066, colors: [:], title: nil, text: strongSelf.presentationData.strings.Group_AdminLog_AntiSpamFalsePositiveReportedText, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), .current, nil)
|
||||||
strongSelf.presentController(UndoOverlayController(presentationData: strongSelf.presentationData, content: content, elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), .current, nil)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})), at: 0
|
})), at: 0
|
||||||
|
@ -529,7 +529,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
|
|||||||
if let peer = self.entry.peers[self.entry.event.peerId] {
|
if let peer = self.entry.peers[self.entry.event.peerId] {
|
||||||
peers[peer.id] = peer
|
peers[peer.id] = peer
|
||||||
}
|
}
|
||||||
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
|
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: message.id.id), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
|
||||||
return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
|
return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
|
||||||
}
|
}
|
||||||
case .participantJoin, .participantLeave:
|
case .participantJoin, .participantLeave:
|
||||||
|
@ -146,6 +146,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
|
|||||||
}, displayDiceTooltip: { _ in
|
}, displayDiceTooltip: { _ in
|
||||||
}, animateDiceSuccess: { _, _ in
|
}, animateDiceSuccess: { _, _ in
|
||||||
}, displayPremiumStickerTooltip: { _, _ in
|
}, displayPremiumStickerTooltip: { _, _ in
|
||||||
|
}, displayEmojiPackTooltip: { _, _ in
|
||||||
}, openPeerContextMenu: { _, _, _, _, _ in
|
}, openPeerContextMenu: { _, _, _, _, _ in
|
||||||
}, openMessageReplies: { _, _, _ in
|
}, openMessageReplies: { _, _, _ in
|
||||||
}, openReplyThreadOriginalMessage: { _ in
|
}, openReplyThreadOriginalMessage: { _ in
|
||||||
|
@ -139,6 +139,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
|||||||
}, displayDiceTooltip: { _ in
|
}, displayDiceTooltip: { _ in
|
||||||
}, animateDiceSuccess: { _, _ in
|
}, animateDiceSuccess: { _, _ in
|
||||||
}, displayPremiumStickerTooltip: { _, _ in
|
}, displayPremiumStickerTooltip: { _, _ in
|
||||||
|
}, displayEmojiPackTooltip: { _, _ in
|
||||||
}, openPeerContextMenu: { _, _, _, _, _ in
|
}, openPeerContextMenu: { _, _, _, _, _ in
|
||||||
}, openMessageReplies: { _, _, _ in
|
}, openMessageReplies: { _, _, _ in
|
||||||
}, openReplyThreadOriginalMessage: { _ in
|
}, openReplyThreadOriginalMessage: { _ in
|
||||||
|
@ -23,6 +23,7 @@ final class PeerInfoScreenMemberItem: PeerInfoScreenItem {
|
|||||||
let enclosingPeer: Peer?
|
let enclosingPeer: Peer?
|
||||||
let member: PeerInfoMember
|
let member: PeerInfoMember
|
||||||
let badge: String?
|
let badge: String?
|
||||||
|
let isAccount: Bool
|
||||||
let action: ((PeerInfoScreenMemberItemAction) -> Void)?
|
let action: ((PeerInfoScreenMemberItemAction) -> Void)?
|
||||||
let contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
let contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ final class PeerInfoScreenMemberItem: PeerInfoScreenItem {
|
|||||||
enclosingPeer: Peer?,
|
enclosingPeer: Peer?,
|
||||||
member: PeerInfoMember,
|
member: PeerInfoMember,
|
||||||
badge: String? = nil,
|
badge: String? = nil,
|
||||||
|
isAccount: Bool,
|
||||||
action: ((PeerInfoScreenMemberItemAction) -> Void)?,
|
action: ((PeerInfoScreenMemberItemAction) -> Void)?,
|
||||||
contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil
|
contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil
|
||||||
) {
|
) {
|
||||||
@ -40,6 +42,7 @@ final class PeerInfoScreenMemberItem: PeerInfoScreenItem {
|
|||||||
self.enclosingPeer = enclosingPeer
|
self.enclosingPeer = enclosingPeer
|
||||||
self.member = member
|
self.member = member
|
||||||
self.badge = badge
|
self.badge = badge
|
||||||
|
self.isAccount = isAccount
|
||||||
self.action = action
|
self.action = action
|
||||||
self.contextAction = contextAction
|
self.contextAction = contextAction
|
||||||
}
|
}
|
||||||
@ -188,7 +191,7 @@ private final class PeerInfoScreenMemberItemNode: PeerInfoScreenItemNode {
|
|||||||
itemText = .presence
|
itemText = .presence
|
||||||
}
|
}
|
||||||
|
|
||||||
let peerItem = ItemListPeerItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: item.context, peer: EnginePeer(item.member.peer), height: itemHeight, presence: item.member.presence.flatMap(EnginePeer.Presence.init), text: itemText, label: itemLabel, editing: ItemListPeerItemEditing(editable: !options.isEmpty, editing: false, revealed: nil), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, selectable: false, sectionId: 0, action: nil, setPeerIdWithRevealedOptions: { lhs, rhs in
|
let peerItem = ItemListPeerItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: item.context, peer: EnginePeer(item.member.peer), height: itemHeight, presence: item.member.presence.flatMap(EnginePeer.Presence.init), text: itemText, label: itemLabel, editing: ItemListPeerItemEditing(editable: !options.isEmpty, editing: false, revealed: nil), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, selectable: false, animateFirstAvatarTransition: !item.isAccount, sectionId: 0, action: nil, setPeerIdWithRevealedOptions: { lhs, rhs in
|
||||||
|
|
||||||
}, removePeer: { _ in
|
}, removePeer: { _ in
|
||||||
|
|
||||||
|
@ -322,6 +322,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var tapped: (() -> Void)?
|
var tapped: (() -> Void)?
|
||||||
|
var emojiTapped: (() -> Void)?
|
||||||
var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
|
|
||||||
private var isFirstAvatarLoading = true
|
private var isFirstAvatarLoading = true
|
||||||
@ -365,6 +366,12 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
self.tapped?()
|
self.tapped?()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func emojiTapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||||
|
if case .ended = recognizer.state {
|
||||||
|
self.emojiTapped?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func updateTransitionFraction(_ fraction: CGFloat, transition: ContainedViewLayoutTransition) {
|
func updateTransitionFraction(_ fraction: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
if let videoNode = self.videoNode {
|
if let videoNode = self.videoNode {
|
||||||
@ -409,6 +416,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
self.containerNode.isGestureEnabled = false
|
self.containerNode.isGestureEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.avatarNode.imageNode.animateFirstTransition = !isSettings
|
||||||
self.avatarNode.setPeer(context: self.context, theme: theme, peer: EnginePeer(peer), overrideImage: overrideImage, clipStyle: .none, synchronousLoad: self.isFirstAvatarLoading, displayDimensions: CGSize(width: avatarSize, height: avatarSize), storeUnrounded: true)
|
self.avatarNode.setPeer(context: self.context, theme: theme, peer: EnginePeer(peer), overrideImage: overrideImage, clipStyle: .none, synchronousLoad: self.isFirstAvatarLoading, displayDimensions: CGSize(width: avatarSize, height: avatarSize), storeUnrounded: true)
|
||||||
|
|
||||||
if let threadInfo = threadInfo {
|
if let threadInfo = threadInfo {
|
||||||
@ -443,7 +451,9 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
containerSize: CGSize(width: avatarSize, height: avatarSize)
|
containerSize: CGSize(width: avatarSize, height: avatarSize)
|
||||||
)
|
)
|
||||||
if let iconComponentView = iconView.view {
|
if let iconComponentView = iconView.view {
|
||||||
|
iconComponentView.isUserInteractionEnabled = true
|
||||||
if iconComponentView.superview == nil {
|
if iconComponentView.superview == nil {
|
||||||
|
iconComponentView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.emojiTapGesture(_:))))
|
||||||
self.avatarNode.view.superview?.addSubview(iconComponentView)
|
self.avatarNode.view.superview?.addSubview(iconComponentView)
|
||||||
}
|
}
|
||||||
iconComponentView.frame = CGRect(origin: CGPoint(), size: CGSize(width: avatarSize, height: avatarSize))
|
iconComponentView.frame = CGRect(origin: CGPoint(), size: CGSize(width: avatarSize, height: avatarSize))
|
||||||
@ -988,6 +998,8 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
|
|||||||
} else {
|
} else {
|
||||||
if let result = self.avatarContainerNode.avatarNode.view.hitTest(self.view.convert(point, to: self.avatarContainerNode.avatarNode.view), with: event) {
|
if let result = self.avatarContainerNode.avatarNode.view.hitTest(self.view.convert(point, to: self.avatarContainerNode.avatarNode.view), with: event) {
|
||||||
return result
|
return result
|
||||||
|
} else if let result = self.avatarContainerNode.iconView?.view?.hitTest(self.view.convert(point, to: self.avatarContainerNode.iconView?.view), with: event) {
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2108,6 +2120,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
|
|
||||||
var displayAvatarContextMenu: ((ASDisplayNode, ContextGesture?) -> Void)?
|
var displayAvatarContextMenu: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
var displayCopyContextMenu: ((ASDisplayNode, Bool, Bool) -> Void)?
|
var displayCopyContextMenu: ((ASDisplayNode, Bool, Bool) -> Void)?
|
||||||
|
var displayEmojiPackTooltip: (() -> Void)?
|
||||||
|
|
||||||
var displayPremiumIntro: ((UIView, PeerEmojiStatus?, Signal<(TelegramMediaFile, LoadedStickerPack)?, NoError>, Bool) -> Void)?
|
var displayPremiumIntro: ((UIView, PeerEmojiStatus?, Signal<(TelegramMediaFile, LoadedStickerPack)?, NoError>, Bool) -> Void)?
|
||||||
|
|
||||||
@ -2241,6 +2254,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
self.avatarListNode.avatarContainerNode.contextAction = { [weak self] node, gesture in
|
self.avatarListNode.avatarContainerNode.contextAction = { [weak self] node, gesture in
|
||||||
self?.displayAvatarContextMenu?(node, gesture)
|
self?.displayAvatarContextMenu?(node, gesture)
|
||||||
}
|
}
|
||||||
|
self.avatarListNode.avatarContainerNode.emojiTapped = { [weak self] in
|
||||||
|
self?.displayEmojiPackTooltip?()
|
||||||
|
}
|
||||||
|
|
||||||
self.editingContentNode.avatarNode.tapped = { [weak self] confirm in
|
self.editingContentNode.avatarNode.tapped = { [weak self] confirm in
|
||||||
self?.initiateAvatarExpansion(gallery: true, first: true)
|
self?.initiateAvatarExpansion(gallery: true, first: true)
|
||||||
|
@ -81,6 +81,7 @@ import ForumCreateTopicScreen
|
|||||||
import NotificationExceptionsScreen
|
import NotificationExceptionsScreen
|
||||||
import ChatTimerScreen
|
import ChatTimerScreen
|
||||||
import NotificationPeerExceptionController
|
import NotificationPeerExceptionController
|
||||||
|
import StickerPackPreviewUI
|
||||||
|
|
||||||
protocol PeerInfoScreenItem: AnyObject {
|
protocol PeerInfoScreenItem: AnyObject {
|
||||||
var id: AnyHashable { get }
|
var id: AnyHashable { get }
|
||||||
@ -705,7 +706,7 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
|
|||||||
if !settings.accountsAndPeers.isEmpty {
|
if !settings.accountsAndPeers.isEmpty {
|
||||||
for (peerAccountContext, peer, badgeCount) in settings.accountsAndPeers {
|
for (peerAccountContext, peer, badgeCount) in settings.accountsAndPeers {
|
||||||
let member: PeerInfoMember = .account(peer: RenderedPeer(peer: peer._asPeer()))
|
let member: PeerInfoMember = .account(peer: RenderedPeer(peer: peer._asPeer()))
|
||||||
items[.accounts]!.append(PeerInfoScreenMemberItem(id: member.id, context: context.sharedContext.makeTempAccountContext(account: peerAccountContext.account), enclosingPeer: nil, member: member, badge: badgeCount > 0 ? "\(compactNumericCountString(Int(badgeCount), decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))" : nil, action: { action in
|
items[.accounts]!.append(PeerInfoScreenMemberItem(id: member.id, context: context.sharedContext.makeTempAccountContext(account: peerAccountContext.account), enclosingPeer: nil, member: member, badge: badgeCount > 0 ? "\(compactNumericCountString(Int(badgeCount), decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))" : nil, isAccount: true, action: { action in
|
||||||
switch action {
|
switch action {
|
||||||
case .open:
|
case .open:
|
||||||
interaction.switchToAccount(peerAccountContext.account.id)
|
interaction.switchToAccount(peerAccountContext.account.id)
|
||||||
@ -1297,7 +1298,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
|||||||
|
|
||||||
for member in memberList {
|
for member in memberList {
|
||||||
let isAccountPeer = member.id == context.account.peerId
|
let isAccountPeer = member.id == context.account.peerId
|
||||||
items[.peerMembers]!.append(PeerInfoScreenMemberItem(id: member.id, context: context, enclosingPeer: peer, member: member, action: isAccountPeer ? nil : { action in
|
items[.peerMembers]!.append(PeerInfoScreenMemberItem(id: member.id, context: context, enclosingPeer: peer, member: member, isAccount: false, action: isAccountPeer ? nil : { action in
|
||||||
switch action {
|
switch action {
|
||||||
case .open:
|
case .open:
|
||||||
interaction.openPeerInfo(member.peer, true)
|
interaction.openPeerInfo(member.peer, true)
|
||||||
@ -2600,6 +2601,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}, displayDiceTooltip: { _ in
|
}, displayDiceTooltip: { _ in
|
||||||
}, animateDiceSuccess: { _, _ in
|
}, animateDiceSuccess: { _, _ in
|
||||||
}, displayPremiumStickerTooltip: { _, _ in
|
}, displayPremiumStickerTooltip: { _, _ in
|
||||||
|
}, displayEmojiPackTooltip: { _, _ in
|
||||||
}, openPeerContextMenu: { _, _, _, _, _ in
|
}, openPeerContextMenu: { _, _, _, _, _ in
|
||||||
}, openMessageReplies: { _, _, _ in
|
}, openMessageReplies: { _, _, _ in
|
||||||
}, openReplyThreadOriginalMessage: { _ in
|
}, openReplyThreadOriginalMessage: { _ in
|
||||||
@ -3445,6 +3447,40 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
strongSelf.controller?.presentInGlobalOverlay(contextController)
|
strongSelf.controller?.presentInGlobalOverlay(contextController)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.headerNode.displayEmojiPackTooltip = { [weak self] in
|
||||||
|
guard let strongSelf = self, let threadData = strongSelf.data?.threadData else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let icon = threadData.info.icon, icon != 0 {
|
||||||
|
let _ = (strongSelf.context.engine.stickers.resolveInlineStickers(fileIds: [icon])
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] files in
|
||||||
|
if let file = files.first?.value {
|
||||||
|
var stickerPackReference: StickerPackReference?
|
||||||
|
for attribute in file.attributes {
|
||||||
|
if case let .CustomEmoji(_, _, packReference) = attribute {
|
||||||
|
stickerPackReference = packReference
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let stickerPackReference = stickerPackReference {
|
||||||
|
let _ = (strongSelf.context.engine.stickers.loadedStickerPack(reference: stickerPackReference, forceActualized: false)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] stickerPack in
|
||||||
|
if let strongSelf = self, case let .result(info, _, _) = stickerPack {
|
||||||
|
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: file, title: nil, text: strongSelf.presentationData.strings.Stickers_EmojiPackInfoText(info.title).string, undoText: strongSelf.presentationData.strings.Stickers_PremiumPackView, customAction: nil), elevatedLayout: false, action: { [weak self] action in
|
||||||
|
if let strongSelf = self, action == .undo {
|
||||||
|
strongSelf.presentEmojiList(packReference: stickerPackReference)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}), in: .current)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(peerId.namespace) {
|
if [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(peerId.namespace) {
|
||||||
self.displayAsPeersPromise.set(context.engine.calls.cachedGroupCallDisplayAsAvailablePeers(peerId: peerId))
|
self.displayAsPeersPromise.set(context.engine.calls.cachedGroupCallDisplayAsAvailablePeers(peerId: peerId))
|
||||||
}
|
}
|
||||||
@ -4987,7 +5023,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func openAutoremove(currentValue: Int32?) {
|
private func openAutoremove(currentValue: Int32?) {
|
||||||
let controller = ChatTimerScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: self.peerId, style: .default, mode: .autoremove, currentTime: currentValue, dismissByTapOutside: true, completion: { [weak self] value in
|
let controller = ChatTimerScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, style: .default, mode: .autoremove, currentTime: currentValue, dismissByTapOutside: true, completion: { [weak self] value in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -5016,7 +5052,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func openCustomMute() {
|
private func openCustomMute() {
|
||||||
let controller = ChatTimerScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: self.peerId, style: .default, mode: .mute, currentTime: nil, dismissByTapOutside: true, completion: { [weak self] value in
|
let controller = ChatTimerScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, style: .default, mode: .mute, currentTime: nil, dismissByTapOutside: true, completion: { [weak self] value in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -7862,6 +7898,38 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func presentEmojiList(packReference: StickerPackReference) {
|
||||||
|
guard let peerController = self.controller else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let presentationData = self.presentationData
|
||||||
|
let navigationController = peerController.navigationController as? NavigationController
|
||||||
|
let controller = StickerPackScreen(context: self.context, updatedPresentationData: peerController.updatedPresentationData, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: navigationController, sendEmoji: nil, actionPerformed: { [weak self] actions in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let context = strongSelf.context
|
||||||
|
if let (info, items, action) = actions.first {
|
||||||
|
let isEmoji = info.id.namespace == Namespaces.ItemCollection.CloudEmojiPacks
|
||||||
|
|
||||||
|
switch action {
|
||||||
|
case .add:
|
||||||
|
strongSelf.controller?.presentInGlobalOverlay(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: isEmoji ? presentationData.strings.EmojiPackActionInfo_AddedTitle : presentationData.strings.StickerPackActionInfo_AddedTitle, text: isEmoji ? presentationData.strings.EmojiPackActionInfo_AddedText(info.title).string : presentationData.strings.StickerPackActionInfo_AddedText(info.title).string, undo: false, info: info, topItem: items.first, context: context), elevatedLayout: false, animateInAsReplacement: false, action: { _ in
|
||||||
|
return true
|
||||||
|
}))
|
||||||
|
case let .remove(positionInList):
|
||||||
|
strongSelf.controller?.presentInGlobalOverlay(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: isEmoji ? presentationData.strings.EmojiPackActionInfo_RemovedTitle : presentationData.strings.StickerPackActionInfo_RemovedTitle, text: isEmoji ? presentationData.strings.EmojiPackActionInfo_RemovedText(info.title).string : presentationData.strings.StickerPackActionInfo_RemovedText(info.title).string, undo: true, info: info, topItem: items.first, context: context), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
||||||
|
if case .undo = action {
|
||||||
|
let _ = context.engine.stickers.addStickerPackInteractively(info: info, items: items, positionInList: positionInList).start()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
peerController.present(controller, in: .window(.root))
|
||||||
|
}
|
||||||
|
|
||||||
func updatePresentationData(_ presentationData: PresentationData) {
|
func updatePresentationData(_ presentationData: PresentationData) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
|
|
||||||
@ -8231,8 +8299,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var hasQrButton = false
|
||||||
fileprivate func updateNavigation(transition: ContainedViewLayoutTransition, additive: Bool) {
|
fileprivate func updateNavigation(transition: ContainedViewLayoutTransition, additive: Bool) {
|
||||||
let offsetY = self.scrollNode.view.contentOffset.y
|
let offsetY = self.scrollNode.view.contentOffset.y
|
||||||
|
var transition = transition
|
||||||
|
|
||||||
if self.isSettings, !(self.controller?.movingInHierarchy == true) {
|
if self.isSettings, !(self.controller?.movingInHierarchy == true) {
|
||||||
let bottomOffsetY = max(0.0, self.scrollNode.view.contentSize.height + min(83.0, self.scrollNode.view.contentInset.bottom) - offsetY - self.scrollNode.frame.height)
|
let bottomOffsetY = max(0.0, self.scrollNode.view.contentSize.height + min(83.0, self.scrollNode.view.contentInset.bottom) - offsetY - self.scrollNode.frame.height)
|
||||||
@ -8294,14 +8364,24 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
|
|
||||||
transition.updateFrame(node: self.headerNode.navigationButtonContainer, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left, y: layout.statusBarHeight ?? 0.0), size: CGSize(width: layout.size.width - layout.safeInsets.left * 2.0, height: navigationBarHeight)))
|
transition.updateFrame(node: self.headerNode.navigationButtonContainer, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left, y: layout.statusBarHeight ?? 0.0), size: CGSize(width: layout.size.width - layout.safeInsets.left * 2.0, height: navigationBarHeight)))
|
||||||
self.headerNode.navigationButtonContainer.isWhite = self.headerNode.isAvatarExpanded
|
self.headerNode.navigationButtonContainer.isWhite = self.headerNode.isAvatarExpanded
|
||||||
|
|
||||||
var leftNavigationButtons: [PeerInfoHeaderNavigationButtonSpec] = []
|
var leftNavigationButtons: [PeerInfoHeaderNavigationButtonSpec] = []
|
||||||
var rightNavigationButtons: [PeerInfoHeaderNavigationButtonSpec] = []
|
var rightNavigationButtons: [PeerInfoHeaderNavigationButtonSpec] = []
|
||||||
if self.state.isEditing {
|
if self.state.isEditing {
|
||||||
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .done, isForExpandedView: false))
|
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .done, isForExpandedView: false))
|
||||||
} else {
|
} else {
|
||||||
if self.isSettings {
|
if self.isSettings {
|
||||||
leftNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .qrCode, isForExpandedView: false))
|
var hasQrButton = false
|
||||||
|
if self.data?.globalSettings?.privacySettings?.phoneDiscoveryEnabled == true {
|
||||||
|
leftNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .qrCode, isForExpandedView: false))
|
||||||
|
hasQrButton = true
|
||||||
|
}
|
||||||
|
if hasQrButton != self.hasQrButton {
|
||||||
|
self.hasQrButton = hasQrButton
|
||||||
|
if !transition.isAnimated {
|
||||||
|
transition = .animated(duration: 0.2, curve: .easeInOut)
|
||||||
|
}
|
||||||
|
}
|
||||||
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false))
|
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false))
|
||||||
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .search, isForExpandedView: true))
|
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .search, isForExpandedView: true))
|
||||||
} else if peerInfoCanEdit(peer: self.data?.peer, chatLocation: self.chatLocation, threadData: self.data?.threadData, cachedData: self.data?.cachedData, isContact: self.data?.isContact) {
|
} else if peerInfoCanEdit(peer: self.data?.peer, chatLocation: self.chatLocation, threadData: self.data?.threadData, cachedData: self.data?.cachedData, isContact: self.data?.isContact) {
|
||||||
|
@ -1333,6 +1333,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}, displayDiceTooltip: { _ in
|
}, displayDiceTooltip: { _ in
|
||||||
}, animateDiceSuccess: { _, _ in
|
}, animateDiceSuccess: { _, _ in
|
||||||
}, displayPremiumStickerTooltip: { _, _ in
|
}, displayPremiumStickerTooltip: { _, _ in
|
||||||
|
}, displayEmojiPackTooltip: { _, _ in
|
||||||
}, openPeerContextMenu: { _, _, _, _, _ in
|
}, openPeerContextMenu: { _, _, _, _, _ in
|
||||||
}, openMessageReplies: { _, _, _ in
|
}, openMessageReplies: { _, _, _ in
|
||||||
}, openReplyThreadOriginalMessage: { _ in
|
}, openReplyThreadOriginalMessage: { _ in
|
||||||
|
@ -371,7 +371,7 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode {
|
|||||||
let emoticons = keywords.flatMap { $0.emoticons }
|
let emoticons = keywords.flatMap { $0.emoticons }
|
||||||
for emoji in emoticons {
|
for emoji in emoticons {
|
||||||
signals.append(context.engine.stickers.searchStickers(query: emoji.basicEmoji.0)
|
signals.append(context.engine.stickers.searchStickers(query: emoji.basicEmoji.0)
|
||||||
|> take(1)
|
// |> take(1)
|
||||||
|> map { (emoji, $0) })
|
|> map { (emoji, $0) })
|
||||||
}
|
}
|
||||||
return signals
|
return signals
|
||||||
|
@ -464,7 +464,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
func webView(_ webView: WKWebView, requestMediaCapturePermissionFor origin: WKSecurityOrigin, initiatedByFrame frame: WKFrameInfo, type: WKMediaCaptureType, decisionHandler: @escaping (WKPermissionDecision) -> Void) {
|
func webView(_ webView: WKWebView, requestMediaCapturePermissionFor origin: WKSecurityOrigin, initiatedByFrame frame: WKFrameInfo, type: WKMediaCaptureType, decisionHandler: @escaping (WKPermissionDecision) -> Void) {
|
||||||
decisionHandler(.prompt)
|
decisionHandler(.prompt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
|
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
|
||||||
let alertController = textAlertController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, title: nil, text: message, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {
|
let alertController = textAlertController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, title: nil, text: message, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {
|
||||||
completionHandler()
|
completionHandler()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user