mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Peer info bug fixes
This commit is contained in:
parent
9fba3f208a
commit
14e007b236
@ -5330,6 +5330,7 @@ Any member of this group will be able to see messages in the channel.";
|
|||||||
"PeerInfo.ButtonUnmute" = "Unmute";
|
"PeerInfo.ButtonUnmute" = "Unmute";
|
||||||
"PeerInfo.ButtonMore" = "More";
|
"PeerInfo.ButtonMore" = "More";
|
||||||
"PeerInfo.ButtonAddMember" = "Add Members";
|
"PeerInfo.ButtonAddMember" = "Add Members";
|
||||||
|
"PeerInfo.ButtonSearch" = "Search";
|
||||||
|
|
||||||
"PeerInfo.PaneMedia" = "Media";
|
"PeerInfo.PaneMedia" = "Media";
|
||||||
"PeerInfo.PaneFiles" = "Files";
|
"PeerInfo.PaneFiles" = "Files";
|
||||||
@ -5340,3 +5341,5 @@ Any member of this group will be able to see messages in the channel.";
|
|||||||
"PeerInfo.PaneMembers" = "Members";
|
"PeerInfo.PaneMembers" = "Members";
|
||||||
|
|
||||||
"PeerInfo.AddToContacts" = "Add to Contacts";
|
"PeerInfo.AddToContacts" = "Add to Contacts";
|
||||||
|
|
||||||
|
"PeerInfo.BioExpand" = "more";
|
||||||
|
@ -16,6 +16,8 @@ public final class ContextControllerSourceNode: ASDisplayNode {
|
|||||||
|
|
||||||
public func cancelGesture() {
|
public func cancelGesture() {
|
||||||
self.contextGesture?.cancel()
|
self.contextGesture?.cancel()
|
||||||
|
self.contextGesture?.isEnabled = false
|
||||||
|
self.contextGesture?.isEnabled = self.isGestureEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func didLoad() {
|
override public func didLoad() {
|
||||||
|
@ -31,16 +31,26 @@ private func hasHorizontalGestures(_ view: UIView, point: CGPoint?) -> Bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct InteractiveTransitionGestureRecognizerDirections: OptionSet {
|
||||||
|
public var rawValue: Int
|
||||||
|
|
||||||
|
public init(rawValue: Int) {
|
||||||
|
self.rawValue = rawValue
|
||||||
|
}
|
||||||
|
|
||||||
|
public static let left = InteractiveTransitionGestureRecognizerDirections(rawValue: 1 << 0)
|
||||||
|
public static let right = InteractiveTransitionGestureRecognizerDirections(rawValue: 1 << 1)
|
||||||
|
}
|
||||||
|
|
||||||
public class InteractiveTransitionGestureRecognizer: UIPanGestureRecognizer {
|
public class InteractiveTransitionGestureRecognizer: UIPanGestureRecognizer {
|
||||||
private let enableBothDirections: Bool
|
private let allowedDirections: () -> InteractiveTransitionGestureRecognizerDirections
|
||||||
private let canBegin: () -> Bool
|
|
||||||
|
|
||||||
var validatedGesture = false
|
private var validatedGesture = false
|
||||||
var firstLocation: CGPoint = CGPoint()
|
private var firstLocation: CGPoint = CGPoint()
|
||||||
|
private var currentAllowedDirections: InteractiveTransitionGestureRecognizerDirections = []
|
||||||
|
|
||||||
public init(target: Any?, action: Selector?, enableBothDirections: Bool = false, canBegin: @escaping () -> Bool) {
|
public init(target: Any?, action: Selector?, allowedDirections: @escaping () -> InteractiveTransitionGestureRecognizerDirections) {
|
||||||
self.enableBothDirections = enableBothDirections
|
self.allowedDirections = allowedDirections
|
||||||
self.canBegin = canBegin
|
|
||||||
|
|
||||||
super.init(target: target, action: action)
|
super.init(target: target, action: action)
|
||||||
|
|
||||||
@ -50,11 +60,13 @@ public class InteractiveTransitionGestureRecognizer: UIPanGestureRecognizer {
|
|||||||
override public func reset() {
|
override public func reset() {
|
||||||
super.reset()
|
super.reset()
|
||||||
|
|
||||||
validatedGesture = false
|
self.validatedGesture = false
|
||||||
|
self.currentAllowedDirections = []
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
|
override public func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||||
if !self.canBegin() {
|
self.currentAllowedDirections = self.allowedDirections()
|
||||||
|
if self.currentAllowedDirections.isEmpty {
|
||||||
self.state = .failed
|
self.state = .failed
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -79,14 +91,16 @@ public class InteractiveTransitionGestureRecognizer: UIPanGestureRecognizer {
|
|||||||
let absTranslationY: CGFloat = abs(translation.y)
|
let absTranslationY: CGFloat = abs(translation.y)
|
||||||
|
|
||||||
if !self.validatedGesture {
|
if !self.validatedGesture {
|
||||||
if !self.enableBothDirections && self.firstLocation.x < 16.0 {
|
if self.currentAllowedDirections.contains(.left) && self.firstLocation.x < 16.0 {
|
||||||
validatedGesture = true
|
self.validatedGesture = true
|
||||||
} else if !self.enableBothDirections && translation.x < 0.0 {
|
} else if !self.currentAllowedDirections.contains(.left) && translation.x < 0.0 {
|
||||||
|
self.state = .failed
|
||||||
|
} else if !self.currentAllowedDirections.contains(.right) && translation.x > 0.0 {
|
||||||
self.state = .failed
|
self.state = .failed
|
||||||
} else if absTranslationY > 2.0 && absTranslationY > absTranslationX * 2.0 {
|
} else if absTranslationY > 2.0 && absTranslationY > absTranslationX * 2.0 {
|
||||||
self.state = .failed
|
self.state = .failed
|
||||||
} else if absTranslationX > 2.0 && absTranslationY * 2.0 < absTranslationX {
|
} else if absTranslationX > 2.0 && absTranslationY * 2.0 < absTranslationX {
|
||||||
validatedGesture = true
|
self.validatedGesture = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,11 +111,11 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||||||
override func didLoad() {
|
override func didLoad() {
|
||||||
super.didLoad()
|
super.didLoad()
|
||||||
|
|
||||||
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), canBegin: { [weak self] in
|
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self, strongSelf.controllers.count > 1 else {
|
||||||
return false
|
return []
|
||||||
}
|
}
|
||||||
return strongSelf.controllers.count > 1
|
return .right
|
||||||
})
|
})
|
||||||
panRecognizer.delegate = self
|
panRecognizer.delegate = self
|
||||||
panRecognizer.delaysTouchesBegan = false
|
panRecognizer.delaysTouchesBegan = false
|
||||||
|
@ -90,11 +90,11 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes
|
|||||||
self.scrollNode.view.clipsToBounds = false
|
self.scrollNode.view.clipsToBounds = false
|
||||||
self.scrollNode.view.delegate = self
|
self.scrollNode.view.delegate = self
|
||||||
|
|
||||||
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), canBegin: { [weak self] in
|
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self, !strongSelf.isDismissed else {
|
||||||
return false
|
return []
|
||||||
}
|
}
|
||||||
return !strongSelf.isDismissed
|
return .right
|
||||||
})
|
})
|
||||||
self.panRecognizer = panRecognizer
|
self.panRecognizer = panRecognizer
|
||||||
if let layout = self.validLayout {
|
if let layout = self.validLayout {
|
||||||
|
@ -227,6 +227,8 @@ static NSString *makeRandomPadding() {
|
|||||||
+ (MTSignal *)fetchConfigFromAddress:(MTBackupDatacenterAddress *)address currentContext:(MTContext *)currentContext {
|
+ (MTSignal *)fetchConfigFromAddress:(MTBackupDatacenterAddress *)address currentContext:(MTContext *)currentContext {
|
||||||
MTApiEnvironment *apiEnvironment = [currentContext.apiEnvironment copy];
|
MTApiEnvironment *apiEnvironment = [currentContext.apiEnvironment copy];
|
||||||
|
|
||||||
|
apiEnvironment = [apiEnvironment withUpdatedSocksProxySettings:nil];
|
||||||
|
|
||||||
NSMutableDictionary *datacenterAddressOverrides = [[NSMutableDictionary alloc] init];
|
NSMutableDictionary *datacenterAddressOverrides = [[NSMutableDictionary alloc] init];
|
||||||
|
|
||||||
datacenterAddressOverrides[@(address.datacenterId)] = [[MTDatacenterAddress alloc] initWithIp:address.ip port:(uint16_t)address.port preferForMedia:false restrictToTcp:false cdn:false preferForProxy:false secret:address.secret];
|
datacenterAddressOverrides[@(address.datacenterId)] = [[MTDatacenterAddress alloc] initWithIp:address.ip port:(uint16_t)address.port preferForMedia:false restrictToTcp:false cdn:false preferForProxy:false secret:address.secret];
|
||||||
|
@ -8,7 +8,7 @@ import Postbox
|
|||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import AccountContext
|
import AccountContext
|
||||||
|
|
||||||
final class SecretChatKeyController: ViewController {
|
public final class SecretChatKeyController: ViewController {
|
||||||
private var controllerNode: SecretChatKeyControllerNode {
|
private var controllerNode: SecretChatKeyControllerNode {
|
||||||
return self.displayNode as! SecretChatKeyControllerNode
|
return self.displayNode as! SecretChatKeyControllerNode
|
||||||
}
|
}
|
||||||
@ -19,7 +19,7 @@ final class SecretChatKeyController: ViewController {
|
|||||||
|
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
|
|
||||||
init(context: AccountContext, fingerprint: SecretChatKeyFingerprint, peer: Peer) {
|
public init(context: AccountContext, fingerprint: SecretChatKeyFingerprint, peer: Peer) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.fingerprint = fingerprint
|
self.fingerprint = fingerprint
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
@ -34,11 +34,11 @@ final class SecretChatKeyController: ViewController {
|
|||||||
self.title = self.presentationData.strings.EncryptionKey_Title
|
self.title = self.presentationData.strings.EncryptionKey_Title
|
||||||
}
|
}
|
||||||
|
|
||||||
required init(coder aDecoder: NSCoder) {
|
required public init(coder aDecoder: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override func loadDisplayNode() {
|
override public func loadDisplayNode() {
|
||||||
self.displayNode = SecretChatKeyControllerNode(context: self.context, presentationData: self.presentationData, fingerprint: self.fingerprint, peer: self.peer, getNavigationController: { [weak self] in
|
self.displayNode = SecretChatKeyControllerNode(context: self.context, presentationData: self.presentationData, fingerprint: self.fingerprint, peer: self.peer, getNavigationController: { [weak self] in
|
||||||
return self?.navigationController as? NavigationController
|
return self?.navigationController as? NavigationController
|
||||||
})
|
})
|
||||||
|
File diff suppressed because it is too large
Load Diff
12
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonSearch.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonSearch.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "ic_search.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonSearch.imageset/ic_search.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Peer Info/ButtonSearch.imageset/ic_search.pdf
vendored
Normal file
Binary file not shown.
@ -305,7 +305,7 @@ public final class ChatHistoryGridNode: GridNode, ChatHistoryNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let associatedData = ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: false)
|
let associatedData = ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: false)
|
||||||
let processedView = ChatHistoryView(originalView: view, filteredEntries: chatHistoryEntriesForView(location: .peer(peerId), view: view, includeUnreadEntry: false, includeEmptyEntry: false, includeChatInfoEntry: false, includeSearchEntry: false, reverse: false, groupMessages: false, selectedMessages: nil, presentationData: chatPresentationData, historyAppearsCleared: false, associatedData: associatedData, updatingMedia: [:]), associatedData: associatedData, id: id)
|
let processedView = ChatHistoryView(originalView: view, filteredEntries: chatHistoryEntriesForView(location: .peer(peerId), view: view, includeUnreadEntry: false, includeEmptyEntry: false, includeChatInfoEntry: false, includeSearchEntry: false, reverse: false, groupMessages: false, selectedMessages: nil, presentationData: chatPresentationData, historyAppearsCleared: false, associatedData: associatedData, updatingMedia: [:]), associatedData: associatedData, lastHeaderId: 0, id: id)
|
||||||
let previous = previousView.swap(processedView)
|
let previous = previousView.swap(processedView)
|
||||||
|
|
||||||
let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: false, chatLocation: .peer(peerId), controllerInteraction: controllerInteraction, scrollPosition: scrollPosition, initialData: nil, keyboardButtonsMessage: nil, cachedData: nil, cachedDataMessages: nil, readStateData: nil, flashIndicators: flashIndicators, updatedMessageSelection: false)
|
let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: false, chatLocation: .peer(peerId), controllerInteraction: controllerInteraction, scrollPosition: scrollPosition, initialData: nil, keyboardButtonsMessage: nil, cachedData: nil, cachedDataMessages: nil, readStateData: nil, flashIndicators: flashIndicators, updatedMessageSelection: false)
|
||||||
|
@ -66,26 +66,15 @@ private class ChatHistoryListSelectionRecognizer: UIPanGestureRecognizer {
|
|||||||
|
|
||||||
private let historyMessageCount: Int = 90
|
private let historyMessageCount: Int = 90
|
||||||
|
|
||||||
|
public enum ChatHistoryListDisplayHeaders {
|
||||||
|
case none
|
||||||
|
case all
|
||||||
|
case allButLast
|
||||||
|
}
|
||||||
|
|
||||||
public enum ChatHistoryListMode: Equatable {
|
public enum ChatHistoryListMode: Equatable {
|
||||||
case bubbles
|
case bubbles
|
||||||
case list(search: Bool, reversed: Bool)
|
case list(search: Bool, reversed: Bool, displayHeaders: ChatHistoryListDisplayHeaders)
|
||||||
|
|
||||||
public static func ==(lhs: ChatHistoryListMode, rhs: ChatHistoryListMode) -> Bool {
|
|
||||||
switch lhs {
|
|
||||||
case .bubbles:
|
|
||||||
if case .bubbles = rhs {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
case let .list(search, reversed):
|
|
||||||
if case .list(search, reversed) = rhs {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ChatHistoryViewScrollPosition {
|
enum ChatHistoryViewScrollPosition {
|
||||||
@ -122,6 +111,7 @@ struct ChatHistoryView {
|
|||||||
let originalView: MessageHistoryView
|
let originalView: MessageHistoryView
|
||||||
let filteredEntries: [ChatHistoryEntry]
|
let filteredEntries: [ChatHistoryEntry]
|
||||||
let associatedData: ChatMessageItemAssociatedData
|
let associatedData: ChatMessageItemAssociatedData
|
||||||
|
let lastHeaderId: Int64
|
||||||
let id: Int32
|
let id: Int32
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +219,7 @@ private func maxMessageIndexForEntries(_ view: ChatHistoryView, indexRange: (Int
|
|||||||
return (incoming, overall)
|
return (incoming, overall)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLocation, associatedData: ChatMessageItemAssociatedData, controllerInteraction: ChatControllerInteraction, mode: ChatHistoryListMode, entries: [ChatHistoryViewTransitionInsertEntry]) -> [ListViewInsertItem] {
|
private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLocation, associatedData: ChatMessageItemAssociatedData, controllerInteraction: ChatControllerInteraction, mode: ChatHistoryListMode, lastHeaderId: Int64, entries: [ChatHistoryViewTransitionInsertEntry]) -> [ListViewInsertItem] {
|
||||||
return entries.map { entry -> ListViewInsertItem in
|
return entries.map { entry -> ListViewInsertItem in
|
||||||
switch entry.entry {
|
switch entry.entry {
|
||||||
case let .MessageEntry(message, presentationData, read, _, selection, attributes):
|
case let .MessageEntry(message, presentationData, read, _, selection, attributes):
|
||||||
@ -237,8 +227,18 @@ private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLoca
|
|||||||
switch mode {
|
switch mode {
|
||||||
case .bubbles:
|
case .bubbles:
|
||||||
item = ChatMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, content: .message(message: message, read: read, selection: selection, attributes: attributes))
|
item = ChatMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, content: .message(message: message, read: read, selection: selection, attributes: attributes))
|
||||||
case let .list(search, _):
|
case let .list(_, _, displayHeaders):
|
||||||
item = ListMessageItem(theme: presentationData.theme.theme, strings: presentationData.strings, fontSize: presentationData.fontSize, dateTimeFormat: presentationData.dateTimeFormat, context: context, chatLocation: chatLocation, controllerInteraction: controllerInteraction, message: message, selection: selection, displayHeader: search)
|
let displayHeader: Bool
|
||||||
|
switch displayHeaders {
|
||||||
|
case .none:
|
||||||
|
displayHeader = false
|
||||||
|
case .all:
|
||||||
|
displayHeader = true
|
||||||
|
case .allButLast:
|
||||||
|
displayHeader = listMessageDateHeaderId(timestamp: message.timestamp) != lastHeaderId
|
||||||
|
}
|
||||||
|
|
||||||
|
item = ListMessageItem(theme: presentationData.theme.theme, strings: presentationData.strings, fontSize: presentationData.fontSize, dateTimeFormat: presentationData.dateTimeFormat, context: context, chatLocation: chatLocation, controllerInteraction: controllerInteraction, message: message, selection: selection, displayHeader: displayHeader)
|
||||||
}
|
}
|
||||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint)
|
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint)
|
||||||
case let .MessageGroupEntry(_, messages, presentationData):
|
case let .MessageGroupEntry(_, messages, presentationData):
|
||||||
@ -246,9 +246,9 @@ private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLoca
|
|||||||
switch mode {
|
switch mode {
|
||||||
case .bubbles:
|
case .bubbles:
|
||||||
item = ChatMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, content: .group(messages: messages))
|
item = ChatMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, content: .group(messages: messages))
|
||||||
case let .list(search, _):
|
case let .list(_, _, _):
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
item = ListMessageItem(theme: presentationData.theme.theme, strings: presentationData.strings, fontSize: presentationData.fontSize, dateTimeFormat: presentationData.dateTimeFormat, context: context, chatLocation: chatLocation, controllerInteraction: controllerInteraction, message: messages[0].0, selection: .none, displayHeader: search)
|
item = ListMessageItem(theme: presentationData.theme.theme, strings: presentationData.strings, fontSize: presentationData.fontSize, dateTimeFormat: presentationData.dateTimeFormat, context: context, chatLocation: chatLocation, controllerInteraction: controllerInteraction, message: messages[0].0, selection: .none, displayHeader: false)
|
||||||
}
|
}
|
||||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint)
|
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint)
|
||||||
case let .UnreadEntry(_, presentationData):
|
case let .UnreadEntry(_, presentationData):
|
||||||
@ -263,7 +263,7 @@ private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLoca
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLocation, associatedData: ChatMessageItemAssociatedData, controllerInteraction: ChatControllerInteraction, mode: ChatHistoryListMode, entries: [ChatHistoryViewTransitionUpdateEntry]) -> [ListViewUpdateItem] {
|
private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLocation, associatedData: ChatMessageItemAssociatedData, controllerInteraction: ChatControllerInteraction, mode: ChatHistoryListMode, lastHeaderId: Int64, entries: [ChatHistoryViewTransitionUpdateEntry]) -> [ListViewUpdateItem] {
|
||||||
return entries.map { entry -> ListViewUpdateItem in
|
return entries.map { entry -> ListViewUpdateItem in
|
||||||
switch entry.entry {
|
switch entry.entry {
|
||||||
case let .MessageEntry(message, presentationData, read, _, selection, attributes):
|
case let .MessageEntry(message, presentationData, read, _, selection, attributes):
|
||||||
@ -271,8 +271,17 @@ private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLoca
|
|||||||
switch mode {
|
switch mode {
|
||||||
case .bubbles:
|
case .bubbles:
|
||||||
item = ChatMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, content: .message(message: message, read: read, selection: selection, attributes: attributes))
|
item = ChatMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, content: .message(message: message, read: read, selection: selection, attributes: attributes))
|
||||||
case let .list(search, _):
|
case let .list(_, _, displayHeaders):
|
||||||
item = ListMessageItem(theme: presentationData.theme.theme, strings: presentationData.strings, fontSize: presentationData.fontSize, dateTimeFormat: presentationData.dateTimeFormat, context: context, chatLocation: chatLocation, controllerInteraction: controllerInteraction, message: message, selection: selection, displayHeader: search)
|
let displayHeader: Bool
|
||||||
|
switch displayHeaders {
|
||||||
|
case .none:
|
||||||
|
displayHeader = false
|
||||||
|
case .all:
|
||||||
|
displayHeader = true
|
||||||
|
case .allButLast:
|
||||||
|
displayHeader = listMessageDateHeaderId(timestamp: message.timestamp) != lastHeaderId
|
||||||
|
}
|
||||||
|
item = ListMessageItem(theme: presentationData.theme.theme, strings: presentationData.strings, fontSize: presentationData.fontSize, dateTimeFormat: presentationData.dateTimeFormat, context: context, chatLocation: chatLocation, controllerInteraction: controllerInteraction, message: message, selection: selection, displayHeader: displayHeader)
|
||||||
}
|
}
|
||||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint)
|
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint)
|
||||||
case let .MessageGroupEntry(_, messages, presentationData):
|
case let .MessageGroupEntry(_, messages, presentationData):
|
||||||
@ -280,9 +289,9 @@ private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLoca
|
|||||||
switch mode {
|
switch mode {
|
||||||
case .bubbles:
|
case .bubbles:
|
||||||
item = ChatMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, content: .group(messages: messages))
|
item = ChatMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, content: .group(messages: messages))
|
||||||
case let .list(search, _):
|
case let .list(_, _, _):
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
item = ListMessageItem(theme: presentationData.theme.theme, strings: presentationData.strings, fontSize: presentationData.fontSize, dateTimeFormat: presentationData.dateTimeFormat, context: context, chatLocation: chatLocation, controllerInteraction: controllerInteraction, message: messages[0].0, selection: .none, displayHeader: search)
|
item = ListMessageItem(theme: presentationData.theme.theme, strings: presentationData.strings, fontSize: presentationData.fontSize, dateTimeFormat: presentationData.dateTimeFormat, context: context, chatLocation: chatLocation, controllerInteraction: controllerInteraction, message: messages[0].0, selection: .none, displayHeader: false)
|
||||||
}
|
}
|
||||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint)
|
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint)
|
||||||
case let .UnreadEntry(_, presentationData):
|
case let .UnreadEntry(_, presentationData):
|
||||||
@ -297,8 +306,8 @@ private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLoca
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func mappedChatHistoryViewListTransition(context: AccountContext, chatLocation: ChatLocation, associatedData: ChatMessageItemAssociatedData, controllerInteraction: ChatControllerInteraction, mode: ChatHistoryListMode, transition: ChatHistoryViewTransition) -> ChatHistoryListViewTransition {
|
private func mappedChatHistoryViewListTransition(context: AccountContext, chatLocation: ChatLocation, associatedData: ChatMessageItemAssociatedData, controllerInteraction: ChatControllerInteraction, mode: ChatHistoryListMode, lastHeaderId: Int64, transition: ChatHistoryViewTransition) -> ChatHistoryListViewTransition {
|
||||||
return ChatHistoryListViewTransition(historyView: transition.historyView, deleteItems: transition.deleteItems, insertItems: mappedInsertEntries(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, entries: transition.insertEntries), updateItems: mappedUpdateEntries(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, entries: transition.updateEntries), options: transition.options, scrollToItem: transition.scrollToItem, stationaryItemRange: transition.stationaryItemRange, initialData: transition.initialData, keyboardButtonsMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData, cachedDataMessages: transition.cachedDataMessages, readStateData: transition.readStateData, scrolledToIndex: transition.scrolledToIndex, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, animateIn: transition.animateIn, reason: transition.reason, flashIndicators: transition.flashIndicators)
|
return ChatHistoryListViewTransition(historyView: transition.historyView, deleteItems: transition.deleteItems, insertItems: mappedInsertEntries(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, entries: transition.insertEntries), updateItems: mappedUpdateEntries(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, entries: transition.updateEntries), options: transition.options, scrollToItem: transition.scrollToItem, stationaryItemRange: transition.stationaryItemRange, initialData: transition.initialData, keyboardButtonsMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData, cachedDataMessages: transition.cachedDataMessages, readStateData: transition.readStateData, scrolledToIndex: transition.scrolledToIndex, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, animateIn: transition.animateIn, reason: transition.reason, flashIndicators: transition.flashIndicators)
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class ChatHistoryTransactionOpaqueState {
|
private final class ChatHistoryTransactionOpaqueState {
|
||||||
@ -731,7 +740,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
|
|
||||||
var reverse = false
|
var reverse = false
|
||||||
var includeSearchEntry = false
|
var includeSearchEntry = false
|
||||||
if case let .list(search, reverseValue) = mode {
|
if case let .list(search, reverseValue, _) = mode {
|
||||||
includeSearchEntry = search
|
includeSearchEntry = search
|
||||||
reverse = reverseValue
|
reverse = reverseValue
|
||||||
}
|
}
|
||||||
@ -743,7 +752,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
|
|
||||||
let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, animatedEmojiStickers: animatedEmojiStickers, isScheduledMessages: isScheduledMessages)
|
let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, animatedEmojiStickers: animatedEmojiStickers, isScheduledMessages: isScheduledMessages)
|
||||||
|
|
||||||
let processedView = ChatHistoryView(originalView: view, filteredEntries: chatHistoryEntriesForView(location: chatLocation, view: view, includeUnreadEntry: mode == .bubbles, includeEmptyEntry: mode == .bubbles && tagMask == nil, includeChatInfoEntry: mode == .bubbles, includeSearchEntry: includeSearchEntry && tagMask != nil, reverse: reverse, groupMessages: mode == .bubbles, selectedMessages: selectedMessages, presentationData: chatPresentationData, historyAppearsCleared: historyAppearsCleared, associatedData: associatedData, updatingMedia: updatingMedia), associatedData: associatedData, id: id)
|
let filteredEntries = chatHistoryEntriesForView(location: chatLocation, view: view, includeUnreadEntry: mode == .bubbles, includeEmptyEntry: mode == .bubbles && tagMask == nil, includeChatInfoEntry: mode == .bubbles, includeSearchEntry: includeSearchEntry && tagMask != nil, reverse: reverse, groupMessages: mode == .bubbles, selectedMessages: selectedMessages, presentationData: chatPresentationData, historyAppearsCleared: historyAppearsCleared, associatedData: associatedData, updatingMedia: updatingMedia)
|
||||||
|
let lastHeaderId = filteredEntries.last.flatMap { listMessageDateHeaderId(timestamp: $0.index.timestamp) } ?? 0
|
||||||
|
let processedView = ChatHistoryView(originalView: view, filteredEntries: filteredEntries, associatedData: associatedData, lastHeaderId: lastHeaderId, id: id)
|
||||||
let previousValueAndVersion = previousView.swap((processedView, update.1, selectedMessages))
|
let previousValueAndVersion = previousView.swap((processedView, update.1, selectedMessages))
|
||||||
let previous = previousValueAndVersion?.0
|
let previous = previousValueAndVersion?.0
|
||||||
let previousSelectedMessages = previousValueAndVersion?.2
|
let previousSelectedMessages = previousValueAndVersion?.2
|
||||||
@ -794,7 +805,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: reverse, chatLocation: chatLocation, controllerInteraction: controllerInteraction, scrollPosition: updatedScrollPosition, initialData: initialData?.initialData, keyboardButtonsMessage: view.topTaggedMessages.first, cachedData: initialData?.cachedData, cachedDataMessages: initialData?.cachedDataMessages, readStateData: initialData?.readStateData, flashIndicators: flashIndicators, updatedMessageSelection: previousSelectedMessages != selectedMessages)
|
let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: reverse, chatLocation: chatLocation, controllerInteraction: controllerInteraction, scrollPosition: updatedScrollPosition, initialData: initialData?.initialData, keyboardButtonsMessage: view.topTaggedMessages.first, cachedData: initialData?.cachedData, cachedDataMessages: initialData?.cachedDataMessages, readStateData: initialData?.readStateData, flashIndicators: flashIndicators, updatedMessageSelection: previousSelectedMessages != selectedMessages)
|
||||||
let mappedTransition = mappedChatHistoryViewListTransition(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, transition: rawTransition)
|
let mappedTransition = mappedChatHistoryViewListTransition(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, transition: rawTransition)
|
||||||
Queue.mainQueue().async {
|
Queue.mainQueue().async {
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -1670,8 +1681,17 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
switch self.mode {
|
switch self.mode {
|
||||||
case .bubbles:
|
case .bubbles:
|
||||||
item = ChatMessageItem(presentationData: presentationData, context: self.context, chatLocation: self.chatLocation, associatedData: associatedData, controllerInteraction: self.controllerInteraction, content: .message(message: message, read: read, selection: selection, attributes: attributes))
|
item = ChatMessageItem(presentationData: presentationData, context: self.context, chatLocation: self.chatLocation, associatedData: associatedData, controllerInteraction: self.controllerInteraction, content: .message(message: message, read: read, selection: selection, attributes: attributes))
|
||||||
case let .list(search, _):
|
case let .list(_, _, displayHeaders):
|
||||||
item = ListMessageItem(theme: presentationData.theme.theme, strings: presentationData.strings, fontSize: presentationData.fontSize, dateTimeFormat: presentationData.dateTimeFormat, context: self.context, chatLocation: self.chatLocation, controllerInteraction: self.controllerInteraction, message: message, selection: selection, displayHeader: search)
|
let displayHeader: Bool
|
||||||
|
switch displayHeaders {
|
||||||
|
case .none:
|
||||||
|
displayHeader = false
|
||||||
|
case .all:
|
||||||
|
displayHeader = true
|
||||||
|
case .allButLast:
|
||||||
|
displayHeader = listMessageDateHeaderId(timestamp: message.timestamp) != historyView.lastHeaderId
|
||||||
|
}
|
||||||
|
item = ListMessageItem(theme: presentationData.theme.theme, strings: presentationData.strings, fontSize: presentationData.fontSize, dateTimeFormat: presentationData.dateTimeFormat, context: self.context, chatLocation: self.chatLocation, controllerInteraction: self.controllerInteraction, message: message, selection: selection, displayHeader: displayHeader)
|
||||||
}
|
}
|
||||||
let updateItem = ListViewUpdateItem(index: index, previousIndex: index, item: item, directionHint: nil)
|
let updateItem = ListViewUpdateItem(index: index, previousIndex: index, item: item, directionHint: nil)
|
||||||
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [updateItem], options: [.AnimateInsertion], scrollToItem: nil, additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [updateItem], options: [.AnimateInsertion], scrollToItem: nil, additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||||
|
@ -73,7 +73,7 @@ func rightNavigationButtonForChatInterfaceState(_ presentationInterfaceState: Ch
|
|||||||
}
|
}
|
||||||
|
|
||||||
if presentationInterfaceState.isScheduledMessages {
|
if presentationInterfaceState.isScheduledMessages {
|
||||||
return nil
|
return chatInfoNavigationButton
|
||||||
}
|
}
|
||||||
|
|
||||||
if case .standard(true) = presentationInterfaceState.mode {
|
if case .standard(true) = presentationInterfaceState.mode {
|
||||||
|
@ -2311,7 +2311,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, avatarNode.frame)
|
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, avatarNode.frame)
|
||||||
} else {
|
} else {
|
||||||
if item.message.id.peerId == item.context.account.peerId, let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
|
if item.message.id.peerId == item.context.account.peerId, let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
|
||||||
if case .member = channel.participationStatus {
|
if case let .broadcast(info) = channel.info, info.flags.contains(.hasDiscussionGroup) {
|
||||||
|
} else if case .member = channel.participationStatus {
|
||||||
} else {
|
} else {
|
||||||
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
|
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
|
||||||
return true
|
return true
|
||||||
|
@ -14,6 +14,24 @@ private let timezoneOffset: Int32 = {
|
|||||||
return Int32(timeinfoNow.tm_gmtoff)
|
return Int32(timeinfoNow.tm_gmtoff)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
func listMessageDateHeaderId(timestamp: Int32) -> Int64 {
|
||||||
|
var time: time_t = time_t(timestamp + timezoneOffset)
|
||||||
|
var timeinfo: tm = tm()
|
||||||
|
localtime_r(&time, &timeinfo)
|
||||||
|
|
||||||
|
let roundedTimestamp = timeinfo.tm_year * 100 + timeinfo.tm_mon
|
||||||
|
|
||||||
|
return Int64(roundedTimestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listMessageDateHeaderInfo(timestamp: Int32) -> (year: Int32, month: Int32) {
|
||||||
|
var time: time_t = time_t(timestamp + timezoneOffset)
|
||||||
|
var timeinfo: tm = tm()
|
||||||
|
localtime_r(&time, &timeinfo)
|
||||||
|
|
||||||
|
return (timeinfo.tm_year, timeinfo.tm_mon)
|
||||||
|
}
|
||||||
|
|
||||||
final class ListMessageDateHeader: ListViewItemHeader {
|
final class ListMessageDateHeader: ListViewItemHeader {
|
||||||
private let timestamp: Int32
|
private let timestamp: Int32
|
||||||
private let roundedTimestamp: Int32
|
private let roundedTimestamp: Int32
|
||||||
|
@ -150,7 +150,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
|||||||
tagMask = .voiceOrInstantVideo
|
tagMask = .voiceOrInstantVideo
|
||||||
}
|
}
|
||||||
|
|
||||||
self.historyNode = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), tagMask: tagMask, subject: .message(initialMessageId), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: currentIsReversed))
|
self.historyNode = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), tagMask: tagMask, subject: .message(initialMessageId), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: currentIsReversed, displayHeaders: .none))
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
@ -480,7 +480,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
|||||||
tagMask = .voiceOrInstantVideo
|
tagMask = .voiceOrInstantVideo
|
||||||
}
|
}
|
||||||
|
|
||||||
let historyNode = ChatHistoryListNode(context: self.context, chatLocation: .peer(self.peerId), tagMask: tagMask, subject: .message(messageId), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed))
|
let historyNode = ChatHistoryListNode(context: self.context, chatLocation: .peer(self.peerId), tagMask: tagMask, subject: .message(messageId), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, displayHeaders: .none))
|
||||||
historyNode.preloadPages = true
|
historyNode.preloadPages = true
|
||||||
historyNode.stackFromBottom = true
|
historyNode.stackFromBottom = true
|
||||||
historyNode.updateFloatingHeaderOffset = { [weak self] offset, _ in
|
historyNode.updateFloatingHeaderOffset = { [weak self] offset, _ in
|
||||||
|
@ -0,0 +1,118 @@
|
|||||||
|
import AsyncDisplayKit
|
||||||
|
import Display
|
||||||
|
import TelegramPresentationData
|
||||||
|
import SyncCore
|
||||||
|
import EncryptionKeyVisualization
|
||||||
|
|
||||||
|
final class PeerInfoScreenDisclosureEncryptionKeyItem: PeerInfoScreenItem {
|
||||||
|
let id: AnyHashable
|
||||||
|
let text: String
|
||||||
|
let fingerprint: SecretChatKeyFingerprint
|
||||||
|
let action: (() -> Void)?
|
||||||
|
|
||||||
|
init(id: AnyHashable, text: String, fingerprint: SecretChatKeyFingerprint, action: (() -> Void)?) {
|
||||||
|
self.id = id
|
||||||
|
self.text = text
|
||||||
|
self.fingerprint = fingerprint
|
||||||
|
self.action = action
|
||||||
|
}
|
||||||
|
|
||||||
|
func node() -> PeerInfoScreenItemNode {
|
||||||
|
return PeerInfoScreenDisclosureEncryptionKeyItemNode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class PeerInfoScreenDisclosureEncryptionKeyItemNode: PeerInfoScreenItemNode {
|
||||||
|
private let selectionNode: PeerInfoScreenSelectableBackgroundNode
|
||||||
|
private let textNode: ImmediateTextNode
|
||||||
|
private let keyNode: ASImageNode
|
||||||
|
private let arrowNode: ASImageNode
|
||||||
|
private let bottomSeparatorNode: ASDisplayNode
|
||||||
|
|
||||||
|
private var item: PeerInfoScreenDisclosureEncryptionKeyItem?
|
||||||
|
|
||||||
|
override init() {
|
||||||
|
var bringToFrontForHighlightImpl: (() -> Void)?
|
||||||
|
self.selectionNode = PeerInfoScreenSelectableBackgroundNode(bringToFrontForHighlight: { bringToFrontForHighlightImpl?() })
|
||||||
|
|
||||||
|
self.textNode = ImmediateTextNode()
|
||||||
|
self.textNode.displaysAsynchronously = false
|
||||||
|
self.textNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
self.keyNode = ASImageNode()
|
||||||
|
self.keyNode.displaysAsynchronously = false
|
||||||
|
self.keyNode.displayWithoutProcessing = true
|
||||||
|
self.keyNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
self.arrowNode = ASImageNode()
|
||||||
|
self.arrowNode.isLayerBacked = true
|
||||||
|
self.arrowNode.displaysAsynchronously = false
|
||||||
|
self.arrowNode.displayWithoutProcessing = true
|
||||||
|
self.arrowNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
self.bottomSeparatorNode = ASDisplayNode()
|
||||||
|
self.bottomSeparatorNode.isLayerBacked = true
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
bringToFrontForHighlightImpl = { [weak self] in
|
||||||
|
self?.bringToFrontForHighlight?()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addSubnode(self.bottomSeparatorNode)
|
||||||
|
self.addSubnode(self.selectionNode)
|
||||||
|
self.addSubnode(self.textNode)
|
||||||
|
self.addSubnode(self.keyNode)
|
||||||
|
self.addSubnode(self.arrowNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func update(width: CGFloat, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||||
|
guard let item = item as? PeerInfoScreenDisclosureEncryptionKeyItem else {
|
||||||
|
return 10.0
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.item?.fingerprint != item.fingerprint {
|
||||||
|
self.keyNode.image = secretChatKeyImage(item.fingerprint, size: CGSize(width: 24.0, height: 24.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
self.item = item
|
||||||
|
|
||||||
|
self.selectionNode.pressed = item.action
|
||||||
|
|
||||||
|
let sideInset: CGFloat = 16.0
|
||||||
|
|
||||||
|
self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
|
||||||
|
|
||||||
|
self.textNode.maximumNumberOfLines = 1
|
||||||
|
self.textNode.attributedText = NSAttributedString(string: item.text, font: Font.regular(17.0), textColor: presentationData.theme.list.itemPrimaryTextColor)
|
||||||
|
|
||||||
|
let textSize = self.textNode.updateLayout(CGSize(width: width - sideInset * 2.0 - 44.0, height: .greatestFiniteMagnitude))
|
||||||
|
|
||||||
|
let arrowInset: CGFloat = 18.0
|
||||||
|
|
||||||
|
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: 11.0), size: textSize)
|
||||||
|
|
||||||
|
let height = textSize.height + 22.0
|
||||||
|
|
||||||
|
if let arrowImage = PresentationResourcesItemList.disclosureArrowImage(presentationData.theme) {
|
||||||
|
self.arrowNode.image = arrowImage
|
||||||
|
let arrowFrame = CGRect(origin: CGPoint(x: width - 7.0 - arrowImage.size.width, y: floorToScreenPixels((height - arrowImage.size.height) / 2.0)), size: arrowImage.size)
|
||||||
|
transition.updateFrame(node: self.arrowNode, frame: arrowFrame)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let image = self.keyNode.image {
|
||||||
|
self.keyNode.frame = CGRect(origin: CGPoint(x: width - sideInset - arrowInset - image.size.width, y: floor((height - image.size.height) / 2.0)), size: image.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.textNode, frame: textFrame)
|
||||||
|
|
||||||
|
let highlightNodeOffset: CGFloat = topItem == nil ? 0.0 : UIScreenPixel
|
||||||
|
self.selectionNode.update(size: CGSize(width: width, height: height + highlightNodeOffset), theme: presentationData.theme, transition: transition)
|
||||||
|
transition.updateFrame(node: self.selectionNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -highlightNodeOffset), size: CGSize(width: width, height: height + highlightNodeOffset)))
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: sideInset, y: height - UIScreenPixel), size: CGSize(width: width - sideInset, height: UIScreenPixel)))
|
||||||
|
transition.updateAlpha(node: self.bottomSeparatorNode, alpha: bottomItem == nil ? 0.0 : 1.0)
|
||||||
|
|
||||||
|
return height
|
||||||
|
}
|
||||||
|
}
|
@ -200,7 +200,7 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
|
|||||||
textColorValue = presentationData.theme.list.itemAccentColor
|
textColorValue = presentationData.theme.list.itemAccentColor
|
||||||
}
|
}
|
||||||
|
|
||||||
self.expandNode.attributedText = NSAttributedString(string: "more", font: Font.regular(17.0), textColor: presentationData.theme.list.itemAccentColor)
|
self.expandNode.attributedText = NSAttributedString(string: presentationData.strings.PeerInfo_BioExpand, font: Font.regular(17.0), textColor: presentationData.theme.list.itemAccentColor)
|
||||||
let expandSize = self.expandNode.updateLayout(CGSize(width: width, height: 100.0))
|
let expandSize = self.expandNode.updateLayout(CGSize(width: width, height: 100.0))
|
||||||
|
|
||||||
self.labelNode.attributedText = NSAttributedString(string: item.label, font: Font.regular(14.0), textColor: presentationData.theme.list.itemPrimaryTextColor)
|
self.labelNode.attributedText = NSAttributedString(string: item.label, font: Font.regular(14.0), textColor: presentationData.theme.list.itemPrimaryTextColor)
|
||||||
@ -211,7 +211,7 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
|
|||||||
self.textNode.maximumNumberOfLines = 1
|
self.textNode.maximumNumberOfLines = 1
|
||||||
self.textNode.attributedText = NSAttributedString(string: item.text, font: Font.regular(17.0), textColor: textColorValue)
|
self.textNode.attributedText = NSAttributedString(string: item.text, font: Font.regular(17.0), textColor: textColorValue)
|
||||||
case let .multiLine(maxLines, enabledEntities):
|
case let .multiLine(maxLines, enabledEntities):
|
||||||
self.textNode.maximumNumberOfLines = self.isExpanded ? maxLines : 2
|
self.textNode.maximumNumberOfLines = self.isExpanded ? maxLines : 3
|
||||||
self.textNode.cutout = self.isExpanded ? nil : TextNodeCutout(bottomRight: CGSize(width: expandSize.width + 4.0, height: expandSize.height))
|
self.textNode.cutout = self.isExpanded ? nil : TextNodeCutout(bottomRight: CGSize(width: expandSize.width + 4.0, height: expandSize.height))
|
||||||
if enabledEntities.isEmpty {
|
if enabledEntities.isEmpty {
|
||||||
self.textNode.attributedText = NSAttributedString(string: item.text, font: Font.regular(17.0), textColor: textColorValue)
|
self.textNode.attributedText = NSAttributedString(string: item.text, font: Font.regular(17.0), textColor: textColorValue)
|
||||||
|
@ -204,6 +204,9 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cancelPreviewGestures() {
|
||||||
|
}
|
||||||
|
|
||||||
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
|
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
|||||||
self.selectedMessages = chatControllerInteraction.selectionState.flatMap { $0.selectedIds }
|
self.selectedMessages = chatControllerInteraction.selectionState.flatMap { $0.selectedIds }
|
||||||
self.selectedMessagesPromise.set(.single(self.selectedMessages))
|
self.selectedMessagesPromise.set(.single(self.selectedMessages))
|
||||||
|
|
||||||
self.listNode = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), tagMask: tagMask, subject: nil, controllerInteraction: chatControllerInteraction, selectedMessages: self.selectedMessagesPromise.get(), mode: .list(search: false, reversed: false))
|
self.listNode = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), tagMask: tagMask, subject: nil, controllerInteraction: chatControllerInteraction, selectedMessages: self.selectedMessagesPromise.get(), mode: .list(search: false, reversed: false, displayHeaders: .allButLast))
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
@ -106,6 +106,9 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cancelPreviewGestures() {
|
||||||
|
}
|
||||||
|
|
||||||
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
|
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
|
||||||
var transitionNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
var transitionNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
||||||
self.listNode.forEachItemNode { itemNode in
|
self.listNode.forEachItemNode { itemNode in
|
||||||
|
@ -247,6 +247,9 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cancelPreviewGestures() {
|
||||||
|
}
|
||||||
|
|
||||||
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
|
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,10 @@ private final class VisualMediaItemNode: ASDisplayNode {
|
|||||||
return .waitForSingleTap
|
return .waitForSingleTap
|
||||||
}
|
}
|
||||||
self.imageNode.view.addGestureRecognizer(recognizer)
|
self.imageNode.view.addGestureRecognizer(recognizer)
|
||||||
|
|
||||||
|
self.mediaBadgeNode.pressed = { [weak self] in
|
||||||
|
self?.progressPressed()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func tapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
@objc func tapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||||
@ -99,13 +103,62 @@ private final class VisualMediaItemNode: ASDisplayNode {
|
|||||||
if let (gesture, _) = recognizer.lastRecognizedGestureAndLocation {
|
if let (gesture, _) = recognizer.lastRecognizedGestureAndLocation {
|
||||||
if case .tap = gesture {
|
if case .tap = gesture {
|
||||||
if let (item, _, _, _) = self.item {
|
if let (item, _, _, _) = self.item {
|
||||||
self.interaction.openMessage(item.message)
|
var media: Media?
|
||||||
|
for value in item.message.media {
|
||||||
|
if let image = value as? TelegramMediaImage {
|
||||||
|
media = image
|
||||||
|
break
|
||||||
|
} else if let file = value as? TelegramMediaFile {
|
||||||
|
media = file
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let media = media {
|
||||||
|
if let file = media as? TelegramMediaFile {
|
||||||
|
if isMediaStreamable(message: item.message, media: file) {
|
||||||
|
self.interaction.openMessage(item.message)
|
||||||
|
} else {
|
||||||
|
self.progressPressed()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.interaction.openMessage(item.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func progressPressed() {
|
||||||
|
guard let message = self.item?.0.message else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var media: Media?
|
||||||
|
for value in message.media {
|
||||||
|
if let image = value as? TelegramMediaImage {
|
||||||
|
media = image
|
||||||
|
break
|
||||||
|
} else if let file = value as? TelegramMediaFile {
|
||||||
|
media = file
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let resourceStatus = self.resourceStatus, let file = media as? TelegramMediaFile {
|
||||||
|
switch resourceStatus {
|
||||||
|
case .Fetching:
|
||||||
|
messageMediaFileCancelInteractiveFetch(context: self.context, messageId: message.id, file: file)
|
||||||
|
case .Local:
|
||||||
|
self.interaction.openMessage(message)
|
||||||
|
case .Remote:
|
||||||
|
self.fetchDisposable.set(messageMediaFileInteractiveFetched(context: self.context, message: message, file: file, userInitiated: true).start())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func cancelPreviewGesture() {
|
func cancelPreviewGesture() {
|
||||||
self.containerNode.cancelGesture()
|
self.containerNode.cancelGesture()
|
||||||
}
|
}
|
||||||
@ -146,32 +199,36 @@ private final class VisualMediaItemNode: ASDisplayNode {
|
|||||||
self.mediaBadgeNode.isHidden = false
|
self.mediaBadgeNode.isHidden = false
|
||||||
|
|
||||||
self.resourceStatus = nil
|
self.resourceStatus = nil
|
||||||
self.fetchStatusDisposable.set((messageMediaFileStatus(context: context, messageId: item.message.id, file: file) |> deliverOnMainQueue).start(next: { [weak self] status in
|
|
||||||
|
self.item = (item, media, size, mediaDimensions)
|
||||||
|
|
||||||
|
self.fetchStatusDisposable.set((messageMediaFileStatus(context: context, messageId: item.message.id, file: file)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||||
if let strongSelf = self, let (item, _, _, _) = strongSelf.item {
|
if let strongSelf = self, let (item, _, _, _) = strongSelf.item {
|
||||||
strongSelf.resourceStatus = status
|
strongSelf.resourceStatus = status
|
||||||
|
|
||||||
let isStreamable = isMediaStreamable(message: item.message, media: file)
|
let isStreamable = isMediaStreamable(message: item.message, media: file)
|
||||||
|
|
||||||
let statusState: RadialStatusNodeState = .none
|
var statusState: RadialStatusNodeState = .none
|
||||||
/*if isStreamable {
|
if isStreamable {
|
||||||
statusState = .none
|
statusState = .none
|
||||||
} else {
|
} else {
|
||||||
switch status {
|
switch status {
|
||||||
case let .Fetching(_, progress):
|
case let .Fetching(_, progress):
|
||||||
let adjustedProgress = max(progress, 0.027)
|
let adjustedProgress = max(progress, 0.027)
|
||||||
statusState = .progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true)
|
statusState = .progress(color: .white, lineWidth: nil, value: CGFloat(adjustedProgress), cancelEnabled: true)
|
||||||
case .Local:
|
case .Local:
|
||||||
statusState = .none
|
statusState = .none
|
||||||
case .Remote:
|
case .Remote:
|
||||||
statusState = .download(.white)
|
statusState = .download(.white)
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
switch statusState {
|
switch statusState {
|
||||||
case .none:
|
case .none:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
strongSelf.statusNode.isHidden = false
|
strongSelf.statusNode.isHidden = false
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.statusNode.transitionToState(statusState, animated: true, completion: {
|
strongSelf.statusNode.transitionToState(statusState, animated: true, completion: {
|
||||||
@ -328,12 +385,64 @@ private final class VisualMediaItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class FloatingHeaderNode: ASDisplayNode {
|
||||||
|
private let backgroundNode: ASImageNode
|
||||||
|
private let labelNode: ImmediateTextNode
|
||||||
|
|
||||||
|
private var currentParams: (constrainedWidth: CGFloat, year: Int32, month: Int32, theme: PresentationTheme)?
|
||||||
|
private var currentSize: CGSize?
|
||||||
|
|
||||||
|
override init() {
|
||||||
|
self.backgroundNode = ASImageNode()
|
||||||
|
self.backgroundNode.displaysAsynchronously = false
|
||||||
|
self.backgroundNode.displayWithoutProcessing = true
|
||||||
|
|
||||||
|
self.labelNode = ImmediateTextNode()
|
||||||
|
self.labelNode.displaysAsynchronously = false
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.addSubnode(self.backgroundNode)
|
||||||
|
self.addSubnode(self.labelNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(constrainedWidth: CGFloat, year: Int32, month: Int32, theme: PresentationTheme, strings: PresentationStrings) -> CGSize {
|
||||||
|
if let currentParams = self.currentParams, let currentSize = self.currentSize {
|
||||||
|
if currentParams.constrainedWidth == constrainedWidth &&
|
||||||
|
currentParams.year == year &&
|
||||||
|
currentParams.month == month &&
|
||||||
|
currentParams.theme === theme {
|
||||||
|
return currentSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.currentParams?.theme !== theme {
|
||||||
|
self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 27.0, color: theme.rootController.navigationBar.backgroundColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.currentParams = (constrainedWidth, year, month, theme)
|
||||||
|
|
||||||
|
self.labelNode.attributedText = NSAttributedString(string: stringForMonth(strings: strings, month: month, ofYear: year).uppercased(), font: Font.regular(14.0), textColor: theme.rootController.navigationBar.secondaryTextColor)
|
||||||
|
let labelSize = self.labelNode.updateLayout(CGSize(width: constrainedWidth, height: .greatestFiniteMagnitude))
|
||||||
|
|
||||||
|
let sideInset: CGFloat = 10.0
|
||||||
|
self.labelNode.frame = CGRect(origin: CGPoint(x: sideInset, y: floor((27.0 - labelSize.height) / 2.0)), size: labelSize)
|
||||||
|
self.backgroundNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: labelSize.width + sideInset * 2.0, height: 27.0))
|
||||||
|
|
||||||
|
let size = CGSize(width: labelSize.width + sideInset * 2.0, height: 27.0)
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDelegate {
|
final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDelegate {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let peerId: PeerId
|
private let peerId: PeerId
|
||||||
private let chatControllerInteraction: ChatControllerInteraction
|
private let chatControllerInteraction: ChatControllerInteraction
|
||||||
|
|
||||||
private let scrollNode: ASScrollNode
|
private let scrollNode: ASScrollNode
|
||||||
|
private let floatingHeaderNode: FloatingHeaderNode
|
||||||
|
private var flashHeaderDelayTimer: Foundation.Timer?
|
||||||
|
private var isDeceleratingAfterTracking = false
|
||||||
|
|
||||||
private var _itemInteraction: VisualMediaItemInteraction?
|
private var _itemInteraction: VisualMediaItemInteraction?
|
||||||
private var itemInteraction: VisualMediaItemInteraction {
|
private var itemInteraction: VisualMediaItemInteraction {
|
||||||
@ -366,6 +475,8 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
self.chatControllerInteraction = chatControllerInteraction
|
self.chatControllerInteraction = chatControllerInteraction
|
||||||
|
|
||||||
self.scrollNode = ASScrollNode()
|
self.scrollNode = ASScrollNode()
|
||||||
|
self.floatingHeaderNode = FloatingHeaderNode()
|
||||||
|
self.floatingHeaderNode.alpha = 0.0
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
@ -392,6 +503,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
self.scrollNode.view.delegate = self
|
self.scrollNode.view.delegate = self
|
||||||
|
|
||||||
self.addSubnode(self.scrollNode)
|
self.addSubnode(self.scrollNode)
|
||||||
|
self.addSubnode(self.floatingHeaderNode)
|
||||||
|
|
||||||
self.requestHistoryAroundVisiblePosition()
|
self.requestHistoryAroundVisiblePosition()
|
||||||
|
|
||||||
@ -518,6 +630,12 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cancelPreviewGestures() {
|
||||||
|
for (_, itemNode) in self.visibleMediaItems {
|
||||||
|
itemNode.cancelPreviewGesture()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
|
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
|
||||||
for item in self.mediaItems {
|
for item in self.mediaItems {
|
||||||
if item.message.id == messageId {
|
if item.message.id == messageId {
|
||||||
@ -554,7 +672,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
let contentHeight = CGFloat(rowCount + 1) * itemSpacing + CGFloat(rowCount) * itemSize + bottomInset
|
let contentHeight = CGFloat(rowCount + 1) * itemSpacing + CGFloat(rowCount) * itemSize + bottomInset
|
||||||
|
|
||||||
self.scrollNode.view.contentSize = CGSize(width: size.width, height: contentHeight)
|
self.scrollNode.view.contentSize = CGSize(width: size.width, height: contentHeight)
|
||||||
self.updateVisibleItems(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, theme: presentationData.theme, synchronousLoad: synchronous)
|
self.updateVisibleItems(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, theme: presentationData.theme, strings: presentationData.strings, synchronousLoad: synchronous)
|
||||||
|
|
||||||
if isScrollingLockedAtTop {
|
if isScrollingLockedAtTop {
|
||||||
if self.scrollNode.view.contentOffset.y > .ulpOfOne {
|
if self.scrollNode.view.contentOffset.y > .ulpOfOne {
|
||||||
@ -571,11 +689,13 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
for (_, itemNode) in self.visibleMediaItems {
|
for (_, itemNode) in self.visibleMediaItems {
|
||||||
itemNode.cancelPreviewGesture()
|
itemNode.cancelPreviewGesture()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.updateHeaderFlashing(animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
if let (size, sideInset, bottomInset, visibleHeight, _, presentationData) = self.currentParams {
|
if let (size, sideInset, bottomInset, visibleHeight, _, presentationData) = self.currentParams {
|
||||||
self.updateVisibleItems(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, theme: presentationData.theme, synchronousLoad: false)
|
self.updateVisibleItems(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, theme: presentationData.theme, strings: presentationData.strings, synchronousLoad: false)
|
||||||
|
|
||||||
if scrollView.contentOffset.y >= scrollView.contentSize.height - scrollView.bounds.height * 2.0, let currentView = self.currentView, currentView.earlierId != nil {
|
if scrollView.contentOffset.y >= scrollView.contentSize.height - scrollView.bounds.height * 2.0, let currentView = self.currentView, currentView.earlierId != nil {
|
||||||
if !self.isRequestingView {
|
if !self.isRequestingView {
|
||||||
@ -586,7 +706,24 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateVisibleItems(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, theme: PresentationTheme, synchronousLoad: Bool) {
|
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
||||||
|
if decelerate {
|
||||||
|
self.isDeceleratingAfterTracking = true
|
||||||
|
self.updateHeaderFlashing(animated: true)
|
||||||
|
} else {
|
||||||
|
self.isDeceleratingAfterTracking = false
|
||||||
|
self.resetHeaderFlashTimer(start: true)
|
||||||
|
self.updateHeaderFlashing(animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||||
|
self.isDeceleratingAfterTracking = false
|
||||||
|
self.resetHeaderFlashTimer(start: true)
|
||||||
|
self.updateHeaderFlashing(animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateVisibleItems(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, theme: PresentationTheme, strings: PresentationStrings, synchronousLoad: Bool) {
|
||||||
let availableWidth = size.width - sideInset * 2.0
|
let availableWidth = size.width - sideInset * 2.0
|
||||||
|
|
||||||
let itemSpacing: CGFloat = 1.0
|
let itemSpacing: CGFloat = 1.0
|
||||||
@ -595,6 +732,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
|
|
||||||
let rowCount: Int = self.mediaItems.count / itemsInRow + (self.mediaItems.count % itemsInRow == 0 ? 0 : 1)
|
let rowCount: Int = self.mediaItems.count / itemsInRow + (self.mediaItems.count % itemsInRow == 0 ? 0 : 1)
|
||||||
|
|
||||||
|
let headerItemMinY = self.scrollNode.view.bounds.minY + 20.0
|
||||||
let visibleRect = self.scrollNode.view.bounds.insetBy(dx: 0.0, dy: -400.0)
|
let visibleRect = self.scrollNode.view.bounds.insetBy(dx: 0.0, dy: -400.0)
|
||||||
var minVisibleRow = Int(floor((visibleRect.minY - itemSpacing) / (itemSize + itemSpacing)))
|
var minVisibleRow = Int(floor((visibleRect.minY - itemSpacing) / (itemSize + itemSpacing)))
|
||||||
minVisibleRow = max(0, minVisibleRow)
|
minVisibleRow = max(0, minVisibleRow)
|
||||||
@ -604,6 +742,8 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
let minVisibleIndex = minVisibleRow * itemsInRow
|
let minVisibleIndex = minVisibleRow * itemsInRow
|
||||||
let maxVisibleIndex = min(self.mediaItems.count - 1, (maxVisibleRow + 1) * itemsInRow - 1)
|
let maxVisibleIndex = min(self.mediaItems.count - 1, (maxVisibleRow + 1) * itemsInRow - 1)
|
||||||
|
|
||||||
|
var headerItem: Message?
|
||||||
|
|
||||||
var validIds = Set<UInt32>()
|
var validIds = Set<UInt32>()
|
||||||
if minVisibleIndex <= maxVisibleIndex {
|
if minVisibleIndex <= maxVisibleIndex {
|
||||||
for i in minVisibleIndex ... maxVisibleIndex {
|
for i in minVisibleIndex ... maxVisibleIndex {
|
||||||
@ -622,6 +762,9 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
self.scrollNode.addSubnode(itemNode)
|
self.scrollNode.addSubnode(itemNode)
|
||||||
}
|
}
|
||||||
itemNode.frame = itemFrame
|
itemNode.frame = itemFrame
|
||||||
|
if headerItem == nil && itemFrame.maxY > headerItemMinY {
|
||||||
|
headerItem = self.mediaItems[i].message
|
||||||
|
}
|
||||||
var itemSynchronousLoad = false
|
var itemSynchronousLoad = false
|
||||||
if itemFrame.maxY <= visibleHeight {
|
if itemFrame.maxY <= visibleHeight {
|
||||||
itemSynchronousLoad = synchronousLoad
|
itemSynchronousLoad = synchronousLoad
|
||||||
@ -640,6 +783,68 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
itemNode.removeFromSupernode()
|
itemNode.removeFromSupernode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let headerItem = headerItem {
|
||||||
|
let (year, month) = listMessageDateHeaderInfo(timestamp: headerItem.timestamp)
|
||||||
|
let headerSize = self.floatingHeaderNode.update(constrainedWidth: size.width, year: year, month: month, theme: theme, strings: strings)
|
||||||
|
self.floatingHeaderNode.frame = CGRect(origin: CGPoint(x: floor((size.width - headerSize.width) / 2.0), y: 7.0), size: headerSize)
|
||||||
|
self.floatingHeaderNode.isHidden = false
|
||||||
|
} else {
|
||||||
|
self.floatingHeaderNode.isHidden = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func resetHeaderFlashTimer(start: Bool, duration: Double = 0.3) {
|
||||||
|
if let flashHeaderDelayTimer = self.flashHeaderDelayTimer {
|
||||||
|
flashHeaderDelayTimer.invalidate()
|
||||||
|
self.flashHeaderDelayTimer = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if start {
|
||||||
|
final class TimerProxy: NSObject {
|
||||||
|
private let action: () -> ()
|
||||||
|
|
||||||
|
init(_ action: @escaping () -> ()) {
|
||||||
|
self.action = action
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func timerEvent() {
|
||||||
|
self.action()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let timer = Timer(timeInterval: duration, target: TimerProxy { [weak self] in
|
||||||
|
if let strongSelf = self {
|
||||||
|
if let flashHeaderDelayTimer = strongSelf.flashHeaderDelayTimer {
|
||||||
|
flashHeaderDelayTimer.invalidate()
|
||||||
|
strongSelf.flashHeaderDelayTimer = nil
|
||||||
|
strongSelf.updateHeaderFlashing(animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, selector: #selector(TimerProxy.timerEvent), userInfo: nil, repeats: false)
|
||||||
|
self.flashHeaderDelayTimer = timer
|
||||||
|
RunLoop.main.add(timer, forMode: RunLoop.Mode.common)
|
||||||
|
self.updateHeaderFlashing(animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func headerIsFlashing() -> Bool {
|
||||||
|
return self.scrollNode.view.isDragging || self.isDeceleratingAfterTracking || self.flashHeaderDelayTimer != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateHeaderFlashing(animated: Bool) {
|
||||||
|
let flashing = self.headerIsFlashing()
|
||||||
|
let alpha: CGFloat = flashing ? 1.0 : 0.0
|
||||||
|
let previousAlpha = self.floatingHeaderNode.alpha
|
||||||
|
|
||||||
|
if !previousAlpha.isEqual(to: alpha) {
|
||||||
|
self.floatingHeaderNode.alpha = alpha
|
||||||
|
if animated {
|
||||||
|
let duration: Double = flashing ? 0.3 : 0.4
|
||||||
|
self.floatingHeaderNode.layer.animateAlpha(from: previousAlpha, to: alpha, duration: duration)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
|
@ -66,6 +66,7 @@ final class PeerInfoScreenData {
|
|||||||
let groupsInCommon: GroupsInCommonContext?
|
let groupsInCommon: GroupsInCommonContext?
|
||||||
let linkedDiscussionPeer: Peer?
|
let linkedDiscussionPeer: Peer?
|
||||||
let members: PeerInfoMembersData?
|
let members: PeerInfoMembersData?
|
||||||
|
let encryptionKeyFingerprint: SecretChatKeyFingerprint?
|
||||||
|
|
||||||
init(
|
init(
|
||||||
peer: Peer?,
|
peer: Peer?,
|
||||||
@ -77,7 +78,8 @@ final class PeerInfoScreenData {
|
|||||||
availablePanes: [PeerInfoPaneKey],
|
availablePanes: [PeerInfoPaneKey],
|
||||||
groupsInCommon: GroupsInCommonContext?,
|
groupsInCommon: GroupsInCommonContext?,
|
||||||
linkedDiscussionPeer: Peer?,
|
linkedDiscussionPeer: Peer?,
|
||||||
members: PeerInfoMembersData?
|
members: PeerInfoMembersData?,
|
||||||
|
encryptionKeyFingerprint: SecretChatKeyFingerprint?
|
||||||
) {
|
) {
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.cachedData = cachedData
|
self.cachedData = cachedData
|
||||||
@ -89,6 +91,7 @@ final class PeerInfoScreenData {
|
|||||||
self.groupsInCommon = groupsInCommon
|
self.groupsInCommon = groupsInCommon
|
||||||
self.linkedDiscussionPeer = linkedDiscussionPeer
|
self.linkedDiscussionPeer = linkedDiscussionPeer
|
||||||
self.members = members
|
self.members = members
|
||||||
|
self.encryptionKeyFingerprint = encryptionKeyFingerprint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,6 +206,8 @@ private func peerInfoScreenInputData(context: AccountContext, peerId: PeerId) ->
|
|||||||
}
|
}
|
||||||
} else if let group = peer as? TelegramGroup {
|
} else if let group = peer as? TelegramGroup {
|
||||||
return .group(groupId: group.id)
|
return .group(groupId: group.id)
|
||||||
|
} else if let secretChat = peer as? TelegramSecretChat {
|
||||||
|
return .user(userId: secretChat.regularPeerId, secretChatId: peer.id, kind: .user)
|
||||||
} else {
|
} else {
|
||||||
return .none
|
return .none
|
||||||
}
|
}
|
||||||
@ -272,12 +277,13 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
availablePanes: [],
|
availablePanes: [],
|
||||||
groupsInCommon: nil,
|
groupsInCommon: nil,
|
||||||
linkedDiscussionPeer: nil,
|
linkedDiscussionPeer: nil,
|
||||||
members: nil
|
members: nil,
|
||||||
|
encryptionKeyFingerprint: nil
|
||||||
))
|
))
|
||||||
case let .user(peerId, secretChatId, kind):
|
case let .user(userPeerId, secretChatId, kind):
|
||||||
let groupsInCommon: GroupsInCommonContext?
|
let groupsInCommon: GroupsInCommonContext?
|
||||||
if case .user = kind {
|
if case .user = kind {
|
||||||
groupsInCommon = GroupsInCommonContext(account: context.account, peerId: peerId)
|
groupsInCommon = GroupsInCommonContext(account: context.account, peerId: userPeerId)
|
||||||
} else {
|
} else {
|
||||||
groupsInCommon = nil
|
groupsInCommon = nil
|
||||||
}
|
}
|
||||||
@ -306,9 +312,9 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
}
|
}
|
||||||
subscriber.putNext(data)
|
subscriber.putNext(data)
|
||||||
}
|
}
|
||||||
let disposable = (context.account.viewTracker.peerView(peerId, updateData: false)
|
let disposable = (context.account.viewTracker.peerView(userPeerId, updateData: false)
|
||||||
|> map { view -> StatusInputData in
|
|> map { view -> StatusInputData in
|
||||||
guard let user = view.peers[peerId] as? TelegramUser else {
|
guard let user = view.peers[userPeerId] as? TelegramUser else {
|
||||||
return .none
|
return .none
|
||||||
}
|
}
|
||||||
if user.isDeleted {
|
if user.isDeleted {
|
||||||
@ -320,7 +326,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
if user.botInfo != nil {
|
if user.botInfo != nil {
|
||||||
return .bot
|
return .bot
|
||||||
}
|
}
|
||||||
guard let presence = view.peerPresences[peerId] as? TelegramUserPresence else {
|
guard let presence = view.peerPresences[userPeerId] as? TelegramUserPresence else {
|
||||||
return .none
|
return .none
|
||||||
}
|
}
|
||||||
return .presence(presence)
|
return .presence(presence)
|
||||||
@ -366,10 +372,10 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
var combinedKeys: [PostboxViewKey] = []
|
var combinedKeys: [PostboxViewKey] = []
|
||||||
combinedKeys.append(globalNotificationsKey)
|
combinedKeys.append(globalNotificationsKey)
|
||||||
if let secretChatId = secretChatId {
|
if let secretChatId = secretChatId {
|
||||||
combinedKeys.append(.peerChatState(peerId: peerId))
|
combinedKeys.append(.peerChatState(peerId: secretChatId))
|
||||||
}
|
}
|
||||||
return combineLatest(
|
return combineLatest(
|
||||||
context.account.viewTracker.peerView(peerId, updateData: true),
|
context.account.viewTracker.peerView(userPeerId, updateData: true),
|
||||||
peerInfoAvailableMediaPanes(context: context, peerId: peerId),
|
peerInfoAvailableMediaPanes(context: context, peerId: peerId),
|
||||||
context.account.postbox.combinedView(keys: combinedKeys),
|
context.account.postbox.combinedView(keys: combinedKeys),
|
||||||
status
|
status
|
||||||
@ -382,6 +388,13 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var encryptionKeyFingerprint: SecretChatKeyFingerprint?
|
||||||
|
if let secretChatId = secretChatId, let peerChatStateView = combinedView.views[.peerChatState(peerId: secretChatId)] as? PeerChatStateView {
|
||||||
|
if let peerChatState = peerChatStateView.chatState as? SecretChatKeyState {
|
||||||
|
encryptionKeyFingerprint = peerChatState.keyFingerprint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var availablePanes = availablePanes
|
var availablePanes = availablePanes
|
||||||
if availablePanes != nil, groupsInCommon != nil, let cachedData = peerView.cachedData as? CachedUserData {
|
if availablePanes != nil, groupsInCommon != nil, let cachedData = peerView.cachedData as? CachedUserData {
|
||||||
if cachedData.commonGroupCount != 0 {
|
if cachedData.commonGroupCount != 0 {
|
||||||
@ -390,7 +403,7 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
}
|
}
|
||||||
|
|
||||||
return PeerInfoScreenData(
|
return PeerInfoScreenData(
|
||||||
peer: peerView.peers[peerId],
|
peer: peerView.peers[userPeerId],
|
||||||
cachedData: peerView.cachedData,
|
cachedData: peerView.cachedData,
|
||||||
status: status,
|
status: status,
|
||||||
notificationSettings: peerView.notificationSettings as? TelegramPeerNotificationSettings,
|
notificationSettings: peerView.notificationSettings as? TelegramPeerNotificationSettings,
|
||||||
@ -399,7 +412,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
availablePanes: availablePanes ?? [],
|
availablePanes: availablePanes ?? [],
|
||||||
groupsInCommon: groupsInCommon,
|
groupsInCommon: groupsInCommon,
|
||||||
linkedDiscussionPeer: nil,
|
linkedDiscussionPeer: nil,
|
||||||
members: nil
|
members: nil,
|
||||||
|
encryptionKeyFingerprint: encryptionKeyFingerprint
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case .channel:
|
case .channel:
|
||||||
@ -448,7 +462,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
availablePanes: availablePanes ?? [],
|
availablePanes: availablePanes ?? [],
|
||||||
groupsInCommon: nil,
|
groupsInCommon: nil,
|
||||||
linkedDiscussionPeer: discussionPeer,
|
linkedDiscussionPeer: discussionPeer,
|
||||||
members: nil
|
members: nil,
|
||||||
|
encryptionKeyFingerprint: nil
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case let .group(groupId):
|
case let .group(groupId):
|
||||||
@ -519,7 +534,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
|
|||||||
availablePanes: availablePanes ?? [],
|
availablePanes: availablePanes ?? [],
|
||||||
groupsInCommon: nil,
|
groupsInCommon: nil,
|
||||||
linkedDiscussionPeer: discussionPeer,
|
linkedDiscussionPeer: discussionPeer,
|
||||||
members: membersData
|
members: membersData,
|
||||||
|
encryptionKeyFingerprint: nil
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -619,10 +635,12 @@ func availableActionsForMemberOfPeer(accountPeerId: PeerId, peer: Peer, member:
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?) -> [PeerInfoHeaderButtonKey] {
|
func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFromChat: Bool) -> [PeerInfoHeaderButtonKey] {
|
||||||
var result: [PeerInfoHeaderButtonKey] = []
|
var result: [PeerInfoHeaderButtonKey] = []
|
||||||
if let user = peer as? TelegramUser {
|
if let user = peer as? TelegramUser {
|
||||||
result.append(.message)
|
if !isOpenedFromChat {
|
||||||
|
result.append(.message)
|
||||||
|
}
|
||||||
var callsAvailable = false
|
var callsAvailable = false
|
||||||
if !user.isDeleted, user.botInfo == nil, !user.flags.contains(.isSupport) {
|
if !user.isDeleted, user.botInfo == nil, !user.flags.contains(.isSupport) {
|
||||||
if let cachedUserData = cachedData as? CachedUserData {
|
if let cachedUserData = cachedData as? CachedUserData {
|
||||||
@ -634,6 +652,9 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?) -> [PeerInf
|
|||||||
result.append(.call)
|
result.append(.call)
|
||||||
}
|
}
|
||||||
result.append(.mute)
|
result.append(.mute)
|
||||||
|
if isOpenedFromChat {
|
||||||
|
result.append(.search)
|
||||||
|
}
|
||||||
result.append(.more)
|
result.append(.more)
|
||||||
} else if let channel = peer as? TelegramChannel {
|
} else if let channel = peer as? TelegramChannel {
|
||||||
switch channel.info {
|
switch channel.info {
|
||||||
@ -650,6 +671,7 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?) -> [PeerInf
|
|||||||
}
|
}
|
||||||
|
|
||||||
result.append(.mute)
|
result.append(.mute)
|
||||||
|
result.append(.search)
|
||||||
result.append(.more)
|
result.append(.more)
|
||||||
} else if let group = peer as? TelegramGroup {
|
} else if let group = peer as? TelegramGroup {
|
||||||
var canEditGroupInfo = false
|
var canEditGroupInfo = false
|
||||||
@ -681,6 +703,7 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?) -> [PeerInf
|
|||||||
}
|
}
|
||||||
|
|
||||||
result.append(.mute)
|
result.append(.mute)
|
||||||
|
result.append(.search)
|
||||||
result.append(.more)
|
result.append(.more)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
@ -21,6 +21,7 @@ enum PeerInfoHeaderButtonKey: Hashable {
|
|||||||
case mute
|
case mute
|
||||||
case more
|
case more
|
||||||
case addMember
|
case addMember
|
||||||
|
case search
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PeerInfoHeaderButtonIcon {
|
enum PeerInfoHeaderButtonIcon {
|
||||||
@ -30,6 +31,7 @@ enum PeerInfoHeaderButtonIcon {
|
|||||||
case unmute
|
case unmute
|
||||||
case more
|
case more
|
||||||
case addMember
|
case addMember
|
||||||
|
case search
|
||||||
}
|
}
|
||||||
|
|
||||||
final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
|
final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
|
||||||
@ -104,6 +106,8 @@ final class PeerInfoHeaderButtonNode: HighlightableButtonNode {
|
|||||||
imageName = "Peer Info/ButtonMore"
|
imageName = "Peer Info/ButtonMore"
|
||||||
case .addMember:
|
case .addMember:
|
||||||
imageName = "Peer Info/ButtonAddMember"
|
imageName = "Peer Info/ButtonAddMember"
|
||||||
|
case .search:
|
||||||
|
imageName = "Peer Info/ButtonSearch"
|
||||||
}
|
}
|
||||||
if let image = UIImage(bundleImageName: imageName) {
|
if let image = UIImage(bundleImageName: imageName) {
|
||||||
let imageRect = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0)), size: image.size)
|
let imageRect = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0)), size: image.size)
|
||||||
@ -273,7 +277,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
|||||||
|
|
||||||
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: 0.0), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
|
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: 0.0), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
|
||||||
})
|
})
|
||||||
self.leftHighlightNode.isHidden = true
|
self.leftHighlightNode.alpha = 0.0
|
||||||
|
|
||||||
self.rightHighlightNode = ASImageNode()
|
self.rightHighlightNode = ASImageNode()
|
||||||
self.rightHighlightNode.displaysAsynchronously = false
|
self.rightHighlightNode.displaysAsynchronously = false
|
||||||
@ -293,7 +297,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
|||||||
|
|
||||||
context.drawLinearGradient(gradient, start: CGPoint(x: size.width, y: 0.0), end: CGPoint(x: 0.0, y: 0.0), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
|
context.drawLinearGradient(gradient, start: CGPoint(x: size.width, y: 0.0), end: CGPoint(x: 0.0, y: 0.0), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
|
||||||
})
|
})
|
||||||
self.rightHighlightNode.isHidden = true
|
self.rightHighlightNode.alpha = 0.0
|
||||||
|
|
||||||
self.stripContainerNode = ASDisplayNode()
|
self.stripContainerNode = ASDisplayNode()
|
||||||
self.contentNode.addSubnode(self.stripContainerNode)
|
self.contentNode.addSubnode(self.stripContainerNode)
|
||||||
@ -391,12 +395,26 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
if strongSelf.highlightedSide != highlightedSide {
|
if strongSelf.highlightedSide != highlightedSide {
|
||||||
strongSelf.highlightedSide = highlightedSide
|
strongSelf.highlightedSide = highlightedSide
|
||||||
|
let leftAlpha: CGFloat
|
||||||
|
let rightAlpha: CGFloat
|
||||||
if let highlightedSide = highlightedSide {
|
if let highlightedSide = highlightedSide {
|
||||||
strongSelf.leftHighlightNode.isHidden = highlightedSide
|
leftAlpha = highlightedSide ? 0.0 : 1.0
|
||||||
strongSelf.rightHighlightNode.isHidden = !highlightedSide
|
rightAlpha = highlightedSide ? 1.0 : 0.0
|
||||||
} else {
|
} else {
|
||||||
strongSelf.leftHighlightNode.isHidden = true
|
leftAlpha = 0.0
|
||||||
strongSelf.rightHighlightNode.isHidden = true
|
rightAlpha = 0.0
|
||||||
|
}
|
||||||
|
if strongSelf.leftHighlightNode.alpha != leftAlpha {
|
||||||
|
strongSelf.leftHighlightNode.alpha = leftAlpha
|
||||||
|
if leftAlpha.isZero {
|
||||||
|
strongSelf.leftHighlightNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strongSelf.rightHighlightNode.alpha != rightAlpha {
|
||||||
|
strongSelf.rightHighlightNode.alpha = rightAlpha
|
||||||
|
if rightAlpha.isZero {
|
||||||
|
strongSelf.rightHighlightNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1489,7 +1507,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
private var context: AccountContext
|
private var context: AccountContext
|
||||||
private var presentationData: PresentationData?
|
private var presentationData: PresentationData?
|
||||||
|
|
||||||
private let keepExpandedButtons: PeerInfoScreenKeepExpandedButtons
|
private let isOpenedFromChat: Bool
|
||||||
|
|
||||||
private(set) var isAvatarExpanded: Bool
|
private(set) var isAvatarExpanded: Bool
|
||||||
|
|
||||||
@ -1518,10 +1536,10 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
|
|
||||||
var navigationTransition: PeerInfoHeaderNavigationTransition?
|
var navigationTransition: PeerInfoHeaderNavigationTransition?
|
||||||
|
|
||||||
init(context: AccountContext, avatarInitiallyExpanded: Bool, keepExpandedButtons: PeerInfoScreenKeepExpandedButtons) {
|
init(context: AccountContext, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.isAvatarExpanded = avatarInitiallyExpanded
|
self.isAvatarExpanded = avatarInitiallyExpanded
|
||||||
self.keepExpandedButtons = keepExpandedButtons
|
self.isOpenedFromChat = isOpenedFromChat
|
||||||
|
|
||||||
self.avatarListNode = PeerInfoAvatarListNode(context: context, readyWhenGalleryLoads: avatarInitiallyExpanded)
|
self.avatarListNode = PeerInfoAvatarListNode(context: context, readyWhenGalleryLoads: avatarInitiallyExpanded)
|
||||||
|
|
||||||
@ -1698,7 +1716,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
let expandedAvatarListHeight = min(width, containerHeight - expandedAvatarControlsHeight)
|
let expandedAvatarListHeight = min(width, containerHeight - expandedAvatarControlsHeight)
|
||||||
let expandedAvatarListSize = CGSize(width: width, height: expandedAvatarListHeight)
|
let expandedAvatarListSize = CGSize(width: width, height: expandedAvatarListHeight)
|
||||||
|
|
||||||
let buttonKeys: [PeerInfoHeaderButtonKey] = peerInfoHeaderButtons(peer: peer, cachedData: cachedData)
|
let buttonKeys: [PeerInfoHeaderButtonKey] = peerInfoHeaderButtons(peer: peer, cachedData: cachedData, isOpenedFromChat: self.isOpenedFromChat)
|
||||||
|
|
||||||
var isVerified = false
|
var isVerified = false
|
||||||
let titleString: NSAttributedString
|
let titleString: NSAttributedString
|
||||||
@ -2077,6 +2095,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
case .addMember:
|
case .addMember:
|
||||||
buttonText = presentationData.strings.PeerInfo_ButtonAddMember
|
buttonText = presentationData.strings.PeerInfo_ButtonAddMember
|
||||||
buttonIcon = .addMember
|
buttonIcon = .addMember
|
||||||
|
case .search:
|
||||||
|
buttonText = presentationData.strings.PeerInfo_ButtonSearch
|
||||||
|
buttonIcon = .search
|
||||||
}
|
}
|
||||||
buttonNode.update(size: buttonFrame.size, text: buttonText, icon: buttonIcon, isExpanded: self.isAvatarExpanded, presentationData: presentationData, transition: buttonTransition)
|
buttonNode.update(size: buttonFrame.size, text: buttonText, icon: buttonIcon, isExpanded: self.isAvatarExpanded, presentationData: presentationData, transition: buttonTransition)
|
||||||
transition.updateSublayerTransformScaleAdditive(node: buttonNode, scale: buttonsScale)
|
transition.updateSublayerTransformScaleAdditive(node: buttonNode, scale: buttonsScale)
|
||||||
@ -2087,17 +2108,16 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
buttonsAlphaTransition.updateAlpha(node: buttonNode, alpha: buttonsAlpha)
|
buttonsAlphaTransition.updateAlpha(node: buttonNode, alpha: buttonsAlpha)
|
||||||
|
|
||||||
let hiddenWhileExpanded: Bool
|
let hiddenWhileExpanded: Bool
|
||||||
switch self.keepExpandedButtons {
|
if self.isOpenedFromChat {
|
||||||
case .message:
|
|
||||||
switch buttonKey {
|
switch buttonKey {
|
||||||
case .mute:
|
case .message, .search:
|
||||||
hiddenWhileExpanded = true
|
hiddenWhileExpanded = true
|
||||||
default:
|
default:
|
||||||
hiddenWhileExpanded = false
|
hiddenWhileExpanded = false
|
||||||
}
|
}
|
||||||
case .mute:
|
} else {
|
||||||
switch buttonKey {
|
switch buttonKey {
|
||||||
case .message:
|
case .mute, .search:
|
||||||
hiddenWhileExpanded = true
|
hiddenWhileExpanded = true
|
||||||
default:
|
default:
|
||||||
hiddenWhileExpanded = false
|
hiddenWhileExpanded = false
|
||||||
|
@ -16,6 +16,7 @@ protocol PeerInfoPaneNode: ASDisplayNode {
|
|||||||
func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition)
|
func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition)
|
||||||
func scrollToTop() -> Bool
|
func scrollToTop() -> Bool
|
||||||
func transferVelocity(_ velocity: CGFloat)
|
func transferVelocity(_ velocity: CGFloat)
|
||||||
|
func cancelPreviewGestures()
|
||||||
func findLoadedMessage(id: MessageId) -> Message?
|
func findLoadedMessage(id: MessageId) -> Message?
|
||||||
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
||||||
func addToTransitionSurface(view: UIView)
|
func addToTransitionSurface(view: UIView)
|
||||||
@ -449,7 +450,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
|||||||
var openPeerContextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)?
|
var openPeerContextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
var requestPerformPeerMemberAction: ((PeerInfoMember, PeerMembersListAction) -> Void)?
|
var requestPerformPeerMemberAction: ((PeerInfoMember, PeerMembersListAction) -> Void)?
|
||||||
|
|
||||||
var currentPaneUpdated: (() -> Void)?
|
var currentPaneUpdated: ((Bool) -> Void)?
|
||||||
var requestExpandTabs: (() -> Bool)?
|
var requestExpandTabs: (() -> Bool)?
|
||||||
|
|
||||||
private var currentAvailablePanes: [PeerInfoPaneKey]?
|
private var currentAvailablePanes: [PeerInfoPaneKey]?
|
||||||
@ -492,6 +493,8 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
|||||||
|
|
||||||
if let (size, sideInset, bottomInset, visibleHeight, expansionFraction, presentationData, data) = strongSelf.currentParams {
|
if let (size, sideInset, bottomInset, visibleHeight, expansionFraction, presentationData, data) = strongSelf.currentParams {
|
||||||
strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .animated(duration: 0.4, curve: .spring))
|
strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .animated(duration: 0.4, curve: .spring))
|
||||||
|
|
||||||
|
strongSelf.currentPaneUpdated?(true)
|
||||||
}
|
}
|
||||||
} else if strongSelf.pendingSwitchToPaneKey != key {
|
} else if strongSelf.pendingSwitchToPaneKey != key {
|
||||||
strongSelf.pendingSwitchToPaneKey = key
|
strongSelf.pendingSwitchToPaneKey = key
|
||||||
@ -506,11 +509,14 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
|||||||
override func didLoad() {
|
override func didLoad() {
|
||||||
super.didLoad()
|
super.didLoad()
|
||||||
|
|
||||||
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), enableBothDirections: true, canBegin: { [weak self] in
|
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self, let currentPaneKey = strongSelf.currentPaneKey, let availablePanes = strongSelf.currentParams?.data?.availablePanes, let index = availablePanes.index(of: currentPaneKey) else {
|
||||||
return false
|
return []
|
||||||
}
|
}
|
||||||
return strongSelf.currentPanes.count > 1
|
if index == 0 {
|
||||||
|
return .left
|
||||||
|
}
|
||||||
|
return [.left, .right]
|
||||||
})
|
})
|
||||||
panRecognizer.delegate = self
|
panRecognizer.delegate = self
|
||||||
panRecognizer.delaysTouchesBegan = false
|
panRecognizer.delaysTouchesBegan = false
|
||||||
@ -559,6 +565,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
|||||||
directionIsToRight = translation.x > size.width / 2.0
|
directionIsToRight = translation.x > size.width / 2.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var updated = false
|
||||||
if let directionIsToRight = directionIsToRight {
|
if let directionIsToRight = directionIsToRight {
|
||||||
var updatedIndex = currentIndex
|
var updatedIndex = currentIndex
|
||||||
if directionIsToRight {
|
if directionIsToRight {
|
||||||
@ -569,10 +576,12 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
|||||||
let switchToKey = availablePanes[updatedIndex]
|
let switchToKey = availablePanes[updatedIndex]
|
||||||
if switchToKey != self.currentPaneKey && self.currentPanes[switchToKey] != nil{
|
if switchToKey != self.currentPaneKey && self.currentPanes[switchToKey] != nil{
|
||||||
self.currentPaneKey = switchToKey
|
self.currentPaneKey = switchToKey
|
||||||
|
updated = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.transitionFraction = 0.0
|
self.transitionFraction = 0.0
|
||||||
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .animated(duration: 0.35, curve: .spring))
|
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, transition: .animated(duration: 0.35, curve: .spring))
|
||||||
|
self.currentPaneUpdated?(false)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
@ -853,7 +862,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let previousCurrentPaneKey = previousCurrentPaneKey, self.currentPaneKey != previousCurrentPaneKey {
|
if let previousCurrentPaneKey = previousCurrentPaneKey, self.currentPaneKey != previousCurrentPaneKey {
|
||||||
self.currentPaneUpdated?()
|
self.currentPaneUpdated?(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -493,6 +493,7 @@ private final class PeerInfoInteraction {
|
|||||||
let openPeerInfoContextMenu: (PeerInfoContextSubject, ASDisplayNode) -> Void
|
let openPeerInfoContextMenu: (PeerInfoContextSubject, ASDisplayNode) -> Void
|
||||||
let performBioLinkAction: (TextLinkItemActionType, TextLinkItem) -> Void
|
let performBioLinkAction: (TextLinkItemActionType, TextLinkItem) -> Void
|
||||||
let requestLayout: () -> Void
|
let requestLayout: () -> Void
|
||||||
|
let openEncryptionKey: () -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
openUsername: @escaping (String) -> Void,
|
openUsername: @escaping (String) -> Void,
|
||||||
@ -521,7 +522,8 @@ private final class PeerInfoInteraction {
|
|||||||
performMemberAction: @escaping (PeerInfoMember, PeerInfoMemberAction) -> Void,
|
performMemberAction: @escaping (PeerInfoMember, PeerInfoMemberAction) -> Void,
|
||||||
openPeerInfoContextMenu: @escaping (PeerInfoContextSubject, ASDisplayNode) -> Void,
|
openPeerInfoContextMenu: @escaping (PeerInfoContextSubject, ASDisplayNode) -> Void,
|
||||||
performBioLinkAction: @escaping (TextLinkItemActionType, TextLinkItem) -> Void,
|
performBioLinkAction: @escaping (TextLinkItemActionType, TextLinkItem) -> Void,
|
||||||
requestLayout: @escaping () -> Void
|
requestLayout: @escaping () -> Void,
|
||||||
|
openEncryptionKey: @escaping () -> Void
|
||||||
) {
|
) {
|
||||||
self.openUsername = openUsername
|
self.openUsername = openUsername
|
||||||
self.openPhone = openPhone
|
self.openPhone = openPhone
|
||||||
@ -550,6 +552,7 @@ private final class PeerInfoInteraction {
|
|||||||
self.openPeerInfoContextMenu = openPeerInfoContextMenu
|
self.openPeerInfoContextMenu = openPeerInfoContextMenu
|
||||||
self.performBioLinkAction = performBioLinkAction
|
self.performBioLinkAction = performBioLinkAction
|
||||||
self.requestLayout = requestLayout
|
self.requestLayout = requestLayout
|
||||||
|
self.openEncryptionKey = openEncryptionKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -642,6 +645,12 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let encryptionKeyFingerprint = data.encryptionKeyFingerprint {
|
||||||
|
items[.peerInfo]!.append(PeerInfoScreenDisclosureEncryptionKeyItem(id: 6, text: presentationData.strings.Profile_EncryptionKey, fingerprint: encryptionKeyFingerprint, action: {
|
||||||
|
interaction.openEncryptionKey()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
if user.botInfo != nil, !user.isVerified {
|
if user.botInfo != nil, !user.isVerified {
|
||||||
items[.peerInfo]!.append(PeerInfoScreenActionItem(id: 5, text: presentationData.strings.ReportPeer_Report, action: {
|
items[.peerInfo]!.append(PeerInfoScreenActionItem(id: 5, text: presentationData.strings.ReportPeer_Report, action: {
|
||||||
interaction.openReport(false)
|
interaction.openReport(false)
|
||||||
@ -1077,7 +1086,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}
|
}
|
||||||
private var didSetReady = false
|
private var didSetReady = false
|
||||||
|
|
||||||
init(controller: PeerInfoScreen, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, keepExpandedButtons: PeerInfoScreenKeepExpandedButtons, nearbyPeer: Bool) {
|
init(controller: PeerInfoScreen, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeer: Bool) {
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.context = context
|
self.context = context
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
@ -1087,7 +1096,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
self.scrollNode = ASScrollNode()
|
self.scrollNode = ASScrollNode()
|
||||||
self.scrollNode.view.delaysContentTouches = false
|
self.scrollNode.view.delaysContentTouches = false
|
||||||
|
|
||||||
self.headerNode = PeerInfoHeaderNode(context: context, avatarInitiallyExpanded: avatarInitiallyExpanded, keepExpandedButtons: keepExpandedButtons)
|
self.headerNode = PeerInfoHeaderNode(context: context, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat)
|
||||||
self.paneContainerNode = PeerInfoPaneContainerNode(context: context, peerId: peerId)
|
self.paneContainerNode = PeerInfoPaneContainerNode(context: context, peerId: peerId)
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
@ -1173,6 +1182,9 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
},
|
},
|
||||||
requestLayout: { [weak self] in
|
requestLayout: { [weak self] in
|
||||||
self?.requestLayout()
|
self?.requestLayout()
|
||||||
|
},
|
||||||
|
openEncryptionKey: { [weak self] in
|
||||||
|
self?.openEncryptionKey()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1556,7 +1568,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
controller.presentInGlobalOverlay(contextController)
|
controller.presentInGlobalOverlay(contextController)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.paneContainerNode.currentPaneUpdated = { [weak self] in
|
self.paneContainerNode.currentPaneUpdated = { [weak self] expand in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1573,7 +1585,9 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
|
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false)
|
||||||
strongSelf.scrollNode.view.setContentOffset(CGPoint(x: 0.0, y: strongSelf.paneContainerNode.frame.minY - navigationHeight), animated: true)
|
if expand {
|
||||||
|
strongSelf.scrollNode.view.setContentOffset(CGPoint(x: 0.0, y: strongSelf.paneContainerNode.frame.minY - navigationHeight), animated: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2239,6 +2253,8 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
controller.present(actionSheet, in: .window(.root))
|
controller.present(actionSheet, in: .window(.root))
|
||||||
case .addMember:
|
case .addMember:
|
||||||
self.openAddMember()
|
self.openAddMember()
|
||||||
|
case .search:
|
||||||
|
self.openChatWithMessageSearch()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2665,6 +2681,13 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}, completion: { _ in }), in: .window(.root))
|
}, completion: { _ in }), in: .window(.root))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func openEncryptionKey() {
|
||||||
|
guard let data = self.data, let peer = data.peer, let encryptionKeyFingerprint = data.encryptionKeyFingerprint else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.controller?.push(SecretChatKeyController(context: self.context, fingerprint: encryptionKeyFingerprint, peer: peer))
|
||||||
|
}
|
||||||
|
|
||||||
private func openShareBot() {
|
private func openShareBot() {
|
||||||
let _ = (getUserPeer(postbox: self.context.account.postbox, peerId: self.peerId)
|
let _ = (getUserPeer(postbox: self.context.account.postbox, peerId: self.peerId)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] peer, _ in
|
|> deliverOnMainQueue).start(next: { [weak self] peer, _ in
|
||||||
@ -3917,6 +3940,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||||
self.canAddVelocity = true
|
self.canAddVelocity = true
|
||||||
self.canOpenAvatarByDragging = self.headerNode.isAvatarExpanded
|
self.canOpenAvatarByDragging = self.headerNode.isAvatarExpanded
|
||||||
|
self.paneContainerNode.currentPane?.node.cancelPreviewGestures()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var previousVelocityM1: CGFloat = 0.0
|
private var previousVelocityM1: CGFloat = 0.0
|
||||||
@ -4078,16 +4102,11 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum PeerInfoScreenKeepExpandedButtons {
|
|
||||||
case message
|
|
||||||
case mute
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class PeerInfoScreen: ViewController {
|
public final class PeerInfoScreen: ViewController {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let peerId: PeerId
|
private let peerId: PeerId
|
||||||
private let avatarInitiallyExpanded: Bool
|
private let avatarInitiallyExpanded: Bool
|
||||||
private let keepExpandedButtons: PeerInfoScreenKeepExpandedButtons
|
private let isOpenedFromChat: Bool
|
||||||
private let nearbyPeer: Bool
|
private let nearbyPeer: Bool
|
||||||
|
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
@ -4104,11 +4123,11 @@ public final class PeerInfoScreen: ViewController {
|
|||||||
|
|
||||||
private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
|
private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
|
||||||
|
|
||||||
public init(context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool = false, keepExpandedButtons: PeerInfoScreenKeepExpandedButtons = .message, nearbyPeer: Bool = false) {
|
public init(context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeer: Bool) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.avatarInitiallyExpanded = avatarInitiallyExpanded
|
self.avatarInitiallyExpanded = avatarInitiallyExpanded
|
||||||
self.keepExpandedButtons = keepExpandedButtons
|
self.isOpenedFromChat = isOpenedFromChat
|
||||||
self.nearbyPeer = nearbyPeer
|
self.nearbyPeer = nearbyPeer
|
||||||
|
|
||||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
@ -4177,7 +4196,7 @@ public final class PeerInfoScreen: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public func loadDisplayNode() {
|
override public func loadDisplayNode() {
|
||||||
self.displayNode = PeerInfoScreenNode(controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, keepExpandedButtons: self.keepExpandedButtons, nearbyPeer: self.nearbyPeer)
|
self.displayNode = PeerInfoScreenNode(controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, isOpenedFromChat: self.isOpenedFromChat, nearbyPeer: self.nearbyPeer)
|
||||||
|
|
||||||
self._ready.set(self.controllerNode.ready.get())
|
self._ready.set(self.controllerNode.ready.get())
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ private func historyNodeImplForMode(_ mode: PeerMediaCollectionMode, context: Ac
|
|||||||
}
|
}
|
||||||
return node
|
return node
|
||||||
case .file:
|
case .file:
|
||||||
let node = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), tagMask: .file, subject: messageId.flatMap { .message($0) }, controllerInteraction: controllerInteraction, selectedMessages: selectedMessages, mode: .list(search: true, reversed: false))
|
let node = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), tagMask: .file, subject: messageId.flatMap { .message($0) }, controllerInteraction: controllerInteraction, selectedMessages: selectedMessages, mode: .list(search: true, reversed: false, displayHeaders: .all))
|
||||||
node.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
|
node.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
|
||||||
node.didEndScrolling = { [weak node] in
|
node.didEndScrolling = { [weak node] in
|
||||||
guard let node = node else {
|
guard let node = node else {
|
||||||
@ -40,7 +40,7 @@ private func historyNodeImplForMode(_ mode: PeerMediaCollectionMode, context: Ac
|
|||||||
node.preloadPages = true
|
node.preloadPages = true
|
||||||
return node
|
return node
|
||||||
case .music:
|
case .music:
|
||||||
let node = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), tagMask: .music, subject: messageId.flatMap { .message($0) }, controllerInteraction: controllerInteraction, selectedMessages: selectedMessages, mode: .list(search: true, reversed: false))
|
let node = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), tagMask: .music, subject: messageId.flatMap { .message($0) }, controllerInteraction: controllerInteraction, selectedMessages: selectedMessages, mode: .list(search: true, reversed: false, displayHeaders: .all))
|
||||||
node.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
|
node.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
|
||||||
node.didEndScrolling = { [weak node] in
|
node.didEndScrolling = { [weak node] in
|
||||||
guard let node = node else {
|
guard let node = node else {
|
||||||
@ -51,7 +51,7 @@ private func historyNodeImplForMode(_ mode: PeerMediaCollectionMode, context: Ac
|
|||||||
node.preloadPages = true
|
node.preloadPages = true
|
||||||
return node
|
return node
|
||||||
case .webpage:
|
case .webpage:
|
||||||
let node = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), tagMask: .webPage, subject: messageId.flatMap { .message($0) }, controllerInteraction: controllerInteraction, selectedMessages: selectedMessages, mode: .list(search: true, reversed: false))
|
let node = ChatHistoryListNode(context: context, chatLocation: .peer(peerId), tagMask: .webPage, subject: messageId.flatMap { .message($0) }, controllerInteraction: controllerInteraction, selectedMessages: selectedMessages, mode: .list(search: true, reversed: false, displayHeaders: .all))
|
||||||
node.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
|
node.verticalScrollIndicatorColor = theme.list.scrollIndicatorColor
|
||||||
node.didEndScrolling = { [weak node] in
|
node.didEndScrolling = { [weak node] in
|
||||||
guard let node = node else {
|
guard let node = node else {
|
||||||
|
Binary file not shown.
@ -1007,7 +1007,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func makePeerInfoController(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, fromChat: Bool) -> ViewController? {
|
public func makePeerInfoController(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, fromChat: Bool) -> ViewController? {
|
||||||
let controller = peerInfoControllerImpl(context: context, peer: peer, mode: mode, avatarInitiallyExpanded: avatarInitiallyExpanded, keepExpandedButtons: fromChat ? .mute : .message)
|
let controller = peerInfoControllerImpl(context: context, peer: peer, mode: mode, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: fromChat)
|
||||||
controller?.navigationPresentation = .modalInLargeLayout
|
controller?.navigationPresentation = .modalInLargeLayout
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
@ -1249,19 +1249,20 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
|
|
||||||
private let defaultChatControllerInteraction = ChatControllerInteraction.default
|
private let defaultChatControllerInteraction = ChatControllerInteraction.default
|
||||||
|
|
||||||
private func peerInfoControllerImpl(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, keepExpandedButtons: PeerInfoScreenKeepExpandedButtons) -> ViewController? {
|
private func peerInfoControllerImpl(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool) -> ViewController? {
|
||||||
if let _ = peer as? TelegramGroup {
|
if let _ = peer as? TelegramGroup {
|
||||||
return PeerInfoScreen(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, keepExpandedButtons: keepExpandedButtons)
|
return PeerInfoScreen(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeer: false)
|
||||||
} else if let channel = peer as? TelegramChannel {
|
} else if let channel = peer as? TelegramChannel {
|
||||||
return PeerInfoScreen(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, keepExpandedButtons: keepExpandedButtons)
|
return PeerInfoScreen(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeer: false)
|
||||||
} else if peer is TelegramUser {
|
} else if peer is TelegramUser {
|
||||||
var nearbyPeer = false
|
var nearbyPeer = false
|
||||||
if case .nearbyPeer = mode {
|
if case .nearbyPeer = mode {
|
||||||
nearbyPeer = true
|
nearbyPeer = true
|
||||||
}
|
}
|
||||||
return PeerInfoScreen(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, keepExpandedButtons: keepExpandedButtons, nearbyPeer: nearbyPeer)
|
return PeerInfoScreen(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeer: nearbyPeer)
|
||||||
} else if peer is TelegramSecretChat {
|
} else if peer is TelegramSecretChat {
|
||||||
return userInfoController(context: context, peerId: peer.id, mode: mode)
|
//return userInfoController(context: context, peerId: peer.id, mode: mode)
|
||||||
|
return PeerInfoScreen(context: context, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeer: false)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
@ -448,12 +448,12 @@ public final class WalletStrings: Equatable {
|
|||||||
public var Wallet_SecureStorageReset_Title: String { return self._s[218]! }
|
public var Wallet_SecureStorageReset_Title: String { return self._s[218]! }
|
||||||
public var Wallet_Receive_CommentHeader: String { return self._s[219]! }
|
public var Wallet_Receive_CommentHeader: String { return self._s[219]! }
|
||||||
public var Wallet_Info_ReceiveGrams: String { return self._s[220]! }
|
public var Wallet_Info_ReceiveGrams: String { return self._s[220]! }
|
||||||
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
|
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
|
||||||
let form = getPluralizationForm(self.lc, value)
|
let form = getPluralizationForm(self.lc, value)
|
||||||
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
||||||
return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue)
|
return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue)
|
||||||
}
|
}
|
||||||
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
|
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
|
||||||
let form = getPluralizationForm(self.lc, value)
|
let form = getPluralizationForm(self.lc, value)
|
||||||
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
||||||
return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue)
|
return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user