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.ReplyName" = "Bob Harris";
|
||||||
"Appearance.ThemePreview.Chat.2.Text" = "Right side. And, uh, with intensity.";
|
"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.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.4.Text" = "For relaxing times, make it Suntory time. 😎";
|
||||||
"Appearance.ThemePreview.Chat.5.Text" = "He wants you to turn, look in camera. O.K.?";
|
"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";
|
"PrivacySettings.WebSessions" = "Active Websites";
|
||||||
|
|
||||||
"Appearance.ShareThemeColor" = "Share";
|
"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 {
|
public enum WallpaperUrlParameter {
|
||||||
case slug(String, WallpaperPresentationOptions, UIColor?, Int32?)
|
case slug(String, WallpaperPresentationOptions, UIColor?, UIColor?, Int32?, Int32?)
|
||||||
case color(UIColor)
|
case color(UIColor)
|
||||||
case gradient(UIColor, UIColor)
|
case gradient(UIColor, UIColor, Int32?)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ResolvedUrl {
|
public enum ResolvedUrl {
|
||||||
|
@ -133,8 +133,11 @@ public final class AuthTransferScanScreen: ViewController {
|
|||||||
if let navigationController = navigationController as? NavigationController {
|
if let navigationController = navigationController as? NavigationController {
|
||||||
let activeSessionsContext = self.activeSessionsContext
|
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
|
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()
|
let _ = activeSessionsContext.remove(hash: session.hash).start()
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}), in: .window(.root))
|
}), in: .window(.root))
|
||||||
|
|
||||||
|
@ -1198,11 +1198,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
|||||||
|
|
||||||
let text = strongSelf.presentationData.strings.ChatList_DeletedChats(Int32(peerIds.count))
|
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 {
|
guard let strongSelf = self else {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
if shouldCommit {
|
if value == .commit {
|
||||||
let context = strongSelf.context
|
let context = strongSelf.context
|
||||||
let presentationData = strongSelf.presentationData
|
let presentationData = strongSelf.presentationData
|
||||||
let progressSignal = Signal<Never, NoError> { subscriber in
|
let progressSignal = Signal<Never, NoError> { subscriber in
|
||||||
@ -1230,7 +1230,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
|||||||
}
|
}
|
||||||
let _ = (signal
|
let _ = (signal
|
||||||
|> deliverOnMainQueue).start()
|
|> deliverOnMainQueue).start()
|
||||||
} else {
|
return true
|
||||||
|
} else if value == .undo {
|
||||||
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerIds.first!)
|
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerIds.first!)
|
||||||
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
|
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
|
||||||
var state = state
|
var state = state
|
||||||
@ -1240,7 +1241,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
|||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
self?.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerIds.first!)
|
self?.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerIds.first!)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}), in: .current)
|
}), in: .current)
|
||||||
|
|
||||||
strongSelf.donePressed()
|
strongSelf.donePressed()
|
||||||
@ -1310,11 +1313,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
|||||||
})
|
})
|
||||||
|
|
||||||
if value {
|
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 {
|
guard let strongSelf = self else {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
if !shouldCommit {
|
if value == .undo {
|
||||||
let _ = (strongSelf.context.account.postbox.transaction { transaction -> Bool in
|
let _ = (strongSelf.context.account.postbox.transaction { transaction -> Bool in
|
||||||
var updatedValue = false
|
var updatedValue = false
|
||||||
updateChatArchiveSettings(transaction: transaction, { settings in
|
updateChatArchiveSettings(transaction: transaction, { settings in
|
||||||
@ -1325,10 +1328,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
|||||||
})
|
})
|
||||||
return updatedValue
|
return updatedValue
|
||||||
}).start()
|
}).start()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}), in: .current)
|
}), in: .current)
|
||||||
} else {
|
} 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)
|
}), in: .current)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -1422,11 +1427,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
|||||||
return true
|
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 {
|
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: {
|
let _ = clearHistoryInteractively(postbox: strongSelf.context.account.postbox, peerId: peerId, type: type).start(completed: {
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -1437,13 +1442,16 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
|||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
} else {
|
return true
|
||||||
|
} else if value == .undo {
|
||||||
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
|
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
|
||||||
var state = state
|
var state = state
|
||||||
state.pendingClearHistoryPeerIds.remove(peer.peerId)
|
state.pendingClearHistoryPeerIds.remove(peer.peerId)
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}), in: .current)
|
}), in: .current)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1618,11 +1626,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
|||||||
deleteSendMessageIntents(peerId: peerId)
|
deleteSendMessageIntents(peerId: peerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
let action: (Bool) -> Void = { shouldCommit in
|
let action: (UndoOverlayAction) -> Bool = { value in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
if !shouldCommit {
|
if value == .undo {
|
||||||
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerIds[0])
|
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerIds[0])
|
||||||
let _ = (postbox.transaction { transaction -> Void in
|
let _ = (postbox.transaction { transaction -> Void in
|
||||||
for peerId in peerIds {
|
for peerId in peerIds {
|
||||||
@ -1635,6 +1643,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
|||||||
}
|
}
|
||||||
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil)
|
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil)
|
||||||
})
|
})
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1721,11 +1732,11 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
|||||||
return true
|
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 {
|
guard let strongSelf = self else {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
if shouldCommit {
|
if value == .commit {
|
||||||
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId)
|
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId)
|
||||||
if let channel = chatPeer as? TelegramChannel {
|
if let channel = chatPeer as? TelegramChannel {
|
||||||
strongSelf.context.peerChannelMemberCategoriesContextsManager.externallyRemoved(peerId: channel.id, memberId: strongSelf.context.account.peerId)
|
strongSelf.context.peerChannelMemberCategoriesContextsManager.externallyRemoved(peerId: channel.id, memberId: strongSelf.context.account.peerId)
|
||||||
@ -1744,7 +1755,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
|||||||
deleteSendMessageIntents(peerId: peerId)
|
deleteSendMessageIntents(peerId: peerId)
|
||||||
})
|
})
|
||||||
completion()
|
completion()
|
||||||
} else {
|
return true
|
||||||
|
} else if value == .undo {
|
||||||
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId)
|
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerId)
|
||||||
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
|
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
|
||||||
var state = state
|
var state = state
|
||||||
@ -1752,7 +1764,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
|||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil)
|
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}), in: .current)
|
}), in: .current)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,7 +529,7 @@ public func channelAdminsController(context: AccountContext, peerId: PeerId, loa
|
|||||||
guard let peer = peer, let user = user else {
|
guard let peer = peer, let user = user else {
|
||||||
return
|
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
|
clearDisposable.set((signal
|
||||||
|> deliverOnMainQueue).start(completed: {
|
|> deliverOnMainQueue).start(completed: {
|
||||||
statsPromise.set(.single(.result(resultStats)))
|
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
|
clearDisposable.set((signal
|
||||||
|> deliverOnMainQueue).start(completed: {
|
|> deliverOnMainQueue).start(completed: {
|
||||||
statsPromise.set(.single(.result(resultStats)))
|
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
|
clearDisposable.set((signal
|
||||||
|> deliverOnMainQueue).start(completed: {
|
|> deliverOnMainQueue).start(completed: {
|
||||||
statsPromise.set(.single(.result(resultStats)))
|
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 {
|
public enum EditThemeControllerMode: Equatable {
|
||||||
case create(PresentationTheme?)
|
case create(PresentationTheme?, TelegramThemeSettings?)
|
||||||
case edit(PresentationCloudTheme)
|
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 {
|
public func editThemeController(context: AccountContext, mode: EditThemeControllerMode, navigateToChat: ((PeerId) -> Void)? = nil, completion: ((PresentationThemeReference) -> Void)? = nil) -> ViewController {
|
||||||
let initialState: EditThemeControllerState
|
let initialState: EditThemeControllerState
|
||||||
let previewThemePromise = Promise<PresentationTheme>()
|
let previewThemePromise = Promise<PresentationTheme>()
|
||||||
|
let settingsPromise = Promise<TelegramThemeSettings?>(nil)
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
switch mode {
|
switch mode {
|
||||||
case let .create(existingTheme):
|
case let .create(existingTheme, settings):
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let wallpaper: TelegramWallpaper
|
let wallpaper: TelegramWallpaper
|
||||||
if let existingTheme = existingTheme {
|
if let existingTheme = existingTheme {
|
||||||
theme = existingTheme
|
theme = existingTheme
|
||||||
wallpaper = theme.chat.defaultWallpaper
|
wallpaper = theme.chat.defaultWallpaper
|
||||||
|
settingsPromise.set(.single(settings))
|
||||||
} else {
|
} else {
|
||||||
theme = presentationData.theme
|
theme = presentationData.theme
|
||||||
wallpaper = presentationData.chatWallpaper
|
wallpaper = presentationData.chatWallpaper
|
||||||
|
settingsPromise.set(.single(nil))
|
||||||
}
|
}
|
||||||
initialState = EditThemeControllerState(mode: mode, title: generateThemeName(accentColor: theme.rootController.navigationBar.buttonColor), slug: "", updatedTheme: nil, updating: false)
|
initialState = EditThemeControllerState(mode: mode, title: generateThemeName(accentColor: theme.rootController.navigationBar.buttonColor), slug: "", updatedTheme: nil, updating: false)
|
||||||
previewThemePromise.set(.single(theme.withUpdated(name: "", defaultWallpaper: wallpaper)))
|
previewThemePromise.set(.single(theme.withUpdated(name: "", defaultWallpaper: wallpaper)))
|
||||||
@ -292,6 +295,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
|||||||
} else {
|
} else {
|
||||||
previewThemePromise.set(.single(theme.withUpdated(name: nil, defaultWallpaper: info.resolvedWallpaper)))
|
previewThemePromise.set(.single(theme.withUpdated(name: nil, defaultWallpaper: info.resolvedWallpaper)))
|
||||||
}
|
}
|
||||||
|
settingsPromise.set(.single(info.theme.settings))
|
||||||
} else {
|
} else {
|
||||||
previewThemePromise.set(.single(presentationData.theme.withUpdated(name: "", defaultWallpaper: presentationData.chatWallpaper)))
|
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
|
let arguments = EditThemeControllerArguments(context: context, updateState: { f in
|
||||||
updateState(f)
|
updateState(f)
|
||||||
}, openColors: {
|
}, openColors: {
|
||||||
let _ = (previewThemePromise.get()
|
let _ = (combineLatest(queue: Queue.mainQueue(), previewThemePromise.get(), settingsPromise.get())
|
||||||
|> take(1)
|
|> take(1)).start(next: { theme, previousSettings in
|
||||||
|> deliverOnMainQueue).start(next: { theme in
|
|
||||||
var controllerDismissImpl: (() -> Void)?
|
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
|
updateState { current in
|
||||||
var state = current
|
var state = current
|
||||||
previewThemePromise.set(.single(updatedTheme))
|
previewThemePromise.set(.single(updatedTheme))
|
||||||
state.updatedTheme = updatedTheme
|
state.updatedTheme = updatedTheme
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
if previousSettings != nil {
|
||||||
|
settingsPromise.set(.single(settings))
|
||||||
|
}
|
||||||
controllerDismissImpl?()
|
controllerDismissImpl?()
|
||||||
}))
|
}))
|
||||||
controllerDismissImpl = { [weak controller] in
|
controllerDismissImpl = { [weak controller] in
|
||||||
@ -371,6 +377,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
|||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
settingsPromise.set(.single(nil))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.EditTheme_FileReadError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
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
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = (previewThemePromise.get()
|
let _ = (combineLatest(queue: Queue.mainQueue(), previewThemePromise.get(), settingsPromise.get())
|
||||||
|> deliverOnMainQueue).start(next: { previewTheme in
|
|> take(1)).start(next: { previewTheme, settings in
|
||||||
let saveThemeTemplateFile: (String, LocalFileMediaResource, @escaping () -> Void) -> Void = { title, resource, completion 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 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)
|
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 {
|
switch mode {
|
||||||
case .create:
|
case .create:
|
||||||
if let themeResource = themeResource {
|
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
|
|> deliverOnMainQueue).start(next: { next in
|
||||||
if case let .result(resultTheme) = next {
|
if case let .result(resultTheme) = next {
|
||||||
let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: resultTheme).start()
|
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):
|
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
|
|> deliverOnMainQueue).start(next: { next in
|
||||||
if case let .result(resultTheme) = next {
|
if case let .result(resultTheme) = next {
|
||||||
let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: resultTheme).start()
|
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 {
|
enum ThemeAccentColorControllerMode {
|
||||||
case colors(themeReference: PresentationThemeReference, create: Bool)
|
case colors(themeReference: PresentationThemeReference, create: Bool)
|
||||||
case background(themeReference: PresentationThemeReference)
|
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? {
|
var themeReference: PresentationThemeReference? {
|
||||||
switch self {
|
switch self {
|
||||||
@ -181,13 +181,25 @@ final class ThemeAccentColorController: ViewController {
|
|||||||
let _ = (prepare
|
let _ = (prepare
|
||||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||||
let updatedTheme: PresentationTheme
|
let updatedTheme: PresentationTheme
|
||||||
|
|
||||||
|
var settings: TelegramThemeSettings?
|
||||||
|
|
||||||
if let themeReference = themeReference {
|
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
|
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 {
|
} else {
|
||||||
updatedTheme = customizePresentationTheme(theme, editing: false, accentColor: state.accentColor, backgroundColors: state.backgroundColors, bubbleColors: state.messagesColors, wallpaper: state.initialWallpaper ?? coloredWallpaper)
|
updatedTheme = customizePresentationTheme(theme, editing: false, accentColor: state.accentColor, backgroundColors: state.backgroundColors, bubbleColors: state.messagesColors, wallpaper: state.initialWallpaper ?? coloredWallpaper)
|
||||||
}
|
}
|
||||||
|
|
||||||
completion(updatedTheme)
|
completion(updatedTheme, settings)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
let _ = (prepare
|
let _ = (prepare
|
||||||
|
@ -15,6 +15,7 @@ import WallpaperResources
|
|||||||
import OverlayStatusController
|
import OverlayStatusController
|
||||||
import AppBundle
|
import AppBundle
|
||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
|
import UndoUI
|
||||||
|
|
||||||
public enum ThemePreviewSource {
|
public enum ThemePreviewSource {
|
||||||
case settings(PresentationThemeReference, TelegramWallpaper?)
|
case settings(PresentationThemeReference, TelegramWallpaper?)
|
||||||
@ -279,7 +280,7 @@ public final class ThemePreviewController: ViewController {
|
|||||||
|> mapToSignal { themes -> Signal<PresentationThemeReference, NoError> in
|
|> mapToSignal { themes -> Signal<PresentationThemeReference, NoError> in
|
||||||
let similarTheme = themes.first(where: { $0.isCreator && $0.title == info.title })
|
let similarTheme = themes.first(where: { $0.isCreator && $0.title == info.title })
|
||||||
if let similarTheme = similarTheme {
|
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)
|
|> map(Optional.init)
|
||||||
|> `catch` { _ -> Signal<CreateThemeResult?, NoError> in
|
|> `catch` { _ -> Signal<CreateThemeResult?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
@ -298,7 +299,7 @@ public final class ThemePreviewController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} 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)
|
|> map(Optional.init)
|
||||||
|> `catch` { _ -> Signal<CreateThemeResult?, NoError> in
|
|> `catch` { _ -> Signal<CreateThemeResult?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
@ -322,31 +323,52 @@ public final class ThemePreviewController: ViewController {
|
|||||||
return .single(theme)
|
return .single(theme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> mapToSignal { updatedTheme -> Signal<Void, NoError> in
|
|> mapToSignal { updatedTheme -> Signal<(PresentationThemeReference, Bool)?, NoError> in
|
||||||
if case let .cloud(info) = updatedTheme {
|
if case let .cloud(info) = updatedTheme {
|
||||||
let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: info.theme).start()
|
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 _ = saveThemeInteractively(account: context.account, accountManager: context.sharedContext.accountManager, theme: info.theme).start()
|
||||||
}
|
}
|
||||||
let autoNightModeTriggered = context.sharedContext.currentPresentationData.with { $0 }.autoNightModeTriggered
|
let autoNightModeTriggered = context.sharedContext.currentPresentationData.with { $0 }.autoNightModeTriggered
|
||||||
return updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current -> PresentationThemeSettings in
|
var switchingFromDefaultTheme = false
|
||||||
var updated: PresentationThemeSettings
|
|
||||||
if autoNightModeTriggered {
|
return context.sharedContext.accountManager.transaction { transaction -> (PresentationThemeReference, Bool)? in
|
||||||
var automaticThemeSwitchSetting = current.automaticThemeSwitchSetting
|
var previousDefaultTheme: (PresentationThemeReference, Bool)?
|
||||||
automaticThemeSwitchSetting.theme = updatedTheme
|
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in
|
||||||
updated = current.withUpdatedAutomaticThemeSwitchSetting(automaticThemeSwitchSetting)
|
let currentSettings: PresentationThemeSettings
|
||||||
} else {
|
if let entry = entry as? PresentationThemeSettings {
|
||||||
updated = current.withUpdatedTheme(updatedTheme)
|
currentSettings = entry
|
||||||
}
|
} else {
|
||||||
|
currentSettings = PresentationThemeSettings.defaultSettings
|
||||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
}
|
||||||
themeSpecificChatWallpapers[updatedTheme.index] = nil
|
|
||||||
return updated.withUpdatedThemeSpecificChatWallpapers(themeSpecificChatWallpapers)
|
var updatedSettings: PresentationThemeSettings
|
||||||
})
|
if autoNightModeTriggered {
|
||||||
|
if case .builtin = currentSettings.automaticThemeSwitchSetting.theme {
|
||||||
|
previousDefaultTheme = (currentSettings.automaticThemeSwitchSetting.theme, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
var automaticThemeSwitchSetting = currentSettings.automaticThemeSwitchSetting
|
||||||
|
automaticThemeSwitchSetting.theme = updatedTheme
|
||||||
|
updatedSettings = currentSettings.withUpdatedAutomaticThemeSwitchSetting(automaticThemeSwitchSetting)
|
||||||
|
} else {
|
||||||
|
if case .builtin = currentSettings.theme {
|
||||||
|
previousDefaultTheme = (currentSettings.theme, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedSettings = currentSettings.withUpdatedTheme(updatedTheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
var themeSpecificChatWallpapers = updatedSettings.themeSpecificChatWallpapers
|
||||||
|
themeSpecificChatWallpapers[updatedTheme.index] = nil
|
||||||
|
return updatedSettings.withUpdatedThemeSpecificChatWallpapers(themeSpecificChatWallpapers)
|
||||||
|
})
|
||||||
|
return previousDefaultTheme
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var cancelImpl: (() -> Void)?
|
var cancelImpl: (() -> Void)?
|
||||||
let progress = Signal<Never, NoError> { [weak self] subscriber in
|
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?()
|
cancelImpl?()
|
||||||
}))
|
}))
|
||||||
self?.present(controller, in: .window(.root))
|
self?.present(controller, in: .window(.root))
|
||||||
@ -369,9 +391,34 @@ public final class ThemePreviewController: ViewController {
|
|||||||
progressDisposable.dispose()
|
progressDisposable.dispose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> deliverOnMainQueue).start(completed: {[weak self] in
|
|> deliverOnMainQueue).start(next: { [weak self] previousDefaultTheme in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
Queue.mainQueue().after(0.3) {
|
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()
|
strongSelf.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,8 +200,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
strongSelf.blurredNode.image = image
|
strongSelf.blurredNode.image = image
|
||||||
strongSelf.blurredNode.blurView.blurRadius = 45.0
|
strongSelf.blurredNode.blurView.blurRadius = 45.0
|
||||||
strongSelf.ready.set(.single(true))
|
|
||||||
}
|
}
|
||||||
|
self?.ready.set(.single(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.colorDisposable = (self.wallpaperPromise.get()
|
self.colorDisposable = (self.wallpaperPromise.get()
|
||||||
|
@ -14,17 +14,21 @@ import PresentationDataUtils
|
|||||||
|
|
||||||
private enum ThemeSettingsColorEntryId: Hashable {
|
private enum ThemeSettingsColorEntryId: Hashable {
|
||||||
case color(Int)
|
case color(Int)
|
||||||
|
case theme(Int64)
|
||||||
case picker
|
case picker
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ThemeSettingsColorEntry: Comparable, Identifiable {
|
private enum ThemeSettingsColorEntry: Comparable, Identifiable {
|
||||||
case color(Int, PresentationThemeReference, PresentationThemeAccentColor?, Bool)
|
case color(Int, PresentationThemeReference, PresentationThemeAccentColor?, Bool)
|
||||||
|
case theme(Int, PresentationThemeReference, PresentationThemeReference, Bool)
|
||||||
case picker
|
case picker
|
||||||
|
|
||||||
var stableId: ThemeSettingsColorEntryId {
|
var stableId: ThemeSettingsColorEntryId {
|
||||||
switch self {
|
switch self {
|
||||||
case let .color(index, _, _, _):
|
case let .color(index, _, _, _):
|
||||||
return .color(index)
|
return .color(index)
|
||||||
|
case let .theme(_, _, theme, _):
|
||||||
|
return .theme(theme.index)
|
||||||
case .picker:
|
case .picker:
|
||||||
return .picker
|
return .picker
|
||||||
}
|
}
|
||||||
@ -38,6 +42,12 @@ private enum ThemeSettingsColorEntry: Comparable, Identifiable {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
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:
|
case .picker:
|
||||||
if case .picker = rhs {
|
if case .picker = rhs {
|
||||||
return true
|
return true
|
||||||
@ -51,36 +61,109 @@ private enum ThemeSettingsColorEntry: Comparable, Identifiable {
|
|||||||
switch lhs {
|
switch lhs {
|
||||||
case .picker:
|
case .picker:
|
||||||
return true
|
return true
|
||||||
case let .color(lhsIndex, _, _, _):
|
case let .color(lhsIndex, _, _, _), let .theme(lhsIndex, _, _, _):
|
||||||
switch rhs {
|
switch rhs {
|
||||||
case let .color(rhsIndex, _, _, _):
|
case let .color(rhsIndex, _, _, _):
|
||||||
return lhsIndex < rhsIndex
|
return lhsIndex < rhsIndex
|
||||||
|
case let .theme(rhsIndex, _, _, _):
|
||||||
|
return lhsIndex < rhsIndex
|
||||||
case .picker:
|
case .picker:
|
||||||
return false
|
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 {
|
switch self {
|
||||||
case let .color(_, themeReference, accentColor, selected):
|
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:
|
case .picker:
|
||||||
return ThemeSettingsAccentColorPickerItem(action: openColorPicker)
|
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 {
|
private class ThemeSettingsAccentColorIconItem: ListViewItem {
|
||||||
let themeReference: PresentationThemeReference
|
let themeReference: PresentationThemeReference
|
||||||
let accentColor: PresentationThemeAccentColor?
|
let color: ThemeSettingsColorOption?
|
||||||
let selected: Bool
|
let selected: Bool
|
||||||
let action: (PresentationThemeAccentColor?, Bool) -> Void
|
let action: (ThemeSettingsColorOption?, Bool) -> Void
|
||||||
let contextAction: ((PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> 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.themeReference = themeReference
|
||||||
self.accentColor = accentColor
|
self.color = color
|
||||||
self.selected = selected
|
self.selected = selected
|
||||||
self.action = action
|
self.action = action
|
||||||
self.contextAction = contextAction
|
self.contextAction = contextAction
|
||||||
@ -128,7 +211,7 @@ private class ThemeSettingsAccentColorIconItem: ListViewItem {
|
|||||||
|
|
||||||
public var selectable = true
|
public var selectable = true
|
||||||
public func selected(listView: ListView) {
|
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()
|
gesture.cancel()
|
||||||
return
|
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 updatedAccentColor = false
|
||||||
var updatedSelected = false
|
var updatedSelected = false
|
||||||
|
|
||||||
if currentItem == nil || currentItem?.accentColor != item.accentColor {
|
if currentItem == nil || currentItem?.color != item.color {
|
||||||
updatedAccentColor = true
|
updatedAccentColor = true
|
||||||
}
|
}
|
||||||
if currentItem?.selected != item.selected {
|
if currentItem?.selected != item.selected {
|
||||||
@ -271,8 +354,8 @@ private final class ThemeSettingsAccentColorIconItemNode : ListViewItemNode {
|
|||||||
strongSelf.item = item
|
strongSelf.item = item
|
||||||
|
|
||||||
if updatedAccentColor {
|
if updatedAccentColor {
|
||||||
var fillColor = item.accentColor?.color
|
var fillColor = item.color?.accentColor
|
||||||
var strokeColor = item.accentColor?.baseColor.color
|
var strokeColor = item.color?.baseColor
|
||||||
if strokeColor == .clear {
|
if strokeColor == .clear {
|
||||||
strokeColor = fillColor
|
strokeColor = fillColor
|
||||||
}
|
}
|
||||||
@ -288,12 +371,12 @@ private final class ThemeSettingsAccentColorIconItemNode : ListViewItemNode {
|
|||||||
var topColor: UIColor?
|
var topColor: UIColor?
|
||||||
var bottomColor: UIColor?
|
var bottomColor: UIColor?
|
||||||
|
|
||||||
if let colors = item.accentColor?.plainBubbleColors {
|
if let colors = item.color?.plainBubbleColors {
|
||||||
topColor = colors.0
|
topColor = colors.0
|
||||||
bottomColor = colors.1
|
bottomColor = colors.1
|
||||||
} else if case .builtin(.dayClassic) = item.themeReference {
|
} else if case .builtin(.dayClassic) = item.themeReference {
|
||||||
if let accentColor = item.accentColor {
|
if let accentColor = item.color?.accentColor {
|
||||||
let hsb = accentColor.color.hsb
|
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)
|
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
|
topColor = bubbleColor
|
||||||
bottomColor = bubbleColor
|
bottomColor = bubbleColor
|
||||||
@ -490,15 +573,18 @@ enum ThemeSettingsAccentColor {
|
|||||||
case color(PresentationThemeBaseColor)
|
case color(PresentationThemeBaseColor)
|
||||||
case preset(PresentationThemeAccentColor)
|
case preset(PresentationThemeAccentColor)
|
||||||
case custom(PresentationThemeAccentColor)
|
case custom(PresentationThemeAccentColor)
|
||||||
|
case theme(PresentationThemeReference)
|
||||||
|
|
||||||
var index: Int32? {
|
var index: Int64? {
|
||||||
switch self {
|
switch self {
|
||||||
case .default:
|
case .default:
|
||||||
return nil
|
return nil
|
||||||
case let .color(color):
|
case let .color(color):
|
||||||
return 10 + color.rawValue
|
return Int64(10 + color.rawValue)
|
||||||
case let .preset(color), let .custom(color):
|
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 theme: PresentationTheme
|
||||||
let themeReference: PresentationThemeReference
|
let themeReference: PresentationThemeReference
|
||||||
let colors: [ThemeSettingsAccentColor]
|
let colors: [ThemeSettingsAccentColor]
|
||||||
let currentColor: PresentationThemeAccentColor?
|
let currentColor: ThemeSettingsColorOption?
|
||||||
let updated: (PresentationThemeAccentColor?) -> Void
|
let updated: (ThemeSettingsColorOption?) -> Void
|
||||||
let contextAction: ((PresentationThemeReference, PresentationThemeAccentColor?, ASDisplayNode, ContextGesture?) -> Void)?
|
let contextAction: ((PresentationThemeReference, ThemeSettingsColorOption?, ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
let openColorPicker: (Bool) -> Void
|
let openColorPicker: (Bool) -> Void
|
||||||
let tag: ItemListItemTag?
|
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.theme = theme
|
||||||
self.themeReference = themeReference
|
self.themeReference = themeReference
|
||||||
self.colors = colors
|
self.colors = colors
|
||||||
@ -568,7 +654,7 @@ private struct ThemeSettingsAccentColorItemNodeTransition {
|
|||||||
let crossfade: Bool
|
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 (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||||
|
|
||||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
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)
|
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?
|
var resultNode: ThemeSettingsAccentColorIconItemNode?
|
||||||
listNode.forEachItemNode { node in
|
listNode.forEachItemNode { node in
|
||||||
if resultNode == nil, let node = node as? ThemeSettingsAccentColorIconItemNode {
|
if resultNode == nil, let node = node as? ThemeSettingsAccentColorIconItemNode {
|
||||||
if node.item?.accentColor?.index == accentColor?.index {
|
if node.item?.color?.index == accentColor?.index {
|
||||||
resultNode = node
|
resultNode = node
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -768,33 +854,61 @@ class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
let selected = item.currentColor == nil
|
let selected = item.currentColor == nil
|
||||||
entries.append(.color(index, item.themeReference, nil, selected))
|
entries.append(.color(index, item.themeReference, nil, selected))
|
||||||
case let .color(color):
|
case let .color(color):
|
||||||
let selected = item.currentColor?.baseColor == color
|
var selected = false
|
||||||
let accentColor: PresentationThemeAccentColor
|
if let currentColor = item.currentColor, case let .accentColor(accentColor) = currentColor {
|
||||||
|
selected = accentColor.baseColor == color
|
||||||
|
}
|
||||||
|
let accentColor: ThemeSettingsColorOption
|
||||||
if let currentColor = item.currentColor, selected {
|
if let currentColor = item.currentColor, selected {
|
||||||
accentColor = currentColor
|
accentColor = currentColor
|
||||||
} else {
|
} 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):
|
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))
|
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
|
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 let strongSelf = self, let item = strongSelf.item {
|
||||||
if selected {
|
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 {
|
} else {
|
||||||
item.updated(color)
|
item.updated(color)
|
||||||
}
|
}
|
||||||
ensureColorVisible(listNode: strongSelf.listNode, accentColor: color, animated: true)
|
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 {
|
if let strongSelf = self, let item = strongSelf.item {
|
||||||
item.contextAction?(item.themeReference, color, node, gesture)
|
item.contextAction?(item.themeReference, color, node, gesture)
|
||||||
}
|
}
|
||||||
|
@ -81,9 +81,9 @@ private final class ThemeSettingsControllerArguments {
|
|||||||
let selectAppIcon: (String) -> Void
|
let selectAppIcon: (String) -> Void
|
||||||
let editTheme: (PresentationCloudTheme) -> Void
|
let editTheme: (PresentationCloudTheme) -> Void
|
||||||
let themeContextAction: (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> 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.context = context
|
||||||
self.selectTheme = selectTheme
|
self.selectTheme = selectTheme
|
||||||
self.selectFontSize = selectFontSize
|
self.selectFontSize = selectFontSize
|
||||||
@ -133,7 +133,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
|||||||
case fontSize(PresentationTheme, PresentationFontSize)
|
case fontSize(PresentationTheme, PresentationFontSize)
|
||||||
case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, [ChatPreviewMessageItem])
|
case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, [ChatPreviewMessageItem])
|
||||||
case wallpaper(PresentationTheme, String)
|
case wallpaper(PresentationTheme, String)
|
||||||
case accentColor(PresentationTheme, PresentationThemeReference, PresentationThemeCustomColors?, PresentationThemeAccentColor?)
|
case accentColor(PresentationTheme, PresentationThemeReference, PresentationThemeCustomColors?, [PresentationThemeReference], ThemeSettingsColorOption?)
|
||||||
case autoNightTheme(PresentationTheme, String, String)
|
case autoNightTheme(PresentationTheme, String, String)
|
||||||
case textSize(PresentationTheme, String, String)
|
case textSize(PresentationTheme, String, String)
|
||||||
case themeItem(PresentationTheme, PresentationStrings, [PresentationThemeReference], PresentationThemeReference, [Int64: PresentationThemeAccentColor], [Int64: TelegramWallpaper], PresentationThemeAccentColor?)
|
case themeItem(PresentationTheme, PresentationStrings, [PresentationThemeReference], PresentationThemeReference, [Int64: PresentationThemeAccentColor], [Int64: TelegramWallpaper], PresentationThemeAccentColor?)
|
||||||
@ -208,8 +208,8 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .accentColor(lhsTheme, lhsCurrentTheme, lhsCustomColors, lhsColor):
|
case let .accentColor(lhsTheme, lhsCurrentTheme, lhsCustomColors, lhsThemes, lhsColor):
|
||||||
if case let .accentColor(rhsTheme, rhsCurrentTheme, rhsCustomColors, rhsColor) = rhs, lhsTheme === rhsTheme, lhsCurrentTheme == rhsCurrentTheme, lhsCustomColors == rhsCustomColors, lhsColor == rhsColor {
|
if case let .accentColor(rhsTheme, rhsCurrentTheme, rhsCustomColors, rhsThemes, rhsColor) = rhs, lhsTheme === rhsTheme, lhsCurrentTheme == rhsCurrentTheme, lhsCustomColors == rhsCustomColors, lhsThemes == rhsThemes, lhsColor == rhsColor {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -308,18 +308,30 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
|||||||
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.openWallpaperSettings()
|
arguments.openWallpaperSettings()
|
||||||
})
|
})
|
||||||
case let .accentColor(theme, currentTheme, customColors, color):
|
case let .accentColor(theme, currentTheme, customColors, themes, color):
|
||||||
var colorItems: [ThemeSettingsAccentColor] = []
|
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 defaultColor: PresentationThemeAccentColor? = PresentationThemeAccentColor(baseColor: .blue)
|
||||||
var colors = PresentationThemeBaseColor.allCases
|
var colors = PresentationThemeBaseColor.allCases
|
||||||
colors = colors.filter { $0 != .custom && $0 != .preset }
|
colors = colors.filter { $0 != .custom && $0 != .preset }
|
||||||
if case let .builtin(name) = currentTheme {
|
if case let .builtin(name) = generalThemeReference {
|
||||||
if name == .dayClassic {
|
if name == .dayClassic {
|
||||||
colorItems.append(.default)
|
colorItems.append(.default)
|
||||||
defaultColor = nil
|
defaultColor = nil
|
||||||
|
|
||||||
let patternWallpaper: (String, Int32, Int32?, Int32?, Int32?) -> TelegramWallpaper = { slug, topColor, bottomColor, intensity, rotation in
|
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))))
|
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 }
|
colors = colors.filter { $0 != .white }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let currentColor = color ?? defaultColor
|
let currentColor = color ?? defaultColor.flatMap { .accentColor($0) }
|
||||||
colorItems.append(contentsOf: colors.map { .color($0) })
|
colorItems.append(contentsOf: colors.map { .color($0) })
|
||||||
|
|
||||||
if let customColors = customColors {
|
if let customColors = customColors {
|
||||||
colorItems.insert(contentsOf: customColors.colors.reversed().map { .custom($0) }, at: 0)
|
colorItems.insert(contentsOf: customColors.colors.reversed().map { .custom($0) }, at: 0)
|
||||||
} else {
|
} else {
|
||||||
if let currentColor = currentColor, currentColor.baseColor == .custom {
|
// if let currentColor = currentColor, currentColor.baseColor == .custom {
|
||||||
colorItems.insert(.custom(currentColor), at: 0)
|
// colorItems.insert(.custom(currentColor), at: 0)
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
return ThemeSettingsAccentColorItem(theme: theme, sectionId: self.section, themeReference: currentTheme, colors: colorItems, currentColor: currentColor, updated: { color in
|
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
|
}, contextAction: { theme, color, node, gesture in
|
||||||
arguments.colorContextAction(theme, color, node, gesture)
|
arguments.colorContextAction(theme, color, node, gesture)
|
||||||
}, openColorPicker: { create in
|
}, openColorPicker: { create in
|
||||||
arguments.openAccentColorPicker(currentTheme, create)
|
arguments.openAccentColorPicker(generalThemeReference, create)
|
||||||
}, tag: ThemeSettingsEntryTag.accentColor)
|
}, tag: ThemeSettingsEntryTag.accentColor)
|
||||||
case let .autoNightTheme(theme, text, value):
|
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: {
|
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()
|
let title = presentationData.autoNightModeTriggered ? strings.Appearance_ColorThemeNight.uppercased() : strings.Appearance_ColorTheme.uppercased()
|
||||||
entries.append(.themeListHeader(presentationData.theme, title))
|
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(.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 {
|
let generalThemes: [PresentationThemeReference] = availableThemes.filter { reference in
|
||||||
entries.append(.accentColor(presentationData.theme, themeReference, presentationThemeSettings.themeSpecificCustomColors[themeReference.index], presentationThemeSettings.themeSpecificAccentColors[themeReference.index]))
|
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))
|
entries.append(.wallpaper(presentationData.theme, strings.Settings_ChatBackground))
|
||||||
@ -555,16 +607,23 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||||||
if autoNightModeTriggered {
|
if autoNightModeTriggered {
|
||||||
currentTheme = current.automaticThemeSwitchSetting.theme
|
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
|
return current
|
||||||
}
|
}
|
||||||
|
|
||||||
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||||
var themeSpecificAccentColors = current.themeSpecificAccentColors
|
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 {
|
if let wallpaper = presetWallpaper, let color = accentColor {
|
||||||
themeSpecificChatWallpapers[coloredThemeIndex(reference: currentTheme, accentColor: color)] = wallpaper
|
themeSpecificChatWallpapers[coloredThemeIndex(reference: currentTheme, accentColor: color)] = wallpaper
|
||||||
} else if let wallpaper = current.themeSpecificChatWallpapers[currentTheme.index], wallpaper.isColorOrGradient || wallpaper.isPattern || wallpaper.isBuiltin {
|
} 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
|
let _ = (resolvedWallpaper
|
||||||
|> deliverOnMainQueue).start(next: { wallpaper in
|
|> 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 = 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), navigateToChat: { peerId in
|
let controller = editThemeController(context: context, mode: .create(result, nil), navigateToChat: { peerId in
|
||||||
if let navigationController = getNavigationControllerImpl?() {
|
if let navigationController = getNavigationControllerImpl?() {
|
||||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId)))
|
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)
|
presentInGlobalOverlayImpl?(contextController, nil)
|
||||||
})
|
})
|
||||||
}, colorContextAction: { reference, accentColor, node, gesture in
|
}, 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
|
let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings) as? PresentationThemeSettings ?? PresentationThemeSettings.defaultSettings
|
||||||
var wallpaper: TelegramWallpaper?
|
var wallpaper: TelegramWallpaper?
|
||||||
if let accentColor = accentColor {
|
if let accentColor = accentColor {
|
||||||
wallpaper = settings.themeSpecificChatWallpapers[coloredThemeIndex(reference: reference, accentColor: accentColor)]
|
switch accentColor {
|
||||||
}
|
case let .accentColor(accentColor):
|
||||||
if wallpaper == nil {
|
wallpaper = settings.themeSpecificChatWallpapers[coloredThemeIndex(reference: reference, accentColor: accentColor)]
|
||||||
wallpaper = settings.themeSpecificChatWallpapers[reference.index]
|
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)
|
return (accentColor, wallpaper)
|
||||||
} |> mapToSignal { accentColor, wallpaper -> Signal<(PresentationTheme?, TelegramWallpaper?), NoError> in
|
} |> mapToSignal { accentColor, wallpaper -> Signal<(PresentationTheme?, TelegramWallpaper?), NoError> in
|
||||||
@ -785,12 +853,21 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||||||
if let wallpaper = wallpaper {
|
if let wallpaper = wallpaper {
|
||||||
effectiveWallpaper = wallpaper
|
effectiveWallpaper = wallpaper
|
||||||
} else {
|
} 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())
|
effectiveWallpaper = theme?.chat.defaultWallpaper ?? .builtin(WallpaperSettings())
|
||||||
}
|
}
|
||||||
return chatServiceBackgroundColor(wallpaper: effectiveWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox)
|
return chatServiceBackgroundColor(wallpaper: effectiveWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox)
|
||||||
|> map { serviceBackgroundColor in
|
|> 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
|
|> 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))
|
let themeController = ThemePreviewController(context: context, previewTheme: theme, source: .settings(reference, wallpaper))
|
||||||
var items: [ContextMenuItem] = []
|
var items: [ContextMenuItem] = []
|
||||||
|
|
||||||
if let accentColor = accentColor, accentColor.baseColor == .custom {
|
if let accentColor = accentColor {
|
||||||
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)
|
if case let .accentColor(color) = accentColor, color.baseColor != .custom {
|
||||||
}, action: { c, f in
|
} else {
|
||||||
c.dismiss(completion: {
|
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)
|
||||||
let actionSheet = ActionSheetController(presentationData: presentationData)
|
}, action: { c, f in
|
||||||
var items: [ActionSheetItem] = []
|
c.dismiss(completion: {
|
||||||
items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_RemoveThemeColorConfirmation, color: .destructive, action: { [weak actionSheet] in
|
|
||||||
actionSheet?.dismissAnimated()
|
|
||||||
|
|
||||||
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
})
|
||||||
let themeReference: PresentationThemeReference
|
})))
|
||||||
if presentationData.autoNightModeTriggered {
|
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)
|
||||||
themeReference = current.automaticThemeSwitchSetting.theme
|
}, action: { c, f in
|
||||||
} else {
|
c.dismiss(completion: {
|
||||||
themeReference = current.theme
|
let actionSheet = ActionSheetController(presentationData: presentationData)
|
||||||
}
|
var items: [ActionSheetItem] = []
|
||||||
|
items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_RemoveThemeColorConfirmation, color: .destructive, action: { [weak actionSheet] in
|
||||||
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()
|
actionSheet?.dismissAnimated()
|
||||||
})
|
|
||||||
])])
|
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||||
presentControllerImpl?(actionSheet, nil)
|
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)
|
let contextController = ContextController(account: context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture)
|
||||||
presentInGlobalOverlayImpl?(contextController, nil)
|
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 {
|
if let controller = controller, controller.isNodeLoaded, let navigationController = controller.navigationController as? NavigationController, navigationController.topViewController === controller {
|
||||||
var topOffset: CGFloat?
|
var topOffset: CGFloat?
|
||||||
var bottomOffset: CGFloat?
|
var bottomOffset: CGFloat?
|
||||||
|
var leftOffset: CGFloat?
|
||||||
var themeItemNode: ThemeSettingsThemeItemNode?
|
var themeItemNode: ThemeSettingsThemeItemNode?
|
||||||
var colorItemNode: ThemeSettingsAccentColorItemNode?
|
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
|
controller.forEachItemNode { node in
|
||||||
if let itemNode = node as? ItemListItemNode {
|
if let itemNode = node as? ItemListItemNode {
|
||||||
if let itemTag = itemNode.tag {
|
if let itemTag = itemNode.tag {
|
||||||
if itemTag.isEqual(to: ThemeSettingsEntryTag.theme) {
|
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
|
topOffset = frame.minY
|
||||||
bottomOffset = frame.maxY
|
bottomOffset = frame.maxY
|
||||||
if let itemNode = node as? ThemeSettingsThemeItemNode {
|
if let itemNode = node as? ThemeSettingsThemeItemNode {
|
||||||
themeItemNode = itemNode
|
themeItemNode = itemNode
|
||||||
}
|
}
|
||||||
} else if itemTag.isEqual(to: ThemeSettingsEntryTag.accentColor) && hasAccentColors {
|
} 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
|
bottomOffset = frame.maxY
|
||||||
if let itemNode = node as? ThemeSettingsAccentColorItemNode {
|
if let itemNode = node as? ThemeSettingsAccentColorItemNode {
|
||||||
colorItemNode = itemNode
|
colorItemNode = itemNode
|
||||||
@ -965,13 +1062,17 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
themeItemNode?.prepareCrossfadeTransition()
|
if view != nil {
|
||||||
colorItemNode?.prepareCrossfadeTransition()
|
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
|
crossfadeController.didAppear = { [weak themeItemNode, weak colorItemNode] in
|
||||||
themeItemNode?.animateCrossfadeTransition()
|
if view != nil {
|
||||||
colorItemNode?.animateCrossfadeTransition()
|
themeItemNode?.animateCrossfadeTransition()
|
||||||
|
colorItemNode?.animateCrossfadeTransition()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.sharedContext.presentGlobalController(crossfadeController, nil)
|
context.sharedContext.presentGlobalController(crossfadeController, nil)
|
||||||
@ -1044,8 +1145,8 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||||||
return themeReference
|
return themeReference
|
||||||
}
|
}
|
||||||
|> deliverOnMainQueue).start(next: { themeReference in
|
|> 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 = 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), navigateToChat: { peerId in
|
let controller = editThemeController(context: context, mode: .create(result, settings), navigateToChat: { peerId in
|
||||||
if let navigationController = getNavigationControllerImpl?() {
|
if let navigationController = getNavigationControllerImpl?() {
|
||||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId)))
|
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId)))
|
||||||
}
|
}
|
||||||
@ -1063,7 +1164,6 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
pushControllerImpl?(controller)
|
pushControllerImpl?(controller)
|
||||||
|
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
||||||
@ -1081,11 +1181,41 @@ public final class ThemeSettingsCrossfadeController: ViewController {
|
|||||||
|
|
||||||
private var topSnapshotView: UIView?
|
private var topSnapshotView: UIView?
|
||||||
private var bottomSnapshotView: UIView?
|
private var bottomSnapshotView: UIView?
|
||||||
|
private var sideSnapshotView: UIView?
|
||||||
|
|
||||||
fileprivate var didAppear: (() -> Void)?
|
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 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) {
|
if let view = view.snapshotView(afterScreenUpdates: false) {
|
||||||
view.clipsToBounds = true
|
view.clipsToBounds = true
|
||||||
view.contentMode = .top
|
view.contentMode = .top
|
||||||
@ -1135,6 +1265,9 @@ public final class ThemeSettingsCrossfadeController: ViewController {
|
|||||||
if let bottomSnapshotView = self.bottomSnapshotView {
|
if let bottomSnapshotView = self.bottomSnapshotView {
|
||||||
self.displayNode.view.addSubview(bottomSnapshotView)
|
self.displayNode.view.addSubview(bottomSnapshotView)
|
||||||
}
|
}
|
||||||
|
if let sideSnapshotView = self.sideSnapshotView {
|
||||||
|
self.displayNode.view.addSubview(sideSnapshotView)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func viewDidAppear(_ animated: Bool) {
|
override public func viewDidAppear(_ animated: Bool) {
|
||||||
|
@ -24,8 +24,8 @@ public enum WallpaperListType {
|
|||||||
|
|
||||||
public enum WallpaperListSource {
|
public enum WallpaperListSource {
|
||||||
case list(wallpapers: [TelegramWallpaper], central: TelegramWallpaper, type: WallpaperListType)
|
case list(wallpapers: [TelegramWallpaper], central: TelegramWallpaper, type: WallpaperListType)
|
||||||
case wallpaper(TelegramWallpaper, WallpaperPresentationOptions?, UIColor?, Int32?, Message?)
|
case wallpaper(TelegramWallpaper, WallpaperPresentationOptions?, UIColor?, UIColor?, Int32?, Int32?, Message?)
|
||||||
case slug(String, TelegramMediaFile?, WallpaperPresentationOptions?, UIColor?, Int32?, Message?)
|
case slug(String, TelegramMediaFile?, WallpaperPresentationOptions?, UIColor?, UIColor?, Int32?, Int32?, Message?)
|
||||||
case asset(PHAsset)
|
case asset(PHAsset)
|
||||||
case contextResult(ChatContextResult)
|
case contextResult(ChatContextResult)
|
||||||
case customColor(Int32?)
|
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 {
|
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 {
|
} else {
|
||||||
return wallpaper
|
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"
|
let isPattern = file.mimeType == "image/png"
|
||||||
var colorValue: Int32?
|
var firstColorValue: Int32?
|
||||||
|
var secondColorValue: Int32?
|
||||||
var intensityValue: Int32?
|
var intensityValue: Int32?
|
||||||
if let color = color {
|
if let firstColor = firstColor {
|
||||||
colorValue = Int32(bitPattern: color.rgb)
|
firstColorValue = Int32(bitPattern: firstColor.rgb)
|
||||||
intensityValue = intensity
|
intensityValue = intensity
|
||||||
} else if isPattern {
|
} else if isPattern {
|
||||||
colorValue = 0xd6e2ee
|
firstColorValue = 0xd6e2ee
|
||||||
intensityValue = 50
|
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 {
|
public class WallpaperGalleryController: ViewController {
|
||||||
@ -183,15 +187,15 @@ public class WallpaperGalleryController: ViewController {
|
|||||||
if case let .wallpapers(wallpaperOptions) = type, let options = wallpaperOptions {
|
if case let .wallpapers(wallpaperOptions) = type, let options = wallpaperOptions {
|
||||||
self.initialOptions = options
|
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 {
|
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)]
|
entries = [.wallpaper(wallpaper, message)]
|
||||||
centralEntryIndex = 0
|
centralEntryIndex = 0
|
||||||
self.initialOptions = options
|
self.initialOptions = options
|
||||||
}
|
}
|
||||||
case let .wallpaper(wallpaper, options, color, intensity, message):
|
case let .wallpaper(wallpaper, options, firstColor, secondColor, intensity, rotation, message):
|
||||||
let wallpaper = updatedFileWallpaper(wallpaper: wallpaper, color: color, intensity: intensity)
|
let wallpaper = updatedFileWallpaper(wallpaper: wallpaper, firstColor: firstColor, secondColor: secondColor, intensity: intensity, rotation: rotation)
|
||||||
entries = [.wallpaper(wallpaper, message)]
|
entries = [.wallpaper(wallpaper, message)]
|
||||||
centralEntryIndex = 0
|
centralEntryIndex = 0
|
||||||
self.initialOptions = options
|
self.initialOptions = options
|
||||||
@ -776,11 +780,18 @@ public class WallpaperGalleryController: ViewController {
|
|||||||
case let .file(_, _, _, _, isPattern, _, slug, _, settings):
|
case let .file(_, _, _, _, isPattern, _, slug, _, settings):
|
||||||
if isPattern {
|
if isPattern {
|
||||||
if let color = settings.color {
|
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 {
|
if let intensity = settings.intensity {
|
||||||
options.append("intensity=\(intensity)")
|
options.append("intensity=\(intensity)")
|
||||||
}
|
}
|
||||||
|
if let rotation = settings.rotation {
|
||||||
|
options.append("rotation=\(rotation)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var optionsString = ""
|
var optionsString = ""
|
||||||
|
@ -233,10 +233,10 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
|
|||||||
actionSignal = .single(defaultAction)
|
actionSignal = .single(defaultAction)
|
||||||
colorSignal = chatServiceBackgroundColor(wallpaper: wallpaper, mediaBox: self.context.account.postbox.mediaBox)
|
colorSignal = chatServiceBackgroundColor(wallpaper: wallpaper, mediaBox: self.context.account.postbox.mediaBox)
|
||||||
isBlurrable = false
|
isBlurrable = false
|
||||||
case let .gradient(topColor, bottomColor, _):
|
case let .gradient(topColor, bottomColor, settings):
|
||||||
displaySize = CGSize(width: 1.0, height: 1.0)
|
displaySize = CGSize(width: 1.0, height: 1.0)
|
||||||
contentSize = displaySize
|
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()
|
fetchSignal = .complete()
|
||||||
statusSignal = .single(.Local)
|
statusSignal = .single(.Local)
|
||||||
subtitleSignal = .single(nil)
|
subtitleSignal = .single(nil)
|
||||||
|
@ -150,7 +150,7 @@ final class WallpaperPatternPanelNode: ASDisplayNode {
|
|||||||
|
|
||||||
var updatedWallpaper = wallpaper
|
var updatedWallpaper = wallpaper
|
||||||
if case let .file(file) = updatedWallpaper {
|
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)
|
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
|
}, displaySwipeToReplyHint: { [weak self] in
|
||||||
if let strongSelf = self, let validLayout = strongSelf.validLayout, min(validLayout.size.width, validLayout.size.height) > 320.0 {
|
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
|
}, dismissReplyMarkupMessage: { [weak self] message in
|
||||||
guard let strongSelf = self, strongSelf.presentationInterfaceState.keyboardButtonsMessage?.id == message.id else {
|
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
|
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
|
strongSelf.present(UndoOverlayController(presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, content: .removedChat(text: statusText), elevatedLayout: true, action: { value in
|
||||||
if shouldCommit {
|
if value == .commit {
|
||||||
let _ = clearHistoryInteractively(postbox: account.postbox, peerId: peerId, type: type).start(completed: {
|
let _ = clearHistoryInteractively(postbox: account.postbox, peerId: peerId, type: type).start(completed: {
|
||||||
self?.chatDisplayNode.historyNode.historyAppearsCleared = false
|
self?.chatDisplayNode.historyNode.historyAppearsCleared = false
|
||||||
})
|
})
|
||||||
} else {
|
return true
|
||||||
|
} else if value == .undo {
|
||||||
self?.chatDisplayNode.historyNode.historyAppearsCleared = false
|
self?.chatDisplayNode.historyNode.historyAppearsCleared = false
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}), in: .current)
|
}), in: .current)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5349,7 +5352,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
disposable.set((signal
|
disposable.set((signal
|
||||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||||
if let strongSelf = self, let layout = strongSelf.validLayout {
|
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])
|
mediaAndFlags = (webpage.image ?? file, [.preferMediaBeforeText])
|
||||||
}
|
}
|
||||||
} else if webpage.type == "telegram_background" {
|
} else if webpage.type == "telegram_background" {
|
||||||
var patternColor: UIColor?
|
var topColor: UIColor?
|
||||||
if let wallpaper = parseWallpaperUrl(webpage.url), case let .slug(_, _, color, intensity) = wallpaper {
|
var bottomColor: UIColor?
|
||||||
patternColor = color?.withAlphaComponent(CGFloat(intensity ?? 50) / 100.0)
|
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])
|
mediaAndFlags = (media, [.preferMediaAspectFilled])
|
||||||
if let fileSize = file.size {
|
if let fileSize = file.size {
|
||||||
badge = dataSizeString(fileSize, decimalSeparator: item.presentationData.dateTimeFormat.decimalSeparator)
|
badge = dataSizeString(fileSize, decimalSeparator: item.presentationData.dateTimeFormat.decimalSeparator)
|
||||||
@ -279,23 +283,31 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
} else if let type = webpage.type {
|
} else if let type = webpage.type {
|
||||||
if type == "telegram_background" {
|
if type == "telegram_background" {
|
||||||
if let text = webpage.text {
|
var topColor: UIColor?
|
||||||
let colorCodeRange = text.range(of: "#")
|
var bottomColor: UIColor?
|
||||||
if colorCodeRange != nil {
|
var rotation: Int32?
|
||||||
let components = text.replacingOccurrences(of: "#", with: "").components(separatedBy: "-")
|
if let wallpaper = parseWallpaperUrl(webpage.url) {
|
||||||
if components.count == 2, let topColorCode = components.first, let bottomColorCode = components.last {
|
if case let .color(color) = wallpaper {
|
||||||
if let topColor = UIColor(hexString: topColorCode), let bottomColor = UIColor(hexString: bottomColorCode) {
|
topColor = color
|
||||||
let media = WallpaperPreviewMedia(content: .gradient(topColor, bottomColor, 0))
|
} else if case let .gradient(topColorValue, bottomColorValue, rotationValue) = wallpaper {
|
||||||
mediaAndFlags = (media, ChatMessageAttachedContentNodeMediaFlags())
|
topColor = topColorValue
|
||||||
}
|
bottomColor = bottomColorValue
|
||||||
} else if components.count == 1, let colorCode = components.first {
|
rotation = rotationValue
|
||||||
if let color = UIColor(hexString: colorCode) {
|
|
||||||
let media = WallpaperPreviewMedia(content: .color(color))
|
|
||||||
mediaAndFlags = (media, ChatMessageAttachedContentNodeMediaFlags())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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" {
|
} else if type == "telegram_theme" {
|
||||||
var file: TelegramMediaFile?
|
var file: TelegramMediaFile?
|
||||||
var isSupported = false
|
var isSupported = false
|
||||||
|
@ -486,12 +486,12 @@ func openChatWallpaper(context: AccountContext, message: Message, present: @esca
|
|||||||
if case let .wallpaper(parameter) = resolvedUrl {
|
if case let .wallpaper(parameter) = resolvedUrl {
|
||||||
let source: WallpaperListSource
|
let source: WallpaperListSource
|
||||||
switch parameter {
|
switch parameter {
|
||||||
case let .slug(slug, options, color, intensity):
|
case let .slug(slug, options, firstColor, secondColor, intensity, rotation):
|
||||||
source = .slug(slug, content.file, options, color, intensity, message)
|
source = .slug(slug, content.file, options, firstColor, secondColor, intensity, rotation, message)
|
||||||
case let .color(color):
|
case let .color(color):
|
||||||
source = .wallpaper(.color(Int32(color.rgb)), nil, nil, nil, message)
|
source = .wallpaper(.color(Int32(color.rgb)), nil, nil, nil, nil, nil, message)
|
||||||
case let .gradient(topColor, bottomColor):
|
case let .gradient(topColor, bottomColor, rotation):
|
||||||
source = .wallpaper(.gradient(Int32(topColor.rgb), Int32(bottomColor.rgb), WallpaperSettings()), nil, nil, nil, message)
|
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)
|
let controller = WallpaperGalleryController(context: context, source: source)
|
||||||
|
@ -249,26 +249,30 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
|||||||
|
|
||||||
let signal: Signal<TelegramWallpaper, GetWallpaperError>
|
let signal: Signal<TelegramWallpaper, GetWallpaperError>
|
||||||
var options: WallpaperPresentationOptions?
|
var options: WallpaperPresentationOptions?
|
||||||
var color: UIColor?
|
var topColor: UIColor?
|
||||||
|
var bottomColor: UIColor?
|
||||||
var intensity: Int32?
|
var intensity: Int32?
|
||||||
|
var rotation: Int32?
|
||||||
switch parameter {
|
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)
|
signal = getWallpaper(account: context.account, slug: slug)
|
||||||
options = wallpaperOptions
|
options = wallpaperOptions
|
||||||
color = patternColor
|
topColor = firstColor
|
||||||
intensity = patternIntensity
|
bottomColor = secondColor
|
||||||
|
intensity = intensityValue
|
||||||
|
rotation = rotationValue
|
||||||
controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
|
controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
|
||||||
present(controller!, nil)
|
present(controller!, nil)
|
||||||
case let .color(color):
|
case let .color(color):
|
||||||
signal = .single(.color(Int32(color.rgb)))
|
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()))
|
signal = .single(.gradient(Int32(topColor.rgb), Int32(bottomColor.rgb), WallpaperSettings()))
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = (signal
|
let _ = (signal
|
||||||
|> deliverOnMainQueue).start(next: { [weak controller] wallpaper in
|
|> deliverOnMainQueue).start(next: { [weak controller] wallpaper in
|
||||||
controller?.dismiss()
|
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)
|
present(galleryController, nil)
|
||||||
}, error: { [weak controller] error in
|
}, error: { [weak controller] error in
|
||||||
controller?.dismiss()
|
controller?.dismiss()
|
||||||
@ -316,8 +320,6 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
|||||||
return disposables
|
return disposables
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
|
|
||||||
present(controller, nil)
|
|
||||||
|
|
||||||
var cancelImpl: (() -> Void)?
|
var cancelImpl: (() -> Void)?
|
||||||
let progressSignal = Signal<Never, NoError> { subscriber in
|
let progressSignal = Signal<Never, NoError> { subscriber in
|
||||||
@ -345,14 +347,12 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
|||||||
progressDisposable.dispose()
|
progressDisposable.dispose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> deliverOnMainQueue).start(next: { [weak controller] dataAndTheme in
|
|> deliverOnMainQueue).start(next: { dataAndTheme in
|
||||||
controller?.dismiss()
|
|
||||||
|
|
||||||
if let theme = makePresentationTheme(data: dataAndTheme.0) {
|
if let theme = makePresentationTheme(data: dataAndTheme.0) {
|
||||||
let previewController = ThemePreviewController(context: context, previewTheme: theme, source: .theme(dataAndTheme.1))
|
let previewController = ThemePreviewController(context: context, previewTheme: theme, source: .theme(dataAndTheme.1))
|
||||||
navigationController?.pushViewController(previewController)
|
navigationController?.pushViewController(previewController)
|
||||||
}
|
}
|
||||||
}, error: { [weak controller] error in
|
}, error: { error in
|
||||||
let errorText: String
|
let errorText: String
|
||||||
switch error {
|
switch error {
|
||||||
case .generic, .slugInvalid:
|
case .generic, .slugInvalid:
|
||||||
@ -361,7 +361,6 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
|||||||
errorText = presentationData.strings.Theme_Unsupported
|
errorText = presentationData.strings.Theme_Unsupported
|
||||||
}
|
}
|
||||||
present(textAlertController(context: context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
present(textAlertController(context: context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||||
controller?.dismiss()
|
|
||||||
}))
|
}))
|
||||||
dismissInput()
|
dismissInput()
|
||||||
case let .wallet(address, amount, comment):
|
case let .wallet(address, amount, comment):
|
||||||
|
@ -546,7 +546,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
} else if parsedUrl.host == "bg" {
|
} else if parsedUrl.host == "bg" {
|
||||||
if let components = URLComponents(string: "/?" + query) {
|
if let components = URLComponents(string: "/?" + query) {
|
||||||
var parameter: String?
|
var parameter: String?
|
||||||
var mode = ""
|
var query: [String] = []
|
||||||
if let queryItems = components.queryItems {
|
if let queryItems = components.queryItems {
|
||||||
for queryItem in queryItems {
|
for queryItem in queryItems {
|
||||||
if let value = queryItem.value {
|
if let value = queryItem.value {
|
||||||
@ -557,13 +557,23 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||||||
} else if queryItem.name == "gradient" {
|
} else if queryItem.name == "gradient" {
|
||||||
parameter = value
|
parameter = value
|
||||||
} else if queryItem.name == "mode" {
|
} 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 {
|
if let parameter = parameter {
|
||||||
convertedUrl = "https://t.me/bg/\(parameter)\(mode)"
|
convertedUrl = "https://t.me/bg/\(parameter)\(queryString)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if parsedUrl.host == "addtheme" {
|
} else if parsedUrl.host == "addtheme" {
|
||||||
|
Binary file not shown.
@ -10,6 +10,32 @@ public enum PresentationBuiltinThemeReference: Int32 {
|
|||||||
case night = 1
|
case night = 1
|
||||||
case day = 2
|
case day = 2
|
||||||
case nightAccent = 3
|
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 {
|
public struct WallpaperPresentationOptions: OptionSet {
|
||||||
|
@ -14,17 +14,23 @@ public enum UndoOverlayContent {
|
|||||||
case actionSucceeded(title: String, text: String, cancel: String)
|
case actionSucceeded(title: String, text: String, cancel: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum UndoOverlayAction {
|
||||||
|
case info
|
||||||
|
case undo
|
||||||
|
case commit
|
||||||
|
}
|
||||||
|
|
||||||
public final class UndoOverlayController: ViewController {
|
public final class UndoOverlayController: ViewController {
|
||||||
private let presentationData: PresentationData
|
private let presentationData: PresentationData
|
||||||
public let content: UndoOverlayContent
|
public let content: UndoOverlayContent
|
||||||
private let elevatedLayout: Bool
|
private let elevatedLayout: Bool
|
||||||
private let animateInAsReplacement: Bool
|
private let animateInAsReplacement: Bool
|
||||||
private var action: (Bool) -> Void
|
private var action: (UndoOverlayAction) -> Bool
|
||||||
|
|
||||||
private var didPlayPresentationAnimation = false
|
private var didPlayPresentationAnimation = false
|
||||||
private var dismissed = 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.presentationData = presentationData
|
||||||
self.content = content
|
self.content = content
|
||||||
self.elevatedLayout = elevatedLayout
|
self.elevatedLayout = elevatedLayout
|
||||||
@ -42,7 +48,7 @@ public final class UndoOverlayController: ViewController {
|
|||||||
|
|
||||||
override public func loadDisplayNode() {
|
override public func loadDisplayNode() {
|
||||||
self.displayNode = UndoOverlayControllerNode(presentationData: self.presentationData, content: self.content, elevatedLayout: self.elevatedLayout, action: { [weak self] value in
|
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
|
}, dismiss: { [weak self] in
|
||||||
self?.dismiss()
|
self?.dismiss()
|
||||||
})
|
})
|
||||||
@ -50,12 +56,12 @@ public final class UndoOverlayController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func dismissWithCommitAction() {
|
public func dismissWithCommitAction() {
|
||||||
self.action(true)
|
self.action(.commit)
|
||||||
self.dismiss()
|
self.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func dismissWithCommitActionAndReplacementAnimation() {
|
public func dismissWithCommitActionAndReplacementAnimation() {
|
||||||
self.action(true)
|
self.action(.commit)
|
||||||
(self.displayNode as! UndoOverlayControllerNode).animateOutWithReplacement(completion: { [weak self] in
|
(self.displayNode as! UndoOverlayControllerNode).animateOutWithReplacement(completion: { [weak self] in
|
||||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||||
})
|
})
|
||||||
|
@ -21,11 +21,12 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
private let animatedStickerNode: AnimatedStickerNode?
|
private let animatedStickerNode: AnimatedStickerNode?
|
||||||
private let titleNode: ImmediateTextNode
|
private let titleNode: ImmediateTextNode
|
||||||
private let textNode: ImmediateTextNode
|
private let textNode: ImmediateTextNode
|
||||||
private let buttonTextNode: ImmediateTextNode
|
|
||||||
private let buttonNode: HighlightTrackingButtonNode
|
private let buttonNode: HighlightTrackingButtonNode
|
||||||
|
private let undoButtonTextNode: ImmediateTextNode
|
||||||
|
private let undoButtonNode: HighlightTrackingButtonNode
|
||||||
private let panelNode: ASDisplayNode
|
private let panelNode: ASDisplayNode
|
||||||
private let panelWrapperNode: ASDisplayNode
|
private let panelWrapperNode: ASDisplayNode
|
||||||
private let action: (Bool) -> Void
|
private let action: (UndoOverlayAction) -> Bool
|
||||||
private let dismiss: () -> Void
|
private let dismiss: () -> Void
|
||||||
|
|
||||||
private let effectView: UIView
|
private let effectView: UIView
|
||||||
@ -38,7 +39,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
|
|
||||||
private var validLayout: ContainerViewLayout?
|
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.elevatedLayout = elevatedLayout
|
||||||
|
|
||||||
self.action = action
|
self.action = action
|
||||||
@ -55,6 +56,8 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
self.textNode.displaysAsynchronously = false
|
self.textNode.displaysAsynchronously = false
|
||||||
self.textNode.maximumNumberOfLines = 0
|
self.textNode.maximumNumberOfLines = 0
|
||||||
|
|
||||||
|
self.buttonNode = HighlightTrackingButtonNode()
|
||||||
|
|
||||||
var displayUndo = true
|
var displayUndo = true
|
||||||
var undoText = presentationData.strings.Undo_Undo
|
var undoText = presentationData.strings.Undo_Undo
|
||||||
var undoTextColor = UIColor(rgb: 0x5ac8fa)
|
var undoTextColor = UIColor(rgb: 0x5ac8fa)
|
||||||
@ -129,12 +132,17 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
self.iconCheckNode = nil
|
self.iconCheckNode = nil
|
||||||
self.animationNode = AnimationNode(animation: "anim_success", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
|
self.animationNode = AnimationNode(animation: "anim_success", colors: ["info1.info1.stroke": self.animationBackgroundColor, "info2.info2.Fill": self.animationBackgroundColor], scale: 1.0)
|
||||||
self.animatedStickerNode = nil
|
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.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
|
displayUndo = true
|
||||||
undoText = cancel
|
undoText = cancel
|
||||||
undoTextColor = UIColor(rgb: 0xff7b74)
|
|
||||||
self.originalRemainingSeconds = 3
|
self.originalRemainingSeconds = 3
|
||||||
case let .emoji(path, text):
|
case let .emoji(path, text):
|
||||||
self.iconNode = nil
|
self.iconNode = nil
|
||||||
@ -167,11 +175,11 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
|
|
||||||
self.statusNode = RadialStatusNode(backgroundNodeColor: .clear)
|
self.statusNode = RadialStatusNode(backgroundNodeColor: .clear)
|
||||||
|
|
||||||
self.buttonTextNode = ImmediateTextNode()
|
self.undoButtonTextNode = ImmediateTextNode()
|
||||||
self.buttonTextNode.displaysAsynchronously = false
|
self.undoButtonTextNode.displaysAsynchronously = false
|
||||||
self.buttonTextNode.attributedText = NSAttributedString(string: undoText, font: Font.regular(17.0), textColor: undoTextColor)
|
self.undoButtonTextNode.attributedText = NSAttributedString(string: undoText, font: Font.regular(17.0), textColor: undoTextColor)
|
||||||
|
|
||||||
self.buttonNode = HighlightTrackingButtonNode()
|
self.undoButtonNode = HighlightTrackingButtonNode()
|
||||||
|
|
||||||
self.panelNode = ASDisplayNode()
|
self.panelNode = ASDisplayNode()
|
||||||
if presentationData.theme.overallDarkAppearance {
|
if presentationData.theme.overallDarkAppearance {
|
||||||
@ -201,25 +209,27 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
self.animatedStickerNode.flatMap(self.panelWrapperNode.addSubnode)
|
self.animatedStickerNode.flatMap(self.panelWrapperNode.addSubnode)
|
||||||
self.panelWrapperNode.addSubnode(self.titleNode)
|
self.panelWrapperNode.addSubnode(self.titleNode)
|
||||||
self.panelWrapperNode.addSubnode(self.textNode)
|
self.panelWrapperNode.addSubnode(self.textNode)
|
||||||
|
self.panelWrapperNode.addSubnode(self.buttonNode)
|
||||||
if displayUndo {
|
if displayUndo {
|
||||||
self.panelWrapperNode.addSubnode(self.buttonTextNode)
|
self.panelWrapperNode.addSubnode(self.undoButtonTextNode)
|
||||||
self.panelWrapperNode.addSubnode(self.buttonNode)
|
self.panelWrapperNode.addSubnode(self.undoButtonNode)
|
||||||
}
|
}
|
||||||
self.addSubnode(self.panelNode)
|
self.addSubnode(self.panelNode)
|
||||||
self.addSubnode(self.panelWrapperNode)
|
self.addSubnode(self.panelWrapperNode)
|
||||||
|
|
||||||
self.buttonNode.highligthedChanged = { [weak self] highlighted in
|
self.undoButtonNode.highligthedChanged = { [weak self] highlighted in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if highlighted {
|
if highlighted {
|
||||||
strongSelf.buttonTextNode.layer.removeAnimation(forKey: "opacity")
|
strongSelf.undoButtonTextNode.layer.removeAnimation(forKey: "opacity")
|
||||||
strongSelf.buttonTextNode.alpha = 0.4
|
strongSelf.undoButtonTextNode.alpha = 0.4
|
||||||
} else {
|
} else {
|
||||||
strongSelf.buttonTextNode.alpha = 1.0
|
strongSelf.undoButtonTextNode.alpha = 1.0
|
||||||
strongSelf.buttonTextNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
strongSelf.undoButtonTextNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
||||||
|
self.undoButtonNode.addTarget(self, action: #selector(self.undoButtonPressed), forControlEvents: .touchUpInside)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func didLoad() {
|
override func didLoad() {
|
||||||
@ -230,7 +240,13 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc private func buttonPressed() {
|
@objc private func buttonPressed() {
|
||||||
self.action(false)
|
if self.action(.info) {
|
||||||
|
self.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func undoButtonPressed() {
|
||||||
|
self.action(.undo)
|
||||||
self.dismiss()
|
self.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +255,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
self.remainingSeconds -= 1
|
self.remainingSeconds -= 1
|
||||||
}
|
}
|
||||||
if self.remainingSeconds == 0 {
|
if self.remainingSeconds == 0 {
|
||||||
self.action(true)
|
self.action(.commit)
|
||||||
self.dismiss()
|
self.dismiss()
|
||||||
} else {
|
} else {
|
||||||
if !self.timerTextNode.bounds.size.width.isZero, let snapshot = self.timerTextNode.view.snapshotContentTree() {
|
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 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
|
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
|
buttonMinX = layout.size.width - layout.safeInsets.left - rightInset - buttonTextSize.width - margin * 2.0
|
||||||
} else {
|
} else {
|
||||||
buttonMinX = layout.size.width - layout.safeInsets.left - rightInset
|
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)
|
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)
|
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)
|
transition.updateFrame(node: self.undoButtonTextNode, 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))
|
|
||||||
|
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 textContentHeight = textSize.height
|
||||||
var textOffset: CGFloat = 0.0
|
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) {
|
if component.count == 6, component.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted) == nil, let color = UIColor(hexString: component) {
|
||||||
parameter = .color(color)
|
parameter = .color(color)
|
||||||
} else if component.count == 13, component.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF-").inverted) == nil {
|
} 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: "-")
|
let components = component.components(separatedBy: "-")
|
||||||
if components.count == 2, let topColor = UIColor(hexString: components[0]), let bottomColor = UIColor(hexString: components[1]) {
|
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 {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var options: WallpaperPresentationOptions = []
|
var options: WallpaperPresentationOptions = []
|
||||||
var intensity: Int32?
|
var intensity: Int32?
|
||||||
var color: UIColor?
|
var topColor: UIColor?
|
||||||
|
var bottomColor: UIColor?
|
||||||
|
var rotation: Int32?
|
||||||
if let queryItems = components.queryItems {
|
if let queryItems = components.queryItems {
|
||||||
for queryItem in queryItems {
|
for queryItem in queryItems {
|
||||||
if let value = queryItem.value{
|
if let value = queryItem.value {
|
||||||
if queryItem.name == "mode" {
|
if queryItem.name == "mode" {
|
||||||
for option in value.components(separatedBy: "+") {
|
for option in value.components(separatedBy: "+") {
|
||||||
switch option.lowercased() {
|
switch option.lowercased() {
|
||||||
@ -202,14 +214,24 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if queryItem.name == "bg_color" {
|
} 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" {
|
} else if queryItem.name == "intensity" {
|
||||||
intensity = Int32(value)
|
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)
|
return .wallpaper(parameter)
|
||||||
} else if pathComponents[0] == "addtheme" {
|
} 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 {
|
guard !colors.isEmpty else {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
@ -572,9 +572,11 @@ public func gradientImage(_ colors: [UIColor], rotation: Int32 = 0) -> Signal<(T
|
|||||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
|
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
|
||||||
|
|
||||||
c.translateBy(x: arguments.drawingSize.width / 2.0, y: arguments.drawingSize.height / 2.0)
|
if let rotation = rotation {
|
||||||
c.rotate(by: CGFloat(rotation) * CGFloat.pi / 180.0)
|
c.translateBy(x: arguments.drawingSize.width / 2.0, y: arguments.drawingSize.height / 2.0)
|
||||||
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])
|
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