Swiftgram/TelegramUI/PeerInfoController.swift
2017-02-19 23:21:03 +03:00

322 lines
15 KiB
Swift

import Foundation
import Display
import Postbox
import SwiftSignalKit
import TelegramCore
func peerInfoController(account: Account, peer: Peer) -> ViewController? {
if let _ = peer as? TelegramGroup {
return groupInfoController(account: account, peerId: peer.id)
} else if let channel = peer as? TelegramChannel {
if case .group = channel.info {
return groupInfoController(account: account, peerId: peer.id)
}
}
return nil
}
final class PeerInfoControllerInteraction {
let updateState: ((PeerInfoState?) -> PeerInfoState?) -> Void
let openSharedMedia: () -> Void
let changeNotificationMuteSettings: () -> Void
let openPeerInfo: (PeerId) -> Void
init(updateState: @escaping ((PeerInfoState?) -> PeerInfoState?) -> Void, openSharedMedia: @escaping () -> Void, changeNotificationMuteSettings: @escaping () -> Void, openPeerInfo: @escaping (PeerId) -> Void) {
self.updateState = updateState
self.openSharedMedia = openSharedMedia
self.changeNotificationMuteSettings = changeNotificationMuteSettings
self.openPeerInfo = openPeerInfo
}
}
private struct PeerInfoSortableStableId: Hashable {
let id: PeerInfoEntryStableId
static func ==(lhs: PeerInfoSortableStableId, rhs: PeerInfoSortableStableId) -> Bool {
return lhs.id.isEqual(to: rhs.id)
}
var hashValue: Int {
return self.id.hashValue
}
}
private struct PeerInfoSortableEntry: Identifiable, Comparable {
let entry: PeerInfoEntry
var stableId: PeerInfoSortableStableId {
return PeerInfoSortableStableId(id: self.entry.stableId)
}
static func ==(lhs: PeerInfoSortableEntry, rhs: PeerInfoSortableEntry) -> Bool {
return lhs.entry.isEqual(to: rhs.entry)
}
static func <(lhs: PeerInfoSortableEntry, rhs: PeerInfoSortableEntry) -> Bool {
return lhs.entry.isOrderedBefore(rhs.entry)
}
}
private struct PeerInfoEntryTransition {
let deletions: [ListViewDeleteItem]
let insertions: [ListViewInsertItem]
let updates: [ListViewUpdateItem]
}
private func preparedPeerInfoEntryTransition(account: Account, from fromEntries: [PeerInfoSortableEntry], to toEntries: [PeerInfoSortableEntry], interaction: PeerInfoControllerInteraction) -> PeerInfoEntryTransition {
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.entry.item(account: account, interaction: interaction), directionHint: nil) }
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.entry.item(account: account, interaction: interaction), directionHint: nil) }
return PeerInfoEntryTransition(deletions: deletions, insertions: insertions, updates: updates)
}
private struct PeerInfoEquatableState: Equatable {
let state: PeerInfoState?
static func ==(lhs: PeerInfoEquatableState, rhs: PeerInfoEquatableState) -> Bool {
if let lhsState = lhs.state, let rhsState = rhs.state {
return lhsState.isEqual(to: rhsState)
} else if (lhs.state != nil) != (rhs.state != nil) {
return false
} else {
return true
}
}
}
public final class PeerInfoController: ListController {
private let account: Account
private let peerId: PeerId
private var _ready = Promise<Bool>()
override public var ready: Promise<Bool> {
return self._ready
}
private var didSetReady = false
private let transitionDisposable = MetaDisposable()
private let changeSettingsDisposable = MetaDisposable()
private let additionalInfoDisposable = MetaDisposable()
private var currentListStyle: ItemListStyle = .plain
private var state = PeerInfoEquatableState(state: nil) {
didSet {
self.statePromise.set(.single(self.state))
}
}
private var statePromise = Promise<PeerInfoEquatableState>(PeerInfoEquatableState(state: nil))
private var leftNavigationButtonItem: UIBarButtonItem?
private var leftNavigationButton: PeerInfoNavigationButton?
private var rightNavigationButtonItem: UIBarButtonItem?
private var rightNavigationButton: PeerInfoNavigationButton?
public init(account: Account, peerId: PeerId) {
self.account = account
self.peerId = peerId
super.init()
self.title = "Info"
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.transitionDisposable.dispose()
self.changeSettingsDisposable.dispose()
self.additionalInfoDisposable.dispose()
}
override public func displayNodeDidLoad() {
super.displayNodeDidLoad()
let interaction = PeerInfoControllerInteraction(updateState: { [weak self] f in
if let strongSelf = self {
strongSelf.state = PeerInfoEquatableState(state: f(strongSelf.state.state))
}
}, openSharedMedia: { [weak self] in
if let strongSelf = self {
if let controller = peerSharedMediaController(account: strongSelf.account, peerId: strongSelf.peerId) {
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller)
}
}
}, changeNotificationMuteSettings: { [weak self] in
if let strongSelf = self {
let controller = ActionSheetController()
let dismissAction: () -> Void = { [weak controller] in
controller?.dismissAnimated()
}
let notificationAction: (Int32) -> Void = { [weak strongSelf] muteUntil in
if let strongSelf = strongSelf {
let muteState: PeerMuteState
if muteUntil <= 0 {
muteState = .unmuted
} else if muteUntil == Int32.max {
muteState = .muted(until: Int32.max)
} else {
muteState = .muted(until: Int32(Date().timeIntervalSince1970) + muteUntil)
}
strongSelf.changeSettingsDisposable.set(changePeerNotificationSettings(account: strongSelf.account, peerId: strongSelf.peerId, settings: TelegramPeerNotificationSettings(muteState: muteState, messageSound: PeerMessageSound.bundledModern(id: 0))).start())
}
}
controller.setItemGroups([
ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: "Enable", action: {
dismissAction()
notificationAction(0)
}),
ActionSheetButtonItem(title: "Mute for 1 hour", action: {
dismissAction()
notificationAction(1 * 60 * 60)
}),
ActionSheetButtonItem(title: "Mute for 8 hours", action: {
dismissAction()
notificationAction(8 * 60 * 60)
}),
ActionSheetButtonItem(title: "Mute for 2 days", action: {
dismissAction()
notificationAction(2 * 24 * 60 * 60)
}),
ActionSheetButtonItem(title: "Disable", action: {
dismissAction()
notificationAction(Int32.max)
})
]),
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: "Cancel", action: { dismissAction() })])
])
strongSelf.present(controller, in: .window)
}
}, openPeerInfo: { [weak self] peerId in
if let strongSelf = self {
let controller = PeerInfoController(account: strongSelf.account, peerId: peerId)
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller)
}
})
self.listDisplayNode.backgroundColor = UIColor.white
let previousEntries = Atomic<[PeerInfoSortableEntry]?>(value: nil)
let account = self.account
let transition = combineLatest(account.viewTracker.peerView(self.peerId), self.statePromise.get()
|> distinctUntilChanged)
|> map { view, state -> (PeerInfoEntryTransition, ItemListStyle, Bool, Bool, PeerInfoNavigationButton?, PeerInfoNavigationButton?) in
let infoEntries = peerInfoEntries(view: view, state: state.state)
let entries = infoEntries.entries.map { PeerInfoSortableEntry(entry: $0) }
assert(entries == entries.sorted())
let previous = previousEntries.swap(entries)
let style: ItemListStyle
if let _ = view.peers[view.peerId] as? TelegramGroup {
style = .blocks
} else if let channel = view.peers[view.peerId] as? TelegramChannel, case .group = channel.info {
style = .blocks
} else {
style = .plain
}
let animated: Bool
if let previous = previous {
animated = (entries.count - previous.count) < 20
} else {
animated = false
}
return (preparedPeerInfoEntryTransition(account: account, from: previous ?? [], to: entries, interaction: interaction), style, previous == nil, animated, infoEntries.leftNavigationButton, infoEntries.rightNavigationButton)
}
|> deliverOnMainQueue
self.transitionDisposable.set(transition.start(next: { [weak self] (transition, style, firstTime, animated, leftButton, rightButton) in
if let strongSelf = self {
strongSelf.enqueueTransition(transition, style: style, firstTime: firstTime, animated: animated)
if let leftButton = leftButton {
if let leftNavigationButtonItem = strongSelf.leftNavigationButtonItem {
if leftNavigationButtonItem.title != leftButton.title {
strongSelf.leftNavigationButtonItem = UIBarButtonItem(title: leftButton.title, style: .plain, target: strongSelf, action: #selector(strongSelf.leftNavigationButtonPressed))
strongSelf.navigationItem.setLeftBarButton(strongSelf.leftNavigationButtonItem, animated: false)
}
strongSelf.leftNavigationButton = leftButton
} else {
strongSelf.leftNavigationButton = leftButton
strongSelf.leftNavigationButtonItem = UIBarButtonItem(title: leftButton.title, style: .plain, target: strongSelf, action: #selector(strongSelf.leftNavigationButtonPressed))
strongSelf.navigationItem.setLeftBarButton(strongSelf.leftNavigationButtonItem, animated: false)
}
} else if strongSelf.leftNavigationButtonItem != nil {
strongSelf.leftNavigationButtonItem = nil
strongSelf.leftNavigationButton = nil
strongSelf.navigationItem.setLeftBarButton(nil, animated: false)
}
if let rightButton = rightButton {
if let rightNavigationButtonItem = strongSelf.rightNavigationButtonItem {
if rightNavigationButtonItem.title != rightButton.title {
strongSelf.rightNavigationButtonItem = UIBarButtonItem(title: rightButton.title, style: .plain, target: strongSelf, action: #selector(strongSelf.rightNavigationButtonPressed))
strongSelf.navigationItem.setRightBarButton(strongSelf.rightNavigationButtonItem, animated: false)
}
strongSelf.rightNavigationButton = rightButton
} else {
strongSelf.rightNavigationButton = rightButton
strongSelf.rightNavigationButtonItem = UIBarButtonItem(title: rightButton.title, style: .plain, target: strongSelf, action: #selector(strongSelf.rightNavigationButtonPressed))
strongSelf.navigationItem.setRightBarButton(strongSelf.rightNavigationButtonItem, animated: false)
}
} else if strongSelf.rightNavigationButtonItem != nil {
strongSelf.rightNavigationButtonItem = nil
strongSelf.rightNavigationButton = nil
strongSelf.navigationItem.setRightBarButton(nil, animated: false)
}
}
}))
if self.peerId.namespace == Namespaces.Peer.CloudChannel {
self.additionalInfoDisposable.set(self.account.viewTracker.updatedCachedChannelParticipants(self.peerId, forceImmediateUpdate: true).start())
}
}
private func enqueueTransition(_ transition: PeerInfoEntryTransition, style: ItemListStyle, firstTime: Bool, animated: Bool) {
if self.currentListStyle != style {
self.currentListStyle = style
switch style {
case .plain:
self.listDisplayNode.backgroundColor = .white
case .blocks:
self.listDisplayNode.backgroundColor = UIColor(0xefeff4)
}
}
var options = ListViewDeleteAndInsertOptions()
if firstTime {
options.insert(.Synchronous)
options.insert(.LowLatency)
} else if animated {
options.insert(.AnimateInsertion)
}
self.listDisplayNode.listView.transaction(deleteIndices: transition.deletions, insertIndicesAndItems: transition.insertions, updateIndicesAndItems: transition.updates, options: options, updateOpaqueState: nil, completion: { [weak self] _ in
if let strongSelf = self {
if !strongSelf.didSetReady {
strongSelf.didSetReady = true
strongSelf._ready.set(.single(true))
}
}
})
}
@objc func leftNavigationButtonPressed() {
if let leftNavigationButton = self.leftNavigationButton {
self.state = PeerInfoEquatableState(state: leftNavigationButton.action(self.state.state))
}
}
@objc func rightNavigationButtonPressed() {
if let rightNavigationButton = self.rightNavigationButton {
self.state = PeerInfoEquatableState(state: rightNavigationButton.action(self.state.state))
}
}
}