Update themes

This commit is contained in:
Ilya Laktyushin 2019-12-23 15:13:21 +03:00
parent 88436ccb5c
commit efacf13a9e
27 changed files with 4748 additions and 4300 deletions

View File

@ -4675,6 +4675,7 @@ Any member of this group will be able to see messages in the channel.";
"Appearance.ThemePreview.Chat.2.ReplyName" = "Bob Harris";
"Appearance.ThemePreview.Chat.2.Text" = "Right side. And, uh, with intensity.";
"Appearance.ThemePreview.Chat.3.Text" = "Is that everything? It seemed like he said quite a bit more than that. 😯";
"Appearance.ThemePreview.Chat.3.TextWithLink" = "Is that everything? It seemed like he said [quite a bit more] than that. 😯";
"Appearance.ThemePreview.Chat.4.Text" = "For relaxing times, make it Suntory time. 😎";
"Appearance.ThemePreview.Chat.5.Text" = "He wants you to turn, look in camera. O.K.?";
@ -5226,3 +5227,6 @@ Any member of this group will be able to see messages in the channel.";
"PrivacySettings.WebSessions" = "Active Websites";
"Appearance.ShareThemeColor" = "Share";
"Theme.ThemeChanged" = "Color Theme Changed";
"Theme.ThemeChangedText" = "You can change it back in\n[Settings > Appearance]().";

View File

@ -148,9 +148,9 @@ public struct ChatAvailableMessageActions {
}
public enum WallpaperUrlParameter {
case slug(String, WallpaperPresentationOptions, UIColor?, Int32?)
case slug(String, WallpaperPresentationOptions, UIColor?, UIColor?, Int32?, Int32?)
case color(UIColor)
case gradient(UIColor, UIColor)
case gradient(UIColor, UIColor, Int32?)
}
public enum ResolvedUrl {

View File

@ -133,8 +133,11 @@ public final class AuthTransferScanScreen: ViewController {
if let navigationController = navigationController as? NavigationController {
let activeSessionsContext = self.activeSessionsContext
self.present(UndoOverlayController(presentationData: self.presentationData, content: .actionSucceeded(title: "Loggin Successful", text: "Telegram for macOS", cancel: "Terminate"), elevatedLayout: false, animateInAsReplacement: false, action: { value in
if !value, let session = session {
if value == .undo, let session = session {
let _ = activeSessionsContext.remove(hash: session.hash).start()
return true
} else {
return false
}
}), in: .window(.root))

View File

@ -1198,11 +1198,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
let text = strongSelf.presentationData.strings.ChatList_DeletedChats(Int32(peerIds.count))
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: text), elevatedLayout: false, animateInAsReplacement: true, action: { shouldCommit in
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: text), elevatedLayout: false, animateInAsReplacement: true, action: { value in
guard let strongSelf = self else {
return
return false
}
if shouldCommit {
if value == .commit {
let context = strongSelf.context
let presentationData = strongSelf.presentationData
let progressSignal = Signal<Never, NoError> { subscriber in
@ -1230,7 +1230,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
}
let _ = (signal
|> deliverOnMainQueue).start()
} else {
return true
} else if value == .undo {
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerIds.first!)
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
var state = state
@ -1240,7 +1241,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
return state
})
self?.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerIds.first!)
return true
}
return false
}), in: .current)
strongSelf.donePressed()
@ -1310,11 +1313,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
})
if value {
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .hidArchive(title: strongSelf.presentationData.strings.ChatList_UndoArchiveHiddenTitle, text: strongSelf.presentationData.strings.ChatList_UndoArchiveHiddenText, undo: false), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] shouldCommit in
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .hidArchive(title: strongSelf.presentationData.strings.ChatList_UndoArchiveHiddenTitle, text: strongSelf.presentationData.strings.ChatList_UndoArchiveHiddenText, undo: false), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] value in
guard let strongSelf = self else {
return
return false
}
if !shouldCommit {
if value == .undo {
let _ = (strongSelf.context.account.postbox.transaction { transaction -> Bool in
var updatedValue = false
updateChatArchiveSettings(transaction: transaction, { settings in
@ -1325,10 +1328,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
})
return updatedValue
}).start()
return true
}
return false
}), in: .current)
} else {
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .revealedArchive(title: strongSelf.presentationData.strings.ChatList_UndoArchiveRevealedTitle, text: strongSelf.presentationData.strings.ChatList_UndoArchiveRevealedText, undo: false), elevatedLayout: false, animateInAsReplacement: true, action: { _ in
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .revealedArchive(title: strongSelf.presentationData.strings.ChatList_UndoArchiveRevealedTitle, text: strongSelf.presentationData.strings.ChatList_UndoArchiveRevealedText, undo: false), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false
}), in: .current)
}
})
@ -1422,11 +1427,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
return true
})
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: strongSelf.presentationData.strings.Undo_ChatCleared), elevatedLayout: false, animateInAsReplacement: true, action: { shouldCommit in
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: strongSelf.presentationData.strings.Undo_ChatCleared), elevatedLayout: false, animateInAsReplacement: true, action: { value in
guard let strongSelf = self else {
return
return false
}
if shouldCommit {
if value == .commit {
let _ = clearHistoryInteractively(postbox: strongSelf.context.account.postbox, peerId: peerId, type: type).start(completed: {
guard let strongSelf = self else {
return
@ -1437,13 +1442,16 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
return state
})
})
} else {
return true
} else if value == .undo {
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
var state = state
state.pendingClearHistoryPeerIds.remove(peer.peerId)
return state
})
return true
}
return false
}), in: .current)
}
@ -1618,11 +1626,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
deleteSendMessageIntents(peerId: peerId)
}
let action: (Bool) -> Void = { shouldCommit in
let action: (UndoOverlayAction) -> Bool = { value in
guard let strongSelf = self else {
return
return false
}
if !shouldCommit {
if value == .undo {
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerIds[0])
let _ = (postbox.transaction { transaction -> Void in
for peerId in peerIds {
@ -1635,6 +1643,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
}
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil)
})
return true
} else {
return false
}
}
@ -1721,11 +1732,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
return true
})
self.present(UndoOverlayController(presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: statusText), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] shouldCommit in
self.present(UndoOverlayController(presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: statusText), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] value in
guard let strongSelf = self else {
return
return false
}
if shouldCommit {
if value == .commit {
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId)
if let channel = chatPeer as? TelegramChannel {
strongSelf.context.peerChannelMemberCategoriesContextsManager.externallyRemoved(peerId: channel.id, memberId: strongSelf.context.account.peerId)
@ -1744,7 +1755,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
deleteSendMessageIntents(peerId: peerId)
})
completion()
} else {
return true
} else if value == .undo {
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId)
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
var state = state
@ -1752,7 +1764,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
return state
})
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil)
return true
}
return false
}), in: .current)
}

View File

@ -529,7 +529,7 @@ public func channelAdminsController(context: AccountContext, peerId: PeerId, loa
guard let peer = peer, let user = user else {
return
}
presentControllerImpl?(UndoOverlayController(presentationData: context.sharedContext.currentPresentationData.with { $0 }, content: .succeed(text: presentationData.strings.Channel_OwnershipTransfer_TransferCompleted(user.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).0), elevatedLayout: false, action: { _ in }), nil)
presentControllerImpl?(UndoOverlayController(presentationData: context.sharedContext.currentPresentationData.with { $0 }, content: .succeed(text: presentationData.strings.Channel_OwnershipTransfer_TransferCompleted(user.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).0), elevatedLayout: false, action: { _ in return false }), nil)
})
}

View File

@ -588,7 +588,7 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P
clearDisposable.set((signal
|> deliverOnMainQueue).start(completed: {
statsPromise.set(.single(.result(resultStats)))
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))", stringForDeviceType()).0), elevatedLayout: false, action: { _ in }), .current, nil)
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))", stringForDeviceType()).0), elevatedLayout: false, action: { _ in return false }), .current, nil)
}))
}
@ -769,7 +769,7 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P
clearDisposable.set((signal
|> deliverOnMainQueue).start(completed: {
statsPromise.set(.single(.result(resultStats)))
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))", stringForDeviceType()).0), elevatedLayout: false, action: { _ in }), .current, nil)
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))", stringForDeviceType()).0), elevatedLayout: false, action: { _ in return false }), .current, nil)
}))
}
@ -896,7 +896,7 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P
clearDisposable.set((signal
|> deliverOnMainQueue).start(completed: {
statsPromise.set(.single(.result(resultStats)))
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(totalSize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))", stringForDeviceType()).0), elevatedLayout: false, action: { _ in }), .current, nil)
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(totalSize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))", stringForDeviceType()).0), elevatedLayout: false, action: { _ in return false }), .current, nil)
}))
}
}

View File

