mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-16 19:30:29 +00:00
Cloud themes improvements
This commit is contained in:
parent
4ed6cbeb9b
commit
a706852dfc
@ -239,9 +239,9 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
|||||||
statePromise.set(stateValue.modify { f($0) })
|
statePromise.set(stateValue.modify { f($0) })
|
||||||
}
|
}
|
||||||
|
|
||||||
var pushControllerImpl: ((ViewController) -> Void)?
|
|
||||||
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
||||||
var dismissImpl: (() -> Void)?
|
var dismissImpl: (() -> Void)?
|
||||||
|
var dismissInputImpl: (() -> Void)?
|
||||||
|
|
||||||
let arguments = EditThemeControllerArguments(context: context, updateState: { f in
|
let arguments = EditThemeControllerArguments(context: context, updateState: { f in
|
||||||
updateState(f)
|
updateState(f)
|
||||||
@ -285,6 +285,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
|||||||
}
|
}
|
||||||
|
|
||||||
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: isComplete, action: {
|
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: isComplete, action: {
|
||||||
|
dismissInputImpl?()
|
||||||
arguments.updateState { current in
|
arguments.updateState { current in
|
||||||
var state = current
|
var state = current
|
||||||
state.updating = true
|
state.updating = true
|
||||||
@ -326,52 +327,106 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
|||||||
}
|
}
|
||||||
|
|
||||||
let themeResource: LocalFileMediaResource?
|
let themeResource: LocalFileMediaResource?
|
||||||
|
let themeData: Data?
|
||||||
if let theme = theme, let themeString = encodePresentationTheme(theme), let themeData = themeString.data(using: .utf8) {
|
if let theme = theme, let themeString = encodePresentationTheme(theme), let data = themeString.data(using: .utf8) {
|
||||||
let resource = LocalFileMediaResource(fileId: arc4random64())
|
let resource = LocalFileMediaResource(fileId: arc4random64())
|
||||||
context.account.postbox.mediaBox.storeResourceData(resource.id, data: themeData)
|
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
|
||||||
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: themeData)
|
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data)
|
||||||
themeResource = resource
|
themeResource = resource
|
||||||
|
themeData = data
|
||||||
} else {
|
} else {
|
||||||
themeResource = nil
|
themeResource = nil
|
||||||
|
themeData = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case .create:
|
case .create:
|
||||||
if let themeResource = themeResource {
|
if let themeResource = themeResource {
|
||||||
let _ = (createTheme(account: context.account, resource: themeResource, title: state.title)
|
let _ = (createTheme(account: context.account, resource: themeResource, title: state.title)
|
||||||
|> deliverOnMainQueue).start(error: { error in
|
|> deliverOnMainQueue).start(next: { next in
|
||||||
|
if case let .result(resultTheme) = next {
|
||||||
|
let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: resultTheme).start()
|
||||||
|
let _ = (context.sharedContext.accountManager.transaction { transaction -> Void in
|
||||||
|
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in
|
||||||
|
let current: PresentationThemeSettings
|
||||||
|
if let entry = entry as? PresentationThemeSettings {
|
||||||
|
current = entry
|
||||||
|
} else {
|
||||||
|
current = PresentationThemeSettings.defaultSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
if let resource = resultTheme.file?.resource, let data = themeData {
|
||||||
|
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data)
|
||||||
|
}
|
||||||
|
|
||||||
|
let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: nil))
|
||||||
|
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||||
|
if let theme = theme {
|
||||||
|
themeSpecificChatWallpapers[themeReference.index] = theme.chat.defaultWallpaper
|
||||||
|
}
|
||||||
|
|
||||||
|
return PresentationThemeSettings(chatWallpaper: theme?.chat.defaultWallpaper ?? current.chatWallpaper, theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||||
|
})
|
||||||
|
} |> deliverOnMainQueue).start(completed: {
|
||||||
|
if !custom {
|
||||||
|
saveThemeTemplateFile(state.title, themeResource, {
|
||||||
|
dismissImpl?()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
dismissImpl?()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, error: { error in
|
||||||
arguments.updateState { current in
|
arguments.updateState { current in
|
||||||
var state = current
|
var state = current
|
||||||
state.updating = false
|
state.updating = false
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
}, completed: {
|
|
||||||
if !custom {
|
|
||||||
saveThemeTemplateFile(state.title, themeResource, {
|
|
||||||
dismissImpl?()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
dismissImpl?()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
case let .edit(theme):
|
case let .edit(info):
|
||||||
let _ = (updateTheme(account: context.account, theme: theme.theme, title: state.title, slug: state.slug, resource: themeResource)
|
let _ = (updateTheme(account: context.account, theme: info.theme, title: state.title, slug: state.slug, resource: themeResource)
|
||||||
|> deliverOnMainQueue).start(error: { error in
|
|> deliverOnMainQueue).start(next: { next in
|
||||||
|
if case let .result(resultTheme) = next {
|
||||||
|
let _ = applyTheme(accountManager: context.sharedContext.accountManager, account: context.account, theme: resultTheme).start()
|
||||||
|
let _ = (context.sharedContext.accountManager.transaction { transaction -> Void in
|
||||||
|
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in
|
||||||
|
let current: PresentationThemeSettings
|
||||||
|
if let entry = entry as? PresentationThemeSettings {
|
||||||
|
current = entry
|
||||||
|
} else {
|
||||||
|
current = PresentationThemeSettings.defaultSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
if let resource = resultTheme.file?.resource, let data = themeData {
|
||||||
|
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data)
|
||||||
|
}
|
||||||
|
|
||||||
|
let themeReference: PresentationThemeReference = .cloud(PresentationCloudTheme(theme: resultTheme, resolvedWallpaper: nil))
|
||||||
|
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
|
||||||
|
if let theme = theme {
|
||||||
|
themeSpecificChatWallpapers[themeReference.index] = theme.chat.defaultWallpaper
|
||||||
|
}
|
||||||
|
|
||||||
|
return PresentationThemeSettings(chatWallpaper: theme?.chat.defaultWallpaper ?? current.chatWallpaper, theme: themeReference, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||||
|
})
|
||||||
|
} |> deliverOnMainQueue).start(completed: {
|
||||||
|
if let themeResource = themeResource, !custom {
|
||||||
|
saveThemeTemplateFile(state.title, themeResource, {
|
||||||
|
dismissImpl?()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
dismissImpl?()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, error: { error in
|
||||||
arguments.updateState { current in
|
arguments.updateState { current in
|
||||||
var state = current
|
var state = current
|
||||||
state.updating = false
|
state.updating = false
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
}, completed: {
|
|
||||||
if let themeResource = themeResource, !custom {
|
|
||||||
saveThemeTemplateFile(state.title, themeResource, {
|
|
||||||
dismissImpl?()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
dismissImpl?()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -395,9 +450,6 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
|||||||
}
|
}
|
||||||
|
|
||||||
let controller = ItemListController(context: context, state: signal)
|
let controller = ItemListController(context: context, state: signal)
|
||||||
pushControllerImpl = { [weak controller] c in
|
|
||||||
(controller?.navigationController as? NavigationController)?.pushViewController(c)
|
|
||||||
}
|
|
||||||
presentControllerImpl = { [weak controller] c, a in
|
presentControllerImpl = { [weak controller] c, a in
|
||||||
controller?.present(c, in: .window(.root), with: a)
|
controller?.present(c, in: .window(.root), with: a)
|
||||||
}
|
}
|
||||||
@ -405,5 +457,8 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
|||||||
controller?.view.endEditing(true)
|
controller?.view.endEditing(true)
|
||||||
let _ = controller?.dismiss()
|
let _ = controller?.dismiss()
|
||||||
}
|
}
|
||||||
|
dismissInputImpl = { [weak controller] in
|
||||||
|
controller?.view.endEditing(true)
|
||||||
|
}
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,8 +45,9 @@ private final class ThemeSettingsControllerArguments {
|
|||||||
let disableAnimations: (Bool) -> Void
|
let disableAnimations: (Bool) -> Void
|
||||||
let selectAppIcon: (String) -> Void
|
let selectAppIcon: (String) -> Void
|
||||||
let presentThemeMenu: (PresentationThemeReference, Bool) -> Void
|
let presentThemeMenu: (PresentationThemeReference, Bool) -> Void
|
||||||
|
let editTheme: (PresentationCloudTheme) -> Void
|
||||||
|
|
||||||
init(context: AccountContext, selectTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, PresentationThemeAccentColor?) -> Void, openAutoNightTheme: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void, presentThemeMenu: @escaping (PresentationThemeReference, Bool) -> Void) {
|
init(context: AccountContext, selectTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, PresentationThemeAccentColor?) -> Void, openAutoNightTheme: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void, presentThemeMenu: @escaping (PresentationThemeReference, Bool) -> Void, editTheme: @escaping (PresentationCloudTheme) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.selectTheme = selectTheme
|
self.selectTheme = selectTheme
|
||||||
self.selectFontSize = selectFontSize
|
self.selectFontSize = selectFontSize
|
||||||
@ -58,6 +59,7 @@ private final class ThemeSettingsControllerArguments {
|
|||||||
self.disableAnimations = disableAnimations
|
self.disableAnimations = disableAnimations
|
||||||
self.selectAppIcon = selectAppIcon
|
self.selectAppIcon = selectAppIcon
|
||||||
self.presentThemeMenu = presentThemeMenu
|
self.presentThemeMenu = presentThemeMenu
|
||||||
|
self.editTheme = editTheme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +291,13 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
|||||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||||
case let .themeItem(theme, strings, themes, currentTheme, themeSpecificAccentColors, currentColor):
|
case let .themeItem(theme, strings, themes, currentTheme, themeSpecificAccentColors, currentColor):
|
||||||
return ThemeSettingsThemeItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, themes: themes, themeSpecificAccentColors: themeSpecificAccentColors, currentTheme: currentTheme, updatedTheme: { theme in
|
return ThemeSettingsThemeItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, themes: themes, themeSpecificAccentColors: themeSpecificAccentColors, currentTheme: currentTheme, updatedTheme: { theme in
|
||||||
arguments.selectTheme(theme)
|
if case let .cloud(theme) = theme, theme.theme.file == nil {
|
||||||
|
if theme.theme.isCreator {
|
||||||
|
arguments.editTheme(theme)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
arguments.selectTheme(theme)
|
||||||
|
}
|
||||||
}, longTapped: { theme in
|
}, longTapped: { theme in
|
||||||
arguments.presentThemeMenu(theme, theme == currentTheme)
|
arguments.presentThemeMenu(theme, theme == currentTheme)
|
||||||
})
|
})
|
||||||
@ -485,24 +493,32 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||||||
items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_RemoveTheme, color: .destructive, action: { [weak actionSheet] in
|
items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_RemoveTheme, color: .destructive, action: { [weak actionSheet] in
|
||||||
actionSheet?.dismissAnimated()
|
actionSheet?.dismissAnimated()
|
||||||
|
|
||||||
// let _ = (cloudThemes.get()
|
let actionSheet = ActionSheetController(presentationTheme: presentationData.theme)
|
||||||
// |> take(1)
|
var items: [ActionSheetItem] = []
|
||||||
// |> deliverOnMainQueue).start(next: { themes in
|
items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_RemoveThemeConfirmation, color: .destructive, action: { [weak actionSheet] in
|
||||||
// if isCurrent, let themeIndex = themes.firstIndex(where: { $0.id == theme.id }) {
|
actionSheet?.dismissAnimated()
|
||||||
// let newTheme: PresentationThemeReference
|
let _ = (cloudThemes.get() |> delay(0.5, queue: Queue.mainQueue())
|
||||||
// if themeIndex > 0 {
|
|> take(1)
|
||||||
// newTheme = .cloud(themes[themeIndex - 1])
|
|> deliverOnMainQueue).start(next: { themes in
|
||||||
// } else {
|
if isCurrent, let themeIndex = themes.firstIndex(where: { $0.id == theme.theme.id }) {
|
||||||
// newTheme = .builtin(.nightAccent)
|
let newTheme: PresentationThemeReference
|
||||||
// }
|
if themeIndex > 0 {
|
||||||
// selectThemeImpl?(newTheme)
|
newTheme = .cloud(PresentationCloudTheme(theme: themes[themeIndex - 1], resolvedWallpaper: nil))
|
||||||
// }
|
} else {
|
||||||
//
|
newTheme = .builtin(.nightAccent)
|
||||||
// let updatedThemes = themes.filter { $0.id != theme.id }
|
}
|
||||||
// cloudThemes.set(.single(updatedThemes) |> then(updatedCloudThemes))
|
selectThemeImpl?(newTheme)
|
||||||
//
|
}
|
||||||
// let _ = (deleteTheme(account: context.account, theme: theme)).start()
|
|
||||||
// })
|
let _ = deleteThemeInteractively(account: context.account, theme: theme.theme).start()
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
||||||
|
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in
|
||||||
|
actionSheet?.dismissAnimated()
|
||||||
|
})
|
||||||
|
])])
|
||||||
|
presentControllerImpl?(actionSheet, nil)
|
||||||
}))
|
}))
|
||||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
||||||
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in
|
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in
|
||||||
@ -510,6 +526,13 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||||||
})
|
})
|
||||||
])])
|
])])
|
||||||
presentControllerImpl?(actionSheet, nil)
|
presentControllerImpl?(actionSheet, nil)
|
||||||
|
}, editTheme: { theme in
|
||||||
|
let controller = editThemeController(context: context, mode: .edit(theme), navigateToChat: { peerId in
|
||||||
|
if let navigationController = getNavigationControllerImpl?() {
|
||||||
|
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
})
|
})
|
||||||
|
|
||||||
let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings]), cloudThemes.get(), availableAppIcons, currentAppIconName.get(), statePromise.get())
|
let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings]), cloudThemes.get(), availableAppIcons, currentAppIconName.get(), statePromise.get())
|
||||||
|
|||||||
@ -41,6 +41,31 @@ private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selec
|
|||||||
})?.stretchableImage(withLeftCapWidth: 15, topCapHeight: 15)
|
})?.stretchableImage(withLeftCapWidth: 15, topCapHeight: 15)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func createThemeImage(theme: PresentationTheme) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||||
|
return .single(theme)
|
||||||
|
|> map { theme -> (TransformImageArguments) -> DrawingContext? in
|
||||||
|
return { arguments in
|
||||||
|
let context = DrawingContext(size: arguments.drawingSize, scale: arguments.scale ?? 0.0, clear: arguments.emptyColor == nil)
|
||||||
|
let drawingRect = arguments.drawingRect
|
||||||
|
|
||||||
|
context.withContext { c in
|
||||||
|
c.setFillColor(theme.list.itemBlocksBackgroundColor.cgColor)
|
||||||
|
c.fill(drawingRect)
|
||||||
|
|
||||||
|
c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0)
|
||||||
|
c.scaleBy(x: 1.0, y: -1.0)
|
||||||
|
c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0)
|
||||||
|
|
||||||
|
if let icon = generateTintedImage(image: UIImage(bundleImageName: "Settings/CreateThemeIcon"), color: theme.list.itemAccentColor) {
|
||||||
|
c.draw(icon.cgImage!, in: CGRect(origin: CGPoint(x: floor((drawingRect.width - icon.size.width) / 2.0) - 3.0, y: floor((drawingRect.height - icon.size.height) / 2.0)), size: icon.size))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func themeIconImage(context: AccountContext, theme: PresentationThemeReference, accentColor: UIColor?) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
private func themeIconImage(context: AccountContext, theme: PresentationThemeReference, accentColor: UIColor?) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||||
let signal: Signal<(UIColor, UIColor, UIColor), NoError>
|
let signal: Signal<(UIColor, UIColor, UIColor), NoError>
|
||||||
if case let .builtin(theme) = theme {
|
if case let .builtin(theme) = theme {
|
||||||
@ -202,7 +227,11 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setup(context: AccountContext, theme: PresentationThemeReference, accentColor: UIColor?, currentTheme: PresentationTheme, title: NSAttributedString, bordered: Bool, selected: Bool, action: @escaping () -> Void, longTapAction: @escaping () -> Void) {
|
func setup(context: AccountContext, theme: PresentationThemeReference, accentColor: UIColor?, currentTheme: PresentationTheme, title: NSAttributedString, bordered: Bool, selected: Bool, action: @escaping () -> Void, longTapAction: @escaping () -> Void) {
|
||||||
self.imageNode.setSignal(themeIconImage(context: context, theme: theme, accentColor: accentColor))
|
if case let .cloud(theme) = theme, theme.theme.file == nil {
|
||||||
|
self.imageNode.setSignal(createThemeImage(theme: currentTheme))
|
||||||
|
} else {
|
||||||
|
self.imageNode.setSignal(themeIconImage(context: context, theme: theme, accentColor: accentColor))
|
||||||
|
}
|
||||||
self.textNode.attributedText = title
|
self.textNode.attributedText = title
|
||||||
self.overlayNode.image = generateBorderImage(theme: currentTheme, bordered: bordered, selected: selected)
|
self.overlayNode.image = generateBorderImage(theme: currentTheme, bordered: bordered, selected: selected)
|
||||||
self.action = {
|
self.action = {
|
||||||
@ -395,7 +424,7 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
self?.scrollToNode(imageNode, animated: true)
|
self?.scrollToNode(imageNode, animated: true)
|
||||||
}
|
}
|
||||||
}, longTapAction: {
|
}, longTapAction: {
|
||||||
item.longTapped(theme)
|
item.longTapped(theme)
|
||||||
})
|
})
|
||||||
|
|
||||||
imageNode.frame = CGRect(origin: CGPoint(x: nodeOffset, y: 0.0), size: nodeSize)
|
imageNode.frame = CGRect(origin: CGPoint(x: nodeOffset, y: 0.0), size: nodeSize)
|
||||||
|
|||||||
@ -2939,8 +2939,8 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !updatedThemes.isEmpty {
|
if !updatedThemes.isEmpty {
|
||||||
let items = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes)
|
let entries = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes)
|
||||||
let themes = items.map { entry -> TelegramTheme in
|
let themes = entries.map { entry -> TelegramTheme in
|
||||||
let theme = entry.contents as! TelegramTheme
|
let theme = entry.contents as! TelegramTheme
|
||||||
if let updatedTheme = updatedThemes[theme.id] {
|
if let updatedTheme = updatedThemes[theme.id] {
|
||||||
return updatedTheme
|
return updatedTheme
|
||||||
@ -2948,13 +2948,13 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
|||||||
return theme
|
return theme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var entries: [OrderedItemListEntry] = []
|
var updatedEntries: [OrderedItemListEntry] = []
|
||||||
for theme in themes {
|
for theme in themes {
|
||||||
var intValue = Int32(entries.count)
|
var intValue = Int32(updatedEntries.count)
|
||||||
let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4))
|
let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4))
|
||||||
entries.append(OrderedItemListEntry(id: id, contents: theme))
|
updatedEntries.append(OrderedItemListEntry(id: id, contents: theme))
|
||||||
}
|
}
|
||||||
transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: entries)
|
transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: updatedEntries)
|
||||||
}
|
}
|
||||||
|
|
||||||
addedIncomingMessageIds.append(contentsOf: addedSecretMessageIds)
|
addedIncomingMessageIds.append(contentsOf: addedSecretMessageIds)
|
||||||
|
|||||||
@ -62,7 +62,16 @@ public func telegramThemes(postbox: Postbox, network: Network, forceUpdate: Bool
|
|||||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedThemesConfiguration, key: ValueBoxKey(length: 0)), entry: CachedThemesConfiguration(hash: hash), collectionSpec: ItemCacheCollectionSpec(lowWaterItemCount: 1, highWaterItemCount: 1))
|
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedThemesConfiguration, key: ValueBoxKey(length: 0)), entry: CachedThemesConfiguration(hash: hash), collectionSpec: ItemCacheCollectionSpec(lowWaterItemCount: 1, highWaterItemCount: 1))
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
}
|
} |> then(
|
||||||
|
postbox.combinedView(keys: [PostboxViewKey.orderedItemList(id: Namespaces.OrderedItemList.CloudThemes)])
|
||||||
|
|> map { view -> [TelegramTheme] in
|
||||||
|
if let view = view.views[.orderedItemList(id: Namespaces.OrderedItemList.CloudThemes)] as? OrderedItemListView {
|
||||||
|
return view.items.compactMap { $0.contents as? TelegramTheme }
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if forceUpdate {
|
if forceUpdate {
|
||||||
@ -122,22 +131,35 @@ public func checkThemeUpdated(account: Account, theme: TelegramTheme) -> Signal<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func saveTheme(account: Account, theme: TelegramTheme) -> Signal<Void, NoError> {
|
|
||||||
return saveUnsaveTheme(account: account, theme: theme, unsave: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func deleteTheme(account: Account, theme: TelegramTheme) -> Signal<Void, NoError> {
|
|
||||||
return saveUnsaveTheme(account: account, theme: theme, unsave: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func saveUnsaveTheme(account: Account, theme: TelegramTheme, unsave: Bool) -> Signal<Void, NoError> {
|
private func saveUnsaveTheme(account: Account, theme: TelegramTheme, unsave: Bool) -> Signal<Void, NoError> {
|
||||||
return account.network.request(Api.functions.account.saveTheme(theme: Api.InputTheme.inputTheme(id: theme.id, accessHash: theme.accessHash), unsave: unsave ? Api.Bool.boolTrue : Api.Bool.boolFalse))
|
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
let entries = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes)
|
||||||
return .complete()
|
var items = entries.map { $0.contents as! TelegramTheme }
|
||||||
}
|
if unsave {
|
||||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
items = items.filter { $0.id != theme.id }
|
||||||
return .single(Void())
|
} else {
|
||||||
}
|
items.insert(theme, at: 0)
|
||||||
|
}
|
||||||
|
var updatedEntries: [OrderedItemListEntry] = []
|
||||||
|
for item in items {
|
||||||
|
var intValue = Int32(updatedEntries.count)
|
||||||
|
let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4))
|
||||||
|
updatedEntries.append(OrderedItemListEntry(id: id, contents: item))
|
||||||
|
}
|
||||||
|
transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: updatedEntries)
|
||||||
|
|
||||||
|
return account.network.request(Api.functions.account.saveTheme(theme: Api.InputTheme.inputTheme(id: theme.id, accessHash: theme.accessHash), unsave: unsave ? Api.Bool.boolTrue : Api.Bool.boolFalse))
|
||||||
|
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||||
|
return telegramThemes(postbox: account.postbox, network: account.network, forceUpdate: true)
|
||||||
|
|> take(1)
|
||||||
|
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} |> switchToLatest
|
||||||
}
|
}
|
||||||
|
|
||||||
private func installTheme(account: Account, theme: TelegramTheme) -> Signal<Never, NoError> {
|
private func installTheme(account: Account, theme: TelegramTheme) -> Signal<Never, NoError> {
|
||||||
@ -234,7 +256,16 @@ public func createTheme(account: Account, resource: MediaResource, title: String
|
|||||||
|> mapToSignal { apiTheme -> Signal<CreateThemeResult, CreateThemeError> in
|
|> mapToSignal { apiTheme -> Signal<CreateThemeResult, CreateThemeError> in
|
||||||
if let theme = TelegramTheme(apiTheme: apiTheme) {
|
if let theme = TelegramTheme(apiTheme: apiTheme) {
|
||||||
return account.postbox.transaction { transaction -> CreateThemeResult in
|
return account.postbox.transaction { transaction -> CreateThemeResult in
|
||||||
|
let entries = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes)
|
||||||
|
var items = entries.map { $0.contents as! TelegramTheme }
|
||||||
|
items.insert(theme, at: 0)
|
||||||
|
var updatedEntries: [OrderedItemListEntry] = []
|
||||||
|
for item in items {
|
||||||
|
var intValue = Int32(updatedEntries.count)
|
||||||
|
let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4))
|
||||||
|
updatedEntries.append(OrderedItemListEntry(id: id, contents: item))
|
||||||
|
}
|
||||||
|
transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: updatedEntries)
|
||||||
return .result(theme)
|
return .result(theme)
|
||||||
}
|
}
|
||||||
|> introduceError(CreateThemeError.self)
|
|> introduceError(CreateThemeError.self)
|
||||||
@ -295,8 +326,27 @@ public func updateTheme(account: Account, theme: TelegramTheme, title: String?,
|
|||||||
return account.network.request(Api.functions.account.updateTheme(flags: flags, theme: .inputTheme(id: theme.id, accessHash: theme.accessHash), slug: slug, title: title, document: inputDocument))
|
return account.network.request(Api.functions.account.updateTheme(flags: flags, theme: .inputTheme(id: theme.id, accessHash: theme.accessHash), slug: slug, title: title, document: inputDocument))
|
||||||
|> mapError { _ in return CreateThemeError.generic }
|
|> mapError { _ in return CreateThemeError.generic }
|
||||||
|> mapToSignal { apiTheme -> Signal<CreateThemeResult, CreateThemeError> in
|
|> mapToSignal { apiTheme -> Signal<CreateThemeResult, CreateThemeError> in
|
||||||
if let theme = TelegramTheme(apiTheme: apiTheme) {
|
if let result = TelegramTheme(apiTheme: apiTheme) {
|
||||||
return .single(.result(theme))
|
return account.postbox.transaction { transaction -> CreateThemeResult in
|
||||||
|
let entries = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes)
|
||||||
|
let items = entries.map { entry -> TelegramTheme in
|
||||||
|
let theme = entry.contents as! TelegramTheme
|
||||||
|
if theme.id == result.id {
|
||||||
|
return result
|
||||||
|
} else {
|
||||||
|
return theme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var updatedEntries: [OrderedItemListEntry] = []
|
||||||
|
for item in items {
|
||||||
|
var intValue = Int32(updatedEntries.count)
|
||||||
|
let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4))
|
||||||
|
updatedEntries.append(OrderedItemListEntry(id: id, contents: item))
|
||||||
|
}
|
||||||
|
transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: updatedEntries)
|
||||||
|
return .result(result)
|
||||||
|
}
|
||||||
|
|> introduceError(CreateThemeError.self)
|
||||||
} else {
|
} else {
|
||||||
return .fail(.generic)
|
return .fail(.generic)
|
||||||
}
|
}
|
||||||
@ -336,6 +386,14 @@ public final class ThemeSettings: PreferencesEntry, Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func saveThemeInteractively(account: Account, theme: TelegramTheme) -> Signal<Void, NoError> {
|
||||||
|
return saveUnsaveTheme(account: account, theme: theme, unsave: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func deleteThemeInteractively(account: Account, theme: TelegramTheme) -> Signal<Void, NoError> {
|
||||||
|
return saveUnsaveTheme(account: account, theme: theme, unsave: true)
|
||||||
|
}
|
||||||
|
|
||||||
public func applyTheme(accountManager: AccountManager, account: Account, theme: TelegramTheme?) -> Signal<Never, NoError> {
|
public func applyTheme(accountManager: AccountManager, account: Account, theme: TelegramTheme?) -> Signal<Never, NoError> {
|
||||||
return accountManager.transaction { transaction -> Signal<Never, NoError> in
|
return accountManager.transaction { transaction -> Signal<Never, NoError> in
|
||||||
transaction.updateSharedData(SharedDataKeys.themeSettings, { _ in
|
transaction.updateSharedData(SharedDataKeys.themeSettings, { _ in
|
||||||
|
|||||||
12
submodules/TelegramUI/Images.xcassets/Settings/CreateThemeIcon.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Settings/CreateThemeIcon.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "ic_iphonetheme.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
submodules/TelegramUI/Images.xcassets/Settings/CreateThemeIcon.imageset/ic_iphonetheme.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Settings/CreateThemeIcon.imageset/ic_iphonetheme.pdf
vendored
Normal file
Binary file not shown.
@ -165,7 +165,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
multiplexedNode.fileSelected = { [weak self] fileReference, sourceNode, sourceRect in
|
multiplexedNode.fileSelected = { [weak self] fileReference, sourceNode, sourceRect in
|
||||||
self?.controllerInteraction.sendGif(fileReference, sourceNode, sourceRect)
|
let _ = self?.controllerInteraction.sendGif(fileReference, sourceNode, sourceRect)
|
||||||
}
|
}
|
||||||
|
|
||||||
multiplexedNode.didScroll = { [weak self] offset, height in
|
multiplexedNode.didScroll = { [weak self] offset, height in
|
||||||
|
|||||||
@ -382,7 +382,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
|
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
|
||||||
|
|
||||||
var needShareButton = false
|
var needShareButton = false
|
||||||
if isFailed {
|
if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) {
|
||||||
needShareButton = false
|
needShareButton = false
|
||||||
} else if item.message.id.peerId == item.context.account.peerId {
|
} else if item.message.id.peerId == item.context.account.peerId {
|
||||||
for attribute in item.content.firstMessage.attributes {
|
for attribute in item.content.firstMessage.attributes {
|
||||||
|
|||||||
@ -699,7 +699,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||||||
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
|
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
|
||||||
|
|
||||||
var needShareButton = false
|
var needShareButton = false
|
||||||
if isFailed {
|
if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) {
|
||||||
needShareButton = false
|
needShareButton = false
|
||||||
} else if item.message.id.peerId == item.context.account.peerId {
|
} else if item.message.id.peerId == item.context.account.peerId {
|
||||||
if let _ = sourceReference {
|
if let _ = sourceReference {
|
||||||
|
|||||||
@ -144,8 +144,13 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
|||||||
avatarInset = 0.0
|
avatarInset = 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
|
||||||
|
|
||||||
var needShareButton = false
|
var needShareButton = false
|
||||||
if item.message.id.peerId == item.context.account.peerId {
|
if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) {
|
||||||
|
needShareButton = false
|
||||||
|
}
|
||||||
|
else if item.message.id.peerId == item.context.account.peerId {
|
||||||
for attribute in item.content.firstMessage.attributes {
|
for attribute in item.content.firstMessage.attributes {
|
||||||
if let _ = attribute as? SourceReferenceMessageAttribute {
|
if let _ = attribute as? SourceReferenceMessageAttribute {
|
||||||
needShareButton = true
|
needShareButton = true
|
||||||
@ -189,7 +194,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var deliveryFailedInset: CGFloat = 0.0
|
var deliveryFailedInset: CGFloat = 0.0
|
||||||
if item.content.firstMessage.flags.contains(.Failed) {
|
if isFailed {
|
||||||
deliveryFailedInset += 24.0
|
deliveryFailedInset += 24.0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -474,7 +479,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
|||||||
strongSelf.replyInfoNode = nil
|
strongSelf.replyInfoNode = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.content.firstMessage.flags.contains(.Failed) {
|
if isFailed {
|
||||||
let deliveryFailedNode: ChatMessageDeliveryFailedNode
|
let deliveryFailedNode: ChatMessageDeliveryFailedNode
|
||||||
var isAppearing = false
|
var isAppearing = false
|
||||||
if let current = strongSelf.deliveryFailedNode {
|
if let current = strongSelf.deliveryFailedNode {
|
||||||
|
|||||||
@ -184,7 +184,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||||||
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
|
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
|
||||||
|
|
||||||
var needShareButton = false
|
var needShareButton = false
|
||||||
if isFailed {
|
if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) {
|
||||||
needShareButton = false
|
needShareButton = false
|
||||||
} else if item.message.id.peerId == item.context.account.peerId {
|
} else if item.message.id.peerId == item.context.account.peerId {
|
||||||
for attribute in item.content.firstMessage.attributes {
|
for attribute in item.content.firstMessage.attributes {
|
||||||
|
|||||||
@ -53,11 +53,13 @@ public struct ApplicationSpecificSharedDataKeys {
|
|||||||
private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 {
|
private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 {
|
||||||
case instantPageStoredState = 0
|
case instantPageStoredState = 0
|
||||||
case cachedInstantPages = 1
|
case cachedInstantPages = 1
|
||||||
|
case resolvedWallpapers = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ApplicationSpecificItemCacheCollectionId {
|
public struct ApplicationSpecificItemCacheCollectionId {
|
||||||
public static let instantPageStoredState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.instantPageStoredState.rawValue)
|
public static let instantPageStoredState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.instantPageStoredState.rawValue)
|
||||||
public static let cachedInstantPages = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.cachedInstantPages.rawValue)
|
public static let cachedInstantPages = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.cachedInstantPages.rawValue)
|
||||||
|
public static let resolvedWallpapers = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.resolvedWallpapers.rawValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ApplicationSpecificOrderedItemListCollectionIdValues: Int32 {
|
private enum ApplicationSpecificOrderedItemListCollectionIdValues: Int32 {
|
||||||
|
|||||||
@ -634,7 +634,6 @@ private func generateBackArrowImage(color: UIColor) -> UIImage? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func themeImage(account: Account, accountManager: AccountManager, fileReference: FileMediaReference, synchronousLoad: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
public func themeImage(account: Account, accountManager: AccountManager, fileReference: FileMediaReference, synchronousLoad: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||||
|
|
||||||
return telegramThemeData(account: account, accountManager: accountManager, resource: fileReference.media.resource, synchronousLoad: synchronousLoad)
|
return telegramThemeData(account: account, accountManager: accountManager, resource: fileReference.media.resource, synchronousLoad: synchronousLoad)
|
||||||
|> map { data in
|
|> map { data in
|
||||||
let theme: PresentationTheme?
|
let theme: PresentationTheme?
|
||||||
@ -649,12 +648,20 @@ public func themeImage(account: Account, accountManager: AccountManager, fileRef
|
|||||||
context.withFlippedContext { c in
|
context.withFlippedContext { c in
|
||||||
c.setBlendMode(.normal)
|
c.setBlendMode(.normal)
|
||||||
if let theme = theme {
|
if let theme = theme {
|
||||||
if case let .color(value) = theme.chat.defaultWallpaper {
|
switch theme.chat.defaultWallpaper {
|
||||||
c.setFillColor(UIColor(rgb: UInt32(bitPattern: value)).cgColor)
|
case .builtin:
|
||||||
} else {
|
if let filePath = frameworkBundle.path(forResource: "ChatWallpaperBuiltin0", ofType: "jpg"), let image = UIImage(contentsOfFile: filePath), let cgImage = image.cgImage {
|
||||||
c.setFillColor(theme.chatList.backgroundColor.cgColor)
|
c.draw(cgImage, in: CGRect(x: 0.0, y: 0.0, width: drawingRect.width, height: drawingRect.height))
|
||||||
|
}
|
||||||
|
case let .color(value):
|
||||||
|
c.setFillColor(UIColor(rgb: UInt32(bitPattern: value)).cgColor)
|
||||||
|
c.fill(drawingRect)
|
||||||
|
case let .file(file):
|
||||||
|
c.setFillColor(theme.chatList.backgroundColor.cgColor)
|
||||||
|
c.fill(drawingRect)
|
||||||
|
default:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
c.fill(drawingRect)
|
|
||||||
|
|
||||||
c.setFillColor(theme.rootController.navigationBar.backgroundColor.cgColor)
|
c.setFillColor(theme.rootController.navigationBar.backgroundColor.cgColor)
|
||||||
c.fill(CGRect(origin: CGPoint(x: 0.0, y: drawingRect.height - 42.0), size: CGSize(width: drawingRect.width, height: 42.0)))
|
c.fill(CGRect(origin: CGPoint(x: 0.0, y: drawingRect.height - 42.0), size: CGSize(width: drawingRect.width, height: 42.0)))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user