mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
Reaction list setup
This commit is contained in:
@@ -0,0 +1,309 @@
|
||||
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):
|
||||
let _ = file
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "\(value) \(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
|
||||
}
|
||||
Reference in New Issue
Block a user