Swiftgram/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenMemberItem.swift
2020-07-10 13:02:28 +03:00

256 lines
9.9 KiB
Swift

import Foundation
import UIKit
import AsyncDisplayKit
import Display
import TelegramPresentationData
import ItemListPeerItem
import SwiftSignalKit
import AccountContext
import Postbox
import SyncCore
import TelegramCore
import ItemListUI
enum PeerInfoScreenMemberItemAction {
case open
case promote
case restrict
case remove
}
final class PeerInfoScreenMemberItem: PeerInfoScreenItem {
let id: AnyHashable
let context: AccountContext
let enclosingPeer: Peer?
let member: PeerInfoMember
let badge: String?
let action: ((PeerInfoScreenMemberItemAction) -> Void)?
let contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
init(
id: AnyHashable,
context: AccountContext,
enclosingPeer: Peer?,
member: PeerInfoMember,
badge: String? = nil,
action: ((PeerInfoScreenMemberItemAction) -> Void)?,
contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil
) {
self.id = id
self.context = context
self.enclosingPeer = enclosingPeer
self.member = member
self.badge = badge
self.action = action
self.contextAction = contextAction
}
func node() -> PeerInfoScreenItemNode {
return PeerInfoScreenMemberItemNode()
}
}
private final class PeerInfoScreenMemberItemNode: PeerInfoScreenItemNode {
private let selectionNode: PeerInfoScreenSelectableBackgroundNode
private let bottomSeparatorNode: ASDisplayNode
private var item: PeerInfoScreenMemberItem?
private var itemNode: ItemListPeerItemNode?
override init() {
var bringToFrontForHighlightImpl: (() -> Void)?
self.selectionNode = PeerInfoScreenSelectableBackgroundNode(bringToFrontForHighlight: { bringToFrontForHighlightImpl?() })
self.selectionNode.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)
}
override func didLoad() {
super.didLoad()
let recognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapLongTapOrDoubleTapGesture(_:)))
recognizer.tapActionAtPoint = { point in
return .keepWithSingleTap
}
recognizer.highlight = { [weak self] point in
guard let strongSelf = self else {
return
}
strongSelf.updateTouchesAtPoint(point)
}
self.view.addGestureRecognizer(recognizer)
}
@objc private func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
switch recognizer.state {
case .ended:
if let (gesture, _) = recognizer.lastRecognizedGestureAndLocation {
switch gesture {
case .tap:
if let item = self.item {
item.action?(.open)
}
default:
break
}
}
default:
break
}
}
override func update(width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, transition: ContainedViewLayoutTransition) -> CGFloat {
guard let item = item as? PeerInfoScreenMemberItem else {
return 10.0
}
self.item = item
self.selectionNode.pressed = item.action.flatMap { action in
return {
action(.open)
}
}
let sideInset: CGFloat = 16.0 + safeInsets.left
self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor
let label: String?
if let rank = item.member.rank {
label = rank
} else {
switch item.member.role {
case .creator:
label = presentationData.strings.GroupInfo_LabelOwner
case .admin:
label = presentationData.strings.GroupInfo_LabelAdmin
case .member:
label = nil
}
}
let actions = availableActionsForMemberOfPeer(accountPeerId: item.context.account.peerId, peer: item.enclosingPeer, member: item.member)
var options: [ItemListPeerItemRevealOption] = []
if actions.contains(.promote) && item.enclosingPeer is TelegramChannel {
options.append(ItemListPeerItemRevealOption(type: .neutral, title: presentationData.strings.GroupInfo_ActionPromote, action: {
item.action?(.promote)
}))
}
if actions.contains(.restrict) {
if item.enclosingPeer is TelegramChannel {
options.append(ItemListPeerItemRevealOption(type: .warning, title: presentationData.strings.GroupInfo_ActionRestrict, action: {
item.action?(.restrict)
}))
}
options.append(ItemListPeerItemRevealOption(type: .destructive, title: presentationData.strings.Common_Delete, action: {
item.action?(.remove)
}))
}
if actions.contains(.logout) {
options.append(ItemListPeerItemRevealOption(type: .destructive, title: presentationData.strings.Settings_Context_Logout, action: {
item.action?(.remove)
}))
}
let itemLabel: ItemListPeerItemLabel
if let label = label {
itemLabel = .text(label, .standard)
} else if let badge = item.badge {
itemLabel = .badge(badge)
} else {
itemLabel = .none
}
let itemHeight: ItemListPeerItemHeight
let itemText: ItemListPeerItemText
var synchronousLoads = false
if case .account = item.member {
itemHeight = .generic
itemText = .none
synchronousLoads = true
} else {
itemHeight = .peerList
itemText = .presence
}
let peerItem = ItemListPeerItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: item.context, peer: item.member.peer, height: itemHeight, presence: item.member.presence, text: itemText, label: itemLabel, editing: ItemListPeerItemEditing(editable: !options.isEmpty, editing: false, revealed: nil), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, selectable: false, sectionId: 0, action: nil, setPeerIdWithRevealedOptions: { lhs, rhs in
}, removePeer: { _ in
}, contextAction: item.contextAction, hasTopStripe: false, hasTopGroupInset: false, noInsets: true, displayDecorations: false)
let params = ListViewItemLayoutParams(width: width, leftInset: safeInsets.left, rightInset: safeInsets.right, availableHeight: 1000.0)
let itemNode: ItemListPeerItemNode
if let current = self.itemNode {
itemNode = current
peerItem.updateNode(async: { $0() }, node: {
return itemNode
}, params: params, previousItem: nil, nextItem: nil, animation: .None, completion: { (layout, apply) in
let nodeFrame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: layout.size.height))
itemNode.contentSize = layout.contentSize
itemNode.insets = layout.insets
itemNode.frame = nodeFrame
apply(ListViewItemApply(isOnScreen: true))
})
} else {
var itemNodeValue: ListViewItemNode?
peerItem.nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: synchronousLoads, previousItem: nil, nextItem: nil, completion: { node, apply in
itemNodeValue = node
apply().1(ListViewItemApply(isOnScreen: true))
})
itemNode = itemNodeValue as! ItemListPeerItemNode
self.itemNode = itemNode
self.addSubnode(itemNode)
}
let height = itemNode.contentSize.height
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(), size: itemNode.bounds.size))
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)))
var separatorInset: CGFloat = sideInset
if bottomItem != nil {
separatorInset += 49.0
}
transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: separatorInset, y: height - UIScreenPixel), size: CGSize(width: width - sideInset, height: UIScreenPixel)))
transition.updateAlpha(node: self.bottomSeparatorNode, alpha: bottomItem == nil ? 0.0 : 1.0)
return height
}
private func updateTouchesAtPoint(_ point: CGPoint?) {
guard let item = self.item else {
return
}
var highlight = point != nil
if case .account = item.member {
} else if item.context.account.peerId == item.member.id {
highlight = false
}
if highlight {
self.selectionNode.updateIsHighlighted(true)
} else {
self.selectionNode.updateIsHighlighted(false)
}
}
}