Swiftgram/submodules/PeerInfoUI/Sources/PeerAllowedReactionListController.swift
2021-12-17 00:52:08 +04:00

318 lines
11 KiB
Swift

import Foundation
import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
import Postbox
import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
import ItemListUI
import PresentationDataUtils
import AccountContext
import PresentationDataUtils
private final class PeerAllowedReactionListControllerArguments {
let context: AccountContext
let toggleAll: () -> Void
let toggleItem: (String) -> Void
init(
context: AccountContext,
toggleAll: @escaping () -> Void,
toggleItem: @escaping (String) -> Void
) {
self.context = context
self.toggleAll = toggleAll
self.toggleItem = toggleItem
}
}
private enum PeerAllowedReactionListControllerSection: Int32 {
case all
case items
}
private enum PeerAllowedReactionListControllerEntry: ItemListNodeEntry {
enum StableId: Hashable {
case allowAll
case allowAllInfo
case itemsHeader
case item(String)
}
case allowAll(text: String, isEnabled: Bool)
case allowAllInfo(String)
case itemsHeader(String)
case item(index: Int, value: String, file: TelegramMediaFile?, text: String, isEnabled: Bool)
var section: ItemListSectionId {
switch self {
case .allowAll, .allowAllInfo:
return PeerAllowedReactionListControllerSection.all.rawValue
case .itemsHeader, .item:
return PeerAllowedReactionListControllerSection.items.rawValue
}
}
var stableId: StableId {
switch self {
case .allowAll:
return .allowAll
case .allowAllInfo:
return .allowAllInfo
case .itemsHeader:
return .itemsHeader
case let .item(_, value, _, _, _):
return .item(value)
}
}
var sortId: Int {
switch self {
case .allowAll:
return 0
case .allowAllInfo:
return 1
case .itemsHeader:
return 2
case let .item(index, _, _, _, _):
return 100 + index
}
}
static func ==(lhs: PeerAllowedReactionListControllerEntry, rhs: PeerAllowedReactionListControllerEntry) -> Bool {
switch lhs {
case let .allowAll(text, isEnabled):
if case .allowAll(text, isEnabled) = rhs {
return true
} else {
return false
}
case let .allowAllInfo(text):
if case .allowAllInfo(text) = rhs {
return true
} else {
return false
}
case let .itemsHeader(text):
if case .itemsHeader(text) = rhs {
return true
} else {
return false
}
case let .item(index, value, file, text, isEnabled):
if case .item(index, value, file, text, isEnabled) = rhs {
return true
} else {
return false
}
}
}
static func <(lhs: PeerAllowedReactionListControllerEntry, rhs: PeerAllowedReactionListControllerEntry) -> Bool {
return lhs.sortId < rhs.sortId
}
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
let arguments = arguments as! PeerAllowedReactionListControllerArguments
switch self {
case let .allowAll(text, isEnabled):
return ItemListSwitchItem(presentationData: presentationData, title: text, value: isEnabled, sectionId: self.section, style: .blocks, updated: { _ in
arguments.toggleAll()
})
case let .allowAllInfo(text):
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
case let .itemsHeader(text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
case let .item(_, value, file, text, isEnabled):
return ItemListReactionItem(
context: arguments.context,
presentationData: presentationData,
file: file,
title: text,
value: isEnabled,
sectionId: self.section,
style: .blocks,
updated: { _ in
arguments.toggleItem(value)
}
)
}
}
}
private struct PeerAllowedReactionListControllerState: Equatable {
var updatedAllowedReactions: Set<String>? = nil
}
private func peerAllowedReactionListControllerEntries(
presentationData: PresentationData,
availableReactions: AvailableReactions?,
cachedData: CachedPeerData?,
state: PeerAllowedReactionListControllerState
) -> [PeerAllowedReactionListControllerEntry] {
var entries: [PeerAllowedReactionListControllerEntry] = []
if let availableReactions = availableReactions, let allowedReactions = state.updatedAllowedReactions {
entries.append(.allowAll(text: "Allow Reactions", isEnabled: !allowedReactions.isEmpty))
entries.append(.allowAllInfo("Allow subscribers to reacts to channel posts."))
entries.append(.itemsHeader("AVAILABLE REACTIONS"))
var index = 0
for availableReaction in availableReactions.reactions {
entries.append(.item(index: index, value: availableReaction.value, file: availableReaction.staticIcon, text: availableReaction.title, isEnabled: allowedReactions.contains(availableReaction.value)))
index += 1
}
}
return entries
}
public func peerAllowedReactionListController(
context: AccountContext,
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
peerId: PeerId
) -> ViewController {
let statePromise = ValuePromise(PeerAllowedReactionListControllerState(), ignoreRepeated: true)
let stateValue = Atomic(value: PeerAllowedReactionListControllerState())
let updateState: ((PeerAllowedReactionListControllerState) -> PeerAllowedReactionListControllerState) -> Void = { f in
statePromise.set(stateValue.modify { f($0) })
}
var dismissImpl: (() -> Void)?
let _ = dismissImpl
let actionsDisposable = DisposableSet()
actionsDisposable.add((context.account.postbox.transaction { transaction -> Set<String>? in
let cachedData = transaction.getPeerCachedData(peerId: peerId)
if let cachedData = cachedData as? CachedChannelData {
return cachedData.allowedReactions.flatMap(Set.init)
} else if let cachedData = cachedData as? CachedGroupData {
return cachedData.allowedReactions.flatMap(Set.init)
} else {
return nil
}
}
|> deliverOnMainQueue).start(next: { allowedReactions in
updateState { state in
var state = state
state.updatedAllowedReactions = allowedReactions
return state
}
}))
let arguments = PeerAllowedReactionListControllerArguments(
context: context,
toggleAll: {
let _ = (context.engine.stickers.availableReactions()
|> take(1)
|> deliverOnMainQueue).start(next: { availableReactions in
guard let availableReactions = availableReactions else {
return
}
updateState { state in
var state = state
if var updatedAllowedReactions = state.updatedAllowedReactions {
if updatedAllowedReactions.isEmpty {
for availableReaction in availableReactions.reactions {
updatedAllowedReactions.insert(availableReaction.value)
}
} else {
updatedAllowedReactions.removeAll()
}
state.updatedAllowedReactions = updatedAllowedReactions
}
return state
}
})
},
toggleItem: { reaction in
updateState { state in
var state = state
if var updatedAllowedReactions = state.updatedAllowedReactions {
if updatedAllowedReactions.contains(reaction) {
updatedAllowedReactions.remove(reaction)
} else {
updatedAllowedReactions.insert(reaction)
}
state.updatedAllowedReactions = updatedAllowedReactions
}
return state
}
}
)
let peerView = context.account.viewTracker.peerView(peerId)
|> deliverOnMainQueue
let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData
let signal = combineLatest(queue: .mainQueue(),
presentationData,
statePromise.get(),
context.engine.stickers.availableReactions(),
peerView
)
|> deliverOnMainQueue
|> map { presentationData, state, availableReactions, peerView -> (ItemListControllerState, (ItemListNodeState, Any)) in
//TODO:localize
let title: String = "Reactions"
let entries = peerAllowedReactionListControllerEntries(
presentationData: presentationData,
availableReactions: availableReactions,
cachedData: peerView.cachedData,
state: state
)
let controllerState = ItemListControllerState(
presentationData: ItemListPresentationData(presentationData),
title: .text(title),
leftNavigationButton: nil,
rightNavigationButton: nil,
backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back),
animateChanges: false
)
let listState = ItemListNodeState(
presentationData: ItemListPresentationData(presentationData),
entries: entries,
style: .blocks,
animateChanges: true
)
return (controllerState, (listState, arguments))
}
|> afterDisposed {
actionsDisposable.dispose()
}
let controller = ItemListController(context: context, state: signal)
controller.willDisappear = { _ in
let _ = (context.account.postbox.transaction { transaction -> Set<String>? in
let cachedData = transaction.getPeerCachedData(peerId: peerId)
if let cachedData = cachedData as? CachedChannelData {
return cachedData.allowedReactions.flatMap(Set.init)
} else if let cachedData = cachedData as? CachedGroupData {
return cachedData.allowedReactions.flatMap(Set.init)
} else {
return nil
}
}
|> deliverOnMainQueue).start(next: { initialAllowedReactions in
let updatedAllowedReactions = stateValue.with({ $0 }).updatedAllowedReactions
if let updatedAllowedReactions = updatedAllowedReactions, initialAllowedReactions != updatedAllowedReactions {
let _ = context.engine.peers.updatePeerAllowedReactions(peerId: peerId, allowedReactions: Array(updatedAllowedReactions)).start()
}
})
}
dismissImpl = { [weak controller] in
guard let controller = controller else {
return
}
controller.dismiss()
}
return controller
}