chore: prerelease 0.1.0

This commit is contained in:
IMDelewer 2026-06-12 19:26:54 +03:00
parent 6e370e06d1
commit c37205ebc7
37 changed files with 56411 additions and 110 deletions

View file

@ -4,8 +4,8 @@ import TelegramCore
import TelegramUIPreferences
import AccountContext
public let maximumNumberOfAccounts = 3
public let maximumPremiumNumberOfAccounts = 4
public let maximumNumberOfAccounts = 100
public let maximumPremiumNumberOfAccounts = 100
public func activeAccountsAndPeers(context: AccountContext, includePrimary: Bool = false) -> Signal<((AccountContext, EnginePeer)?, [(AccountContext, EnginePeer, Int32)]), NoError> {
let sharedContext = context.sharedContext

View file

@ -0,0 +1,607 @@
import Foundation
import UIKit
import Display
import SwiftSignalKit
import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
import ItemListUI
import PresentationDataUtils
import AccountContext
private final class WinterGramSettingsArguments {
let updateSettings: (@escaping (WinterGramSettings) -> WinterGramSettings) -> Void
let presentSendWithoutSound: () -> Void
let presentPeerId: () -> Void
let presentTranslationProvider: () -> Void
let presentWebviewPlatform: () -> Void
let presentIconPack: () -> Void
init(
updateSettings: @escaping (@escaping (WinterGramSettings) -> WinterGramSettings) -> Void,
presentSendWithoutSound: @escaping () -> Void,
presentPeerId: @escaping () -> Void,
presentTranslationProvider: @escaping () -> Void,
presentWebviewPlatform: @escaping () -> Void,
presentIconPack: @escaping () -> Void
) {
self.updateSettings = updateSettings
self.presentSendWithoutSound = presentSendWithoutSound
self.presentPeerId = presentPeerId
self.presentTranslationProvider = presentTranslationProvider
self.presentWebviewPlatform = presentWebviewPlatform
self.presentIconPack = presentIconPack
}
}
private enum WinterGramSettingsSection: Int32 {
case ghost
case history
case stash
case antiFeatures
case confirmations
case chat
case appearance
case liquidGlass
}
private enum WinterGramSettingsEntry: ItemListNodeEntry {
case ghostHeader
case ghostEnabled(Bool)
case ghostReadReceipts(Bool)
case ghostReadStories(Bool)
case ghostOnlineStatus(Bool)
case ghostUploadProgress(Bool)
case ghostOfflineAfterOnline(Bool)
case ghostMarkReadAfterAction(Bool)
case ghostUseScheduled(Bool)
case ghostSendWithoutSound(String)
case ghostSuggestBeforeStory(Bool)
case ghostFooter
case historyHeader
case historySaveDeleted(Bool)
case historySaveEdits(Bool)
case historySemiTransparent(Bool)
case historyFooter
case stashHeader
case stashMute(Bool)
case stashAutoRead(Bool)
case stashFooter
case antiHeader
case antiDisableAds(Bool)
case antiLocalPremium(Bool)
case antiDisableStories(Bool)
case antiHidePremiumStatuses(Bool)
case antiDisableLinkWarning(Bool)
case antiFooter
case confirmHeader
case confirmStickers(Bool)
case confirmGif(Bool)
case confirmVoice(Bool)
case chatHeader
case chatShowSeconds(Bool)
case chatShowPeerId(String)
case chatTranslate(Bool)
case chatTranslateProvider(String)
case chatWebviewPlatform(String)
case chatWebviewHeight(Bool)
case chatOnlyAddedEmoji(Bool)
case appearanceHeader
case appearanceMaterial(Bool)
case appearanceSingleCorner(Bool)
case appearanceIconPack(String)
case glassHeader
case glassEnabled(Bool)
case glassVibrancy(Bool)
case glassChatList(Bool)
case glassNavBars(Bool)
case glassTabBar(Bool)
case glassBubbles(Bool)
case glassFooter
var section: ItemListSectionId {
switch self {
case .ghostHeader, .ghostEnabled, .ghostReadReceipts, .ghostReadStories, .ghostOnlineStatus, .ghostUploadProgress, .ghostOfflineAfterOnline, .ghostMarkReadAfterAction, .ghostUseScheduled, .ghostSendWithoutSound, .ghostSuggestBeforeStory, .ghostFooter:
return WinterGramSettingsSection.ghost.rawValue
case .historyHeader, .historySaveDeleted, .historySaveEdits, .historySemiTransparent, .historyFooter:
return WinterGramSettingsSection.history.rawValue
case .stashHeader, .stashMute, .stashAutoRead, .stashFooter:
return WinterGramSettingsSection.stash.rawValue
case .antiHeader, .antiDisableAds, .antiLocalPremium, .antiDisableStories, .antiHidePremiumStatuses, .antiDisableLinkWarning, .antiFooter:
return WinterGramSettingsSection.antiFeatures.rawValue
case .confirmHeader, .confirmStickers, .confirmGif, .confirmVoice:
return WinterGramSettingsSection.confirmations.rawValue
case .chatHeader, .chatShowSeconds, .chatShowPeerId, .chatTranslate, .chatTranslateProvider, .chatWebviewPlatform, .chatWebviewHeight, .chatOnlyAddedEmoji:
return WinterGramSettingsSection.chat.rawValue
case .appearanceHeader, .appearanceMaterial, .appearanceSingleCorner, .appearanceIconPack:
return WinterGramSettingsSection.appearance.rawValue
case .glassHeader, .glassEnabled, .glassVibrancy, .glassChatList, .glassNavBars, .glassTabBar, .glassBubbles, .glassFooter:
return WinterGramSettingsSection.liquidGlass.rawValue
}
}
var stableId: Int32 {
switch self {
case .ghostHeader: return 0
case .ghostEnabled: return 1
case .ghostReadReceipts: return 2
case .ghostReadStories: return 3
case .ghostOnlineStatus: return 4
case .ghostUploadProgress: return 5
case .ghostOfflineAfterOnline: return 6
case .ghostMarkReadAfterAction: return 7
case .ghostUseScheduled: return 8
case .ghostSendWithoutSound: return 9
case .ghostSuggestBeforeStory: return 10
case .ghostFooter: return 11
case .historyHeader: return 12
case .historySaveDeleted: return 13
case .historySaveEdits: return 14
case .historySemiTransparent: return 15
case .historyFooter: return 16
case .stashHeader: return 17
case .stashMute: return 18
case .stashAutoRead: return 19
case .stashFooter: return 20
case .antiHeader: return 21
case .antiDisableAds: return 22
case .antiLocalPremium: return 23
case .antiDisableStories: return 24
case .antiHidePremiumStatuses: return 25
case .antiDisableLinkWarning: return 26
case .antiFooter: return 27
case .confirmHeader: return 28
case .confirmStickers: return 29
case .confirmGif: return 30
case .confirmVoice: return 31
case .chatHeader: return 32
case .chatShowSeconds: return 33
case .chatShowPeerId: return 34
case .chatTranslate: return 35
case .chatTranslateProvider: return 36
case .chatWebviewPlatform: return 37
case .chatWebviewHeight: return 38
case .chatOnlyAddedEmoji: return 39
case .appearanceHeader: return 40
case .appearanceMaterial: return 41
case .appearanceSingleCorner: return 42
case .appearanceIconPack: return 43
case .glassHeader: return 44
case .glassEnabled: return 45
case .glassVibrancy: return 46
case .glassChatList: return 47
case .glassNavBars: return 48
case .glassTabBar: return 49
case .glassBubbles: return 50
case .glassFooter: return 51
}
}
static func <(lhs: WinterGramSettingsEntry, rhs: WinterGramSettingsEntry) -> Bool {
return lhs.stableId < rhs.stableId
}
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
let arguments = arguments as! WinterGramSettingsArguments
switch self {
case .ghostHeader:
return ItemListSectionHeaderItem(presentationData: presentationData, text: "GHOST MODE", sectionId: self.section)
case let .ghostEnabled(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Ghost Mode", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.ghostModeEnabled = value; return s }
})
case let .ghostReadReceipts(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Send Read Receipts", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.sendReadReceipts = value; return s }
})
case let .ghostReadStories(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Send Story Views", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.sendReadStories = value; return s }
})
case let .ghostOnlineStatus(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Send Online Status", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.sendOnlineStatus = value; return s }
})
case let .ghostUploadProgress(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Send Typing & Upload Status", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.sendUploadProgress = value; return s }
})
case let .ghostOfflineAfterOnline(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Go Offline After Online", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.sendOfflineAfterOnline = value; return s }
})
case let .ghostMarkReadAfterAction(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Mark Read After Action", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.markReadAfterAction = value; return s }
})
case let .ghostUseScheduled(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Use Scheduled Messages", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.useScheduledMessages = value; return s }
})
case let .ghostSendWithoutSound(value):
return ItemListDisclosureItem(presentationData: presentationData, title: "Send Without Sound", label: value, sectionId: self.section, style: .blocks, action: {
arguments.presentSendWithoutSound()
})
case let .ghostSuggestBeforeStory(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Ask Before Viewing Stories", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.suggestGhostBeforeStory = value; return s }
})
case .ghostFooter:
return ItemListTextItem(presentationData: presentationData, text: .plain("When Ghost Mode is on, WinterGram stops sending read receipts, online status and typing activity."), sectionId: self.section)
case .historyHeader:
return ItemListSectionHeaderItem(presentationData: presentationData, text: "HISTORY", sectionId: self.section)
case let .historySaveDeleted(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Save Deleted Messages", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.saveDeletedMessages = value; return s }
})
case let .historySaveEdits(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Save Edit History", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.saveMessagesHistory = value; return s }
})
case let .historySemiTransparent(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Dim Deleted Messages", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.semiTransparentDeletedMessages = value; return s }
})
case .historyFooter:
return ItemListTextItem(presentationData: presentationData, text: .plain("Deleted and edited messages are kept locally on this device only."), sectionId: self.section)
case .stashHeader:
return ItemListSectionHeaderItem(presentationData: presentationData, text: "HIDDEN ARCHIVE", sectionId: self.section)
case let .stashMute(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Mute Notifications", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.stashMuteNotifications = value; return s }
})
case let .stashAutoRead(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Auto Mark as Read", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.stashAutoMarkRead = value; return s }
})
case .stashFooter:
return ItemListTextItem(presentationData: presentationData, text: .plain("Stashed chats are hidden from the main list and accessible only here."), sectionId: self.section)
case .antiHeader:
return ItemListSectionHeaderItem(presentationData: presentationData, text: "FEATURES", sectionId: self.section)
case let .antiDisableAds(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Disable Ads", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.disableAds = value; return s }
})
case let .antiLocalPremium(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Local Premium", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.localPremium = value; return s }
})
case let .antiDisableStories(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Hide Stories", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.disableStories = value; return s }
})
case let .antiHidePremiumStatuses(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Hide Premium Statuses", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.hidePremiumStatuses = value; return s }
})
case let .antiDisableLinkWarning(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Disable Open Link Warning", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.disableOpenLinkWarning = value; return s }
})
case .antiFooter:
return ItemListTextItem(presentationData: presentationData, text: .plain("Local Premium unlocks Premium-only UI on this device; it does not grant server-side Premium."), sectionId: self.section)
case .confirmHeader:
return ItemListSectionHeaderItem(presentationData: presentationData, text: "SEND CONFIRMATIONS", sectionId: self.section)
case let .confirmStickers(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Confirm Stickers", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.stickerConfirmation = value; return s }
})
case let .confirmGif(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Confirm GIFs", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.gifConfirmation = value; return s }
})
case let .confirmVoice(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Confirm Voice Messages", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.voiceConfirmation = value; return s }
})
case .chatHeader:
return ItemListSectionHeaderItem(presentationData: presentationData, text: "CHAT", sectionId: self.section)
case let .chatShowSeconds(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Show Message Seconds", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.showMessageSeconds = value; return s }
})
case let .chatShowPeerId(value):
return ItemListDisclosureItem(presentationData: presentationData, title: "Show Peer ID", label: value, sectionId: self.section, style: .blocks, action: {
arguments.presentPeerId()
})
case let .chatTranslate(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Message Translation", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.translateMessages = value; return s }
})
case let .chatTranslateProvider(value):
return ItemListDisclosureItem(presentationData: presentationData, title: "Translation Provider", label: value, sectionId: self.section, style: .blocks, action: {
arguments.presentTranslationProvider()
})
case let .chatWebviewPlatform(value):
return ItemListDisclosureItem(presentationData: presentationData, title: "WebView Platform", label: value, sectionId: self.section, style: .blocks, action: {
arguments.presentWebviewPlatform()
})
case let .chatWebviewHeight(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Increase WebView Height", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.increaseWebviewHeight = value; return s }
})
case let .chatOnlyAddedEmoji(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Only Added Emoji & Stickers", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.showOnlyAddedEmojisAndStickers = value; return s }
})
case .appearanceHeader:
return ItemListSectionHeaderItem(presentationData: presentationData, text: "APPEARANCE", sectionId: self.section)
case let .appearanceMaterial(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Material Design", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.materialDesign = value; return s }
})
case let .appearanceSingleCorner(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Single Corner Radius", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.singleCornerRadius = value; return s }
})
case let .appearanceIconPack(value):
return ItemListDisclosureItem(presentationData: presentationData, title: "Icon Pack", label: value, sectionId: self.section, style: .blocks, action: {
arguments.presentIconPack()
})
case .glassHeader:
return ItemListSectionHeaderItem(presentationData: presentationData, text: "LIQUID GLASS", sectionId: self.section)
case let .glassEnabled(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Liquid Glass", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.liquidGlass.enabled = value; return s }
})
case let .glassVibrancy(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Vibrancy", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.liquidGlass.vibrancy = value; return s }
})
case let .glassChatList(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Apply to Chat List", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.liquidGlass.applyToChatList = value; return s }
})
case let .glassNavBars(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Apply to Navigation Bars", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.liquidGlass.applyToNavigationBars = value; return s }
})
case let .glassTabBar(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Apply to Tab Bar", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.liquidGlass.applyToTabBar = value; return s }
})
case let .glassBubbles(value):
return ItemListSwitchItem(presentationData: presentationData, title: "Apply to Bubbles", value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.updateSettings { var s = $0; s.liquidGlass.applyToBubbles = value; return s }
})
case .glassFooter:
return ItemListTextItem(presentationData: presentationData, text: .plain("Transparency, blur and tint can be fine-tuned per surface. Turn Liquid Glass off for the standard opaque look."), sectionId: self.section)
}
}
}
private func sendWithoutSoundLabel(_ value: WinterGramSendWithoutSound) -> String {
switch value {
case .never: return "Never"
case .inGhostMode: return "In Ghost Mode"
case .always: return "Always"
}
}
private func peerIdLabel(_ value: WinterGramPeerIdDisplay) -> String {
switch value {
case .hidden: return "Hidden"
case .telegramApi: return "Telegram API"
case .botApi: return "Bot API"
}
}
private func translationProviderLabel(_ value: WinterGramTranslationProvider) -> String {
switch value {
case .telegram: return "Telegram"
case .google: return "Google"
case .yandex: return "Yandex"
case .system: return "System"
}
}
private func webviewPlatformLabel(_ value: WinterGramWebviewPlatform) -> String {
switch value {
case .auto: return "Automatic"
case .ios: return "iOS"
case .android: return "Android"
case .macos: return "macOS"
case .desktop: return "Desktop"
}
}
private func iconPackLabel(_ value: WinterGramIconPack) -> String {
switch value {
case .wintergram: return "WinterGram"
case .ayugram: return "AyuGram"
case .exteragram: return "exteraGram"
case .telegram: return "Telegram"
}
}
private func winterGramSettingsEntries(settings: WinterGramSettings) -> [WinterGramSettingsEntry] {
var entries: [WinterGramSettingsEntry] = []
entries.append(.ghostHeader)
entries.append(.ghostEnabled(settings.ghostModeEnabled))
entries.append(.ghostReadReceipts(settings.sendReadReceipts))
entries.append(.ghostReadStories(settings.sendReadStories))
entries.append(.ghostOnlineStatus(settings.sendOnlineStatus))
entries.append(.ghostUploadProgress(settings.sendUploadProgress))
entries.append(.ghostOfflineAfterOnline(settings.sendOfflineAfterOnline))
entries.append(.ghostMarkReadAfterAction(settings.markReadAfterAction))
entries.append(.ghostUseScheduled(settings.useScheduledMessages))
entries.append(.ghostSendWithoutSound(sendWithoutSoundLabel(settings.sendWithoutSound)))
entries.append(.ghostSuggestBeforeStory(settings.suggestGhostBeforeStory))
entries.append(.ghostFooter)
entries.append(.historyHeader)
entries.append(.historySaveDeleted(settings.saveDeletedMessages))
entries.append(.historySaveEdits(settings.saveMessagesHistory))
entries.append(.historySemiTransparent(settings.semiTransparentDeletedMessages))
entries.append(.historyFooter)
entries.append(.stashHeader)
entries.append(.stashMute(settings.stashMuteNotifications))
entries.append(.stashAutoRead(settings.stashAutoMarkRead))
entries.append(.stashFooter)
entries.append(.antiHeader)
entries.append(.antiDisableAds(settings.disableAds))
entries.append(.antiLocalPremium(settings.localPremium))
entries.append(.antiDisableStories(settings.disableStories))
entries.append(.antiHidePremiumStatuses(settings.hidePremiumStatuses))
entries.append(.antiDisableLinkWarning(settings.disableOpenLinkWarning))
entries.append(.antiFooter)
entries.append(.confirmHeader)
entries.append(.confirmStickers(settings.stickerConfirmation))
entries.append(.confirmGif(settings.gifConfirmation))
entries.append(.confirmVoice(settings.voiceConfirmation))
entries.append(.chatHeader)
entries.append(.chatShowSeconds(settings.showMessageSeconds))
entries.append(.chatShowPeerId(peerIdLabel(settings.showPeerId)))
entries.append(.chatTranslate(settings.translateMessages))
entries.append(.chatTranslateProvider(translationProviderLabel(settings.translationProvider)))
entries.append(.chatWebviewPlatform(webviewPlatformLabel(settings.webviewSpoofPlatform)))
entries.append(.chatWebviewHeight(settings.increaseWebviewHeight))
entries.append(.chatOnlyAddedEmoji(settings.showOnlyAddedEmojisAndStickers))
entries.append(.appearanceHeader)
entries.append(.appearanceMaterial(settings.materialDesign))
entries.append(.appearanceSingleCorner(settings.singleCornerRadius))
entries.append(.appearanceIconPack(iconPackLabel(settings.iconPack)))
entries.append(.glassHeader)
entries.append(.glassEnabled(settings.liquidGlass.enabled))
entries.append(.glassVibrancy(settings.liquidGlass.vibrancy))
entries.append(.glassChatList(settings.liquidGlass.applyToChatList))
entries.append(.glassNavBars(settings.liquidGlass.applyToNavigationBars))
entries.append(.glassTabBar(settings.liquidGlass.applyToTabBar))
entries.append(.glassBubbles(settings.liquidGlass.applyToBubbles))
entries.append(.glassFooter)
return entries
}
public func winterGramSettingsController(context: AccountContext) -> ViewController {
let accountManager = context.sharedContext.accountManager
var presentControllerImpl: ((ViewController, Any?) -> Void)?
let updateSettings: (@escaping (WinterGramSettings) -> WinterGramSettings) -> Void = { f in
let _ = updateWinterGramSettingsInteractively(accountManager: accountManager, f).start()
}
func presentChoice<T>(title: String, options: [(String, T)], apply: @escaping (T, WinterGramSettings) -> WinterGramSettings) {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let controller = ActionSheetController(presentationData: presentationData)
var items: [ActionSheetItem] = []
items.append(ActionSheetTextItem(title: title))
for (label, value) in options {
items.append(ActionSheetButtonItem(title: label, action: { [weak controller] in
controller?.dismissAnimated()
updateSettings { apply(value, $0) }
}))
}
controller.setItemGroups([
ActionSheetItemGroup(items: items),
ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak controller] in
controller?.dismissAnimated()
})
])
])
presentControllerImpl?(controller, nil)
}
let arguments = WinterGramSettingsArguments(
updateSettings: updateSettings,
presentSendWithoutSound: {
presentChoice(title: "Send Without Sound", options: [
("Never", WinterGramSendWithoutSound.never),
("In Ghost Mode", WinterGramSendWithoutSound.inGhostMode),
("Always", WinterGramSendWithoutSound.always)
], apply: { value, settings in
var settings = settings
settings.sendWithoutSound = value
return settings
})
},
presentPeerId: {
presentChoice(title: "Show Peer ID", options: [
("Hidden", WinterGramPeerIdDisplay.hidden),
("Telegram API", WinterGramPeerIdDisplay.telegramApi),
("Bot API", WinterGramPeerIdDisplay.botApi)
], apply: { value, settings in
var settings = settings
settings.showPeerId = value
return settings
})
},
presentTranslationProvider: {
presentChoice(title: "Translation Provider", options: [
("Telegram", WinterGramTranslationProvider.telegram),
("Google", WinterGramTranslationProvider.google),
("Yandex", WinterGramTranslationProvider.yandex),
("System", WinterGramTranslationProvider.system)
], apply: { value, settings in
var settings = settings
settings.translationProvider = value
return settings
})
},
presentWebviewPlatform: {
presentChoice(title: "WebView Platform", options: [
("Automatic", WinterGramWebviewPlatform.auto),
("iOS", WinterGramWebviewPlatform.ios),
("Android", WinterGramWebviewPlatform.android),
("macOS", WinterGramWebviewPlatform.macos),
("Desktop", WinterGramWebviewPlatform.desktop)
], apply: { value, settings in
var settings = settings
settings.webviewSpoofPlatform = value
return settings
})
},
presentIconPack: {
presentChoice(title: "Icon Pack", options: [
("WinterGram", WinterGramIconPack.wintergram),
("AyuGram", WinterGramIconPack.ayugram),
("exteraGram", WinterGramIconPack.exteragram),
("Telegram", WinterGramIconPack.telegram)
], apply: { value, settings in
var settings = settings
settings.iconPack = value
return settings
})
}
)
let signal = combineLatest(queue: .mainQueue(),
context.sharedContext.presentationData,
winterGramSettings(accountManager: accountManager)
)
|> deliverOnMainQueue
|> map { presentationData, settings -> (ItemListControllerState, (ItemListNodeState, Any)) in
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("WinterGram"), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: winterGramSettingsEntries(settings: settings), style: .blocks, animateChanges: true)
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)
}
return controller
}

