Swiftgram/submodules/InstantPageUI/Sources/InstantPagePeerReferenceNode.swift
2019-10-11 16:05:35 +04:00

321 lines
13 KiB
Swift

import Foundation
import UIKit
import TelegramCore
import Postbox
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
}
}
}
}
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: (PeerId) -> 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: Peer?
private var peerDisposable: Disposable?
private let joinDisposable = MetaDisposable()
private var joinState: JoinState = .none
init(context: AccountContext, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, theme: InstantPageTheme, initialPeer: Peer, safeInset: CGFloat, transparent: Bool, rtl: Bool, openPeer: @escaping (PeerId) -> 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 signal = actualizedPeer(postbox: account.postbox, network: account.network, peer: initialPeer)
|> mapToSignal({ peer -> Signal<Peer, NoError> in
if let peer = peer as? TelegramChannel, let username = peer.username, peer.accessHash == nil {
return .single(peer) |> then(resolvePeerByName(account: account, name: username)
|> mapToSignal({ peerId -> Signal<Peer, NoError> in
if let peerId = peerId {
return account.postbox.transaction({ transaction -> Peer in
return transaction.getPeer(peerId) ?? peer
})
} else {
return .single(peer)
}
}))
} else {
return .single(peer)
}
})
self.peerDisposable = (signal |> deliverOnMainQueue).start(next: { [weak self] peer in
if let strongSelf = self {
strongSelf.peer = peer
if let peer = peer as? TelegramChannel {
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()
}
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)
}
}
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()
}
}
}
}
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)
}
}
func transitionNode(media: InstantPageMedia) -> (ASDisplayNode, () -> (UIView?, UIView?))? {
return nil
}
func updateHiddenMedia(media: InstantPageMedia?) {
}
func updateIsVisible(_ isVisible: Bool) {
}
@objc func buttonPressed() {
if let peer = self.peer {
self.openPeer(peer.id)
}
}
@objc func joinPressed() {
if let peer = self.peer, case .notJoined = self.joinState {
self.updateJoinState(.inProgress)
self.joinDisposable.set((joinChannel(account: self.context.account, peerId: peer.id) |> deliverOnMainQueue).start(error: { [weak self] _ in
if let strongSelf = self {
if case .inProgress = strongSelf.joinState {
strongSelf.updateJoinState(.notJoined)
}
}
}))
}
}
}