@ -188,7 +188,7 @@ private enum EditThemeControllerEntry: ItemListNodeEntry {
}
public enum EditThemeControllerMode: Equatable {
case create(PresentationTheme?)
case create(PresentationTheme?, TelegramThemeSettings?)
case edit(PresentationCloudTheme)
}
@ -264,17 +264,20 @@ private func editThemeControllerEntries(presentationData: PresentationData, stat
public func editThemeController(context: AccountContext, mode: EditThemeControllerMode, navigateToChat: ((PeerId) -> Void)? = nil, completion: ((PresentationThemeReference) -> Void)? = nil) -> ViewController {
let initialState: EditThemeControllerState
let previewThemePromise = Promise<PresentationTheme>()
let settingsPromise = Promise<TelegramThemeSettings?>(nil)
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
switch mode {
case let .create(existingTheme):
case let .create(existingTheme, settings):
let theme: PresentationTheme
let wallpaper: TelegramWallpaper
if let existingTheme = existingTheme {
theme = existingTheme
wallpaper = theme.chat.defaultWallpaper
settingsPromise.set(.single(settings))
} else {
theme = presentationData.theme
wallpaper = presentationData.chatWallpaper
settingsPromise.set(.single(nil))
}
initialState = EditThemeControllerState(mode: mode, title: generateThemeName(accentColor: theme.rootController.navigationBar.buttonColor), slug: "", updatedTheme: nil, updating: false)
previewThemePromise.set(.single(theme.withUpdated(name: "", defaultWallpaper: wallpaper)))
@ -292,6 +295,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
} else {
previewThemePromise.set(.single(theme.withUpdated(name: nil, defaultWallpaper: info.resolvedWallpaper)))
}
settingsPromise.set(.single(info.theme.settings))
} else {
previewThemePromise.set(.single(presentationData.theme.withUpdated(name: "", defaultWallpaper: presentationData.chatWallpaper)))
@ -313,17 +317,19 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
let arguments = EditThemeControllerArguments(context: context, updateState: { f in
updateState(f)
}, openColors: {
let _ = (previewThemePromise.get()
|> take(1)
|> deliverOnMainQueue).start(next: { theme in
let _ = (combineLatest(queue: Queue.mainQueue(), previewThemePromise.get(), settingsPromise.get())
|> take(1)).start(next: { theme, previousSettings in
var controllerDismissImpl: (() -> Void)?
let controller = ThemeAccentColorController(context: context, mode: .edit(theme: theme, wallpaper: nil, defaultThemeReference: nil, create: false, completion: { updatedTheme in
let controller = ThemeAccentColorController(context: context, mode: .edit(theme: theme, wallpaper: nil, defaultThemeReference: nil, create: false, completion: { updatedTheme, settings in
updateState { current in
var state = current
previewThemePromise.set(.single(updatedTheme))
state.updatedTheme = updatedTheme
return state
}
if previousSettings != nil {
settingsPromise.set(.single(settings))
}
controllerDismissImpl?()
}))
controllerDismissImpl = { [weak controller] in
@ -371,6 +377,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
return state
}
}
settingsPromise.set(.single(nil))
}
else {
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.EditTheme_FileReadError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
@ -420,8 +427,8 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
return state
}
let _ = (previewThemePromise.get()
|> deliverOnMainQueue).start(next: { previewTheme in
let _ = (combineLatest(queue: Queue.mainQueue(), previewThemePromise.get(), settingsPromise.get())
|> take(1)).start(next: { previewTheme, settings in
let saveThemeTemplateFile: (String, LocalFileMediaResource, @escaping () -> Void) -> Void = { title, resource, completion in
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: resource.fileId), partialReference: nil, resource: resource, previewRepresentations: [], immediateThumbnailData: nil, mimeType: "application/x-tgtheme-ios", size: nil, attributes: [.FileName(fileName: "\(title).tgios-theme")])
let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil)
@ -494,7 +501,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
switch mode {
case .create:
if let themeResource = themeResource {
let _ = (createTheme(account: context.account, title: state.title, resource: themeResource, thumbnailData: themeThumbnailData)
let _ = (createTheme(account: context.account, title: state.title, resource: themeResource, thumbnailData: themeThumbnailData, settings: settings)
|> deliverOnMainQueue).start(next: { next in
if case let .result(resultTheme) = next {
let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: resultTheme).start()
@ -528,7 +535,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
})
}
case let .edit(info):
let _ = (updateTheme(account: context.account, accountManager: context.sharedContext.accountManager, theme: info.theme, title: state.title, slug: state.slug, resource: themeResource)
let _ = (updateTheme(account: context.account, accountManager: context.sharedContext.accountManager, theme: info.theme, title: state.title, slug: state.slug, resource: themeResource, settings: settings)
|> deliverOnMainQueue).start(next: { next in
if case let .result(resultTheme) = next {
let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: resultTheme).start()

View File

@ -17,7 +17,7 @@ private let randomBackgroundColors: [Int32] = [0x007aff, 0x00c2ed, 0x29b327, 0xe
enum ThemeAccentColorControllerMode {
case colors(themeReference: PresentationThemeReference, create: Bool)
case background(themeReference: PresentationThemeReference)
case edit(theme: PresentationTheme, wallpaper: TelegramWallpaper?, defaultThemeReference: PresentationThemeReference?, create: Bool, completion: (PresentationTheme) -> Void)
case edit(theme: PresentationTheme, wallpaper: TelegramWallpaper?, defaultThemeReference: PresentationThemeReference?, create: Bool, completion: (PresentationTheme, TelegramThemeSettings?) -> Void)
var themeReference: PresentationThemeReference? {
switch self {
@ -181,13 +181,25 @@ final class ThemeAccentColorController: ViewController {
let _ = (prepare
|> deliverOnMainQueue).start(completed: { [weak self] in
let updatedTheme: PresentationTheme
var settings: TelegramThemeSettings?
if let themeReference = themeReference {
updatedTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: themeReference, accentColor: state.accentColor, backgroundColors: state.backgroundColors, bubbleColors: state.messagesColors, wallpaper: state.initialWallpaper ?? coloredWallpaper, serviceBackgroundColor: serviceBackgroundColor) ?? defaultPresentationTheme
if case let .builtin(theme) = themeReference {
var messageColors: (Int32, Int32)?
if let colors = state.messagesColors {
messageColors = (Int32(bitPattern: colors.0.rgb), Int32(bitPattern: colors.1?.rgb ?? colors.0.rgb))
}
settings = TelegramThemeSettings(baseTheme: theme.baseTheme, accentColor: Int32(bitPattern: state.accentColor.rgb), messageColors: messageColors, wallpaper: coloredWallpaper)
}
} else {
updatedTheme = customizePresentationTheme(theme, editing: false, accentColor: state.accentColor, backgroundColors: state.backgroundColors, bubbleColors: state.messagesColors, wallpaper: state.initialWallpaper ?? coloredWallpaper)
}
completion(updatedTheme)
completion(updatedTheme, settings)
})
} else {
let _ = (prepare

View File

@ -15,6 +15,7 @@ import WallpaperResources
import OverlayStatusController
import AppBundle
import PresentationDataUtils
import UndoUI
public enum ThemePreviewSource {
case settings(PresentationThemeReference, TelegramWallpaper?)
@ -279,7 +280,7 @@ public final class ThemePreviewController: ViewController {
|> mapToSignal { themes -> Signal<PresentationThemeReference, NoError> in
let similarTheme = themes.first(where: { $0.isCreator && $0.title == info.title })
if let similarTheme = similarTheme {
return updateTheme(account: context.account, accountManager: context.sharedContext.accountManager, theme: similarTheme, title: nil, slug: nil, resource: info.resource, thumbnailData: themeThumbnailData)
return updateTheme(account: context.account, accountManager: context.sharedContext.accountManager, theme: similarTheme, title: nil, slug: nil, resource: info.resource, thumbnailData: themeThumbnailData, settings: nil)
|> map(Optional.init)
|> `catch` { _ -> Signal<CreateThemeResult?, NoError> in
return .single(nil)
@ -298,7 +299,7 @@ public final class ThemePreviewController: ViewController {
}
} else {
return createTheme(account: context.account, title: info.title, resource: info.resource, thumbnailData: themeThumbnailData)
return createTheme(account: context.account, title: info.title, resource: info.resource, thumbnailData: themeThumbnailData, settings: nil)
|> map(Optional.init)
|> `catch` { _ -> Signal<CreateThemeResult?, NoError> in
return .single(nil)
@ -322,26 +323,47 @@ public final class ThemePreviewController: ViewController {
return .single(theme)
}
}
|> mapToSignal { updatedTheme -> Signal<Void, NoError> in
|> mapToSignal { updatedTheme -> Signal<(PresentationThemeReference, Bool)?, NoError> in
if case let .cloud(info) = updatedTheme {
let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: info.theme).start()
let _ = saveThemeInteractively(account: context.account, accountManager: context.sharedContext.accountManager, theme: info.theme).start()
}
let autoNightModeTriggered = context.sharedContext.currentPresentationData.with { $0 }.autoNightModeTriggered
return updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current -> PresentationThemeSettings in
var updated: PresentationThemeSettings
if autoNightModeTriggered {
var automaticThemeSwitchSetting = current.automaticThemeSwitchSetting
automaticThemeSwitchSetting.theme = updatedTheme
updated = current.withUpdatedAutomaticThemeSwitchSetting(automaticThemeSwitchSetting)
var switchingFromDefaultTheme = false
return context.sharedContext.accountManager.transaction { transaction -> (PresentationThemeReference, Bool)? in
var previousDefaultTheme: (PresentationThemeReference, Bool)?
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in
let currentSettings: PresentationThemeSettings
if let entry = entry as? PresentationThemeSettings {
currentSettings = entry
} else {
updated = current.withUpdatedTheme(updatedTheme)
currentSettings = PresentationThemeSettings.defaultSettings
}
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
var updatedSettings: PresentationThemeSettings
if autoNightModeTriggered {
if case .builtin = currentSettings.automaticThemeSwitchSetting.theme {
previousDefaultTheme = (currentSettings.automaticThemeSwitchSetting.theme, true)
}
var automaticThemeSwitchSetting = currentSettings.automaticThemeSwitchSetting
automaticThemeSwitchSetting.theme = updatedTheme
updatedSettings = currentSettings.withUpdatedAutomaticThemeSwitchSetting(automaticThemeSwitchSetting)
} else {
if case .builtin = currentSettings.theme {
previousDefaultTheme = (currentSettings.theme, false)
}
updatedSettings = currentSettings.withUpdatedTheme(updatedTheme)
}
var themeSpecificChatWallpapers = updatedSettings.themeSpecificChatWallpapers
themeSpecificChatWallpapers[updatedTheme.index] = nil
return updated.withUpdatedThemeSpecificChatWallpapers(themeSpecificChatWallpapers)
return updatedSettings.withUpdatedThemeSpecificChatWallpapers(themeSpecificChatWallpapers)
})
return previousDefaultTheme
}
}
var cancelImpl: (() -> Void)?
@ -369,9 +391,34 @@ public final class ThemePreviewController: ViewController {
progressDisposable.dispose()
}
}
|> deliverOnMainQueue).start(completed: {[weak self] in
|> deliverOnMainQueue).start(next: { [weak self] previousDefaultTheme in
if let strongSelf = self {
Queue.mainQueue().after(0.3) {
let navigationController = strongSelf.navigationController as? NavigationController
if let (previousDefaultTheme, autoNightMode) = previousDefaultTheme {
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .actionSucceeded(title: strongSelf.presentationData.strings.Theme_ThemeChanged, text: strongSelf.presentationData.strings.Theme_ThemeChangedText, cancel: strongSelf.presentationData.strings.Undo_Undo), elevatedLayout: false, animateInAsReplacement: false, action: { value in
if value == .undo {
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current -> PresentationThemeSettings in
var updated: PresentationThemeSettings
if autoNightMode {
var automaticThemeSwitchSetting = current.automaticThemeSwitchSetting
automaticThemeSwitchSetting.theme = previousDefaultTheme
updated = current.withUpdatedAutomaticThemeSwitchSetting(automaticThemeSwitchSetting)
} else {
updated = current.withUpdatedTheme(previousDefaultTheme)
}
return updated
}).start()
return true
} else if value == .info {
let controller = themeSettingsController(context: context)
controller.navigationPresentation = .modal
navigationController?.pushViewController(controller, animated: true)
return true
}
return false
}), in: .window(.root))
}
strongSelf.dismiss()
}
}

View File

@ -200,8 +200,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
strongSelf.blurredNode.image = image
strongSelf.blurredNode.blurView.blurRadius = 45.0
strongSelf.ready.set(.single(true))
}
self?.ready.set(.single(true))
}
self.colorDisposable = (self.wallpaperPromise.get()

View File

@ -14,17 +14,21 @@ import PresentationDataUtils
private enum ThemeSettingsColorEntryId: Hashable {
case color(Int)
case theme(Int64)
case picker
}
private enum ThemeSettingsColorEntry: Comparable, Identifiable {
case color(Int, PresentationThemeReference, PresentationThemeAccentColor?, Bool)
case theme(Int, PresentationThemeReference, PresentationThemeReference, Bool)
case picker
var stableId: ThemeSettingsColorEntryId {
switch self {
case let .color(index, _, _, _):
return .color(index)
case let .theme(_, _, theme, _):
return .theme(theme.index)
case .picker:
return .picker
}
@ -38,6 +42,12 @@ private enum ThemeSettingsColorEntry: Comparable, Identifiable {
} else {
return false
}
case let .theme(lhsIndex, lhsBaseThemeReference, lhsTheme, lhsSelected):
if case let .theme(rhsIndex, rhsBaseThemeReference, rhsTheme, rhsSelected) = rhs, lhsIndex == rhsIndex, lhsBaseThemeReference.index == rhsBaseThemeReference.index, lhsTheme == rhsTheme, lhsSelected == rhsSelected {
return true
} else {
return false
}
case .picker:
if case .picker = rhs {
return true
@ -51,36 +61,109 @@ private enum ThemeSettingsColorEntry: Comparable, Identifiable {
switch lhs {
case .picker:
return true
case let .color(lhsIndex, _, _, _):
case let .color(lhsIndex, _, _, _), let .theme(lhsIndex, _, _, _):
switch rhs {
case let .color(rhsIndex, _, _, _):
return lhsIndex < rhsIndex
case let .theme(rhsIndex, _, _, _):
return lhsIndex < rhsIndex
case .picker:
return false
}
}
}
func item(action: @escaping (PresentationThemeAccentColor?, Bool) -> Void, contextAction: ((PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void)?, openColorPicker: @escaping (Bool) -> Void) -> ListViewItem {
func item(action: @escaping (ThemeSettingsColorOption?, Bool) -> Void, contextAction: ((ThemeSettingsColorOption?, ASDisplayNode, ContextGesture?) -> Void)?, openColorPicker: @escaping (Bool) -> Void) -> ListViewItem {
switch self {
case let .color(_, themeReference, accentColor, selected):
return ThemeSettingsAccentColorIconItem(themeReference: themeReference, accentColor: accentColor, selected: selected, action: action, contextAction: contextAction)
return ThemeSettingsAccentColorIconItem(themeReference: themeReference, color: accentColor.flatMap { .accentColor($0) }, selected: selected, action: action, contextAction: contextAction)
case let .theme(_, baseThemeReference, theme, selected):
return ThemeSettingsAccentColorIconItem(themeReference: baseThemeReference, color: .theme(theme), selected: selected, action: action, contextAction: contextAction)
case .picker:
return ThemeSettingsAccentColorPickerItem(action: openColorPicker)
}
}
}
enum ThemeSettingsColorOption: Equatable {
case accentColor(PresentationThemeAccentColor)
case theme(PresentationThemeReference)
var accentColor: UIColor? {
switch self {
case let .accentColor(color):
return color.color
case let .theme(reference):
if case let .cloud(theme) = reference, let settings = theme.theme.settings {
return UIColor(rgb: UInt32(bitPattern: settings.accentColor))
} else {
return nil
}
}
}
var baseColor: UIColor? {
switch self {
case let .accentColor(color):
return color.baseColor.color
case .theme:
return nil
}
}
var plainBubbleColors: (UIColor, UIColor)? {
switch self {
case let .accentColor(color):
return color.plainBubbleColors
case let .theme(reference):
if case let .cloud(theme) = reference, let settings = theme.theme.settings, let messageColors = settings.messageColors {
return (UIColor(rgb: UInt32(bitPattern: messageColors.top)), UIColor(rgb: UInt32(bitPattern: messageColors.bottom)))
} else {
return nil
}
}
}
var customBubbleColors: (UIColor, UIColor?)? {
switch self {
case let .accentColor(color):
return color.customBubbleColors
case let .theme(reference):
if case let .cloud(theme) = reference, let settings = theme.theme.settings, let messageColors = settings.messageColors {
let topColor = UIColor(rgb: UInt32(bitPattern: messageColors.top))
let bottomColor = UIColor(rgb: UInt32(bitPattern: messageColors.bottom))
if topColor.rgb != bottomColor.rgb {
return (topColor, bottomColor)
} else {
return (topColor, nil)
}
} else {
return nil
}
}
}
var index: Int64 {
switch self {
case let .accentColor(color):
return Int64(color.index)
case let .theme(reference):
return reference.index
}
}
}
private class ThemeSettingsAccentColorIconItem: ListViewItem {
let themeReference: PresentationThemeReference
let accentColor: PresentationThemeAccentColor?
let color: ThemeSettingsColorOption?
let selected: Bool
let action: (PresentationThemeAccentColor?, Bool) -> Void
let contextAction: ((PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void)?
let action: (ThemeSettingsColorOption?, Bool) -> Void
let contextAction: ((ThemeSettingsColorOption?, ASDisplayNode, ContextGesture?) -> Void)?
public init(themeReference: PresentationThemeReference, accentColor: PresentationThemeAccentColor?, selected: Bool, action: @escaping (PresentationThemeAccentColor?, Bool) -> Void, contextAction: ((PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void)?) {
public init(themeReference: PresentationThemeReference, color: ThemeSettingsColorOption?, selected: Bool, action: @escaping (ThemeSettingsColorOption?, Bool) -> Void, contextAction: ((ThemeSettingsColorOption?, ASDisplayNode, ContextGesture?) -> Void)?) {
self.themeReference = themeReference
self.accentColor = accentColor
self.color = color
self.selected = selected
self.action = action
self.contextAction = contextAction
@ -128,7 +211,7 @@ private class ThemeSettingsAccentColorIconItem: ListViewItem {
public var selectable = true
public func selected(listView: ListView) {
self.action(self.accentColor, self.selected)
self.action(self.color, self.selected)
}
}
@ -224,7 +307,7 @@ private final class ThemeSettingsAccentColorIconItemNode : ListViewItemNode {
gesture.cancel()
return
}
item.contextAction?(item.accentColor, strongSelf.containerNode, gesture)
item.contextAction?(item.color, strongSelf.containerNode, gesture)
}
}
@ -258,7 +341,7 @@ private final class ThemeSettingsAccentColorIconItemNode : ListViewItemNode {
var updatedAccentColor = false
var updatedSelected = false
if currentItem == nil || currentItem?.accentColor != item.accentColor {
if currentItem == nil || currentItem?.color != item.color {
updatedAccentColor = true
}
if currentItem?.selected != item.selected {
@ -271,8 +354,8 @@ private final class ThemeSettingsAccentColorIconItemNode : ListViewItemNode {
strongSelf.item = item
if updatedAccentColor {
var fillColor = item.accentColor?.color
var strokeColor = item.accentColor?.baseColor.color
var fillColor = item.color?.accentColor
var strokeColor = item.color?.baseColor
if strokeColor == .clear {
strokeColor = fillColor
}
@ -288,12 +371,12 @@ private final class ThemeSettingsAccentColorIconItemNode : ListViewItemNode {
var topColor: UIColor?
var bottomColor: UIColor?
if let colors = item.accentColor?.plainBubbleColors {
if let colors = item.color?.plainBubbleColors {
topColor = colors.0
bottomColor = colors.1
} else if case .builtin(.dayClassic) = item.themeReference {
if let accentColor = item.accentColor {
let hsb = accentColor.color.hsb
if let accentColor = item.color?.accentColor {
let hsb = accentColor.hsb
let bubbleColor = UIColor(hue: hsb.0, saturation: (hsb.1 > 0.0 && hsb.2 > 0.0) ? 0.14 : 0.0, brightness: 0.79 + hsb.2 * 0.21, alpha: 1.0)
topColor = bubbleColor
bottomColor = bubbleColor
@ -490,15 +573,18 @@ enum ThemeSettingsAccentColor {
case color(PresentationThemeBaseColor)
case preset(PresentationThemeAccentColor)
case custom(PresentationThemeAccentColor)
case theme(PresentationThemeReference)
var index: Int32? {
var index: Int64? {
switch self {
case .default:
return nil
case let .color(color):
return 10 + color.rawValue
return Int64(10 + color.rawValue)
case let .preset(color), let .custom(color):
return color.index
return Int64(color.index)
case let .theme(theme):
return theme.index
}
}
}
@ -509,13 +595,13 @@ class ThemeSettingsAccentColorItem: ListViewItem, ItemListItem {
let theme: PresentationTheme
let themeReference: PresentationThemeReference
let colors: [ThemeSettingsAccentColor]
let currentColor: PresentationThemeAccentColor?
let updated: (PresentationThemeAccentColor?) -> Void
let contextAction: ((PresentationThemeReference, PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void)?
let currentColor: ThemeSettingsColorOption?
let updated: (ThemeSettingsColorOption?) -> Void
let contextAction: ((PresentationThemeReference, ThemeSettingsColorOption?, ASDisplayNode, ContextGesture?) -> Void)?
let openColorPicker: (Bool) -> Void
let tag: ItemListItemTag?
init(theme: PresentationTheme, sectionId: ItemListSectionId, themeReference: PresentationThemeReference, colors: [ThemeSettingsAccentColor], currentColor: PresentationThemeAccentColor?, updated: @escaping (PresentationThemeAccentColor?) -> Void, contextAction: ((PresentationThemeReference, PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void)?, openColorPicker: @escaping (Bool) -> Void, tag: ItemListItemTag? = nil) {
init(theme: PresentationTheme, sectionId: ItemListSectionId, themeReference: PresentationThemeReference, colors: [ThemeSettingsAccentColor], currentColor: ThemeSettingsColorOption?, updated: @escaping (ThemeSettingsColorOption?) -> Void, contextAction: ((PresentationThemeReference, ThemeSettingsColorOption?, ASDisplayNode, ContextGesture?) -> Void)?, openColorPicker: @escaping (Bool) -> Void, tag: ItemListItemTag? = nil) {
self.theme = theme
self.themeReference = themeReference
self.colors = colors
@ -568,7 +654,7 @@ private struct ThemeSettingsAccentColorItemNodeTransition {
let crossfade: Bool
}
private func preparedTransition(action: @escaping (PresentationThemeAccentColor?, Bool) -> Void, contextAction: ((PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void)?, openColorPicker: @escaping (Bool) -> Void, from fromEntries: [ThemeSettingsColorEntry], to toEntries: [ThemeSettingsColorEntry], crossfade: Bool) -> ThemeSettingsAccentColorItemNodeTransition {
private func preparedTransition(action: @escaping (ThemeSettingsColorOption?, Bool) -> Void, contextAction: ((ThemeSettingsColorOption?, ASDisplayNode, ContextGesture?) -> Void)?, openColorPicker: @escaping (Bool) -> Void, from fromEntries: [ThemeSettingsColorEntry], to toEntries: [ThemeSettingsColorEntry], crossfade: Bool) -> ThemeSettingsAccentColorItemNodeTransition {
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
@ -578,11 +664,11 @@ private func preparedTransition(action: @escaping (PresentationThemeAccentColor?
return ThemeSettingsAccentColorItemNodeTransition(deletions: deletions, insertions: insertions, updates: updates, crossfade: crossfade)
}
private func ensureColorVisible(listNode: ListView, accentColor: PresentationThemeAccentColor?, animated: Bool) -> Bool {
private func ensureColorVisible(listNode: ListView, accentColor: ThemeSettingsColorOption?, animated: Bool) -> Bool {
var resultNode: ThemeSettingsAccentColorIconItemNode?
listNode.forEachItemNode { node in
if resultNode == nil, let node = node as? ThemeSettingsAccentColorIconItemNode {
if node.item?.accentColor?.index == accentColor?.index {
if node.item?.color?.index == accentColor?.index {
resultNode = node
}
}
@ -768,33 +854,61 @@ class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
let selected = item.currentColor == nil
entries.append(.color(index, item.themeReference, nil, selected))
case let .color(color):
let selected = item.currentColor?.baseColor == color
let accentColor: PresentationThemeAccentColor
var selected = false
if let currentColor = item.currentColor, case let .accentColor(accentColor) = currentColor {
selected = accentColor.baseColor == color
}
let accentColor: ThemeSettingsColorOption
if let currentColor = item.currentColor, selected {
accentColor = currentColor
} else {
accentColor = PresentationThemeAccentColor(index: 10 + color.rawValue, baseColor: color)
accentColor = .accentColor(PresentationThemeAccentColor(index: 10 + color.rawValue, baseColor: color))
}
entries.append(.color(index, item.themeReference, accentColor, selected))
case let .preset(color), let .custom(color):
let selected = item.currentColor == color
switch accentColor {
case let .accentColor(color):
entries.append(.color(index, item.themeReference, color, selected))
case let .theme(theme):
entries.append(.theme(index, item.themeReference, theme, selected))
}
case let .preset(color), let .custom(color):
var selected = false
if let currentColor = item.currentColor {
selected = currentColor.index == Int64(color.index)
}
entries.append(.color(index, item.themeReference, color, selected))
case let .theme(theme):
var selected = false
if let currentColor = item.currentColor {
selected = currentColor.index == theme.index
}
entries.append(.theme(index, item.themeReference, theme, selected))
}
index += 1
}
let action: (PresentationThemeAccentColor?, Bool) -> Void = { [weak self] color, selected in
let action: (ThemeSettingsColorOption?, Bool) -> Void = { [weak self] color, selected in
if let strongSelf = self, let item = strongSelf.item {
if selected {
item.openColorPicker(color?.baseColor != .custom)
var create = true
if let color = color {
switch color {
case let .accentColor(color):
create = color.baseColor != .custom
case let .theme(theme):
if case let .cloud(theme) = theme {
create = !theme.theme.isCreator
}
}
}
item.openColorPicker(create)
} else {
item.updated(color)
}
ensureColorVisible(listNode: strongSelf.listNode, accentColor: color, animated: true)
}
}
let contextAction: ((PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void)? = { [weak item] color, node, gesture in
let contextAction: ((ThemeSettingsColorOption?, ASDisplayNode, ContextGesture?) -> Void)? = { [weak item] color, node, gesture in
if let strongSelf = self, let item = strongSelf.item {
item.contextAction?(item.themeReference, color, node, gesture)
}

View File

@ -81,9 +81,9 @@ private final class ThemeSettingsControllerArguments {
let selectAppIcon: (String) -> Void
let editTheme: (PresentationCloudTheme) -> Void
let themeContextAction: (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void
let colorContextAction: (PresentationThemeReference, PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void
let colorContextAction: (PresentationThemeReference, ThemeSettingsColorOption?, ASDisplayNode, ContextGesture?) -> Void
init(context: AccountContext, selectTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor?) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, Bool) -> Void, openAutoNightTheme: @escaping () -> Void, openTextSize: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void, editTheme: @escaping (PresentationCloudTheme) -> Void, themeContextAction: @escaping (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void, colorContextAction: @escaping (PresentationThemeReference, PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void) {
init(context: AccountContext, selectTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor?) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, Bool) -> Void, openAutoNightTheme: @escaping () -> Void, openTextSize: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void, editTheme: @escaping (PresentationCloudTheme) -> Void, themeContextAction: @escaping (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void, colorContextAction: @escaping (PresentationThemeReference, ThemeSettingsColorOption?, ASDisplayNode, ContextGesture?) -> Void) {
self.context = context
self.selectTheme = selectTheme
self.selectFontSize = selectFontSize
@ -133,7 +133,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
case fontSize(PresentationTheme, PresentationFontSize)
case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, [ChatPreviewMessageItem])
case wallpaper(PresentationTheme, String)
case accentColor(PresentationTheme, PresentationThemeReference, PresentationThemeCustomColors?, PresentationThemeAccentColor?)
case accentColor(PresentationTheme, PresentationThemeReference, PresentationThemeCustomColors?, [PresentationThemeReference], ThemeSettingsColorOption?)
case autoNightTheme(PresentationTheme, String, String)
case textSize(PresentationTheme, String, String)
case themeItem(PresentationTheme, PresentationStrings, [PresentationThemeReference], PresentationThemeReference, [Int64: PresentationThemeAccentColor], [Int64: TelegramWallpaper], PresentationThemeAccentColor?)
@ -208,8 +208,8 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
} else {
return false
}
case let .accentColor(lhsTheme, lhsCurrentTheme, lhsCustomColors, lhsColor):
if case let .accentColor(rhsTheme, rhsCurrentTheme, rhsCustomColors, rhsColor) = rhs, lhsTheme === rhsTheme, lhsCurrentTheme == rhsCurrentTheme, lhsCustomColors == rhsCustomColors, lhsColor == rhsColor {
case let .accentColor(lhsTheme, lhsCurrentTheme, lhsCustomColors, lhsThemes, lhsColor):
if case let .accentColor(rhsTheme, rhsCurrentTheme, rhsCustomColors, rhsThemes, rhsColor) = rhs, lhsTheme === rhsTheme, lhsCurrentTheme == rhsCurrentTheme, lhsCustomColors == rhsCustomColors, lhsThemes == rhsThemes, lhsColor == rhsColor {
return true
} else {
return false
@ -308,18 +308,30 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .blocks, action: {
arguments.openWallpaperSettings()
})
case let .accentColor(theme, currentTheme, customColors, color):
case let .accentColor(theme, currentTheme, customColors, themes, color):
var colorItems: [ThemeSettingsAccentColor] = []
for theme in themes {
colorItems.append(.theme(theme))
}
let generalThemeReference: PresentationThemeReference
if case let .cloud(theme) = currentTheme, let settings = theme.theme.settings {
generalThemeReference = .builtin(PresentationBuiltinThemeReference(baseTheme: settings.baseTheme))
} else {
generalThemeReference = currentTheme
}
var defaultColor: PresentationThemeAccentColor? = PresentationThemeAccentColor(baseColor: .blue)
var colors = PresentationThemeBaseColor.allCases
colors = colors.filter { $0 != .custom && $0 != .preset }
if case let .builtin(name) = currentTheme {
if case let .builtin(name) = generalThemeReference {
if name == .dayClassic {
colorItems.append(.default)
defaultColor = nil
let patternWallpaper: (String, Int32, Int32?, Int32?, Int32?) -> TelegramWallpaper = { slug, topColor, bottomColor, intensity, rotation in
return TelegramWallpaper.file(id: 0, accessHash: 0, isCreator: false, isDefault: true, isPattern: true, isDark: false, slug: slug, file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: []), settings: WallpaperSettings(blur: false, motion: false, color: topColor, bottomColor: bottomColor, intensity: intensity ?? 50, rotation: rotation))
return TelegramWallpaper.file(id: 0, accessHash: 0, isCreator: false, isDefault: true, isPattern: true, isDark: false, slug: slug, file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: []), settings: WallpaperSettings(color: topColor, bottomColor: bottomColor, intensity: intensity ?? 50, rotation: rotation))
}
colorItems.append(.preset(PresentationThemeAccentColor(index: 104, baseColor: .preset, accentColor: 0x5a9e29, bubbleColors: (0xdcf8c6, nil), wallpaper: patternWallpaper("R3j69wKskFIBAAAAoUdXWCKMzCM", 0xede6dd, nil, 50, nil))))
@ -355,23 +367,32 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
colors = colors.filter { $0 != .white }
}
}
let currentColor = color ?? defaultColor
let currentColor = color ?? defaultColor.flatMap { .accentColor($0) }
colorItems.append(contentsOf: colors.map { .color($0) })
if let customColors = customColors {
colorItems.insert(contentsOf: customColors.colors.reversed().map { .custom($0) }, at: 0)
} else {
if let currentColor = currentColor, currentColor.baseColor == .custom {
colorItems.insert(.custom(currentColor), at: 0)
}
// if let currentColor = currentColor, currentColor.baseColor == .custom {
// colorItems.insert(.custom(currentColor), at: 0)
// }
}
return ThemeSettingsAccentColorItem(theme: theme, sectionId: self.section, themeReference: currentTheme, colors: colorItems, currentColor: currentColor, updated: { color in
if let color = color {
switch color {
case let .accentColor(color):
arguments.selectAccentColor(color)
case let .theme(theme):
arguments.selectTheme(theme)
}
} else {
arguments.selectAccentColor(nil)
}
}, contextAction: { theme, color, node, gesture in
arguments.colorContextAction(theme, color, node, gesture)
}, openColorPicker: { create in
arguments.openAccentColorPicker(currentTheme, create)
arguments.openAccentColorPicker(generalThemeReference, create)
}, tag: ThemeSettingsEntryTag.accentColor)
case let .autoNightTheme(theme, text, value):
return ItemListDisclosureItem(presentationData: presentationData, icon: nil, title: text, label: value, labelStyle: .text, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
@ -424,10 +445,41 @@ private func themeSettingsControllerEntries(presentationData: PresentationData,
let title = presentationData.autoNightModeTriggered ? strings.Appearance_ColorThemeNight.uppercased() : strings.Appearance_ColorTheme.uppercased()
entries.append(.themeListHeader(presentationData.theme, title))
entries.append(.chatPreview(presentationData.theme, presentationData.theme, presentationData.chatWallpaper, presentationData.fontSize, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, [ChatPreviewMessageItem(outgoing: false, reply: (presentationData.strings.Appearance_PreviewReplyAuthor, presentationData.strings.Appearance_PreviewReplyText), text: presentationData.strings.Appearance_PreviewIncomingText), ChatPreviewMessageItem(outgoing: true, reply: nil, text: presentationData.strings.Appearance_PreviewOutgoingText)]))
entries.append(.themeItem(presentationData.theme, presentationData.strings, availableThemes, themeReference, presentationThemeSettings.themeSpecificAccentColors, presentationThemeSettings.themeSpecificChatWallpapers, presentationThemeSettings.themeSpecificAccentColors[themeReference.index]))
let generalThemes: [PresentationThemeReference] = availableThemes.filter { reference in
if case let .cloud(theme) = reference {
return theme.theme.settings == nil
} else {
return true
}
}
let generalThemeReference: PresentationThemeReference
if case let .cloud(theme) = themeReference, let settings = theme.theme.settings {
generalThemeReference = .builtin(PresentationBuiltinThemeReference(baseTheme: settings.baseTheme))
} else {
generalThemeReference = themeReference
}
entries.append(.themeItem(presentationData.theme, presentationData.strings, generalThemes, generalThemeReference, presentationThemeSettings.themeSpecificAccentColors, presentationThemeSettings.themeSpecificChatWallpapers, presentationThemeSettings.themeSpecificAccentColors[themeReference.index]))
if case let .builtin(builtinTheme) = generalThemeReference {
let colorThemes = availableThemes.filter { reference in
if case let .cloud(theme) = reference, let settings = theme.theme.settings, settings.baseTheme == builtinTheme.baseTheme {
return true
} else {
return false
}
}
var colorOption: ThemeSettingsColorOption?
if case let .builtin(theme) = themeReference {
entries.append(.accentColor(presentationData.theme, themeReference, presentationThemeSettings.themeSpecificCustomColors[themeReference.index], presentationThemeSettings.themeSpecificAccentColors[themeReference.index]))
colorOption = presentationThemeSettings.themeSpecificAccentColors[themeReference.index].flatMap { .accentColor($0) }
} else {
colorOption = .theme(themeReference)
}
entries.append(.accentColor(presentationData.theme, themeReference, presentationThemeSettings.themeSpecificCustomColors[themeReference.index], colorThemes, colorOption))
}
entries.append(.wallpaper(presentationData.theme, strings.Settings_ChatBackground))
@ -556,15 +608,22 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
currentTheme = current.automaticThemeSwitchSetting.theme
}
guard let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: currentTheme, accentColor: accentColor?.color) else {
let generalThemeReference: PresentationThemeReference
if case let .cloud(theme) = currentTheme, let settings = theme.theme.settings {
generalThemeReference = .builtin(PresentationBuiltinThemeReference(baseTheme: settings.baseTheme))
} else {
generalThemeReference = currentTheme
}
guard let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: generalThemeReference, accentColor: accentColor?.color) else {
return current
}
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
var themeSpecificAccentColors = current.themeSpecificAccentColors
themeSpecificAccentColors[currentTheme.index] = accentColor
themeSpecificAccentColors[generalThemeReference.index] = accentColor
if case let .builtin(theme) = currentTheme {
if case let .builtin(theme) = generalThemeReference {
if let wallpaper = presetWallpaper, let color = accentColor {
themeSpecificChatWallpapers[coloredThemeIndex(reference: currentTheme, accentColor: color)] = wallpaper
} else if let wallpaper = current.themeSpecificChatWallpapers[currentTheme.index], wallpaper.isColorOrGradient || wallpaper.isPattern || wallpaper.isBuiltin {
@ -694,8 +753,8 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
let _ = (resolvedWallpaper
|> deliverOnMainQueue).start(next: { wallpaper in
let controller = ThemeAccentColorController(context: context, mode: .edit(theme: theme, wallpaper: wallpaper, defaultThemeReference: nil, create: true, completion: { result in
let controller = editThemeController(context: context, mode: .create(result), navigateToChat: { peerId in
let controller = ThemeAccentColorController(context: context, mode: .edit(theme: theme, wallpaper: wallpaper, defaultThemeReference: nil, create: true, completion: { result, settings in
let controller = editThemeController(context: context, mode: .create(result, nil), navigateToChat: { peerId in
if let navigationController = getNavigationControllerImpl?() {
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId)))
}
@ -770,27 +829,45 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
presentInGlobalOverlayImpl?(contextController, nil)
})
}, colorContextAction: { reference, accentColor, node, gesture in
let _ = (context.sharedContext.accountManager.transaction { transaction -> (PresentationThemeAccentColor?, TelegramWallpaper?) in
let _ = (context.sharedContext.accountManager.transaction { transaction -> (ThemeSettingsColorOption?, TelegramWallpaper?) in
let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings) as? PresentationThemeSettings ?? PresentationThemeSettings.defaultSettings
var wallpaper: TelegramWallpaper?
if let accentColor = accentColor {
switch accentColor {
case let .accentColor(accentColor):
wallpaper = settings.themeSpecificChatWallpapers[coloredThemeIndex(reference: reference, accentColor: accentColor)]
}
if wallpaper == nil {
wallpaper = settings.themeSpecificChatWallpapers[reference.index]
}
case let .theme(theme):
wallpaper = settings.themeSpecificChatWallpapers[coloredThemeIndex(reference: theme, accentColor: nil)]
}
} else {
if wallpaper == nil {
wallpaper = settings.themeSpecificChatWallpapers[reference.index]
}
}
return (accentColor, wallpaper)
} |> mapToSignal { accentColor, wallpaper -> Signal<(PresentationTheme?, TelegramWallpaper?), NoError> in
let effectiveWallpaper: TelegramWallpaper
if let wallpaper = wallpaper {
effectiveWallpaper = wallpaper
} else {
let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: reference, accentColor: accentColor?.color, bubbleColors: accentColor?.customBubbleColors)
let theme: PresentationTheme?
if let accentColor = accentColor, case let .theme(themeReference) = accentColor {
theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: themeReference)
} else {
theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: reference, accentColor: accentColor?.accentColor, bubbleColors: accentColor?.customBubbleColors)
}
effectiveWallpaper = theme?.chat.defaultWallpaper ?? .builtin(WallpaperSettings())
}
return chatServiceBackgroundColor(wallpaper: effectiveWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox)
|> map { serviceBackgroundColor in
return (makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: reference, accentColor: accentColor?.color, bubbleColors: accentColor?.customBubbleColors, serviceBackgroundColor: serviceBackgroundColor), wallpaper)
if let accentColor = accentColor, case let .theme(themeReference) = accentColor {
return (makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: themeReference, serviceBackgroundColor: serviceBackgroundColor), wallpaper)
} else {
return (makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: reference, accentColor: accentColor?.accentColor, bubbleColors: accentColor?.customBubbleColors, serviceBackgroundColor: serviceBackgroundColor), wallpaper)
}
}
}
|> deliverOnMainQueue).start(next: { theme, wallpaper in
@ -803,7 +880,15 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
let themeController = ThemePreviewController(context: context, previewTheme: theme, source: .settings(reference, wallpaper))
var items: [ContextMenuItem] = []
if let accentColor = accentColor, accentColor.baseColor == .custom {
if let accentColor = accentColor {
if case let .accentColor(color) = accentColor, color.baseColor != .custom {
} else {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_ShareThemeColor, textColor: .primary, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor)
}, action: { c, f in
c.dismiss(completion: {
})
})))
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_RemoveThemeColor, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
}, action: { c, f in
c.dismiss(completion: {
@ -859,6 +944,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
})
})))
}
}
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture)
presentInGlobalOverlayImpl?(contextController, nil)
})
@ -933,21 +1019,32 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
if let controller = controller, controller.isNodeLoaded, let navigationController = controller.navigationController as? NavigationController, navigationController.topViewController === controller {
var topOffset: CGFloat?
var bottomOffset: CGFloat?
var leftOffset: CGFloat?
var themeItemNode: ThemeSettingsThemeItemNode?
var colorItemNode: ThemeSettingsAccentColorItemNode?
var view = controller.navigationController?.view
let controllerFrame = controller.view.convert(controller.view.bounds, to: controller.navigationController?.view)
if controllerFrame.minX > 0.0 {
leftOffset = controllerFrame.minX
}
if controllerFrame.minY > 100.0 {
view = nil
}
controller.forEachItemNode { node in
if let itemNode = node as? ItemListItemNode {
if let itemTag = itemNode.tag {
if itemTag.isEqual(to: ThemeSettingsEntryTag.theme) {
let frame = node.convert(node.bounds, to: controller.displayNode)
let frame = node.view.convert(node.view.bounds, to: controller.navigationController?.view)
topOffset = frame.minY
bottomOffset = frame.maxY
if let itemNode = node as? ThemeSettingsThemeItemNode {
themeItemNode = itemNode
}
} else if itemTag.isEqual(to: ThemeSettingsEntryTag.accentColor) && hasAccentColors {
let frame = node.convert(node.bounds, to: controller.displayNode)
let frame = node.view.convert(node.view.bounds, to: controller.navigationController?.view)
bottomOffset = frame.maxY
if let itemNode = node as? ThemeSettingsAccentColorItemNode {
colorItemNode = itemNode
@ -965,14 +1062,18 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
}
}
if view != nil {
themeItemNode?.prepareCrossfadeTransition()
colorItemNode?.prepareCrossfadeTransition()
}
let crossfadeController = ThemeSettingsCrossfadeController(view: controller.view, topOffset: topOffset, bottomOffset: bottomOffset)
let crossfadeController = ThemeSettingsCrossfadeController(view: view, topOffset: topOffset, bottomOffset: bottomOffset, leftOffset: leftOffset)
crossfadeController.didAppear = { [weak themeItemNode, weak colorItemNode] in
if view != nil {
themeItemNode?.animateCrossfadeTransition()
colorItemNode?.animateCrossfadeTransition()
}
}
context.sharedContext.presentGlobalController(crossfadeController, nil)
}
@ -1044,8 +1145,8 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
return themeReference
}
|> deliverOnMainQueue).start(next: { themeReference in
let controller = ThemeAccentColorController(context: context, mode: .edit(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper, defaultThemeReference: themeReference, create: true, completion: { result in
let controller = editThemeController(context: context, mode: .create(result), navigateToChat: { peerId in
let controller = ThemeAccentColorController(context: context, mode: .edit(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper, defaultThemeReference: themeReference, create: true, completion: { result, settings in
let controller = editThemeController(context: context, mode: .create(result, settings), navigateToChat: { peerId in
if let navigationController = getNavigationControllerImpl?() {
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId)))
}
@ -1063,7 +1164,6 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
})
}))
pushControllerImpl?(controller)
})
}))
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
@ -1081,11 +1181,41 @@ public final class ThemeSettingsCrossfadeController: ViewController {
private var topSnapshotView: UIView?
private var bottomSnapshotView: UIView?
private var sideSnapshotView: UIView?
fileprivate var didAppear: (() -> Void)?
public init(view: UIView? = nil, topOffset: CGFloat? = nil, bottomOffset: CGFloat? = nil) {
public init(view: UIView? = nil, topOffset: CGFloat? = nil, bottomOffset: CGFloat? = nil, leftOffset: CGFloat? = nil) {
if let view = view {
if var leftOffset = leftOffset {
leftOffset += UIScreenPixel
if let view = view.snapshotView(afterScreenUpdates: false) {
let clipView = UIView()
clipView.clipsToBounds = true
clipView.addSubview(view)
view.clipsToBounds = true
view.contentMode = .topLeft
if let topOffset = topOffset, let bottomOffset = bottomOffset {
var frame = view.frame
frame.origin.y = topOffset
frame.size.width = leftOffset
frame.size.height = bottomOffset - topOffset
clipView.frame = frame
frame = view.frame
frame.origin.y = -topOffset
frame.size.width = leftOffset
frame.size.height = bottomOffset
view.frame = frame
}
self.sideSnapshotView = clipView
}
}
if let view = view.snapshotView(afterScreenUpdates: false) {
view.clipsToBounds = true
view.contentMode = .top
@ -1135,6 +1265,9 @@ public final class ThemeSettingsCrossfadeController: ViewController {
if let bottomSnapshotView = self.bottomSnapshotView {
self.displayNode.view.addSubview(bottomSnapshotView)
}
if let sideSnapshotView = self.sideSnapshotView {
self.displayNode.view.addSubview(sideSnapshotView)
}
}
override public func viewDidAppear(_ animated: Bool) {

View File

@ -24,8 +24,8 @@ public enum WallpaperListType {
public enum WallpaperListSource {
case list(wallpapers: [TelegramWallpaper], central: TelegramWallpaper, type: WallpaperListType)
case wallpaper(TelegramWallpaper, WallpaperPresentationOptions?, UIColor?, Int32?, Message?)
case slug(String, TelegramMediaFile?, WallpaperPresentationOptions?, UIColor?, Int32?, Message?)
case wallpaper(TelegramWallpaper, WallpaperPresentationOptions?, UIColor?, UIColor?, Int32?, Int32?, Message?)
case slug(String, TelegramMediaFile?, WallpaperPresentationOptions?, UIColor?, UIColor?, Int32?, Int32?, Message?)
case asset(PHAsset)
case contextResult(ChatContextResult)
case customColor(Int32?)
@ -97,27 +97,31 @@ class WallpaperGalleryControllerNode: GalleryControllerNode {
}
}
private func updatedFileWallpaper(wallpaper: TelegramWallpaper, color: UIColor?, intensity: Int32?) -> TelegramWallpaper {
private func updatedFileWallpaper(wallpaper: TelegramWallpaper, firstColor: UIColor?, secondColor: UIColor?, intensity: Int32?, rotation: Int32?) -> TelegramWallpaper {
if case let .file(file) = wallpaper {
return updatedFileWallpaper(id: file.id, accessHash: file.accessHash, slug: file.slug, file: file.file, color: color, intensity: intensity)
return updatedFileWallpaper(id: file.id, accessHash: file.accessHash, slug: file.slug, file: file.file, firstColor: firstColor, secondColor: secondColor, intensity: intensity, rotation: rotation)
} else {
return wallpaper
}
}
private func updatedFileWallpaper(id: Int64? = nil, accessHash: Int64? = nil, slug: String, file: TelegramMediaFile, color: UIColor?, intensity: Int32?) -> TelegramWallpaper {
private func updatedFileWallpaper(id: Int64? = nil, accessHash: Int64? = nil, slug: String, file: TelegramMediaFile, firstColor: UIColor?, secondColor: UIColor?, intensity: Int32?, rotation: Int32?) -> TelegramWallpaper {
let isPattern = file.mimeType == "image/png"
var colorValue: Int32?
var firstColorValue: Int32?
var secondColorValue: Int32?
var intensityValue: Int32?
if let color = color {
colorValue = Int32(bitPattern: color.rgb)
if let firstColor = firstColor {
firstColorValue = Int32(bitPattern: firstColor.rgb)
intensityValue = intensity
} else if isPattern {
colorValue = 0xd6e2ee
firstColorValue = 0xd6e2ee
intensityValue = 50
}
if let secondColor = secondColor {
secondColorValue = Int32(bitPattern: secondColor.rgb)
}
return .file(id: id ?? 0, accessHash: accessHash ?? 0, isCreator: false, isDefault: false, isPattern: isPattern, isDark: false, slug: slug, file: file, settings: WallpaperSettings(blur: false, motion: false, color: colorValue, intensity: intensityValue))
return .file(id: id ?? 0, accessHash: accessHash ?? 0, isCreator: false, isDefault: false, isPattern: isPattern, isDark: false, slug: slug, file: file, settings: WallpaperSettings(color: firstColorValue, bottomColor: secondColorValue, intensity: intensityValue, rotation: rotation))
}
public class WallpaperGalleryController: ViewController {
@ -183,15 +187,15 @@ public class WallpaperGalleryController: ViewController {
if case let .wallpapers(wallpaperOptions) = type, let options = wallpaperOptions {
self.initialOptions = options
}
case let .slug(slug, file, options, color, intensity, message):
case let .slug(slug, file, options, firstColor, secondColor, intensity, rotation, message):
if let file = file {
let wallpaper = updatedFileWallpaper(slug: slug, file: file, color: color, intensity: intensity)
let wallpaper = updatedFileWallpaper(slug: slug, file: file, firstColor: firstColor, secondColor: secondColor, intensity: intensity, rotation: rotation)
entries = [.wallpaper(wallpaper, message)]
centralEntryIndex = 0
self.initialOptions = options
}
case let .wallpaper(wallpaper, options, color, intensity, message):
let wallpaper = updatedFileWallpaper(wallpaper: wallpaper, color: color, intensity: intensity)
case let .wallpaper(wallpaper, options, firstColor, secondColor, intensity, rotation, message):
let wallpaper = updatedFileWallpaper(wallpaper: wallpaper, firstColor: firstColor, secondColor: secondColor, intensity: intensity, rotation: rotation)
entries = [.wallpaper(wallpaper, message)]
centralEntryIndex = 0
self.initialOptions = options
@ -776,11 +780,18 @@ public class WallpaperGalleryController: ViewController {
case let .file(_, _, _, _, isPattern, _, slug, _, settings):
if isPattern {
if let color = settings.color {
if let bottomColor = settings.bottomColor {
options.append("bg_color=\(UIColor(rgb: UInt32(bitPattern: color)).hexString)-\(UIColor(rgb: UInt32(bitPattern: bottomColor)).hexString)")
} else {
options.append("bg_color=\(UIColor(rgb: UInt32(bitPattern: color)).hexString)")
}
}
if let intensity = settings.intensity {
options.append("intensity=\(intensity)")
}
if let rotation = settings.rotation {
options.append("rotation=\(rotation)")
}
}
var optionsString = ""

View File

@ -233,10 +233,10 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
actionSignal = .single(defaultAction)
colorSignal = chatServiceBackgroundColor(wallpaper: wallpaper, mediaBox: self.context.account.postbox.mediaBox)
isBlurrable = false
case let .gradient(topColor, bottomColor, _):
case let .gradient(topColor, bottomColor, settings):
displaySize = CGSize(width: 1.0, height: 1.0)
contentSize = displaySize
signal = gradientImage([UIColor(rgb: UInt32(bitPattern: topColor)), UIColor(rgb: UInt32(bitPattern: bottomColor))])
signal = gradientImage([UIColor(rgb: UInt32(bitPattern: topColor)), UIColor(rgb: UInt32(bitPattern: bottomColor))], rotation: settings.rotation)
fetchSignal = .complete()
statusSignal = .single(.Local)
subtitleSignal = .single(nil)

View File

@ -150,7 +150,7 @@ final class WallpaperPatternPanelNode: ASDisplayNode {
var updatedWallpaper = wallpaper
if case let .file(file) = updatedWallpaper {
let settings = WallpaperSettings(blur: false, motion: false, color: Int32(bitPattern: backgroundColors.0.rgb), bottomColor: backgroundColors.1.flatMap { Int32(bitPattern: $0.rgb) }, intensity: 100)
let settings = WallpaperSettings(color: Int32(bitPattern: backgroundColors.0.rgb), bottomColor: backgroundColors.1.flatMap { Int32(bitPattern: $0.rgb) }, intensity: 100)
updatedWallpaper = .file(id: file.id, accessHash: file.accessHash, isCreator: file.isCreator, isDefault: file.isDefault, isPattern: file.isPattern, isDark: file.isDark, slug: file.slug, file: file.file, settings: settings)
}

View File

@ -1727,7 +1727,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
}, displaySwipeToReplyHint: { [weak self] in
if let strongSelf = self, let validLayout = strongSelf.validLayout, min(validLayout.size.width, validLayout.size.height) > 320.0 {
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .swipeToReply(title: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintTitle, text: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintText), elevatedLayout: true, action: { _ in }), in: .window(.root))
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .swipeToReply(title: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintTitle, text: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintText), elevatedLayout: true, action: { _ in return false }), in: .window(.root))
}
}, dismissReplyMarkupMessage: { [weak self] message in
guard let strongSelf = self, strongSelf.presentationInterfaceState.keyboardButtonsMessage?.id == message.id else {
@ -5079,14 +5079,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
statusText = strongSelf.presentationData.strings.Undo_ChatCleared
}
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: statusText), elevatedLayout: true, action: { shouldCommit in
if shouldCommit {
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: statusText), elevatedLayout: true, action: { value in
if value == .commit {
let _ = clearHistoryInteractively(postbox: account.postbox, peerId: peerId, type: type).start(completed: {
self?.chatDisplayNode.historyNode.historyAppearsCleared = false
})
} else {
return true
} else if value == .undo {
self?.chatDisplayNode.historyNode.historyAppearsCleared = false
return true
}
return false
}), in: .current)
}
@ -5349,7 +5352,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
disposable.set((signal
|> deliverOnMainQueue).start(completed: { [weak self] in
if let strongSelf = self, let layout = strongSelf.validLayout {
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))", stringForDeviceType()).0), elevatedLayout: true, action: { _ in }), in: .current)
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .succeed(text: presentationData.strings.ClearCache_Success("\(dataSizeString(selectedSize, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))", stringForDeviceType()).0), elevatedLayout: true, action: { _ in return false }), in: .current)
}
}))

View File

@ -247,11 +247,15 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
mediaAndFlags = (webpage.image ?? file, [.preferMediaBeforeText])
}
} else if webpage.type == "telegram_background" {
var patternColor: UIColor?
if let wallpaper = parseWallpaperUrl(webpage.url), case let .slug(_, _, color, intensity) = wallpaper {
patternColor = color?.withAlphaComponent(CGFloat(intensity ?? 50) / 100.0)
var topColor: UIColor?
var bottomColor: UIColor?
var rotation: Int32?
if let wallpaper = parseWallpaperUrl(webpage.url), case let .slug(_, _, firstColor, secondColor, intensity, rotationValue) = wallpaper {
topColor = firstColor?.withAlphaComponent(CGFloat(intensity ?? 50) / 100.0)
bottomColor = secondColor?.withAlphaComponent(CGFloat(intensity ?? 50) / 100.0)
rotation = rotationValue
}
let media = WallpaperPreviewMedia(content: .file(file, patternColor, nil, 0, false, false))
let media = WallpaperPreviewMedia(content: .file(file, topColor, bottomColor, rotation, false, false))
mediaAndFlags = (media, [.preferMediaAspectFilled])
if let fileSize = file.size {
badge = dataSizeString(fileSize, decimalSeparator: item.presentationData.dateTimeFormat.decimalSeparator)
@ -279,23 +283,31 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
}
} else if let type = webpage.type {
if type == "telegram_background" {
if let text = webpage.text {
let colorCodeRange = text.range(of: "#")
if colorCodeRange != nil {
let components = text.replacingOccurrences(of: "#", with: "").components(separatedBy: "-")
if components.count == 2, let topColorCode = components.first, let bottomColorCode = components.last {
if let topColor = UIColor(hexString: topColorCode), let bottomColor = UIColor(hexString: bottomColorCode) {
let media = WallpaperPreviewMedia(content: .gradient(topColor, bottomColor, 0))
mediaAndFlags = (media, ChatMessageAttachedContentNodeMediaFlags())
}
} else if components.count == 1, let colorCode = components.first {
if let color = UIColor(hexString: colorCode) {
let media = WallpaperPreviewMedia(content: .color(color))
mediaAndFlags = (media, ChatMessageAttachedContentNodeMediaFlags())
var topColor: UIColor?
var bottomColor: UIColor?
var rotation: Int32?
if let wallpaper = parseWallpaperUrl(webpage.url) {
if case let .color(color) = wallpaper {
topColor = color
} else if case let .gradient(topColorValue, bottomColorValue, rotationValue) = wallpaper {
topColor = topColorValue
bottomColor = bottomColorValue
rotation = rotationValue
}
}
var content: WallpaperPreviewMediaContent?
if let topColor = topColor {
if let bottomColor = bottomColor {
content = .gradient(topColor, bottomColor, rotation)
} else {
content = .color(topColor)
}
}
if let content = content {
let media = WallpaperPreviewMedia(content: content)
mediaAndFlags = (media, [])
}
} else if type == "telegram_theme" {
var file: TelegramMediaFile?
var isSupported = false

View File

@ -486,12 +486,12 @@ func openChatWallpaper(context: AccountContext, message: Message, present: @esca
if case let .wallpaper(parameter) = resolvedUrl {
let source: WallpaperListSource
switch parameter {
case let .slug(slug, options, color, intensity):
source = .slug(slug, content.file, options, color, intensity, message)
case let .slug(slug, options, firstColor, secondColor, intensity, rotation):
source = .slug(slug, content.file, options, firstColor, secondColor, intensity, rotation, message)
case let .color(color):
source = .wallpaper(.color(Int32(color.rgb)), nil, nil, nil, message)
case let .gradient(topColor, bottomColor):
source = .wallpaper(.gradient(Int32(topColor.rgb), Int32(bottomColor.rgb), WallpaperSettings()), nil, nil, nil, message)
source = .wallpaper(.color(Int32(color.rgb)), nil, nil, nil, nil, nil, message)
case let .gradient(topColor, bottomColor, rotation):
source = .wallpaper(.gradient(Int32(topColor.rgb), Int32(bottomColor.rgb), WallpaperSettings(rotation: rotation)), nil, nil, nil, nil, rotation, message)
}
let controller = WallpaperGalleryController(context: context, source: source)

View File

@ -249,26 +249,30 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
let signal: Signal<TelegramWallpaper, GetWallpaperError>
var options: WallpaperPresentationOptions?
var color: UIColor?
var topColor: UIColor?
var bottomColor: UIColor?
var intensity: Int32?
var rotation: Int32?
switch parameter {
case let .slug(slug, wallpaperOptions, patternColor, patternIntensity):
case let .slug(slug, wallpaperOptions, firstColor, secondColor, intensityValue, rotationValue):
signal = getWallpaper(account: context.account, slug: slug)
options = wallpaperOptions
color = patternColor
intensity = patternIntensity
topColor = firstColor
bottomColor = secondColor
intensity = intensityValue
rotation = rotationValue
controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
present(controller!, nil)
case let .color(color):
signal = .single(.color(Int32(color.rgb)))
case let .gradient(topColor, bottomColor):
case let .gradient(topColor, bottomColor, rotation):
signal = .single(.gradient(Int32(topColor.rgb), Int32(bottomColor.rgb), WallpaperSettings()))
}
let _ = (signal
|> deliverOnMainQueue).start(next: { [weak controller] wallpaper in
controller?.dismiss()
let galleryController = WallpaperGalleryController(context: context, source: .wallpaper(wallpaper, options, color, intensity, nil))
let galleryController = WallpaperGalleryController(context: context, source: .wallpaper(wallpaper, options, topColor, bottomColor, intensity, rotation, nil))
present(galleryController, nil)
}, error: { [weak controller] error in
controller?.dismiss()
@ -316,8 +320,6 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
return disposables
}
}
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
present(controller, nil)
var cancelImpl: (() -> Void)?
let progressSignal = Signal<Never, NoError> { subscriber in
@ -345,14 +347,12 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
progressDisposable.dispose()
}
}
|> deliverOnMainQueue).start(next: { [weak controller] dataAndTheme in
controller?.dismiss()
|> deliverOnMainQueue).start(next: { dataAndTheme in
if let theme = makePresentationTheme(data: dataAndTheme.0) {
let previewController = ThemePreviewController(context: context, previewTheme: theme, source: .theme(dataAndTheme.1))
navigationController?.pushViewController(previewController)
}
}, error: { [weak controller] error in
}, error: { error in
let errorText: String
switch error {
case .generic, .slugInvalid:
@ -361,7 +361,6 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
errorText = presentationData.strings.Theme_Unsupported
}
present(textAlertController(context: context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
controller?.dismiss()
}))
dismissInput()
case let .wallet(address, amount, comment):

View File

@ -546,7 +546,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
} else if parsedUrl.host == "bg" {
if let components = URLComponents(string: "/?" + query) {
var parameter: String?
var mode = ""
var query: [String] = []
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
@ -557,13 +557,23 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
} else if queryItem.name == "gradient" {
parameter = value
} else if queryItem.name == "mode" {
mode = "?mode=\(value)"
query.append("mode=\(value)")
} else if queryItem.name == "bg_color" {
query.append("bg_color=\(value)")
} else if queryItem.name == "intensity" {
query.append("intensity=\(value)")
} else if queryItem.name == "rotation" {
query.append("rotation=\(value)")
}
}
}
}
var queryString = ""
if !query.isEmpty {
queryString = "?\(query.joined(separator: "&"))"
}
if let parameter = parameter {
convertedUrl = "https://t.me/bg/\(parameter)\(mode)"
convertedUrl = "https://t.me/bg/\(parameter)\(queryString)"
}
}
} else if parsedUrl.host == "addtheme" {

View File

@ -10,6 +10,32 @@ public enum PresentationBuiltinThemeReference: Int32 {
case night = 1
case day = 2
case nightAccent = 3
public init(baseTheme: TelegramBaseTheme) {
switch baseTheme {
case .classic:
self = .dayClassic
case .day:
self = .day
case .night:
self = .night
case .tinted:
self = .nightAccent
}
}
public var baseTheme: TelegramBaseTheme {
switch self {
case .dayClassic:
return .classic
case .day:
return .day
case .night:
return .night
case .nightAccent:
return .tinted
}
}
}
public struct WallpaperPresentationOptions: OptionSet {

View File

@ -14,17 +14,23 @@ public enum UndoOverlayContent {
case actionSucceeded(title: String, text: String, cancel: String)
}
public enum UndoOverlayAction {
case info
case undo
case commit
}
public final class UndoOverlayController: ViewController {
private let presentationData: PresentationData
public let content: UndoOverlayContent
private let elevatedLayout: Bool
private let animateInAsReplacement: Bool
private var action: (Bool) -> Void
private var action: (UndoOverlayAction) -> Bool
private var didPlayPresentationAnimation = false
private var dismissed = false
public init(presentationData: PresentationData, content: UndoOverlayContent, elevatedLayout: Bool, animateInAsReplacement: Bool = false, action: @escaping (Bool) -> Void) {
public init(presentationData: PresentationData, content: UndoOverlayContent, elevatedLayout: Bool, animateInAsReplacement: Bool = false, action: @escaping (UndoOverlayAction) -> Bool) {
self.presentationData = presentationData
self.content = content
self.elevatedLayout = elevatedLayout
@ -42,7 +48,7 @@ public final class UndoOverlayController: ViewController {
override public func loadDisplayNode() {
self.displayNode = UndoOverlayControllerNode(presentationData: self.presentationData, content: self.content, elevatedLayout: self.elevatedLayout, action: { [weak self] value in
self?.action(value)
return self?.action(value) ?? false
}, dismiss: { [weak self] in
self?.dismiss()
})
@ -50,12 +56,12 @@ public final class UndoOverlayController: ViewController {
}
public func dismissWithCommitAction() {
self.action(true)
self.action(.commit)
self.dismiss()
}
public func dismissWithCommitActionAndReplacementAnimation() {
self.action(true)
self.action(.commit)
(self.displayNode as! UndoOverlayControllerNode).animateOutWithReplacement(completion: { [weak self] in
self?.presentingViewController?.dismiss(animated: false, completion: nil)
})

View File

@ -21,11 +21,12 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
private let animatedStickerNode: AnimatedStickerNode?
private let titleNode: ImmediateTextNode
private let textNode: ImmediateTextNode
private let buttonTextNode: ImmediateTextNode
private let buttonNode: HighlightTrackingButtonNode
private let undoButtonTextNode: ImmediateTextNode
private let undoButtonNode: HighlightTrackingButtonNode
private let panelNode: ASDisplayNode
private let panelWrapperNode: ASDisplayNode
private let action: (Bool) -> Void
private let action: (UndoOverlayAction) -> Bool
private let dismiss: () -> Void
private let effectView: UIView
@ -38,7 +39,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
private var validLayout: ContainerViewLayout?
init(presentationData: PresentationData, content: UndoOverlayContent, elevatedLayout: Bool, action: @escaping (Bool) -> Void, dismiss: @escaping () -> Void) {
init(presentationData: PresentationData, content: UndoOverlayContent, elevatedLayout: Bool, action: @escaping (UndoOverlayAction) -> Bool, dismiss: @escaping () -> Void) {
self.elevatedLayout = elevatedLayout
self.action = action
@ -55,6 +56,8 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.textNode.displaysAsynchronously = false
self.textNode.maximumNumberOfLines = 0
self.buttonNode = HighlightTrackingButtonNode()
var displayUndo = true
var undoText = presentationData.strings.Undo_Undo
var undoTextColor = UIColor(rgb: 0x5ac8fa)
@ -130,11 +133,16 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.animationNode = AnimationNode(animation: "anim_success", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
self.animatedStickerNode = nil
undoTextColor = UIColor(rgb: 0xff7b74)
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
let link = MarkdownAttributeSet(font: Font.regular(14.0), textColor: undoTextColor)
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in return nil }), textAlignment: .natural)
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white)
self.textNode.attributedText = attributedText
displayUndo = true
undoText = cancel
undoTextColor = UIColor(rgb: 0xff7b74)
self.originalRemainingSeconds = 3
case let .emoji(path, text):
self.iconNode = nil
@ -167,11 +175,11 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.statusNode = RadialStatusNode(backgroundNodeColor: .clear)
self.buttonTextNode = ImmediateTextNode()
self.buttonTextNode.displaysAsynchronously = false
self.buttonTextNode.attributedText = NSAttributedString(string: undoText, font: Font.regular(17.0), textColor: undoTextColor)
self.undoButtonTextNode = ImmediateTextNode()
self.undoButtonTextNode.displaysAsynchronously = false
self.undoButtonTextNode.attributedText = NSAttributedString(string: undoText, font: Font.regular(17.0), textColor: undoTextColor)
self.buttonNode = HighlightTrackingButtonNode()
self.undoButtonNode = HighlightTrackingButtonNode()
self.panelNode = ASDisplayNode()
if presentationData.theme.overallDarkAppearance {
@ -201,25 +209,27 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.animatedStickerNode.flatMap(self.panelWrapperNode.addSubnode)
self.panelWrapperNode.addSubnode(self.titleNode)
self.panelWrapperNode.addSubnode(self.textNode)
if displayUndo {
self.panelWrapperNode.addSubnode(self.buttonTextNode)
self.panelWrapperNode.addSubnode(self.buttonNode)
if displayUndo {
self.panelWrapperNode.addSubnode(self.undoButtonTextNode)
self.panelWrapperNode.addSubnode(self.undoButtonNode)
}
self.addSubnode(self.panelNode)
self.addSubnode(self.panelWrapperNode)
self.buttonNode.highligthedChanged = { [weak self] highlighted in
self.undoButtonNode.highligthedChanged = { [weak self] highlighted in
if let strongSelf = self {
if highlighted {
strongSelf.buttonTextNode.layer.removeAnimation(forKey: "opacity")
strongSelf.buttonTextNode.alpha = 0.4
strongSelf.undoButtonTextNode.layer.removeAnimation(forKey: "opacity")
strongSelf.undoButtonTextNode.alpha = 0.4
} else {
strongSelf.buttonTextNode.alpha = 1.0
strongSelf.buttonTextNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
strongSelf.undoButtonTextNode.alpha = 1.0
strongSelf.undoButtonTextNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
}
}
}
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
self.undoButtonNode.addTarget(self, action: #selector(self.undoButtonPressed), forControlEvents: .touchUpInside)
}
override func didLoad() {
@ -230,7 +240,13 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
}
@objc private func buttonPressed() {
self.action(false)
if self.action(.info) {
self.dismiss()
}
}
@objc private func undoButtonPressed() {
self.action(.undo)
self.dismiss()
}
@ -239,7 +255,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.remainingSeconds -= 1
}
if self.remainingSeconds == 0 {
self.action(true)
self.action(.commit)
self.dismiss()
} else {
if !self.timerTextNode.bounds.size.width.isZero, let snapshot = self.timerTextNode.view.snapshotContentTree() {
@ -286,9 +302,9 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
let margin: CGFloat = 16.0
let buttonTextSize = self.buttonTextNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude))
let buttonTextSize = self.undoButtonTextNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude))
let buttonMinX: CGFloat
if self.buttonNode.supernode != nil {
if self.undoButtonNode.supernode != nil {
buttonMinX = layout.size.width - layout.safeInsets.left - rightInset - buttonTextSize.width - margin * 2.0
} else {
buttonMinX = layout.size.width - layout.safeInsets.left - rightInset
@ -316,8 +332,12 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.effectView.frame = CGRect(x: 0.0, y: 0.0, width: layout.size.width - margin * 2.0 - layout.safeInsets.left - layout.safeInsets.right, height: contentHeight)
let buttonTextFrame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - rightInset - buttonTextSize.width - margin * 2.0, y: floor((contentHeight - buttonTextSize.height) / 2.0)), size: buttonTextSize)
transition.updateFrame(node: self.buttonTextNode, frame: buttonTextFrame)
self.buttonNode.frame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - rightInset - buttonTextSize.width - 8.0 - margin * 2.0, y: 0.0), size: CGSize(width: layout.safeInsets.right + rightInset + buttonTextSize.width + 8.0 + margin, height: contentHeight))
transition.updateFrame(node: self.undoButtonTextNode, frame: buttonTextFrame)
let undoButtonFrame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - rightInset - buttonTextSize.width - 8.0 - margin * 2.0, y: 0.0), size: CGSize(width: layout.safeInsets.right + rightInset + buttonTextSize.width + 8.0 + margin, height: contentHeight))
self.undoButtonNode.frame = undoButtonFrame
self.buttonNode.frame = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: 0.0), size: CGSize(width: undoButtonFrame.minX - layout.safeInsets.left, height: contentHeight))
var textContentHeight = textSize.height
var textOffset: CGFloat = 0.0

View File

@ -177,19 +177,31 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
if component.count == 6, component.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted) == nil, let color = UIColor(hexString: component) {
parameter = .color(color)
} else if component.count == 13, component.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF-").inverted) == nil {
var rotation: Int32?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "rotation" {
rotation = Int32(value)
}
}
}
}
let components = component.components(separatedBy: "-")
if components.count == 2, let topColor = UIColor(hexString: components[0]), let bottomColor = UIColor(hexString: components[1]) {
parameter = .gradient(topColor, bottomColor)
parameter = .gradient(topColor, bottomColor, rotation)
} else {
return nil
}
} else {
var options: WallpaperPresentationOptions = []
var intensity: Int32?
var color: UIColor?
var topColor: UIColor?
var bottomColor: UIColor?
var rotation: Int32?
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value{
if let value = queryItem.value {
if queryItem.name == "mode" {
for option in value.components(separatedBy: "+") {
switch option.lowercased() {
@ -202,14 +214,24 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
}
}
} else if queryItem.name == "bg_color" {
color = UIColor(hexString: value)
if value.count == 6, value.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted) == nil, let color = UIColor(hexString: value) {
topColor = color
} else if value.count == 13, value.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF-").inverted) == nil {
let components = value.components(separatedBy: "-")
if components.count == 2, let topColorValue = UIColor(hexString: components[0]), let bottomColorValue = UIColor(hexString: components[1]) {
topColor = topColorValue
bottomColor = bottomColorValue
}
}
} else if queryItem.name == "intensity" {
intensity = Int32(value)
} else if queryItem.name == "rotation" {
rotation = Int32(value)
}
}
}
}
parameter = .slug(component, options, color, intensity)
parameter = .slug(component, options, topColor, bottomColor, intensity, rotation)
}
return .wallpaper(parameter)
} else if pathComponents[0] == "addtheme" {

View File

@ -547,7 +547,7 @@ public func solidColorImage(_ color: UIColor) -> Signal<(TransformImageArguments
})
}
public func gradientImage(_ colors: [UIColor], rotation: Int32 = 0) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
public func gradientImage(_ colors: [UIColor], rotation: Int32? = nil) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
guard !colors.isEmpty else {
return .complete()
}
@ -572,9 +572,11 @@ public func gradientImage(_ colors: [UIColor], rotation: Int32 = 0) -> Signal<(T
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
if let rotation = rotation {
c.translateBy(x: arguments.drawingSize.width / 2.0, y: arguments.drawingSize.height / 2.0)
c.rotate(by: CGFloat(rotation) * CGFloat.pi / 180.0)
c.translateBy(x: -arguments.drawingSize.width / 2.0, y: -arguments.drawingSize.height / 2.0)
}
c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: arguments.drawingSize.height), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
}