mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-16 03:09:56 +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) })
|
||||
}
|
||||
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
||||
var dismissImpl: (() -> Void)?
|
||||
var dismissInputImpl: (() -> Void)?
|
||||
|
||||
let arguments = EditThemeControllerArguments(context: context, updateState: { f in
|
||||
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: {
|
||||
dismissInputImpl?()
|
||||
arguments.updateState { current in
|
||||
var state = current
|
||||
state.updating = true
|
||||
@ -326,52 +327,106 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
||||
}
|
||||
|
||||
let themeResource: LocalFileMediaResource?
|
||||
|
||||
if let theme = theme, let themeString = encodePresentationTheme(theme), let themeData = themeString.data(using: .utf8) {
|
||||
let themeData: Data?
|
||||
if let theme = theme, let themeString = encodePresentationTheme(theme), let data = themeString.data(using: .utf8) {
|
||||
let resource = LocalFileMediaResource(fileId: arc4random64())
|
||||
context.account.postbox.mediaBox.storeResourceData(resource.id, data: themeData)
|
||||
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: themeData)
|
||||
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
|
||||
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data)
|
||||
themeResource = resource
|
||||
themeData = data
|
||||
} else {
|
||||
themeResource = nil
|
||||
themeData = nil
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .create:
|
||||
if let themeResource = themeResource {
|
||||
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
|
||||
var state = current
|
||||
state.updating = false
|
||||
return state
|
||||
}
|
||||
}, completed: {
|
||||
if !custom {
|
||||
saveThemeTemplateFile(state.title, themeResource, {
|
||||
dismissImpl?()
|
||||
})
|
||||
} else {
|
||||
dismissImpl?()
|
||||
}
|
||||
})
|
||||
}
|
||||
case let .edit(theme):
|
||||
let _ = (updateTheme(account: context.account, theme: theme.theme, title: state.title, slug: state.slug, resource: themeResource)
|
||||
|> deliverOnMainQueue).start(error: { error in
|
||||
case let .edit(info):
|
||||
let _ = (updateTheme(account: context.account, theme: info.theme, title: state.title, slug: state.slug, resource: themeResource)
|
||||
|> 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
|
||||
var state = current
|
||||
state.updating = false
|
||||
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)
|
||||
pushControllerImpl = { [weak controller] c in
|
||||
(controller?.navigationController as? NavigationController)?.pushViewController(c)
|
||||
}
|
||||
presentControllerImpl = { [weak controller] c, a in
|
||||
controller?.present(c, in: .window(.root), with: a)
|
||||
}
|
||||
@ -405,5 +457,8 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
||||
controller?.view.endEditing(true)
|
||||
let _ = controller?.dismiss()
|
||||
}
|
||||
dismissInputImpl = { [weak controller] in
|
||||
controller?.view.endEditing(true)
|
||||
}
|
||||
return controller
|
||||
}
|
||||
|
||||
@ -45,8 +45,9 @@ private final class ThemeSettingsControllerArguments {
|
||||
let disableAnimations: (Bool) -> Void
|
||||
let selectAppIcon: (String) -> 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.selectTheme = selectTheme
|
||||
self.selectFontSize = selectFontSize
|
||||
@ -58,6 +59,7 @@ private final class ThemeSettingsControllerArguments {
|
||||
self.disableAnimations = disableAnimations
|
||||
self.selectAppIcon = selectAppIcon
|
||||
self.presentThemeMenu = presentThemeMenu
|
||||
self.editTheme = editTheme
|
||||
}
|
||||
}
|
||||
|
||||
@ -289,7 +291,13 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||
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
|
||||
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
|
||||
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
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
// let _ = (cloudThemes.get()
|
||||
// |> take(1)
|
||||
// |> deliverOnMainQueue).start(next: { themes in
|
||||
// if isCurrent, let themeIndex = themes.firstIndex(where: { $0.id == theme.id }) {
|
||||
// let newTheme: PresentationThemeReference
|
||||
// if themeIndex > 0 {
|
||||
// newTheme = .cloud(themes[themeIndex - 1])
|
||||
// } else {
|
||||
// newTheme = .builtin(.nightAccent)
|
||||
// }
|
||||
// selectThemeImpl?(newTheme)
|
||||
// }
|
||||
//
|
||||
// let updatedThemes = themes.filter { $0.id != theme.id }
|
||||
// cloudThemes.set(.single(updatedThemes) |> then(updatedCloudThemes))
|
||||
//
|
||||
// let _ = (deleteTheme(account: context.account, theme: theme)).start()
|
||||
// })
|
||||
let actionSheet = ActionSheetController(presentationTheme: presentationData.theme)
|
||||
var items: [ActionSheetItem] = []
|
||||
items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_RemoveThemeConfirmation, color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
let _ = (cloudThemes.get() |> delay(0.5, queue: Queue.mainQueue())
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { themes in
|
||||
if isCurrent, let themeIndex = themes.firstIndex(where: { $0.id == theme.theme.id }) {
|
||||
let newTheme: PresentationThemeReference
|
||||
if themeIndex > 0 {
|
||||
newTheme = .cloud(PresentationCloudTheme(theme: themes[themeIndex - 1], resolvedWallpaper: nil))
|
||||
} else {
|
||||
newTheme = .builtin(.nightAccent)
|
||||
}
|
||||
selectThemeImpl?(newTheme)
|
||||
}
|
||||
|
||||
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: [
|
||||
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)
|
||||
}, 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())
|
||||
|
||||
@ -41,6 +41,31 @@ private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selec
|
||||
})?.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> {
|
||||
let signal: Signal<(UIColor, UIColor, UIColor), NoError>
|
||||
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) {
|
||||
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.overlayNode.image = generateBorderImage(theme: currentTheme, bordered: bordered, selected: selected)
|
||||
self.action = {
|
||||
@ -395,7 +424,7 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
||||
self?.scrollToNode(imageNode, animated: true)
|
||||
}
|
||||
}, longTapAction: {
|
||||
item.longTapped(theme)
|
||||
item.longTapped(theme)
|
||||
})
|
||||
|
||||
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 {
|
||||
let items = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes)
|
||||
let themes = items.map { entry -> TelegramTheme in
|
||||
let entries = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes)
|
||||
let themes = entries.map { entry -> TelegramTheme in
|
||||
let theme = entry.contents as! TelegramTheme
|
||||
if let updatedTheme = updatedThemes[theme.id] {
|
||||
return updatedTheme
|
||||
@ -2948,13 +2948,13 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
||||
return theme
|
||||
}
|
||||
}
|
||||
var entries: [OrderedItemListEntry] = []
|
||||
var updatedEntries: [OrderedItemListEntry] = []
|
||||
for theme in themes {
|
||||
var intValue = Int32(entries.count)
|
||||
var intValue = Int32(updatedEntries.count)
|
||||
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)
|
||||
|
||||
@ -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))
|
||||
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 {
|
||||
@ -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> {
|
||||
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 .single(Void())
|
||||
}
|
||||
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
let entries = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes)
|
||||
var items = entries.map { $0.contents as! TelegramTheme }
|
||||
if unsave {
|
||||
items = items.filter { $0.id != theme.id }
|
||||
} 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> {
|
||||
@ -234,7 +256,16 @@ public func createTheme(account: Account, resource: MediaResource, title: String
|
||||
|> mapToSignal { apiTheme -> Signal<CreateThemeResult, CreateThemeError> in
|
||||
if let theme = TelegramTheme(apiTheme: apiTheme) {
|
||||
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)
|
||||
}
|
||||
|> 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))
|
||||
|> mapError { _ in return CreateThemeError.generic }
|
||||
|> mapToSignal { apiTheme -> Signal<CreateThemeResult, CreateThemeError> in
|
||||
if let theme = TelegramTheme(apiTheme: apiTheme) {
|
||||
return .single(.result(theme))
|
||||
if let result = TelegramTheme(apiTheme: apiTheme) {
|
||||
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 {
|
||||
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> {
|
||||
return accountManager.transaction { transaction -> Signal<Never, NoError> 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
|
||||
self?.controllerInteraction.sendGif(fileReference, sourceNode, sourceRect)
|
||||
let _ = self?.controllerInteraction.sendGif(fileReference, sourceNode, sourceRect)
|
||||
}
|
||||
|
||||
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())
|
||||
|
||||
var needShareButton = false
|
||||
if isFailed {
|
||||
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 {
|
||||
|
||||
@ -699,7 +699,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
|
||||
|
||||
var needShareButton = false
|
||||
if isFailed {
|
||||
if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) {
|
||||
needShareButton = false
|
||||
} else if item.message.id.peerId == item.context.account.peerId {
|
||||
if let _ = sourceReference {
|
||||
|
||||
@ -144,8 +144,13 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
avatarInset = 0.0
|
||||
}
|
||||
|
||||
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
|
||||
|
||||
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 {
|
||||
if let _ = attribute as? SourceReferenceMessageAttribute {
|
||||
needShareButton = true
|
||||
@ -189,7 +194,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
}
|
||||
|
||||
var deliveryFailedInset: CGFloat = 0.0
|
||||
if item.content.firstMessage.flags.contains(.Failed) {
|
||||
if isFailed {
|
||||
deliveryFailedInset += 24.0
|
||||
}
|
||||
|
||||
@ -474,7 +479,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
strongSelf.replyInfoNode = nil
|
||||
}
|
||||
|
||||
if item.content.firstMessage.flags.contains(.Failed) {
|
||||
if isFailed {
|
||||
let deliveryFailedNode: ChatMessageDeliveryFailedNode
|
||||
var isAppearing = false
|
||||
if let current = strongSelf.deliveryFailedNode {
|
||||
|
||||
@ -184,7 +184,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
|
||||
|
||||
var needShareButton = false
|
||||
if isFailed {
|
||||
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 {
|
||||
|
||||
@ -53,11 +53,13 @@ public struct ApplicationSpecificSharedDataKeys {
|
||||
private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 {
|
||||
case instantPageStoredState = 0
|
||||
case cachedInstantPages = 1
|
||||
case resolvedWallpapers = 2
|
||||
}
|
||||
|
||||
public struct ApplicationSpecificItemCacheCollectionId {
|
||||
public static let instantPageStoredState = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.instantPageStoredState.rawValue)
|
||||
public static let cachedInstantPages = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.cachedInstantPages.rawValue)
|
||||
public static let resolvedWallpapers = applicationSpecificItemCacheCollectionId(ApplicationSpecificItemCacheCollectionIdValues.resolvedWallpapers.rawValue)
|
||||
}
|
||||
|
||||
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> {
|
||||
|
||||
return telegramThemeData(account: account, accountManager: accountManager, resource: fileReference.media.resource, synchronousLoad: synchronousLoad)
|
||||
|> map { data in
|
||||
let theme: PresentationTheme?
|
||||
@ -649,12 +648,20 @@ public func themeImage(account: Account, accountManager: AccountManager, fileRef
|
||||
context.withFlippedContext { c in
|
||||
c.setBlendMode(.normal)
|
||||
if let theme = theme {
|
||||
if case let .color(value) = theme.chat.defaultWallpaper {
|
||||
c.setFillColor(UIColor(rgb: UInt32(bitPattern: value)).cgColor)
|
||||
} else {
|
||||
c.setFillColor(theme.chatList.backgroundColor.cgColor)
|
||||
switch theme.chat.defaultWallpaper {
|
||||
case .builtin:
|
||||
if let filePath = frameworkBundle.path(forResource: "ChatWallpaperBuiltin0", ofType: "jpg"), let image = UIImage(contentsOfFile: filePath), let cgImage = image.cgImage {
|
||||
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.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