View file

@ -279,8 +279,10 @@ public func performAppGroupUpgrades(appGroupPath: String, rootPath: String) {
}
do {
// WinterGram: include account data in the device's iCloud/iTunes backup so accounts
// survive a restore. Tradeoff: session auth keys are then part of the iCloud backup.
var resourceValues = URLResourceValues()
resourceValues.isExcludedFromBackup = true
resourceValues.isExcludedFromBackup = false
var mutableUrl = URL(fileURLWithPath: rootPath)
try mutableUrl.setResourceValues(resourceValues)
} catch let e {

View file

@ -131,7 +131,8 @@ public struct PresentationResourcesSettings {
public static let powerSaving = renderSettingsIcon(name: "Item List/Icons/PowerSaving", backgroundColors: [colorOrange])
public static let business = renderSettingsIcon(name: "Item List/Icons/Business", backgroundColors: [UIColor(rgb: 0xA95CE3), UIColor(rgb: 0xF16B80)])
public static let myProfile = renderSettingsIcon(name: "Item List/Icons/Profile", backgroundColors: [colorRed])
public static let winterGram = renderSettingsIcon(name: "Item List/Icons/WinterGram", backgroundColors: [colorLightBlue])
public static let birthday = renderSettingsIcon(name: "Item List/Icons/Cake", backgroundColors: [colorBlue])
public static let aiTools = renderSettingsIcon(name: "Item List/Icons/AITools", backgroundColors: [colorPurple])
public static let yourColor = renderSettingsIcon(name: "Item List/Icons/Brush", backgroundColors: [colorLightBlue])

View file

@ -18,6 +18,7 @@ swift_library(
"//submodules/OverlayStatusController",
"//submodules/UrlWhitelist",
"//submodules/TelegramPresentationData",
"//submodules/TelegramUIPreferences",
"//submodules/TelegramUI/Components/AlertComponent",
"//submodules/TelegramUI/Components/AlertComponent/AlertCheckComponent",
"//submodules/TelegramUI/Components/AlertComponent/AlertWebpagePreviewComponent",

View file

@ -8,6 +8,7 @@ import AccountContext
import OverlayStatusController
import UrlWhitelist
import TelegramPresentationData
import TelegramUIPreferences
import AlertComponent
import AlertCheckComponent
import AlertWebpagePreviewComponent
@ -111,7 +112,7 @@ public func openUserGeneratedUrl(
}
}
if concealed && !skipConcealedAlert {
if concealed && !skipConcealedAlert && !currentWinterGramSettings.disableOpenLinkWarning {
var rawDisplayUrl: String = parsedString
let maxLength = 180
if rawDisplayUrl.count > maxLength {

View file

@ -188,6 +188,7 @@ enum PeerInfoSettingsSection {
case premiumManagement
case stars
case ton
case winterGram
}
enum PeerInfoReportType {

View file

@ -217,15 +217,15 @@ extension PeerInfoScreenNode {
guard let strongSelf = self else {
return
}
var maximumAvailableAccounts: Int = 3
var maximumAvailableAccounts: Int = maximumNumberOfAccounts
if accountAndPeer?.1.isPremium == true && !strongSelf.context.account.testingEnvironment {
maximumAvailableAccounts = 4
maximumAvailableAccounts = maximumPremiumNumberOfAccounts
}
var count: Int = 1
for (accountContext, peer, _) in accountsAndPeers {
if !accountContext.account.testingEnvironment {
if peer.isPremium {
maximumAvailableAccounts = 4
maximumAvailableAccounts = maximumPremiumNumberOfAccounts
}
count += 1
}
@ -271,6 +271,8 @@ extension PeerInfoScreenNode {
self.interaction.editingOpenNameColorSetup()
case .powerSaving:
push(energySavingSettingsScreen(context: self.context))
case .winterGram:
push(winterGramSettingsController(context: self.context))
case .businessSetup:
guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else {
return

View file

@ -14,6 +14,7 @@ import TelegramStringFormatting
import PeerNameColorItem
enum SettingsSection: Int, CaseIterable {
case winterGram
case edit
case phone
case accounts
@ -36,7 +37,11 @@ func settingsItems(data: PeerInfoScreenData?, context: AccountContext, presentat
for section in SettingsSection.allCases {
items[section] = []
}
items[.winterGram]!.append(PeerInfoScreenDisclosureItem(id: 0, text: "WinterGram", icon: PresentationResourcesSettings.winterGram, action: {
interaction.openSettings(.winterGram)
}))
let setPhotoTitle: String
if let peer = data.peer, !peer.profileImageRepresentations.isEmpty {
setPhotoTitle = presentationData.strings.Settings_ChangeProfilePhoto
@ -239,7 +244,7 @@ func settingsItems(data: PeerInfoScreenData?, context: AccountContext, presentat
items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 3, text: presentationData.strings.Settings_Appearance, icon: PresentationResourcesSettings.appearance, action: {
interaction.openSettings(.appearance)
}))
items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 6, label: .text(data.isPowerSavingEnabled == true ? presentationData.strings.Settings_PowerSavingOn : presentationData.strings.Settings_PowerSavingOff), text: presentationData.strings.Settings_PowerSaving, icon: PresentationResourcesSettings.powerSaving, action: {
interaction.openSettings(.powerSaving)
}))

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "wintergram_snowball_30.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -56,6 +56,17 @@ private let handleVoipNotifications = false
private var testIsLaunched = false
func normalizeWinterGramUrlScheme(_ url: URL) -> URL {
guard let scheme = url.scheme, scheme.lowercased() == "wnt" else {
return url
}
guard var components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
return url
}
components.scheme = "tg"
return components.url ?? url
}
private func isKeyboardWindow(window: NSObject) -> Bool {
let typeName = NSStringFromClass(type(of: window))
if #available(iOS 9.0, *) {
@ -248,7 +259,8 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
private let openNotificationSettingsWhenReadyDisposable = MetaDisposable()
private let openChatWhenReadyDisposable = MetaDisposable()
private let openUrlWhenReadyDisposable = MetaDisposable()
private let winterGramSettingsDisposable = MetaDisposable()
private let badgeDisposable = MetaDisposable()
private let quickActionsDisposable = MetaDisposable()
@ -1191,6 +1203,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
})
let sharedApplicationContext = SharedApplicationContext(sharedContext: sharedContext, notificationManager: notificationManager, wakeupManager: wakeupManager)
sharedApplicationContext.sharedContext.mediaManager.overlayMediaManager.attachOverlayMediaController(sharedApplicationContext.overlayMediaController)
self.winterGramSettingsDisposable.set(observeWinterGramSettings(accountManager: accountManager))
return accountManager.transaction { transaction -> (SharedApplicationContext, LoggingSettings) in
return (sharedApplicationContext, transaction.getSharedData(SharedDataKeys.loggingSettings)?.get(LoggingSettings.self) ?? LoggingSettings.defaultSettings)
@ -1513,10 +1526,10 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
})
if let url = launchOptions?[.url] {
if let url = url as? URL, url.scheme == "tg" || url.scheme == buildConfig.appSpecificUrlScheme {
self.openUrlWhenReady(url: url, external: true)
} else if let urlString = url as? String, urlString.lowercased().hasPrefix("tg:") || urlString.lowercased().hasPrefix("\(buildConfig.appSpecificUrlScheme):"), let url = URL(string: urlString) {
self.openUrlWhenReady(url: url, external: true)
if let url = url as? URL, url.scheme == "tg" || url.scheme == "wnt" || url.scheme == buildConfig.appSpecificUrlScheme {
self.openUrlWhenReady(url: normalizeWinterGramUrlScheme(url), external: true)
} else if let urlString = url as? String, urlString.lowercased().hasPrefix("tg:") || urlString.lowercased().hasPrefix("wnt:") || urlString.lowercased().hasPrefix("\(buildConfig.appSpecificUrlScheme):"), let url = URL(string: urlString) {
self.openUrlWhenReady(url: normalizeWinterGramUrlScheme(url), external: true)
}
}
@ -2500,6 +2513,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
}
private func openUrl(url: URL) {
let url = normalizeWinterGramUrlScheme(url)
let _ = (self.sharedContextPromise.get()
|> take(1)
|> mapToSignal { sharedApplicationContext -> Signal<(SharedAccountContextImpl, AuthorizedApplicationContext?, UnauthorizedApplicationContext?), NoError> in

View file

@ -6,6 +6,7 @@ import TemporaryCachedPeerDataManager
import Emoji
import AccountContext
import TelegramPresentationData
import TelegramUIPreferences
import ChatHistoryEntry
import ChatMessageItemCommon
import TextFormat
@ -687,7 +688,7 @@ func chatHistoryEntriesForView(
}
if view.laterId == nil && !view.isLoading {
if !entries.isEmpty, case let .MessageEntry(lastMessage, _, _, _, _, _) = entries[entries.count - 1], let message = adMessage {
if !entries.isEmpty, !currentWinterGramSettings.disableAds, case let .MessageEntry(lastMessage, _, _, _, _, _) = entries[entries.count - 1], let message = adMessage {
var nextAdMessageId: Int32 = 10000
let updatedMessage = Message(
stableId: ChatHistoryListNodeImpl.fixedAdMessageStableId,

View file

@ -51,6 +51,7 @@ private enum ApplicationSpecificSharedDataKeyValues: Int32 {
case mediaDisplaySettings = 20
case updateSettings = 21
case chatSettings = 22
case winterGramSettings = 23
}
public struct ApplicationSpecificSharedDataKeys {
@ -77,6 +78,7 @@ public struct ApplicationSpecificSharedDataKeys {
public static let mediaDisplaySettings: EngineDataBuffer = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.mediaDisplaySettings.rawValue)
public static let updateSettings: EngineDataBuffer = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.updateSettings.rawValue)
public static let chatSettings: EngineDataBuffer = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.chatSettings.rawValue)
public static let winterGramSettings: EngineDataBuffer = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.winterGramSettings.rawValue)
}
private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 {

View file

@ -0,0 +1,491 @@
import Foundation
import TelegramCore
import SwiftSignalKit
public enum WinterGramSendWithoutSound: Int32, Codable {
case never = 0
case inGhostMode = 1
case always = 2
}
public enum WinterGramPeerIdDisplay: Int32, Codable {
case hidden = 0
case telegramApi = 1
case botApi = 2
}
public enum WinterGramTranslationProvider: Int32, Codable {
case telegram = 0
case google = 1
case yandex = 2
case system = 3
}
public enum WinterGramWebviewPlatform: Int32, Codable {
case auto = 0
case ios = 1
case android = 2
case macos = 3
case desktop = 4
}
public enum WinterGramIconPack: Int32, Codable {
case wintergram = 0
case ayugram = 1
case exteragram = 2
case telegram = 3
}
public struct WinterGramLiquidGlass: Codable, Equatable {
public var enabled: Bool
public var transparency: Double
public var blurRadius: Double
public var tintColor: Int32?
public var vibrancy: Bool
public var applyToChatList: Bool
public var applyToBubbles: Bool
public var applyToNavigationBars: Bool
public var applyToTabBar: Bool
public static var defaultSettings: WinterGramLiquidGlass {
return WinterGramLiquidGlass(
enabled: true,
transparency: 0.75,
blurRadius: 30.0,
tintColor: nil,
vibrancy: true,
applyToChatList: true,
applyToBubbles: false,
applyToNavigationBars: true,
applyToTabBar: true
)
}
public init(
enabled: Bool,
transparency: Double,
blurRadius: Double,
tintColor: Int32?,
vibrancy: Bool,
applyToChatList: Bool,
applyToBubbles: Bool,
applyToNavigationBars: Bool,
applyToTabBar: Bool
) {
self.enabled = enabled
self.transparency = transparency
self.blurRadius = blurRadius
self.tintColor = tintColor
self.vibrancy = vibrancy
self.applyToChatList = applyToChatList
self.applyToBubbles = applyToBubbles
self.applyToNavigationBars = applyToNavigationBars
self.applyToTabBar = applyToTabBar
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
self.enabled = try container.decodeIfPresent(Bool.self, forKey: "enabled") ?? true
self.transparency = try container.decodeIfPresent(Double.self, forKey: "transparency") ?? 0.75
self.blurRadius = try container.decodeIfPresent(Double.self, forKey: "blurRadius") ?? 30.0
self.tintColor = try container.decodeIfPresent(Int32.self, forKey: "tintColor")
self.vibrancy = try container.decodeIfPresent(Bool.self, forKey: "vibrancy") ?? true
self.applyToChatList = try container.decodeIfPresent(Bool.self, forKey: "applyToChatList") ?? true
self.applyToBubbles = try container.decodeIfPresent(Bool.self, forKey: "applyToBubbles") ?? false
self.applyToNavigationBars = try container.decodeIfPresent(Bool.self, forKey: "applyToNavigationBars") ?? true
self.applyToTabBar = try container.decodeIfPresent(Bool.self, forKey: "applyToTabBar") ?? true
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(self.enabled, forKey: "enabled")
try container.encode(self.transparency, forKey: "transparency")
try container.encode(self.blurRadius, forKey: "blurRadius")
try container.encodeIfPresent(self.tintColor, forKey: "tintColor")
try container.encode(self.vibrancy, forKey: "vibrancy")
try container.encode(self.applyToChatList, forKey: "applyToChatList")
try container.encode(self.applyToBubbles, forKey: "applyToBubbles")
try container.encode(self.applyToNavigationBars, forKey: "applyToNavigationBars")
try container.encode(self.applyToTabBar, forKey: "applyToTabBar")
}
}
public struct WinterGramSettings: Codable, Equatable {
// Ghost mode & privacy
public var ghostModeEnabled: Bool
public var sendReadReceipts: Bool
public var sendReadStories: Bool
public var sendOnlineStatus: Bool
public var sendUploadProgress: Bool
public var sendOfflineAfterOnline: Bool
public var markReadAfterAction: Bool
public var useScheduledMessages: Bool
public var sendWithoutSound: WinterGramSendWithoutSound
public var suggestGhostBeforeStory: Bool
// Local history
public var saveDeletedMessages: Bool
public var saveMessagesHistory: Bool
public var saveForBots: Bool
public var hideFromBlocked: Bool
public var semiTransparentDeletedMessages: Bool
// Hidden archive ("ААрхив")
public var stashedPeerIds: [Int64]
public var stashMuteNotifications: Bool
public var stashAutoMarkRead: Bool
// Anti-features
public var disableAds: Bool
public var localPremium: Bool
public var shadowBanIds: [Int64]
public var disableStories: Bool
public var hidePremiumStatuses: Bool
public var disableOpenLinkWarning: Bool
// Confirmations
public var stickerConfirmation: Bool
public var gifConfirmation: Bool
public var voiceConfirmation: Bool
// Message decorations
public var showMessageSeconds: Bool
public var showPeerId: WinterGramPeerIdDisplay
public var deletedMark: String
public var editedMark: String
public var recentStickersCount: Int32
// Translation
public var translateMessages: Bool
public var translationProvider: WinterGramTranslationProvider
// Webview / platform spoofing
public var webviewSpoofPlatform: WinterGramWebviewPlatform
public var increaseWebviewHeight: Bool
// Appearance & customization
public var liquidGlass: WinterGramLiquidGlass
public var materialDesign: Bool
public var avatarCornerRadius: Int32
public var singleCornerRadius: Bool
public var messageBubbleRadius: Int32
public var removeMessageTail: Bool
public var customFont: String?
public var monoFont: String?
public var appIcon: String
public var iconPack: WinterGramIconPack
public var showOnlyAddedEmojisAndStickers: Bool
public static var defaultSettings: WinterGramSettings {
return WinterGramSettings(
ghostModeEnabled: false,
sendReadReceipts: true,
sendReadStories: true,
sendOnlineStatus: true,
sendUploadProgress: true,
sendOfflineAfterOnline: false,
markReadAfterAction: true,
useScheduledMessages: false,
sendWithoutSound: .never,
suggestGhostBeforeStory: true,
saveDeletedMessages: true,
saveMessagesHistory: true,
saveForBots: false,
hideFromBlocked: false,
semiTransparentDeletedMessages: false,
stashedPeerIds: [],
stashMuteNotifications: true,
stashAutoMarkRead: false,
disableAds: true,
localPremium: false,
shadowBanIds: [],
disableStories: false,
hidePremiumStatuses: false,
disableOpenLinkWarning: false,
stickerConfirmation: false,
gifConfirmation: false,
voiceConfirmation: false,
showMessageSeconds: false,
showPeerId: .botApi,
deletedMark: "🧹",
editedMark: "",
recentStickersCount: 100,
translateMessages: false,
translationProvider: .telegram,
webviewSpoofPlatform: .auto,
increaseWebviewHeight: false,
liquidGlass: .defaultSettings,
materialDesign: true,
avatarCornerRadius: 50,
singleCornerRadius: false,
messageBubbleRadius: 16,
removeMessageTail: false,
customFont: nil,
monoFont: nil,
appIcon: "WinterGramDark",
iconPack: .wintergram,
showOnlyAddedEmojisAndStickers: false
)
}
public init(
ghostModeEnabled: Bool,
sendReadReceipts: Bool,
sendReadStories: Bool,
sendOnlineStatus: Bool,
sendUploadProgress: Bool,
sendOfflineAfterOnline: Bool,
markReadAfterAction: Bool,
useScheduledMessages: Bool,
sendWithoutSound: WinterGramSendWithoutSound,
suggestGhostBeforeStory: Bool,
saveDeletedMessages: Bool,
saveMessagesHistory: Bool,
saveForBots: Bool,
hideFromBlocked: Bool,
semiTransparentDeletedMessages: Bool,
stashedPeerIds: [Int64],
stashMuteNotifications: Bool,
stashAutoMarkRead: Bool,
disableAds: Bool,
localPremium: Bool,
shadowBanIds: [Int64],
disableStories: Bool,
hidePremiumStatuses: Bool,
disableOpenLinkWarning: Bool,
stickerConfirmation: Bool,
gifConfirmation: Bool,
voiceConfirmation: Bool,
showMessageSeconds: Bool,
showPeerId: WinterGramPeerIdDisplay,
deletedMark: String,
editedMark: String,
recentStickersCount: Int32,
translateMessages: Bool,
translationProvider: WinterGramTranslationProvider,
webviewSpoofPlatform: WinterGramWebviewPlatform,
increaseWebviewHeight: Bool,
liquidGlass: WinterGramLiquidGlass,
materialDesign: Bool,
avatarCornerRadius: Int32,
singleCornerRadius: Bool,
messageBubbleRadius: Int32,
removeMessageTail: Bool,
customFont: String?,
monoFont: String?,
appIcon: String,
iconPack: WinterGramIconPack,
showOnlyAddedEmojisAndStickers: Bool
) {
self.ghostModeEnabled = ghostModeEnabled
self.sendReadReceipts = sendReadReceipts
self.sendReadStories = sendReadStories
self.sendOnlineStatus = sendOnlineStatus
self.sendUploadProgress = sendUploadProgress
self.sendOfflineAfterOnline = sendOfflineAfterOnline
self.markReadAfterAction = markReadAfterAction
self.useScheduledMessages = useScheduledMessages
self.sendWithoutSound = sendWithoutSound
self.suggestGhostBeforeStory = suggestGhostBeforeStory
self.saveDeletedMessages = saveDeletedMessages
self.saveMessagesHistory = saveMessagesHistory
self.saveForBots = saveForBots
self.hideFromBlocked = hideFromBlocked
self.semiTransparentDeletedMessages = semiTransparentDeletedMessages
self.stashedPeerIds = stashedPeerIds
self.stashMuteNotifications = stashMuteNotifications
self.stashAutoMarkRead = stashAutoMarkRead
self.disableAds = disableAds
self.localPremium = localPremium
self.shadowBanIds = shadowBanIds
self.disableStories = disableStories
self.hidePremiumStatuses = hidePremiumStatuses
self.disableOpenLinkWarning = disableOpenLinkWarning
self.stickerConfirmation = stickerConfirmation
self.gifConfirmation = gifConfirmation
self.voiceConfirmation = voiceConfirmation
self.showMessageSeconds = showMessageSeconds
self.showPeerId = showPeerId
self.deletedMark = deletedMark
self.editedMark = editedMark
self.recentStickersCount = recentStickersCount
self.translateMessages = translateMessages
self.translationProvider = translationProvider
self.webviewSpoofPlatform = webviewSpoofPlatform
self.increaseWebviewHeight = increaseWebviewHeight
self.liquidGlass = liquidGlass
self.materialDesign = materialDesign
self.avatarCornerRadius = avatarCornerRadius
self.singleCornerRadius = singleCornerRadius
self.messageBubbleRadius = messageBubbleRadius
self.removeMessageTail = removeMessageTail
self.customFont = customFont
self.monoFont = monoFont
self.appIcon = appIcon
self.iconPack = iconPack
self.showOnlyAddedEmojisAndStickers = showOnlyAddedEmojisAndStickers
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: StringCodingKey.self)
let defaults = WinterGramSettings.defaultSettings
self.ghostModeEnabled = try container.decodeIfPresent(Bool.self, forKey: "ghostModeEnabled") ?? defaults.ghostModeEnabled
self.sendReadReceipts = try container.decodeIfPresent(Bool.self, forKey: "sendReadReceipts") ?? defaults.sendReadReceipts
self.sendReadStories = try container.decodeIfPresent(Bool.self, forKey: "sendReadStories") ?? defaults.sendReadStories
self.sendOnlineStatus = try container.decodeIfPresent(Bool.self, forKey: "sendOnlineStatus") ?? defaults.sendOnlineStatus
self.sendUploadProgress = try container.decodeIfPresent(Bool.self, forKey: "sendUploadProgress") ?? defaults.sendUploadProgress
self.sendOfflineAfterOnline = try container.decodeIfPresent(Bool.self, forKey: "sendOfflineAfterOnline") ?? defaults.sendOfflineAfterOnline
self.markReadAfterAction = try container.decodeIfPresent(Bool.self, forKey: "markReadAfterAction") ?? defaults.markReadAfterAction
self.useScheduledMessages = try container.decodeIfPresent(Bool.self, forKey: "useScheduledMessages") ?? defaults.useScheduledMessages
self.sendWithoutSound = try container.decodeIfPresent(WinterGramSendWithoutSound.self, forKey: "sendWithoutSound") ?? defaults.sendWithoutSound
self.suggestGhostBeforeStory = try container.decodeIfPresent(Bool.self, forKey: "suggestGhostBeforeStory") ?? defaults.suggestGhostBeforeStory
self.saveDeletedMessages = try container.decodeIfPresent(Bool.self, forKey: "saveDeletedMessages") ?? defaults.saveDeletedMessages
self.saveMessagesHistory = try container.decodeIfPresent(Bool.self, forKey: "saveMessagesHistory") ?? defaults.saveMessagesHistory
self.saveForBots = try container.decodeIfPresent(Bool.self, forKey: "saveForBots") ?? defaults.saveForBots
self.hideFromBlocked = try container.decodeIfPresent(Bool.self, forKey: "hideFromBlocked") ?? defaults.hideFromBlocked
self.semiTransparentDeletedMessages = try container.decodeIfPresent(Bool.self, forKey: "semiTransparentDeletedMessages") ?? defaults.semiTransparentDeletedMessages
self.stashedPeerIds = try container.decodeIfPresent([Int64].self, forKey: "stashedPeerIds") ?? defaults.stashedPeerIds
self.stashMuteNotifications = try container.decodeIfPresent(Bool.self, forKey: "stashMuteNotifications") ?? defaults.stashMuteNotifications
self.stashAutoMarkRead = try container.decodeIfPresent(Bool.self, forKey: "stashAutoMarkRead") ?? defaults.stashAutoMarkRead
self.disableAds = try container.decodeIfPresent(Bool.self, forKey: "disableAds") ?? defaults.disableAds
self.localPremium = try container.decodeIfPresent(Bool.self, forKey: "localPremium") ?? defaults.localPremium
self.shadowBanIds = try container.decodeIfPresent([Int64].self, forKey: "shadowBanIds") ?? defaults.shadowBanIds
self.disableStories = try container.decodeIfPresent(Bool.self, forKey: "disableStories") ?? defaults.disableStories
self.hidePremiumStatuses = try container.decodeIfPresent(Bool.self, forKey: "hidePremiumStatuses") ?? defaults.hidePremiumStatuses
self.disableOpenLinkWarning = try container.decodeIfPresent(Bool.self, forKey: "disableOpenLinkWarning") ?? defaults.disableOpenLinkWarning
self.stickerConfirmation = try container.decodeIfPresent(Bool.self, forKey: "stickerConfirmation") ?? defaults.stickerConfirmation
self.gifConfirmation = try container.decodeIfPresent(Bool.self, forKey: "gifConfirmation") ?? defaults.gifConfirmation
self.voiceConfirmation = try container.decodeIfPresent(Bool.self, forKey: "voiceConfirmation") ?? defaults.voiceConfirmation
self.showMessageSeconds = try container.decodeIfPresent(Bool.self, forKey: "showMessageSeconds") ?? defaults.showMessageSeconds
self.showPeerId = try container.decodeIfPresent(WinterGramPeerIdDisplay.self, forKey: "showPeerId") ?? defaults.showPeerId
self.deletedMark = try container.decodeIfPresent(String.self, forKey: "deletedMark") ?? defaults.deletedMark
self.editedMark = try container.decodeIfPresent(String.self, forKey: "editedMark") ?? defaults.editedMark
self.recentStickersCount = try container.decodeIfPresent(Int32.self, forKey: "recentStickersCount") ?? defaults.recentStickersCount
self.translateMessages = try container.decodeIfPresent(Bool.self, forKey: "translateMessages") ?? defaults.translateMessages
self.translationProvider = try container.decodeIfPresent(WinterGramTranslationProvider.self, forKey: "translationProvider") ?? defaults.translationProvider
self.webviewSpoofPlatform = try container.decodeIfPresent(WinterGramWebviewPlatform.self, forKey: "webviewSpoofPlatform") ?? defaults.webviewSpoofPlatform
self.increaseWebviewHeight = try container.decodeIfPresent(Bool.self, forKey: "increaseWebviewHeight") ?? defaults.increaseWebviewHeight
self.liquidGlass = try container.decodeIfPresent(WinterGramLiquidGlass.self, forKey: "liquidGlass") ?? defaults.liquidGlass
self.materialDesign = try container.decodeIfPresent(Bool.self, forKey: "materialDesign") ?? defaults.materialDesign
self.avatarCornerRadius = try container.decodeIfPresent(Int32.self, forKey: "avatarCornerRadius") ?? defaults.avatarCornerRadius
self.singleCornerRadius = try container.decodeIfPresent(Bool.self, forKey: "singleCornerRadius") ?? defaults.singleCornerRadius
self.messageBubbleRadius = try container.decodeIfPresent(Int32.self, forKey: "messageBubbleRadius") ?? defaults.messageBubbleRadius
self.removeMessageTail = try container.decodeIfPresent(Bool.self, forKey: "removeMessageTail") ?? defaults.removeMessageTail
self.customFont = try container.decodeIfPresent(String.self, forKey: "customFont")
self.monoFont = try container.decodeIfPresent(String.self, forKey: "monoFont")
self.appIcon = try container.decodeIfPresent(String.self, forKey: "appIcon") ?? defaults.appIcon
self.iconPack = try container.decodeIfPresent(WinterGramIconPack.self, forKey: "iconPack") ?? defaults.iconPack
self.showOnlyAddedEmojisAndStickers = try container.decodeIfPresent(Bool.self, forKey: "showOnlyAddedEmojisAndStickers") ?? defaults.showOnlyAddedEmojisAndStickers
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
try container.encode(self.ghostModeEnabled, forKey: "ghostModeEnabled")
try container.encode(self.sendReadReceipts, forKey: "sendReadReceipts")
try container.encode(self.sendReadStories, forKey: "sendReadStories")
try container.encode(self.sendOnlineStatus, forKey: "sendOnlineStatus")
try container.encode(self.sendUploadProgress, forKey: "sendUploadProgress")
try container.encode(self.sendOfflineAfterOnline, forKey: "sendOfflineAfterOnline")
try container.encode(self.markReadAfterAction, forKey: "markReadAfterAction")
try container.encode(self.useScheduledMessages, forKey: "useScheduledMessages")
try container.encode(self.sendWithoutSound, forKey: "sendWithoutSound")
try container.encode(self.suggestGhostBeforeStory, forKey: "suggestGhostBeforeStory")
try container.encode(self.saveDeletedMessages, forKey: "saveDeletedMessages")
try container.encode(self.saveMessagesHistory, forKey: "saveMessagesHistory")
try container.encode(self.saveForBots, forKey: "saveForBots")
try container.encode(self.hideFromBlocked, forKey: "hideFromBlocked")
try container.encode(self.semiTransparentDeletedMessages, forKey: "semiTransparentDeletedMessages")
try container.encode(self.stashedPeerIds, forKey: "stashedPeerIds")
try container.encode(self.stashMuteNotifications, forKey: "stashMuteNotifications")
try container.encode(self.stashAutoMarkRead, forKey: "stashAutoMarkRead")
try container.encode(self.disableAds, forKey: "disableAds")
try container.encode(self.localPremium, forKey: "localPremium")
try container.encode(self.shadowBanIds, forKey: "shadowBanIds")
try container.encode(self.disableStories, forKey: "disableStories")
try container.encode(self.hidePremiumStatuses, forKey: "hidePremiumStatuses")
try container.encode(self.disableOpenLinkWarning, forKey: "disableOpenLinkWarning")
try container.encode(self.stickerConfirmation, forKey: "stickerConfirmation")
try container.encode(self.gifConfirmation, forKey: "gifConfirmation")
try container.encode(self.voiceConfirmation, forKey: "voiceConfirmation")
try container.encode(self.showMessageSeconds, forKey: "showMessageSeconds")
try container.encode(self.showPeerId, forKey: "showPeerId")
try container.encode(self.deletedMark, forKey: "deletedMark")
try container.encode(self.editedMark, forKey: "editedMark")
try container.encode(self.recentStickersCount, forKey: "recentStickersCount")
try container.encode(self.translateMessages, forKey: "translateMessages")
try container.encode(self.translationProvider, forKey: "translationProvider")
try container.encode(self.webviewSpoofPlatform, forKey: "webviewSpoofPlatform")
try container.encode(self.increaseWebviewHeight, forKey: "increaseWebviewHeight")
try container.encode(self.liquidGlass, forKey: "liquidGlass")
try container.encode(self.materialDesign, forKey: "materialDesign")
try container.encode(self.avatarCornerRadius, forKey: "avatarCornerRadius")
try container.encode(self.singleCornerRadius, forKey: "singleCornerRadius")
try container.encode(self.messageBubbleRadius, forKey: "messageBubbleRadius")
try container.encode(self.removeMessageTail, forKey: "removeMessageTail")
try container.encodeIfPresent(self.customFont, forKey: "customFont")
try container.encodeIfPresent(self.monoFont, forKey: "monoFont")
try container.encode(self.appIcon, forKey: "appIcon")
try container.encode(self.iconPack, forKey: "iconPack")
try container.encode(self.showOnlyAddedEmojisAndStickers, forKey: "showOnlyAddedEmojisAndStickers")
}
public func isShadowBanned(_ peerId: Int64) -> Bool {
return self.shadowBanIds.contains(peerId)
}
public func isStashed(_ peerId: Int64) -> Bool {
return self.stashedPeerIds.contains(peerId)
}
public var shouldSendWithoutSound: Bool {
switch self.sendWithoutSound {
case .never:
return false
case .inGhostMode:
return self.ghostModeEnabled
case .always:
return true
}
}
}
public func updateWinterGramSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (WinterGramSettings) -> WinterGramSettings) -> Signal<Void, NoError> {
return accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.winterGramSettings, { entry in
let currentSettings: WinterGramSettings
if let entry = entry?.get(WinterGramSettings.self) {
currentSettings = entry
} else {
currentSettings = .defaultSettings
}
return SharedPreferencesEntry(f(currentSettings))
})
}
}
public func winterGramSettings(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<WinterGramSettings, NoError> {
return accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.winterGramSettings])
|> map { sharedData -> WinterGramSettings in
return sharedData.entries[ApplicationSpecificSharedDataKeys.winterGramSettings]?.get(WinterGramSettings.self) ?? .defaultSettings
}
}
private let currentWinterGramSettingsValue = Atomic<WinterGramSettings>(value: .defaultSettings)
// Synchronous snapshot of the latest settings, for hook points that cannot subscribe to a Signal.
// Kept up to date by observeWinterGramSettings(accountManager:) at app startup.
public var currentWinterGramSettings: WinterGramSettings {
return currentWinterGramSettingsValue.with { $0 }
}
public func setCurrentWinterGramSettings(_ settings: WinterGramSettings) {
let _ = currentWinterGramSettingsValue.swap(settings)
}
public func observeWinterGramSettings(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Disposable {
return (winterGramSettings(accountManager: accountManager)
|> deliverOnMainQueue).start(next: { settings in
setCurrentWinterGramSettings(settings)
})
}