Swiftgram/Swiftgram/SGSettingsUI/Sources/SGSettingsController.swift
2025-06-30 22:47:13 +03:00

722 lines
46 KiB
Swift

// MARK: Swiftgram
import SGLogging
import SGSimpleSettings
import SGStrings
import SGAPIToken
import SGItemListUI
import Foundation
import UIKit
import Display
import SwiftSignalKit
import Postbox
import TelegramCore
import MtProtoKit
import MessageUI
import TelegramPresentationData
import TelegramUIPreferences
import ItemListUI
import PresentationDataUtils
import OverlayStatusController
import AccountContext
import AppBundle
import WebKit
import PeerNameColorScreen
import UndoUI
private enum SGControllerSection: Int32, SGItemListSection {
case search
case content
case tabs
case folders
case chatList
case profiles
case stories
case translation
case voiceMessages
case photo
case stickers
case videoNotes
case contextMenu
case accountColors
case other
}
private enum SGBoolSetting: String {
case hidePhoneInSettings
case showTabNames
case showContactsTab
case showCallsTab
case foldersAtBottom
case startTelescopeWithRearCam
case hideStories
case uploadSpeedBoost
case showProfileId
case warnOnStoriesOpen
case sendWithReturnKey
case rememberLastFolder
case sendLargePhotos
case storyStealthMode
case disableSwipeToRecordStory
case disableDeleteChatSwipeOption
case quickTranslateButton
case hideReactions
case showRepostToStory
case contextShowSelectFromUser
case contextShowSaveToCloud
case contextShowHideForwardName
case contextShowRestrict
case contextShowReport
case contextShowReply
case contextShowPin
case contextShowSaveMedia
case contextShowMessageReplies
case contextShowJson
case disableScrollToNextChannel
case disableScrollToNextTopic
case disableChatSwipeOptions
case disableGalleryCamera
case disableGalleryCameraPreview
case disableSendAsButton
case disableSnapDeletionEffect
case stickerTimestamp
case hideRecordingButton
case hideTabBar
case showDC
case showCreationDate
case showRegDate
case compactChatList
case compactFolderNames
case allChatsHidden
case defaultEmojisFirst
case messageDoubleTapActionOutgoingEdit
case wideChannelPosts
case forceEmojiTab
case forceBuiltInMic
case secondsInMessages
case hideChannelBottomButton
case confirmCalls
case swipeForVideoPIP
}
private enum SGOneFromManySetting: String {
case bottomTabStyle
case downloadSpeedBoost
case allChatsTitleLengthOverride
// case allChatsFolderPositionOverride
case translationBackend
case transcriptionBackend
}
private enum SGSliderSetting: String {
case accountColorsSaturation
case outgoingPhotoQuality
case stickerSize
}
private enum SGDisclosureLink: String {
case contentSettings
case languageSettings
}
private struct PeerNameColorScreenState: Equatable {
var updatedNameColor: PeerNameColor?
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: 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: presentationData.strings.Common_Search))
if appConfiguration.sgWebSettings.global.canEditSettings {
entries.append(.disclosure(id: id.count, section: .content, link: .contentSettings, text: i18n("Settings.ContentSettings", lang)))
} else {
id.increment(1)
}
entries.append(.header(id: id.count, section: .tabs, text: i18n("Settings.Tabs.Header", lang), badge: nil))
entries.append(.toggle(id: id.count, section: .tabs, settingName: .hideTabBar, value: SGSimpleSettings.shared.hideTabBar, text: i18n("Settings.Tabs.HideTabBar", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .tabs, settingName: .showContactsTab, value: callListSettings.showContactsTab, text: i18n("Settings.Tabs.ShowContacts", lang), enabled: !SGSimpleSettings.shared.hideTabBar))
entries.append(.toggle(id: id.count, section: .tabs, settingName: .showCallsTab, value: callListSettings.showTab, text: presentationData.strings.CallSettings_TabIcon, enabled: !SGSimpleSettings.shared.hideTabBar))
entries.append(.toggle(id: id.count, section: .tabs, settingName: .showTabNames, value: SGSimpleSettings.shared.showTabNames, text: i18n("Settings.Tabs.ShowNames", lang), enabled: !SGSimpleSettings.shared.hideTabBar))
entries.append(.header(id: id.count, section: .folders, text: presentationData.strings.Settings_ChatFolders.uppercased(), badge: nil))
entries.append(.toggle(id: id.count, section: .folders, settingName: .foldersAtBottom, value: experimentalUISettings.foldersTabAtBottom, text: i18n("Settings.Folders.BottomTab", lang), enabled: true))
entries.append(.oneFromManySelector(id: id.count, section: .folders, settingName: .bottomTabStyle, text: i18n("Settings.Folders.BottomTabStyle", lang), value: i18n("Settings.Folders.BottomTabStyle.\(SGSimpleSettings.shared.bottomTabStyle)", lang), enabled: experimentalUISettings.foldersTabAtBottom))
entries.append(.toggle(id: id.count, section: .folders, settingName: .allChatsHidden, value: SGSimpleSettings.shared.allChatsHidden, text: i18n("Settings.Folders.AllChatsHidden", lang, presentationData.strings.ChatList_Tabs_AllChats), enabled: true))
#if DEBUG
// entries.append(.oneFromManySelector(id: id.count, section: .folders, settingName: .allChatsFolderPositionOverride, text: i18n("Settings.Folders.AllChatsPlacement", lang), value: i18n("Settings.Folders.AllChatsPlacement.\(SGSimpleSettings.shared.allChatsFolderPositionOverride)", lang), enabled: true))
#endif
entries.append(.toggle(id: id.count, section: .folders, settingName: .compactFolderNames, value: SGSimpleSettings.shared.compactFolderNames, text: i18n("Settings.Folders.CompactNames", lang), enabled: SGSimpleSettings.shared.bottomTabStyle != SGSimpleSettings.BottomTabStyleValues.ios.rawValue))
entries.append(.oneFromManySelector(id: id.count, section: .folders, settingName: .allChatsTitleLengthOverride, text: i18n("Settings.Folders.AllChatsTitle", lang), value: i18n("Settings.Folders.AllChatsTitle.\(SGSimpleSettings.shared.allChatsTitleLengthOverride)", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .folders, settingName: .rememberLastFolder, value: SGSimpleSettings.shared.rememberLastFolder, text: i18n("Settings.Folders.RememberLast", lang), enabled: true))
entries.append(.notice(id: id.count, section: .folders, text: i18n("Settings.Folders.RememberLast.Notice", lang)))
entries.append(.header(id: id.count, section: .chatList, text: i18n("Settings.ChatList.Header", lang), badge: nil))
entries.append(.toggle(id: id.count, section: .chatList, settingName: .compactChatList, value: SGSimpleSettings.shared.compactChatList, text: i18n("Settings.CompactChatList", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .chatList, settingName: .disableChatSwipeOptions, value: !SGSimpleSettings.shared.disableChatSwipeOptions, text: i18n("Settings.ChatSwipeOptions", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .chatList, settingName: .disableDeleteChatSwipeOption, value: !SGSimpleSettings.shared.disableDeleteChatSwipeOption, text: i18n("Settings.DeleteChatSwipeOption", lang), enabled: !SGSimpleSettings.shared.disableChatSwipeOptions))
entries.append(.header(id: id.count, section: .profiles, text: i18n("Settings.Profiles.Header", lang), badge: nil))
entries.append(.toggle(id: id.count, section: .profiles, settingName: .showProfileId, value: SGSettings.showProfileId, text: i18n("Settings.ShowProfileID", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .profiles, settingName: .showDC, value: SGSimpleSettings.shared.showDC, text: i18n("Settings.ShowDC", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .profiles, settingName: .showRegDate, value: SGSimpleSettings.shared.showRegDate, text: i18n("Settings.ShowRegDate", lang), enabled: true))
entries.append(.notice(id: id.count, section: .profiles, text: i18n("Settings.ShowRegDate.Notice", lang)))
entries.append(.toggle(id: id.count, section: .profiles, settingName: .showCreationDate, value: SGSimpleSettings.shared.showCreationDate, text: i18n("Settings.ShowCreationDate", lang), enabled: true))
entries.append(.notice(id: id.count, section: .profiles, text: i18n("Settings.ShowCreationDate.Notice", lang)))
entries.append(.toggle(id: id.count, section: .profiles, settingName: .confirmCalls, value: SGSimpleSettings.shared.confirmCalls, text: i18n("Settings.CallConfirmation", lang), enabled: true))
entries.append(.notice(id: id.count, section: .profiles, text: i18n("Settings.CallConfirmation.Notice", lang)))
entries.append(.header(id: id.count, section: .stories, text: presentationData.strings.AutoDownloadSettings_Stories.uppercased(), badge: nil))
entries.append(.toggle(id: id.count, section: .stories, settingName: .hideStories, value: SGSettings.hideStories, text: i18n("Settings.Stories.Hide", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .stories, settingName: .disableSwipeToRecordStory, value: SGSimpleSettings.shared.disableSwipeToRecordStory, text: i18n("Settings.Stories.DisableSwipeToRecord", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .stories, settingName: .warnOnStoriesOpen, value: SGSettings.warnOnStoriesOpen, text: i18n("Settings.Stories.WarnBeforeView", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .stories, settingName: .showRepostToStory, value: SGSimpleSettings.shared.showRepostToStory, text: presentationData.strings.Share_RepostToStory.replacingOccurrences(of: "\n", with: " "), enabled: true))
if SGSimpleSettings.shared.canUseStealthMode {
entries.append(.toggle(id: id.count, section: .stories, settingName: .storyStealthMode, value: SGSimpleSettings.shared.storyStealthMode, text: presentationData.strings.Story_StealthMode_Title, enabled: true))
entries.append(.notice(id: id.count, section: .stories, text: presentationData.strings.Story_StealthMode_ControlText))
} else {
id.increment(2)
}
entries.append(.header(id: id.count, section: .translation, text: presentationData.strings.Localization_TranslateMessages.uppercased(), badge: nil))
entries.append(.oneFromManySelector(id: id.count, section: .translation, settingName: .translationBackend, text: i18n("Settings.Translation.Backend", lang), value: i18n("Settings.Translation.Backend.\(SGSimpleSettings.shared.translationBackend)", lang), enabled: true))
entries.append(.notice(id: id.count, section: .translation, text: i18n("Settings.Translation.Backend.Notice", lang)))
entries.append(.toggle(id: id.count, section: .translation, settingName: .quickTranslateButton, value: SGSimpleSettings.shared.quickTranslateButton, text: i18n("Settings.Translation.QuickTranslateButton", lang), enabled: true))
entries.append(.disclosure(id: id.count, section: .translation, link: .languageSettings, text: presentationData.strings.Localization_TranslateEntireChat))
entries.append(.notice(id: id.count, section: .translation, text: i18n("Common.NoTelegramPremiumNeeded", lang, presentationData.strings.Settings_Premium)))
entries.append(.header(id: id.count, section: .voiceMessages, text: "Settings.Transcription.Header".i18n(lang), badge: nil))
entries.append(.oneFromManySelector(id: id.count, section: .voiceMessages, settingName: .transcriptionBackend, text: i18n("Settings.Transcription.Backend", lang), value: i18n("Settings.Transcription.Backend.\(SGSimpleSettings.shared.transcriptionBackend)", lang), enabled: true))
entries.append(.notice(id: id.count, section: .voiceMessages, text: i18n("Settings.Transcription.Backend.Notice", lang)))
entries.append(.header(id: id.count, section: .voiceMessages, text: presentationData.strings.Privacy_VoiceMessages.uppercased(), badge: nil))
entries.append(.toggle(id: id.count, section: .voiceMessages, settingName: .forceBuiltInMic, value: SGSimpleSettings.shared.forceBuiltInMic, text: i18n("Settings.forceBuiltInMic", lang), enabled: true))
entries.append(.notice(id: id.count, section: .voiceMessages, text: i18n("Settings.forceBuiltInMic.Notice", lang)))
entries.append(.header(id: id.count, section: .photo, text: presentationData.strings.NetworkUsageSettings_MediaImageDataSection, badge: nil))
entries.append(.header(id: id.count, section: .photo, text: presentationData.strings.PhotoEditor_QualityTool.uppercased(), badge: nil))
entries.append(.percentageSlider(id: id.count, section: .photo, settingName: .outgoingPhotoQuality, value: SGSimpleSettings.shared.outgoingPhotoQuality))
entries.append(.notice(id: id.count, section: .photo, text: i18n("Settings.Photo.Quality.Notice", lang)))
entries.append(.toggle(id: id.count, section: .photo, settingName: .sendLargePhotos, value: SGSimpleSettings.shared.sendLargePhotos, text: i18n("Settings.Photo.SendLarge", lang), enabled: true))
entries.append(.notice(id: id.count, section: .photo, text: i18n("Settings.Photo.SendLarge.Notice", lang)))
entries.append(.header(id: id.count, section: .stickers, text: presentationData.strings.StickerPacksSettings_Title.uppercased(), badge: nil))
entries.append(.header(id: id.count, section: .stickers, text: i18n("Settings.Stickers.Size", lang), badge: nil))
entries.append(.percentageSlider(id: id.count, section: .stickers, settingName: .stickerSize, value: SGSimpleSettings.shared.stickerSize))
entries.append(.toggle(id: id.count, section: .stickers, settingName: .stickerTimestamp, value: SGSimpleSettings.shared.stickerTimestamp, text: i18n("Settings.Stickers.Timestamp", lang), enabled: true))
entries.append(.header(id: id.count, section: .videoNotes, text: i18n("Settings.VideoNotes.Header", lang), badge: nil))
entries.append(.toggle(id: id.count, section: .videoNotes, settingName: .startTelescopeWithRearCam, value: SGSimpleSettings.shared.startTelescopeWithRearCam, text: i18n("Settings.VideoNotes.StartWithRearCam", lang), enabled: true))
entries.append(.header(id: id.count, section: .contextMenu, text: i18n("Settings.ContextMenu", lang), badge: nil))
entries.append(.notice(id: id.count, section: .contextMenu, text: i18n("Settings.ContextMenu.Notice", lang)))
entries.append(.toggle(id: id.count, section: .contextMenu, settingName: .contextShowSaveToCloud, value: SGSimpleSettings.shared.contextShowSaveToCloud, text: i18n("ContextMenu.SaveToCloud", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .contextMenu, settingName: .contextShowHideForwardName, value: SGSimpleSettings.shared.contextShowHideForwardName, text: presentationData.strings.Conversation_ForwardOptions_HideSendersNames, enabled: true))
entries.append(.toggle(id: id.count, section: .contextMenu, settingName: .contextShowSelectFromUser, value: SGSimpleSettings.shared.contextShowSelectFromUser, text: i18n("ContextMenu.SelectFromUser", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .contextMenu, settingName: .contextShowRestrict, value: SGSimpleSettings.shared.contextShowRestrict, text: presentationData.strings.Conversation_ContextMenuBan, enabled: true))
entries.append(.toggle(id: id.count, section: .contextMenu, settingName: .contextShowReport, value: SGSimpleSettings.shared.contextShowReport, text: presentationData.strings.Conversation_ContextMenuReport, enabled: true))
entries.append(.toggle(id: id.count, section: .contextMenu, settingName: .contextShowReply, value: SGSimpleSettings.shared.contextShowReply, text: presentationData.strings.Conversation_ContextMenuReply, enabled: true))
entries.append(.toggle(id: id.count, section: .contextMenu, settingName: .contextShowPin, value: SGSimpleSettings.shared.contextShowPin, text: presentationData.strings.Conversation_Pin, enabled: true))
entries.append(.toggle(id: id.count, section: .contextMenu, settingName: .contextShowSaveMedia, value: SGSimpleSettings.shared.contextShowSaveMedia, text: presentationData.strings.Conversation_SaveToFiles, enabled: true))
entries.append(.toggle(id: id.count, section: .contextMenu, settingName: .contextShowMessageReplies, value: SGSimpleSettings.shared.contextShowMessageReplies, text: presentationData.strings.Conversation_ContextViewThread, enabled: true))
entries.append(.toggle(id: id.count, section: .contextMenu, settingName: .contextShowJson, value: SGSimpleSettings.shared.contextShowJson, text: "JSON", enabled: true))
/* entries.append(.toggle(id: id.count, section: .contextMenu, settingName: .contextShowRestrict, value: SGSimpleSettings.shared.contextShowRestrict, text: presentationData.strings.Conversation_ContextMenuBan)) */
entries.append(.header(id: id.count, section: .accountColors, text: i18n("Settings.CustomColors.Header", lang), badge: nil))
entries.append(.header(id: id.count, section: .accountColors, text: i18n("Settings.CustomColors.Saturation", lang), badge: nil))
let accountColorSaturation = SGSimpleSettings.shared.accountColorsSaturation
entries.append(.percentageSlider(id: id.count, section: .accountColors, settingName: .accountColorsSaturation, value: accountColorSaturation))
// let nameColor: PeerNameColor
// if let updatedNameColor = state.updatedNameColor {
// nameColor = updatedNameColor
// } else {
// nameColor = .blue
// }
// let _ = nameColors.get(nameColor, dark: presentationData.theme.overallDarkAppearance)
// entries.append(.peerColorPicker(id: entries.count, section: .other,
// colors: nameColors,
// currentColor: nameColor, // TODO: PeerNameColor(rawValue: <#T##Int32#>)
// currentSaturation: accountColorSaturation
// ))
if accountColorSaturation == 0 {
id.increment(100)
entries.append(.peerColorDisclosurePreview(id: id.count, section: .accountColors, name: "\(presentationData.strings.UserInfo_FirstNamePlaceholder) \(presentationData.strings.UserInfo_LastNamePlaceholder)", color: presentationData.theme.chat.message.incoming.accentTextColor))
} else {
id.increment(200)
for index in nameColors.displayOrder.prefix(3) {
let color: PeerNameColor = PeerNameColor(rawValue: index)
let colors = nameColors.get(color, dark: presentationData.theme.overallDarkAppearance)
entries.append(.peerColorDisclosurePreview(id: id.count, section: .accountColors, name: "\(presentationData.strings.UserInfo_FirstNamePlaceholder) \(presentationData.strings.UserInfo_LastNamePlaceholder)", color: colors.main))
}
}
entries.append(.notice(id: id.count, section: .accountColors, text: i18n("Settings.CustomColors.Saturation.Notice", lang)))
id.increment(10000)
entries.append(.header(id: id.count, section: .other, text: presentationData.strings.Appearance_Other.uppercased(), badge: nil))
entries.append(.toggle(id: id.count, section: .other, settingName: .swipeForVideoPIP, value: SGSimpleSettings.shared.videoPIPSwipeDirection == SGSimpleSettings.VideoPIPSwipeDirection.up.rawValue, text: i18n("Settings.swipeForVideoPIP", lang), enabled: true))
entries.append(.notice(id: id.count, section: .other, text: i18n("Settings.swipeForVideoPIP.Notice", lang)))
entries.append(.toggle(id: id.count, section: .other, settingName: .hideChannelBottomButton, value: !SGSimpleSettings.shared.hideChannelBottomButton, text: i18n("Settings.showChannelBottomButton", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .other, settingName: .wideChannelPosts, value: SGSimpleSettings.shared.wideChannelPosts, text: i18n("Settings.wideChannelPosts", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .other, settingName: .secondsInMessages, value: SGSimpleSettings.shared.secondsInMessages, text: i18n("Settings.secondsInMessages", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .other, settingName: .messageDoubleTapActionOutgoingEdit, value: SGSimpleSettings.shared.messageDoubleTapActionOutgoing == SGSimpleSettings.MessageDoubleTapAction.edit.rawValue, text: i18n("Settings.messageDoubleTapActionOutgoingEdit", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .other, settingName: .hideRecordingButton, value: !SGSimpleSettings.shared.hideRecordingButton, text: i18n("Settings.RecordingButton", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .other, settingName: .disableSnapDeletionEffect, value: !SGSimpleSettings.shared.disableSnapDeletionEffect, text: i18n("Settings.SnapDeletionEffect", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .other, settingName: .disableSendAsButton, value: !SGSimpleSettings.shared.disableSendAsButton, text: i18n("Settings.SendAsButton", lang, presentationData.strings.Conversation_SendMesageAs), enabled: true))
entries.append(.toggle(id: id.count, section: .other, settingName: .disableGalleryCamera, value: !SGSimpleSettings.shared.disableGalleryCamera, text: i18n("Settings.GalleryCamera", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .other, settingName: .disableGalleryCameraPreview, value: !SGSimpleSettings.shared.disableGalleryCameraPreview, text: i18n("Settings.GalleryCameraPreview", lang), enabled: !SGSimpleSettings.shared.disableGalleryCamera))
entries.append(.toggle(id: id.count, section: .other, settingName: .disableScrollToNextChannel, value: !SGSimpleSettings.shared.disableScrollToNextChannel, text: i18n("Settings.PullToNextChannel", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .other, settingName: .disableScrollToNextTopic, value: !SGSimpleSettings.shared.disableScrollToNextTopic, text: i18n("Settings.PullToNextTopic", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .other, settingName: .hideReactions, value: SGSimpleSettings.shared.hideReactions, text: i18n("Settings.HideReactions", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .other, settingName: .uploadSpeedBoost, value: SGSimpleSettings.shared.uploadSpeedBoost, text: i18n("Settings.UploadsBoost", lang), enabled: true))
entries.append(.oneFromManySelector(id: id.count, section: .other, settingName: .downloadSpeedBoost, text: i18n("Settings.DownloadsBoost", lang), value: i18n("Settings.DownloadsBoost.\(SGSimpleSettings.shared.downloadSpeedBoost)", lang), enabled: true))
entries.append(.notice(id: id.count, section: .other, text: i18n("Settings.DownloadsBoost.Notice", lang)))
entries.append(.toggle(id: id.count, section: .other, settingName: .sendWithReturnKey, value: SGSettings.sendWithReturnKey, text: i18n("Settings.SendWithReturnKey", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .other, settingName: .forceEmojiTab, value: SGSimpleSettings.shared.forceEmojiTab, text: i18n("Settings.ForceEmojiTab", lang), enabled: true))
entries.append(.toggle(id: id.count, section: .other, settingName: .defaultEmojisFirst, value: SGSimpleSettings.shared.defaultEmojisFirst, text: i18n("Settings.DefaultEmojisFirst", lang), enabled: true))
entries.append(.notice(id: id.count, section: .other, text: i18n("Settings.DefaultEmojisFirst.Notice", lang)))
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 filterSGItemListUIEntrires(entries: entries, by: state.searchQuery)
}
public func sgSettingsController(context: AccountContext/*, focusOnItemTag: Int? = nil*/) -> ViewController {
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
var pushControllerImpl: ((ViewController) -> Void)?
// var getRootControllerImpl: (() -> UIViewController?)?
// var getNavigationControllerImpl: (() -> NavigationController?)?
var askForRestart: (() -> Void)?
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)
// let _: ((Int32) -> Int32) -> Void = { f in
// sliderPromise.set(sliderStateValue.modify( {f($0)}))
// }
let simplePromise = ValuePromise(true, ignoreRepeated: false)
let arguments = SGItemListArguments<SGBoolSetting, SGSliderSetting, SGOneFromManySetting, SGDisclosureLink, AnyHashable>(
context: context,
/*updatePeerColor: { color in
updateState { state in
var updatedState = state
updatedState.updatedNameColor = color
return updatedState
}
},*/ setBoolValue: { setting, value in
switch setting {
case .hidePhoneInSettings:
SGSimpleSettings.shared.hidePhoneInSettings = value
askForRestart?()
case .showTabNames:
SGSimpleSettings.shared.showTabNames = value
askForRestart?()
case .showContactsTab:
let _ = (
updateCallListSettingsInteractively(
accountManager: context.sharedContext.accountManager, { $0.withUpdatedShowContactsTab(value) }
)
).start()
case .showCallsTab:
let _ = (
updateCallListSettingsInteractively(
accountManager: context.sharedContext.accountManager, { $0.withUpdatedShowTab(value) }
)
).start()
case .foldersAtBottom:
let _ = (
updateExperimentalUISettingsInteractively(accountManager: context.sharedContext.accountManager, { settings in
var settings = settings
settings.foldersTabAtBottom = value
return settings
}
)
).start()
case .startTelescopeWithRearCam:
SGSimpleSettings.shared.startTelescopeWithRearCam = value
case .hideStories:
let _ = (
updateSGUISettings(engine: context.engine, { settings in
var settings = settings
settings.hideStories = value
return settings
})
).start()
case .showProfileId:
let _ = (
updateSGUISettings(engine: context.engine, { settings in
var settings = settings
settings.showProfileId = value
return settings
})
).start()
case .warnOnStoriesOpen:
let _ = (
updateSGUISettings(engine: context.engine, { settings in
var settings = settings
settings.warnOnStoriesOpen = value
return settings
})
).start()
case .sendWithReturnKey:
let _ = (
updateSGUISettings(engine: context.engine, { settings in
var settings = settings
settings.sendWithReturnKey = value
return settings
})
).start()
case .rememberLastFolder:
SGSimpleSettings.shared.rememberLastFolder = value
case .sendLargePhotos:
SGSimpleSettings.shared.sendLargePhotos = value
case .storyStealthMode:
SGSimpleSettings.shared.storyStealthMode = value
case .disableSwipeToRecordStory:
SGSimpleSettings.shared.disableSwipeToRecordStory = value
case .quickTranslateButton:
SGSimpleSettings.shared.quickTranslateButton = value
case .uploadSpeedBoost:
SGSimpleSettings.shared.uploadSpeedBoost = value
case .hideReactions:
SGSimpleSettings.shared.hideReactions = value
case .showRepostToStory:
SGSimpleSettings.shared.showRepostToStory = value
case .contextShowSelectFromUser:
SGSimpleSettings.shared.contextShowSelectFromUser = value
case .contextShowSaveToCloud:
SGSimpleSettings.shared.contextShowSaveToCloud = value
case .contextShowRestrict:
SGSimpleSettings.shared.contextShowRestrict = value
case .contextShowHideForwardName:
SGSimpleSettings.shared.contextShowHideForwardName = value
case .disableScrollToNextChannel:
SGSimpleSettings.shared.disableScrollToNextChannel = !value
case .disableScrollToNextTopic:
SGSimpleSettings.shared.disableScrollToNextTopic = !value
case .disableChatSwipeOptions:
SGSimpleSettings.shared.disableChatSwipeOptions = !value
simplePromise.set(true) // Trigger update for 'enabled' field of other toggles
askForRestart?()
case .disableDeleteChatSwipeOption:
SGSimpleSettings.shared.disableDeleteChatSwipeOption = !value
askForRestart?()
case .disableGalleryCamera:
SGSimpleSettings.shared.disableGalleryCamera = !value
simplePromise.set(true)
case .disableGalleryCameraPreview:
SGSimpleSettings.shared.disableGalleryCameraPreview = !value
case .disableSendAsButton:
SGSimpleSettings.shared.disableSendAsButton = !value
case .disableSnapDeletionEffect:
SGSimpleSettings.shared.disableSnapDeletionEffect = !value
case .contextShowReport:
SGSimpleSettings.shared.contextShowReport = value
case .contextShowReply:
SGSimpleSettings.shared.contextShowReply = value
case .contextShowPin:
SGSimpleSettings.shared.contextShowPin = value
case .contextShowSaveMedia:
SGSimpleSettings.shared.contextShowSaveMedia = value
case .contextShowMessageReplies:
SGSimpleSettings.shared.contextShowMessageReplies = value
case .stickerTimestamp:
SGSimpleSettings.shared.stickerTimestamp = value
case .contextShowJson:
SGSimpleSettings.shared.contextShowJson = value
case .hideRecordingButton:
SGSimpleSettings.shared.hideRecordingButton = !value
case .hideTabBar:
SGSimpleSettings.shared.hideTabBar = value
simplePromise.set(true) // Trigger update for 'enabled' field of other toggles
askForRestart?()
case .showDC:
SGSimpleSettings.shared.showDC = value
case .showCreationDate:
SGSimpleSettings.shared.showCreationDate = value
case .showRegDate:
SGSimpleSettings.shared.showRegDate = value
case .compactChatList:
SGSimpleSettings.shared.compactChatList = value
askForRestart?()
case .compactFolderNames:
SGSimpleSettings.shared.compactFolderNames = value
case .allChatsHidden:
SGSimpleSettings.shared.allChatsHidden = value
askForRestart?()
case .defaultEmojisFirst:
SGSimpleSettings.shared.defaultEmojisFirst = value
case .messageDoubleTapActionOutgoingEdit:
SGSimpleSettings.shared.messageDoubleTapActionOutgoing = value ? SGSimpleSettings.MessageDoubleTapAction.edit.rawValue : SGSimpleSettings.MessageDoubleTapAction.default.rawValue
case .wideChannelPosts:
SGSimpleSettings.shared.wideChannelPosts = value
case .forceEmojiTab:
SGSimpleSettings.shared.forceEmojiTab = value
case .forceBuiltInMic:
SGSimpleSettings.shared.forceBuiltInMic = value
case .hideChannelBottomButton:
SGSimpleSettings.shared.hideChannelBottomButton = !value
case .secondsInMessages:
SGSimpleSettings.shared.secondsInMessages = value
case .confirmCalls:
SGSimpleSettings.shared.confirmCalls = value
case .swipeForVideoPIP:
SGSimpleSettings.shared.videoPIPSwipeDirection = value ? SGSimpleSettings.VideoPIPSwipeDirection.up.rawValue : SGSimpleSettings.VideoPIPSwipeDirection.none.rawValue
}
}, updateSliderValue: { setting, value in
switch (setting) {
case .accountColorsSaturation:
if SGSimpleSettings.shared.accountColorsSaturation != value {
SGSimpleSettings.shared.accountColorsSaturation = value
simplePromise.set(true)
}
case .outgoingPhotoQuality:
if SGSimpleSettings.shared.outgoingPhotoQuality != value {
SGSimpleSettings.shared.outgoingPhotoQuality = value
simplePromise.set(true)
}
case .stickerSize:
if SGSimpleSettings.shared.stickerSize != value {
SGSimpleSettings.shared.stickerSize = value
simplePromise.set(true)
}
}
}, setOneFromManyValue: { setting in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let actionSheet = ActionSheetController(presentationData: presentationData)
var items: [ActionSheetItem] = []
switch (setting) {
case .downloadSpeedBoost:
let setAction: (String) -> Void = { value in
SGSimpleSettings.shared.downloadSpeedBoost = value
let enableDownloadX: Bool
switch (value) {
case SGSimpleSettings.DownloadSpeedBoostValues.none.rawValue:
enableDownloadX = false
default:
enableDownloadX = true
}
// Updating controller
simplePromise.set(true)
let _ = updateNetworkSettingsInteractively(postbox: context.account.postbox, network: context.account.network, { settings in
var settings = settings
settings.useExperimentalDownload = enableDownloadX
return settings
}).start(completed: {
Queue.mainQueue().async {
askForRestart?()
}
})
}
for value in SGSimpleSettings.DownloadSpeedBoostValues.allCases {
items.append(ActionSheetButtonItem(title: i18n("Settings.DownloadsBoost.\(value.rawValue)", presentationData.strings.baseLanguageCode), color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
setAction(value.rawValue)
}))
}
case .bottomTabStyle:
let setAction: (String) -> Void = { value in
SGSimpleSettings.shared.bottomTabStyle = value
simplePromise.set(true)
}
for value in SGSimpleSettings.BottomTabStyleValues.allCases {
items.append(ActionSheetButtonItem(title: i18n("Settings.Folders.BottomTabStyle.\(value.rawValue)", presentationData.strings.baseLanguageCode), color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
setAction(value.rawValue)
}))
}
case .allChatsTitleLengthOverride:
let setAction: (String) -> Void = { value in
SGSimpleSettings.shared.allChatsTitleLengthOverride = value
simplePromise.set(true)
}
for value in SGSimpleSettings.AllChatsTitleLengthOverride.allCases {
let title: String
switch (value) {
case SGSimpleSettings.AllChatsTitleLengthOverride.short:
title = "\"\(presentationData.strings.ChatList_Tabs_All)\""
case SGSimpleSettings.AllChatsTitleLengthOverride.long:
title = "\"\(presentationData.strings.ChatList_Tabs_AllChats)\""
default:
title = i18n("Settings.Folders.AllChatsTitle.none", presentationData.strings.baseLanguageCode)
}
items.append(ActionSheetButtonItem(title: title, color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
setAction(value.rawValue)
}))
}
// case .allChatsFolderPositionOverride:
// let setAction: (String) -> Void = { value in
// SGSimpleSettings.shared.allChatsFolderPositionOverride = value
// simplePromise.set(true)
// }
//
// for value in SGSimpleSettings.AllChatsFolderPositionOverride.allCases {
// items.append(ActionSheetButtonItem(title: i18n("Settings.Folders.AllChatsTitle.\(value)", presentationData.strings.baseLanguageCode), color: .accent, action: { [weak actionSheet] in
// actionSheet?.dismissAnimated()
// setAction(value.rawValue)
// }))
// }
case .translationBackend:
let setAction: (String) -> Void = { value in
SGSimpleSettings.shared.translationBackend = value
simplePromise.set(true)
}
for value in SGSimpleSettings.TranslationBackend.allCases {
items.append(ActionSheetButtonItem(title: i18n("Settings.Translation.Backend.\(value.rawValue)", presentationData.strings.baseLanguageCode), color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
setAction(value.rawValue)
}))
}
case .transcriptionBackend:
let setAction: (String) -> Void = { value in
SGSimpleSettings.shared.transcriptionBackend = value
simplePromise.set(true)
}
for value in SGSimpleSettings.TranscriptionBackend.allCases {
if #available(iOS 13.0, *) {
} else {
if value == .apple {
continue // Apple recognition is not available on iOS 12
}
}
items.append(ActionSheetButtonItem(title: i18n("Settings.Transcription.Backend.\(value.rawValue)", presentationData.strings.baseLanguageCode), color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
setAction(value.rawValue)
}))
}
}
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
})
])])
presentControllerImpl?(actionSheet, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}, openDisclosureLink: { link in
switch (link) {
case .languageSettings:
pushControllerImpl?(context.sharedContext.makeLocalizationListController(context: context))
case .contentSettings:
let _ = (getSGSettingsURL(context: context) |> deliverOnMainQueue).start(next: { [weak context] url in
guard let strongContext = context else {
return
}
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])
let preferences = context.account.postbox.preferencesView(keys: [ApplicationSpecificPreferencesKeys.SGUISettings, PreferencesKeys.appConfiguration])
let updatedContentSettingsConfiguration = contentSettingsConfiguration(network: context.account.network)
|> map(Optional.init)
let contentSettingsConfiguration = Promise<ContentSettingsConfiguration?>()
contentSettingsConfiguration.set(.single(nil)
|> then(updatedContentSettingsConfiguration))
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
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 controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("Swiftgram"), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
// TODO(swiftgram): focusOnItemTag support
/* var index = 0
var scrollToItem: ListViewScrollToItem?
if let focusOnItemTag = focusOnItemTag {
for entry in entries {
if entry.tag?.isEqual(to: focusOnItemTag) ?? false {
scrollToItem = ListViewScrollToItem(index: index, position: .top(0.0), animated: false, curve: .Default(duration: 0.0), directionHint: .Up)
}
index += 1
}
} */
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: entries, style: .blocks, ensureVisibleItemTag: /*focusOnItemTag*/ nil, initialScrollToItem: nil /* scrollToItem*/ )
return (controllerState, (listState, arguments))
}
let controller = ItemListController(context: context, state: signal)
presentControllerImpl = { [weak controller] c, a in
controller?.present(c, in: .window(.root), with: a)
}
pushControllerImpl = { [weak controller] c in
(controller?.navigationController as? NavigationController)?.pushViewController(c)
}
// getRootControllerImpl = { [weak controller] in
// return controller?.view.window?.rootViewController
// }
// getNavigationControllerImpl = { [weak controller] in
// return controller?.navigationController as? NavigationController
// }
askForRestart = { [weak context] in
guard let context = context else {
return
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(
UndoOverlayController(
presentationData: presentationData,
content: .info(title: nil, // i18n("Common.RestartRequired", presentationData.strings.baseLanguageCode),
text: i18n("Common.RestartRequired", presentationData.strings.baseLanguageCode),
timeout: nil,
customUndoText: i18n("Common.RestartNow", presentationData.strings.baseLanguageCode) //presentationData.strings.Common_Yes
),
elevatedLayout: false,
action: { action in if action == .undo { exit(0) }; return true }
),
nil
)
}
return controller
}