Files
TelegramSwift/Telegram-Mac/ModalOptionSetController.swift
Mikhail Filimonov 7e1ad83535 - release
2024-04-11 09:11:18 +04:00

178 lines
6.0 KiB
Swift

//
// ModalOptionSetController.swift
// Telegram
//
// Created by Mikhail Filimonov on 10/06/2019.
// Copyright © 2019 Telegram. All rights reserved.
//
import Cocoa
import SwiftSignalKit
import TGUIKit
private final class ModalOptionsArguments {
let context: AccountContext
let toggleOption: (Int)->Void
init(context: AccountContext, toggleOption:@escaping(Int)->Void) {
self.context = context
self.toggleOption = toggleOption
}
}
struct ModalOptionSet : Equatable {
let title: String
let selected: Bool
let editable: Bool
init(title: String, selected: Bool, editable: Bool) {
self.title = title
self.selected = selected
self.editable = editable
}
func withUpdatedSelected(_ selected: Bool) -> ModalOptionSet {
return ModalOptionSet(title: self.title, selected: selected, editable: self.editable)
}
}
enum ModalOptionSetResult {
case selected
case none
}
private struct ModalOptionsState: Equatable {
let options: [ModalOptionSet]
let selectOne: Bool
init(options:[ModalOptionSet], selectOne: Bool) {
self.options = options
self.selectOne = selectOne
}
func withToggledOptionAt(_ index: Int) -> ModalOptionsState {
var options = self.options
options[index] = options[index].withUpdatedSelected(!options[index].selected)
if selectOne {
for i in 0 ..< options.count {
options[i] = options[i].withUpdatedSelected(false)
}
options[index] = options[index].withUpdatedSelected(true)
}
return ModalOptionsState(options: options, selectOne: self.selectOne)
}
}
private let _id_title: InputDataIdentifier = InputDataIdentifier("_id_title")
private let _id_border: InputDataIdentifier = InputDataIdentifier("_id_border")
private func _id_option(_ index: Int)->InputDataIdentifier {
return InputDataIdentifier("_id_option_\(index)")
}
private func modalOptionsSetEntries(state: ModalOptionsState, desc: String?, header: String?, arguments: ModalOptionsArguments) -> [InputDataEntry] {
var entries: [InputDataEntry] = []
var sectionId: Int32 = 0
var index: Int32 = 0
entries.append(.sectionId(sectionId, type: .customModern(10)))
sectionId += 1
if let header {
entries.append(.desc(sectionId: sectionId, index: index, text: .plain(header), data: .init(color: theme.colors.listGrayText, viewType: .textTopItem)))
index += 1
}
if let desc = desc {
entries.append(InputDataEntry.custom(sectionId: sectionId, index: index, value: .none, identifier: _id_title, equatable: InputDataEquatable(desc), comparable: nil, item: { initialSize, stableId in
return GeneralBlockTextRowItem.init(initialSize, stableId: stableId, viewType: .singleItem, text: desc, font: .normal(.text))
}))
index += 1
entries.append(.sectionId(sectionId, type: .normal))
sectionId += 1
}
for (i, option) in state.options.enumerated() {
entries.append(InputDataEntry.custom(sectionId: sectionId, index: index, value: .none, identifier: _id_option(i), equatable: InputDataEquatable(option), comparable: nil, item: { initialSize, stableId in
return GeneralInteractedRowItem(initialSize, stableId: stableId, name: option.title, type: .selectable(option.selected), viewType: bestGeneralViewType(state.options, for: i), action: {
arguments.toggleOption(i)
}, enabled: option.editable, disabledAction: {
})
}))
index += 1
}
entries.append(.sectionId(sectionId, type: .customModern(20)))
sectionId += 1
return entries
}
func ModalOptionSetController(context: AccountContext, options: [ModalOptionSet], selectOne: Bool = false, actionText: (String, NSColor), desc: String? = nil, header: String? = nil, title: String, result: @escaping ([ModalOptionSetResult])->Void) -> InputDataModalController {
let initialState: ModalOptionsState = ModalOptionsState(options: options, selectOne: selectOne)
let stateValue: Atomic<ModalOptionsState> = Atomic(value: initialState)
let statePromise: ValuePromise<ModalOptionsState> = ValuePromise(initialState, ignoreRepeated: true)
let updateState: (_ f:(ModalOptionsState)->ModalOptionsState)->Void = { f in
statePromise.set(stateValue.modify(f))
}
let arguments = ModalOptionsArguments(context: context, toggleOption: { index in
updateState {
$0.withToggledOptionAt(index)
}
})
let actionsDisposable = DisposableSet()
let dataSignal = statePromise.get() |> mapToSignal { state in
return .single(modalOptionsSetEntries(state: state, desc: desc, header: header, arguments: arguments))
} |> map { entries in
return InputDataSignalValue(entries: entries)
}
var dismiss:(()->Void)?
let controller = InputDataController(dataSignal: dataSignal, title: title, validateData: { data in
result(stateValue.with { state in
return state.options.map { option in
if option.selected {
return .selected
} else {
return .none
}
}
})
dismiss?()
return .fail(.none)
}, afterDisappear: {
actionsDisposable.dispose()
})
let modalInteractions: ModalInteractions = ModalInteractions(acceptTitle: actionText.0, accept: { [weak controller] in
controller?.validateInputValues()
}, singleButton: true)
let modalController = InputDataModalController(controller, modalInteractions: modalInteractions, size: NSMakeSize(300, 300))
dismiss = { [weak modalController] in
modalController?.close()
}
controller.leftModalHeader = ModalHeaderData(image: theme.icons.modalClose, handler: dismiss)
return modalController
}