mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-02 00:17:02 +00:00
Update themes
This commit is contained in:
parent
88436ccb5c
commit
efacf13a9e
@ -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]().";
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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,31 +323,52 @@ 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)
|
||||
} else {
|
||||
updated = current.withUpdatedTheme(updatedTheme)
|
||||
}
|
||||
|
||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||
themeSpecificChatWallpapers[updatedTheme.index] = nil
|
||||
return updated.withUpdatedThemeSpecificChatWallpapers(themeSpecificChatWallpapers)
|
||||
})
|
||||
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 {
|
||||
currentSettings = PresentationThemeSettings.defaultSettings
|
||||
}
|
||||
|
||||
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 updatedSettings.withUpdatedThemeSpecificChatWallpapers(themeSpecificChatWallpapers)
|
||||
})
|
||||
return previousDefaultTheme
|
||||
}
|
||||
}
|
||||
|
||||
var cancelImpl: (() -> Void)?
|
||||
let progress = Signal<Never, NoError> { [weak self] subscriber in
|
||||
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
|
||||
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
|
||||
cancelImpl?()
|
||||
}))
|
||||
self?.present(controller, in: .window(.root))
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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))
|
||||
}
|
||||
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))
|
||||
}
|
||||
entries.append(.color(index, item.themeReference, accentColor, selected))
|
||||
case let .preset(color), let .custom(color):
|
||||
let selected = item.currentColor == 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)
|
||||
}
|
||||
|
@ -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
|
||||
arguments.selectAccentColor(color)
|
||||
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)
|
||||
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]))
|
||||
|
||||
if case let .builtin(theme) = themeReference {
|
||||
entries.append(.accentColor(presentationData.theme, themeReference, presentationThemeSettings.themeSpecificCustomColors[themeReference.index], 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 {
|
||||
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))
|
||||
@ -555,16 +607,23 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
if autoNightModeTriggered {
|
||||
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,14 +829,23 @@ 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 {
|
||||
wallpaper = settings.themeSpecificChatWallpapers[coloredThemeIndex(reference: reference, accentColor: accentColor)]
|
||||
}
|
||||
if wallpaper == nil {
|
||||
wallpaper = settings.themeSpecificChatWallpapers[reference.index]
|
||||
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
|
||||
@ -785,12 +853,21 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
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,61 +880,70 @@ 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 {
|
||||
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: {
|
||||
let actionSheet = ActionSheetController(presentationData: presentationData)
|
||||
var items: [ActionSheetItem] = []
|
||||
items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_RemoveThemeColorConfirmation, color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
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: {
|
||||
|
||||
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||
let themeReference: PresentationThemeReference
|
||||
if presentationData.autoNightModeTriggered {
|
||||
themeReference = current.automaticThemeSwitchSetting.theme
|
||||
} else {
|
||||
themeReference = current.theme
|
||||
}
|
||||
|
||||
var themeSpecificAccentColors = current.themeSpecificAccentColors
|
||||
var themeSpecificCustomColors = current.themeSpecificCustomColors
|
||||
var customColors = themeSpecificCustomColors[themeReference.index]?.colors ?? []
|
||||
|
||||
var updatedAccentColor: PresentationThemeAccentColor?
|
||||
if let index = customColors.firstIndex(where: { $0.index == accentColor.index }) {
|
||||
if index > 0 {
|
||||
updatedAccentColor = customColors[index - 1]
|
||||
} else {
|
||||
if case let .builtin(theme) = themeReference {
|
||||
switch theme {
|
||||
case .dayClassic:
|
||||
updatedAccentColor = nil
|
||||
case .day, .night, .nightAccent:
|
||||
updatedAccentColor = PresentationThemeAccentColor(baseColor: .blue)
|
||||
}
|
||||
} else {
|
||||
updatedAccentColor = PresentationThemeAccentColor(baseColor: .blue)
|
||||
}
|
||||
}
|
||||
customColors.remove(at: index)
|
||||
} else {
|
||||
updatedAccentColor = PresentationThemeAccentColor(baseColor: .blue)
|
||||
}
|
||||
|
||||
themeSpecificAccentColors[themeReference.index] = updatedAccentColor
|
||||
themeSpecificCustomColors[themeReference.index] = PresentationThemeCustomColors(colors: customColors)
|
||||
return current.withUpdatedThemeSpecificCustomColors(themeSpecificCustomColors).withUpdatedThemeSpecificAccentColors(themeSpecificAccentColors)
|
||||
}).start()
|
||||
}))
|
||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
})
|
||||
})))
|
||||
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: {
|
||||
let actionSheet = ActionSheetController(presentationData: presentationData)
|
||||
var items: [ActionSheetItem] = []
|
||||
items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_RemoveThemeColorConfirmation, color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
})
|
||||
])])
|
||||
presentControllerImpl?(actionSheet, nil)
|
||||
})
|
||||
})))
|
||||
|
||||
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||
let themeReference: PresentationThemeReference
|
||||
if presentationData.autoNightModeTriggered {
|
||||
themeReference = current.automaticThemeSwitchSetting.theme
|
||||
} else {
|
||||
themeReference = current.theme
|
||||
}
|
||||
|
||||
var themeSpecificAccentColors = current.themeSpecificAccentColors
|
||||
var themeSpecificCustomColors = current.themeSpecificCustomColors
|
||||
var customColors = themeSpecificCustomColors[themeReference.index]?.colors ?? []
|
||||
|
||||
var updatedAccentColor: PresentationThemeAccentColor?
|
||||
if let index = customColors.firstIndex(where: { $0.index == accentColor.index }) {
|
||||
if index > 0 {
|
||||
updatedAccentColor = customColors[index - 1]
|
||||
} else {
|
||||
if case let .builtin(theme) = themeReference {
|
||||
switch theme {
|
||||
case .dayClassic:
|
||||
updatedAccentColor = nil
|
||||
case .day, .night, .nightAccent:
|
||||
updatedAccentColor = PresentationThemeAccentColor(baseColor: .blue)
|
||||
}
|
||||
} else {
|
||||
updatedAccentColor = PresentationThemeAccentColor(baseColor: .blue)
|
||||
}
|
||||
}
|
||||
customColors.remove(at: index)
|
||||
} else {
|
||||
updatedAccentColor = PresentationThemeAccentColor(baseColor: .blue)
|
||||
}
|
||||
|
||||
themeSpecificAccentColors[themeReference.index] = updatedAccentColor
|
||||
themeSpecificCustomColors[themeReference.index] = PresentationThemeCustomColors(colors: customColors)
|
||||
return current.withUpdatedThemeSpecificCustomColors(themeSpecificCustomColors).withUpdatedThemeSpecificAccentColors(themeSpecificAccentColors)
|
||||
}).start()
|
||||
}))
|
||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
})
|
||||
])])
|
||||
presentControllerImpl?(actionSheet, nil)
|
||||
})
|
||||
})))
|
||||
}
|
||||
}
|
||||
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,13 +1062,17 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
||||
}
|
||||
}
|
||||
|
||||
themeItemNode?.prepareCrossfadeTransition()
|
||||
colorItemNode?.prepareCrossfadeTransition()
|
||||
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
|
||||
themeItemNode?.animateCrossfadeTransition()
|
||||
colorItemNode?.animateCrossfadeTransition()
|
||||
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) {
|
||||
|
@ -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 {
|
||||
options.append("bg_color=\(UIColor(rgb: UInt32(bitPattern: color)).hexString)")
|
||||
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 = ""
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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)
|
||||
}
|
||||
}))
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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" {
|
||||
|
Binary file not shown.
@ -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 {
|
||||
|
@ -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)
|
||||
})
|
||||
|
@ -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)
|
||||
@ -129,12 +132,17 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
self.iconCheckNode = nil
|
||||
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)
|
||||
self.panelWrapperNode.addSubnode(self.buttonNode)
|
||||
if displayUndo {
|
||||
self.panelWrapperNode.addSubnode(self.buttonTextNode)
|
||||
self.panelWrapperNode.addSubnode(self.buttonNode)
|
||||
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
|
||||
|
@ -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" {
|
||||
|
@ -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)!
|
||||
|
||||
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)
|
||||
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])
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user