mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
WIP Settings search
This commit is contained in:
parent
7f7cd9c06b
commit
916740e988
@ -55,6 +55,7 @@ public final class SGItemListArguments<BoolSetting: Hashable, SliderSetting: Has
|
||||
let setOneFromManyValue: (OneFromManySetting) -> Void
|
||||
let openDisclosureLink: (DisclosureLink) -> Void
|
||||
let action: (ActionType) -> Void
|
||||
let searchInput: (String) -> Void
|
||||
|
||||
|
||||
public init(
|
||||
@ -64,7 +65,8 @@ public final class SGItemListArguments<BoolSetting: Hashable, SliderSetting: Has
|
||||
updateSliderValue: @escaping (SliderSetting, Int32) -> Void = { _,_ in },
|
||||
setOneFromManyValue: @escaping (OneFromManySetting) -> Void = { _ in },
|
||||
openDisclosureLink: @escaping (DisclosureLink) -> Void = { _ in},
|
||||
action: @escaping (ActionType) -> Void = { _ in }
|
||||
action: @escaping (ActionType) -> Void = { _ in },
|
||||
searchInput: @escaping (String) -> Void = { _ in }
|
||||
) {
|
||||
self.context = context
|
||||
//
|
||||
@ -73,6 +75,7 @@ public final class SGItemListArguments<BoolSetting: Hashable, SliderSetting: Has
|
||||
self.setOneFromManyValue = setOneFromManyValue
|
||||
self.openDisclosureLink = openDisclosureLink
|
||||
self.action = action
|
||||
self.searchInput = searchInput
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,6 +88,7 @@ public enum SGItemListUIEntry<Section: SGItemListSection, BoolSetting: Hashable,
|
||||
case disclosure(id: Int, section: Section, link: DisclosureLink, text: String)
|
||||
case peerColorDisclosurePreview(id: Int, section: Section, name: String, color: UIColor)
|
||||
case action(id: Int, section: Section, actionType: ActionType, text: String, kind: ItemListActionKind)
|
||||
case searchInput(id: Int, section: Section, title: NSAttributedString, text: String, placeholder: String)
|
||||
|
||||
public var section: ItemListSectionId {
|
||||
switch self {
|
||||
@ -108,6 +112,9 @@ public enum SGItemListUIEntry<Section: SGItemListSection, BoolSetting: Hashable,
|
||||
|
||||
case let .action(_, sectionId, _, _, _):
|
||||
return sectionId.rawValue
|
||||
|
||||
case let .searchInput(_, sectionId, _, _, _):
|
||||
return sectionId.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,6 +136,8 @@ public enum SGItemListUIEntry<Section: SGItemListSection, BoolSetting: Hashable,
|
||||
return stableIdValue
|
||||
case let .action(stableIdValue, _, _, _, _):
|
||||
return stableIdValue
|
||||
case let .searchInput(stableIdValue, _, _, _, _):
|
||||
return stableIdValue
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,6 +169,9 @@ public enum SGItemListUIEntry<Section: SGItemListSection, BoolSetting: Hashable,
|
||||
return id1 == id2 && section1 == section2 && settingName1 == settingName2 && text1 == text2 && value1 == value2 && enabled1 == enabled2
|
||||
case let (.action(id1, section1, actionType1, text1, kind1), .action(id2, section2, actionType2, text2, kind2)):
|
||||
return id1 == id2 && section1 == section2 && actionType1 == actionType2 && text1 == text2 && kind1 == kind2
|
||||
|
||||
case let (.searchInput(id1, lhsValue1, lhsValue2, lhsValue3, lhsValue4), .searchInput(id2, rhsValue1, rhsValue2, rhsValue3, rhsValue4)):
|
||||
return id1 == id2 && lhsValue1 == rhsValue1 && lhsValue2 == rhsValue2 && lhsValue3 == rhsValue3 && lhsValue4 == rhsValue4
|
||||
|
||||
default:
|
||||
return false
|
||||
@ -206,6 +218,80 @@ public enum SGItemListUIEntry<Section: SGItemListSection, BoolSetting: Hashable,
|
||||
return ItemListActionItem(presentationData: presentationData, title: text, kind: kind, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.action(actionType)
|
||||
})
|
||||
case let .searchInput(_, _, title, text, placeholder):
|
||||
return ItemListSingleLineInputItem(presentationData: presentationData, title: title, text: text, placeholder: placeholder, returnKeyType: .done, spacing: 3.0, clearType: .always, selectAllOnFocus: true, secondaryStyle: true, sectionId: self.section, textUpdated: { input in arguments.searchInput(input) }, action: {}, dismissKeyboardOnEnter: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func filterSGItemListUIEntrires<Section: SGItemListSection & Hashable, BoolSetting: Hashable, SliderSetting: Hashable, OneFromManySetting: Hashable, DisclosureLink: Hashable, ActionType: Hashable>(
|
||||
entries: [SGItemListUIEntry<Section, BoolSetting, SliderSetting, OneFromManySetting, DisclosureLink, ActionType>],
|
||||
by searchQuery: String?
|
||||
) -> [SGItemListUIEntry<Section, BoolSetting, SliderSetting, OneFromManySetting, DisclosureLink, ActionType>] {
|
||||
|
||||
guard let query = searchQuery?.lowercased(), !query.isEmpty else {
|
||||
return entries
|
||||
}
|
||||
|
||||
var sectionsWithMatches: Set<Section> = []
|
||||
var filteredEntries: [SGItemListUIEntry<Section, BoolSetting, SliderSetting, OneFromManySetting, DisclosureLink, ActionType>] = []
|
||||
|
||||
func entryMatches(_ entry: SGItemListUIEntry<Section, BoolSetting, SliderSetting, OneFromManySetting, DisclosureLink, ActionType>, query: String) -> Bool {
|
||||
switch entry {
|
||||
case .header(_, _, let text, _):
|
||||
return text.lowercased().contains(query)
|
||||
case .toggle(_, _, _, _, let text, _):
|
||||
return text.lowercased().contains(query)
|
||||
case .notice(_, _, let text):
|
||||
return text.lowercased().contains(query)
|
||||
case .percentageSlider:
|
||||
return false // Assuming percentage sliders don't have searchable text
|
||||
case .oneFromManySelector(_, _, _, let text, let value, _):
|
||||
return text.lowercased().contains(query) || value.lowercased().contains(query)
|
||||
case .disclosure(_, _, _, let text):
|
||||
return text.lowercased().contains(query)
|
||||
case .peerColorDisclosurePreview(_, _, let name, _):
|
||||
return name.lowercased().contains(query)
|
||||
case .action(_, _, _, let text, _):
|
||||
return text.lowercased().contains(query)
|
||||
case .searchInput:
|
||||
return true // Never hidding search
|
||||
}
|
||||
}
|
||||
|
||||
// First pass: identify sections with matches
|
||||
for entry in entries {
|
||||
if entryMatches(entry, query: query) {
|
||||
switch entry {
|
||||
case .header(_, let section, _, _),
|
||||
.toggle(_, let section, _, _, _, _),
|
||||
.notice(_, let section, _),
|
||||
.percentageSlider(_, let section, _, _),
|
||||
.oneFromManySelector(_, let section, _, _, _, _),
|
||||
.disclosure(_, let section, _, _),
|
||||
.peerColorDisclosurePreview(_, let section, _, _),
|
||||
.action(_, let section, _, _, _):
|
||||
sectionsWithMatches.insert(section)
|
||||
case .searchInput:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: keep matching entries and headers of sections with matches
|
||||
for entry in entries {
|
||||
switch entry {
|
||||
case .header(_, let section, _, _):
|
||||
if sectionsWithMatches.contains(section) {
|
||||
filteredEntries.append(entry)
|
||||
}
|
||||
default:
|
||||
if entryMatches(entry, query: query) {
|
||||
filteredEntries.append(entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filteredEntries
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import UndoUI
|
||||
|
||||
|
||||
private enum SGControllerSection: Int32, SGItemListSection {
|
||||
case search
|
||||
case content
|
||||
case tabs
|
||||
case folders
|
||||
@ -118,15 +119,20 @@ private struct PeerNameColorScreenState: Equatable {
|
||||
var updatedBackgroundEmojiId: Int64?
|
||||
}
|
||||
|
||||
private struct SGSettingsControllerState: Equatable {
|
||||
var searchQuery: String?
|
||||
}
|
||||
|
||||
private typealias SGControllerEntry = SGItemListUIEntry<SGControllerSection, SGBoolSetting, SGSliderSetting, SGOneFromManySetting, SGDisclosureLink, AnyHashable>
|
||||
|
||||
private func SGControllerEntries(presentationData: PresentationData, callListSettings: CallListSettings, experimentalUISettings: ExperimentalUISettings, SGSettings: SGUISettings, appConfiguration: AppConfiguration, nameColors: PeerNameColors /*state: PeerNameColorScreenState,*/) -> [SGControllerEntry] {
|
||||
private func SGControllerEntries(presentationData: PresentationData, callListSettings: CallListSettings, experimentalUISettings: ExperimentalUISettings, SGSettings: SGUISettings, appConfiguration: AppConfiguration, nameColors: PeerNameColors, state: SGSettingsControllerState) -> [SGControllerEntry] {
|
||||
|
||||
let lang = presentationData.strings.baseLanguageCode
|
||||
var entries: [SGControllerEntry] = []
|
||||
|
||||
let id = SGItemListCounter()
|
||||
|
||||
entries.append(.searchInput(id: id.count, section: .search, title: NSAttributedString(string: "🔍"), text: state.searchQuery ?? "", placeholder: "Search"))
|
||||
if appConfiguration.sgWebSettings.global.canEditSettings {
|
||||
entries.append(.disclosure(id: id.count, section: .content, link: .contentSettings, text: i18n("Settings.ContentSettings", lang)))
|
||||
} else {
|
||||
@ -267,7 +273,7 @@ private func SGControllerEntries(presentationData: PresentationData, callListSet
|
||||
entries.append(.toggle(id: id.count, section: .other, settingName: .hidePhoneInSettings, value: SGSimpleSettings.shared.hidePhoneInSettings, text: i18n("Settings.HidePhoneInSettingsUI", lang), enabled: true))
|
||||
entries.append(.notice(id: id.count, section: .other, text: i18n("Settings.HidePhoneInSettingsUI.Notice", lang)))
|
||||
|
||||
return entries
|
||||
return filterSGItemListUIEntrires(entries: entries, by: state.searchQuery)
|
||||
}
|
||||
|
||||
public func sgSettingsController(context: AccountContext/*, focusOnItemTag: Int? = nil*/) -> ViewController {
|
||||
@ -277,11 +283,12 @@ public func sgSettingsController(context: AccountContext/*, focusOnItemTag: Int?
|
||||
// var getNavigationControllerImpl: (() -> NavigationController?)?
|
||||
var askForRestart: (() -> Void)?
|
||||
|
||||
// let statePromise = ValuePromise(PeerNameColorScreenState(), ignoreRepeated: true)
|
||||
// let stateValue = Atomic(value: PeerNameColorScreenState())
|
||||
// let updateState: ((PeerNameColorScreenState) -> PeerNameColorScreenState) -> Void = { f in
|
||||
// statePromise.set(stateValue.modify { f($0) })
|
||||
// }
|
||||
let initialState = SGSettingsControllerState()
|
||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: initialState)
|
||||
let updateState: ((SGSettingsControllerState) -> SGSettingsControllerState) -> Void = { f in
|
||||
statePromise.set(stateValue.modify { f($0) })
|
||||
}
|
||||
|
||||
// let sliderPromise = ValuePromise(SGSimpleSettings.shared.accountColorsSaturation, ignoreRepeated: true)
|
||||
// let sliderStateValue = Atomic(value: SGSimpleSettings.shared.accountColorsSaturation)
|
||||
@ -574,6 +581,12 @@ public func sgSettingsController(context: AccountContext/*, focusOnItemTag: Int?
|
||||
strongContext.sharedContext.applicationBindings.openUrl(url)
|
||||
})
|
||||
}
|
||||
}, searchInput: { searchQuery in
|
||||
updateState { state in
|
||||
var updatedState = state
|
||||
updatedState.searchQuery = searchQuery
|
||||
return updatedState
|
||||
}
|
||||
})
|
||||
|
||||
let sharedData = context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.callListSettings, ApplicationSpecificSharedDataKeys.experimentalUISettings])
|
||||
@ -584,18 +597,18 @@ public func sgSettingsController(context: AccountContext/*, focusOnItemTag: Int?
|
||||
contentSettingsConfiguration.set(.single(nil)
|
||||
|> then(updatedContentSettingsConfiguration))
|
||||
|
||||
let signal = combineLatest(simplePromise.get(), /*sliderPromise.get(),*/ /*statePromise.get(),*/ context.sharedContext.presentationData, sharedData, preferences, contentSettingsConfiguration.get(),
|
||||
let signal = combineLatest(simplePromise.get(), /*sliderPromise.get(),*/ statePromise.get(), context.sharedContext.presentationData, sharedData, preferences, contentSettingsConfiguration.get(),
|
||||
context.engine.accountData.observeAvailableColorOptions(scope: .replies),
|
||||
context.engine.accountData.observeAvailableColorOptions(scope: .profile)
|
||||
)
|
||||
|> map { _, /*sliderValue,*/ /*state,*/ presentationData, sharedData, view, contentSettingsConfiguration, availableReplyColors, availableProfileColors -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
|> map { _, /*sliderValue,*/ state, presentationData, sharedData, view, contentSettingsConfiguration, availableReplyColors, availableProfileColors -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
|
||||
let sgUISettings: SGUISettings = view.values[ApplicationSpecificPreferencesKeys.SGUISettings]?.get(SGUISettings.self) ?? SGUISettings.default
|
||||
let appConfiguration: AppConfiguration = view.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self) ?? AppConfiguration.defaultValue
|
||||
let callListSettings: CallListSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.callListSettings]?.get(CallListSettings.self) ?? CallListSettings.defaultSettings
|
||||
let experimentalUISettings: ExperimentalUISettings = sharedData.entries[ApplicationSpecificSharedDataKeys.experimentalUISettings]?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
||||
|
||||
let entries = SGControllerEntries(presentationData: presentationData, callListSettings: callListSettings, experimentalUISettings: experimentalUISettings, SGSettings: sgUISettings, appConfiguration: appConfiguration, nameColors: PeerNameColors.with(availableReplyColors: availableReplyColors, availableProfileColors: availableProfileColors) /*state: state,*/)
|
||||
let entries = SGControllerEntries(presentationData: presentationData, callListSettings: callListSettings, experimentalUISettings: experimentalUISettings, SGSettings: sgUISettings, appConfiguration: appConfiguration, nameColors: PeerNameColors.with(availableReplyColors: availableReplyColors, availableProfileColors: availableProfileColors), state: state)
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("Swiftgram"), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||
|
||||
|
@ -67,9 +67,10 @@ public class ItemListSingleLineInputItem: ListViewItem, ItemListItem {
|
||||
let processPaste: ((String) -> String)?
|
||||
let updatedFocus: ((Bool) -> Void)?
|
||||
let cleared: (() -> Void)?
|
||||
let dismissKeyboardOnEnter: Bool // MARK: Swiftgram
|
||||
public let tag: ItemListItemTag?
|
||||
|
||||
public init(context: AccountContext? = nil, presentationData: ItemListPresentationData, title: NSAttributedString, text: String, placeholder: String, label: String? = nil, type: ItemListSingleLineInputItemType = .regular(capitalization: true, autocorrection: true), returnKeyType: UIReturnKeyType = .`default`, alignment: ItemListSingleLineInputAlignment = .default, spacing: CGFloat = 0.0, clearType: ItemListSingleLineInputClearType = .none, maxLength: Int = 0, enabled: Bool = true, selectAllOnFocus: Bool = false, secondaryStyle: Bool = false, tag: ItemListItemTag? = nil, sectionId: ItemListSectionId, textUpdated: @escaping (String) -> Void, shouldUpdateText: @escaping (String) -> Bool = { _ in return true }, processPaste: ((String) -> String)? = nil, updatedFocus: ((Bool) -> Void)? = nil, action: @escaping () -> Void, cleared: (() -> Void)? = nil) {
|
||||
public init(context: AccountContext? = nil, presentationData: ItemListPresentationData, title: NSAttributedString, text: String, placeholder: String, label: String? = nil, type: ItemListSingleLineInputItemType = .regular(capitalization: true, autocorrection: true), returnKeyType: UIReturnKeyType = .`default`, alignment: ItemListSingleLineInputAlignment = .default, spacing: CGFloat = 0.0, clearType: ItemListSingleLineInputClearType = .none, maxLength: Int = 0, enabled: Bool = true, selectAllOnFocus: Bool = false, secondaryStyle: Bool = false, tag: ItemListItemTag? = nil, sectionId: ItemListSectionId, textUpdated: @escaping (String) -> Void, shouldUpdateText: @escaping (String) -> Bool = { _ in return true }, processPaste: ((String) -> String)? = nil, updatedFocus: ((Bool) -> Void)? = nil, action: @escaping () -> Void, cleared: (() -> Void)? = nil, dismissKeyboardOnEnter: Bool = false) {
|
||||
self.context = context
|
||||
self.presentationData = presentationData
|
||||
self.title = title
|
||||
@ -93,6 +94,7 @@ public class ItemListSingleLineInputItem: ListViewItem, ItemListItem {
|
||||
self.updatedFocus = updatedFocus
|
||||
self.action = action
|
||||
self.cleared = cleared
|
||||
self.dismissKeyboardOnEnter = dismissKeyboardOnEnter
|
||||
}
|
||||
|
||||
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||
@ -590,6 +592,10 @@ public class ItemListSingleLineInputItemNode: ListViewItemNode, UITextFieldDeleg
|
||||
|
||||
@objc public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
self.item?.action()
|
||||
// MARK: Swiftgram
|
||||
if self.item?.dismissKeyboardOnEnter ?? false && self.textNode.textField.canResignFirstResponder {
|
||||
self.textNode.textField.resignFirstResponder()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user