mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
322 lines
14 KiB
Swift
322 lines
14 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import TelegramCore
|
|
import SwiftSignalKit
|
|
import AsyncDisplayKit
|
|
import Display
|
|
import TelegramPresentationData
|
|
import TelegramUIPreferences
|
|
import ActivityIndicator
|
|
import AccountContext
|
|
import AppBundle
|
|
|
|
private enum JoinState: Equatable {
|
|
case none
|
|
case notJoined
|
|
case inProgress
|
|
case joined(justNow: Bool)
|
|
|
|
static func ==(lhs: JoinState, rhs: JoinState) -> Bool {
|
|
switch lhs {
|
|
case .none:
|
|
if case .none = rhs {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case .notJoined:
|
|
if case .notJoined = rhs {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case .inProgress:
|
|
if case .inProgress = rhs {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
case let .joined(justNow):
|
|
if case .joined(justNow) = rhs {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public final class InstantPagePeerReferenceNode: ASDisplayNode, InstantPageNode {
|
|
private let context: AccountContext
|
|
let safeInset: CGFloat
|
|
private let transparent: Bool
|
|
private let rtl: Bool
|
|
private var strings: PresentationStrings
|
|
private var nameDisplayOrder: PresentationPersonNameOrder
|
|
private var theme: InstantPageTheme
|
|
private let openPeer: (EnginePeer) -> Void
|
|
|
|
private let highlightedBackgroundNode: ASDisplayNode
|
|
private let buttonNode: HighlightableButtonNode
|
|
private let nameNode: ASTextNode
|
|
private let joinNode: HighlightableButtonNode
|
|
private let activityIndicator: ActivityIndicator
|
|
private let checkNode: ASImageNode
|
|
|
|
var peer: EnginePeer?
|
|
private var peerDisposable: Disposable?
|
|
|
|
private let joinDisposable = MetaDisposable()
|
|
private var joinState: JoinState = .none
|
|
|
|
init(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, initialPeer: EnginePeer, safeInset: CGFloat, transparent: Bool, rtl: Bool, openPeer: @escaping (EnginePeer) -> Void) {
|
|
self.context = context
|
|
self.strings = strings
|
|
self.nameDisplayOrder = nameDisplayOrder
|
|
self.theme = theme
|
|
self.peer = initialPeer
|
|
self.safeInset = safeInset
|
|
self.transparent = transparent
|
|
self.rtl = rtl
|
|
self.openPeer = openPeer
|
|
|
|
self.highlightedBackgroundNode = ASDisplayNode()
|
|
self.highlightedBackgroundNode.isLayerBacked = true
|
|
self.highlightedBackgroundNode.alpha = 0.0
|
|
|
|
self.buttonNode = HighlightableButtonNode()
|
|
|
|
self.nameNode = ASTextNode()
|
|
self.nameNode.isUserInteractionEnabled = false
|
|
self.nameNode.maximumNumberOfLines = 1
|
|
|
|
self.joinNode = HighlightableButtonNode()
|
|
self.joinNode.hitTestSlop = UIEdgeInsets(top: -17.0, left: -17.0, bottom: -17.0, right: -17.0)
|
|
|
|
self.activityIndicator = ActivityIndicator(type: .custom(theme.panelAccentColor, 22.0, 2.0, false))
|
|
|
|
self.checkNode = ASImageNode()
|
|
self.checkNode.isLayerBacked = true
|
|
self.checkNode.displayWithoutProcessing = true
|
|
self.checkNode.displaysAsynchronously = false
|
|
self.checkNode.isHidden = true
|
|
|
|
super.init()
|
|
|
|
if self.transparent {
|
|
self.backgroundColor = UIColor(white: 0.0, alpha: 0.6)
|
|
self.highlightedBackgroundNode.backgroundColor = UIColor(white: 1.0, alpha: 0.1)
|
|
} else {
|
|
self.backgroundColor = theme.panelBackgroundColor
|
|
self.highlightedBackgroundNode.backgroundColor = theme.panelHighlightedBackgroundColor
|
|
}
|
|
|
|
self.addSubnode(self.highlightedBackgroundNode)
|
|
self.addSubnode(self.buttonNode)
|
|
self.addSubnode(self.joinNode)
|
|
self.addSubnode(self.checkNode)
|
|
self.addSubnode(self.nameNode)
|
|
|
|
self.buttonNode.highligthedChanged = { [weak self] highlighted in
|
|
if let strongSelf = self {
|
|
if highlighted {
|
|
strongSelf.highlightedBackgroundNode.layer.removeAnimation(forKey: "opacity")
|
|
strongSelf.highlightedBackgroundNode.alpha = 1.0
|
|
} else {
|
|
strongSelf.highlightedBackgroundNode.alpha = 0.0
|
|
strongSelf.highlightedBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
|
|
}
|
|
}
|
|
}
|
|
|
|
self.joinNode.highligthedChanged = { [weak self] highlighted in
|
|
if let strongSelf = self {
|
|
if highlighted {
|
|
strongSelf.joinNode.layer.removeAnimation(forKey: "opacity")
|
|
strongSelf.joinNode.alpha = 0.4
|
|
} else {
|
|
strongSelf.joinNode.alpha = 1.0
|
|
strongSelf.joinNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
|
}
|
|
}
|
|
}
|
|
|
|
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
|
self.joinNode.addTarget(self, action: #selector(self.joinPressed), forControlEvents: .touchUpInside)
|
|
|
|
let account = self.context.account
|
|
let engine = context.engine
|
|
let signal: Signal<EnginePeer, NoError> = actualizedPeer(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, peer: initialPeer._asPeer())
|
|
|> mapToSignal({ peer -> Signal<EnginePeer, NoError> in
|
|
if let peer = peer as? TelegramChannel, let username = peer.addressName, peer.accessHash == nil {
|
|
return .single(.channel(peer)) |> then(engine.peers.resolvePeerByName(name: username, referrer: nil)
|
|
|> mapToSignal({ result -> Signal<EnginePeer, NoError> in
|
|
guard case let .result(updatedPeer) = result else {
|
|
return .complete()
|
|
}
|
|
if let updatedPeer = updatedPeer {
|
|
return .single(updatedPeer)
|
|
} else {
|
|
return .single(.channel(peer))
|
|
}
|
|
}))
|
|
} else {
|
|
return .single(EnginePeer(peer))
|
|
}
|
|
})
|
|
|
|
self.peerDisposable = (signal |> deliverOnMainQueue).start(next: { [weak self] peer in
|
|
if let strongSelf = self {
|
|
strongSelf.peer = peer
|
|
if case let .channel(peer) = peer {
|
|
var joinState = strongSelf.joinState
|
|
if case .member = peer.participationStatus {
|
|
switch joinState {
|
|
case .none:
|
|
joinState = .joined(justNow: false)
|
|
case .inProgress, .notJoined:
|
|
joinState = .joined(justNow: true)
|
|
case .joined:
|
|
break
|
|
}
|
|
} else {
|
|
joinState = .notJoined
|
|
}
|
|
strongSelf.updateJoinState(joinState)
|
|
}
|
|
strongSelf.applyThemeAndStrings(themeUpdated: false)
|
|
strongSelf.setNeedsLayout()
|
|
}
|
|
})
|
|
|
|
self.applyThemeAndStrings(themeUpdated: true)
|
|
}
|
|
|
|
deinit {
|
|
self.peerDisposable?.dispose()
|
|
self.joinDisposable.dispose()
|
|
}
|
|
|
|
public func update(strings: PresentationStrings, theme: InstantPageTheme) {
|
|
if self.strings !== strings || self.theme !== theme {
|
|
let themeUpdated = self.theme !== theme
|
|
self.strings = strings
|
|
self.theme = theme
|
|
self.applyThemeAndStrings(themeUpdated: themeUpdated)
|
|
}
|
|
}
|
|
|
|
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
|
}
|
|
|
|
private func applyThemeAndStrings(themeUpdated: Bool) {
|
|
if let peer = self.peer {
|
|
let textColor = self.transparent ? UIColor.white : self.theme.panelPrimaryColor
|
|
self.nameNode.attributedText = NSAttributedString(string: peer.displayTitle(strings: self.strings, displayOrder: self.nameDisplayOrder), font: Font.medium(17.0), textColor: textColor)
|
|
}
|
|
let accentColor = self.transparent ? UIColor.white : self.theme.panelAccentColor
|
|
self.joinNode.setAttributedTitle(NSAttributedString(string: self.strings.Channel_JoinChannel, font: Font.medium(17.0), textColor: accentColor), for: [])
|
|
|
|
if themeUpdated {
|
|
let secondaryColor = self.transparent ? UIColor.white : self.theme.panelSecondaryColor
|
|
self.checkNode.image = generateTintedImage(image: UIImage(bundleImageName: "Instant View/PanelCheck"), color: secondaryColor)
|
|
self.activityIndicator.type = .custom(self.theme.panelAccentColor, 22.0, 2.0, false)
|
|
|
|
if !self.transparent {
|
|
self.backgroundColor = self.theme.panelBackgroundColor
|
|
self.highlightedBackgroundNode.backgroundColor = self.theme.panelHighlightedBackgroundColor
|
|
}
|
|
}
|
|
self.setNeedsLayout()
|
|
}
|
|
|
|
private func updateJoinState(_ joinState: JoinState) {
|
|
if self.joinState != joinState {
|
|
self.joinState = joinState
|
|
|
|
switch joinState {
|
|
case .none:
|
|
self.joinNode.isHidden = true
|
|
self.checkNode.isHidden = true
|
|
if self.activityIndicator.supernode != nil {
|
|
self.activityIndicator.removeFromSupernode()
|
|
}
|
|
case .notJoined:
|
|
self.joinNode.isHidden = false
|
|
self.checkNode.isHidden = true
|
|
if self.activityIndicator.supernode != nil {
|
|
self.activityIndicator.removeFromSupernode()
|
|
}
|
|
case .inProgress:
|
|
self.joinNode.isHidden = true
|
|
self.checkNode.isHidden = true
|
|
if self.activityIndicator.supernode == nil {
|
|
self.addSubnode(self.activityIndicator)
|
|
}
|
|
case let .joined(justNow):
|
|
self.joinNode.isHidden = true
|
|
self.checkNode.isHidden = !justNow
|
|
if self.activityIndicator.supernode != nil {
|
|
self.activityIndicator.removeFromSupernode()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public override func layout() {
|
|
super.layout()
|
|
|
|
let size = self.bounds.size
|
|
let inset: CGFloat = 17.0 + safeInset
|
|
|
|
self.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(), size: size)
|
|
self.buttonNode.frame = CGRect(origin: CGPoint(), size: size)
|
|
|
|
let joinSize = self.joinNode.measure(size)
|
|
let nameSize = self.nameNode.measure(CGSize(width: size.width - inset * 2.0 - joinSize.width, height: size.height))
|
|
let checkSize = self.checkNode.measure(size)
|
|
let indicatorSize = self.activityIndicator.measure(size)
|
|
|
|
if self.rtl {
|
|
self.nameNode.frame = CGRect(origin: CGPoint(x: size.width - inset - nameSize.width, y: floor((size.height - nameSize.height) / 2.0)), size: nameSize)
|
|
self.joinNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((size.height - joinSize.height) / 2.0)), size: joinSize)
|
|
self.checkNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((size.height - checkSize.height) / 2.0)), size: checkSize)
|
|
self.activityIndicator.frame = CGRect(origin: CGPoint(x: inset, y: floor((size.height - indicatorSize.height) / 2.0)), size: indicatorSize)
|
|
} else {
|
|
self.nameNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((size.height - nameSize.height) / 2.0)), size: nameSize)
|
|
self.joinNode.frame = CGRect(origin: CGPoint(x: size.width - inset - joinSize.width, y: floor((size.height - joinSize.height) / 2.0)), size: joinSize)
|
|
self.checkNode.frame = CGRect(origin: CGPoint(x: size.width - inset - checkSize.width, y: floor((size.height - checkSize.height) / 2.0)), size: checkSize)
|
|
self.activityIndicator.frame = CGRect(origin: CGPoint(x: size.width - inset - indicatorSize.width, y: floor((size.height - indicatorSize.height) / 2.0)), size: indicatorSize)
|
|
}
|
|
}
|
|
|
|
public func transitionNode(media: InstantPageMedia) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
|
|
return nil
|
|
}
|
|
|
|
public func updateHiddenMedia(media: InstantPageMedia?) {
|
|
}
|
|
|
|
public func updateIsVisible(_ isVisible: Bool) {
|
|
}
|
|
|
|
@objc func buttonPressed() {
|
|
if let peer = self.peer {
|
|
self.openPeer(peer)
|
|
}
|
|
}
|
|
|
|
@objc func joinPressed() {
|
|
if let peer = self.peer, case .notJoined = self.joinState {
|
|
self.updateJoinState(.inProgress)
|
|
self.joinDisposable.set((self.context.engine.peers.joinChannel(peerId: peer.id, hash: nil) |> deliverOnMainQueue).start(error: { [weak self] _ in
|
|
if let strongSelf = self {
|
|
if case .inProgress = strongSelf.joinState {
|
|
strongSelf.updateJoinState(.notJoined)
|
|
}
|
|
}
|
|
}))
|
|
}
|
|
}
|
|
}
|