Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
overtake 2019-09-04 13:34:18 +03:00
commit a3874367c8
64 changed files with 3875 additions and 3672 deletions

View File

@ -243,6 +243,7 @@
"Common.Search" = "Search";
"Common.More" = "More";
"Common.Select" = "Select";
"Items.NOfM" = "%1$@ of %2$@";
// State
"State.Connecting" = "Connecting...";
@ -309,6 +310,7 @@
"LastSeen.HoursAgo_0" = "last seen %@ hours ago";
"LastSeen.YesterdayAt" = "last seen yesterday at %@";
"LastSeen.AtDate" = "last seen %@";
"LastSeen.TodayAt" = "last seen today at %@";
"LastSeen.Lately" = "last seen recently";
"LastSeen.WithinAWeek" = "last seen within a week";
"LastSeen.WithinAMonth" = "last seen within a month";
@ -4679,7 +4681,8 @@ Any member of this group will be able to see messages in the channel.";
"EditTheme.Preview" = "CHAT PREVIEW";
"EditTheme.UploadNewTheme" = "Create from File...";
"EditTheme.UploadEditedTheme" = "Update from File...";
"EditTheme.ThemeTemplateAlert" = "New Theme Added\n\nPress and hold on your theme to edit it or get a sharing link. Users who install your theme will get automatic updates each time you change it.\n\nFor advanced editing purposes, you can find a file with your theme in Saved Messages.";
"EditTheme.ThemeTemplateAlertTitle" = "New Theme Added";
"EditTheme.ThemeTemplateAlertText" = "Press and hold on your theme to edit it or get a sharing link. Users who install your theme will get automatic updates each time you change it.\n\nFor advanced editing purposes, you can find a file with your theme in Saved Messages.";
"EditTheme.FileReadError" = "Invalid theme file";
"EditTheme.Create.TopInfo" = "The theme will be based on your currently selected colors and wallpaper.";
@ -4707,6 +4710,7 @@ Any member of this group will be able to see messages in the channel.";
"EditTheme.Edit.Preview.OutgoingText" = "Or upload a theme file";
"EditTheme.ErrorLinkTaken" = "Sorry, this link is already taken";
"EditTheme.ErrorInvalidCharacters" = "Sorry, this link is invalid.";
"Wallpaper.ErrorNotFound" = "Sorry, this chat background doesn't seem to exist.";
"Theme.ErrorNotFound" = "Sorry, this color theme doesn't seem to exist.";

View File

@ -115,7 +115,7 @@ public class ChatListSearchItemNode: ListViewItemNode {
let baseWidth = params.width - params.leftInset - params.rightInset
let backgroundColor = nextIsPinned ? item.theme.chatList.pinnedItemBackgroundColor : item.theme.chatList.itemBackgroundColor
let placeholderColor = item.theme.rootController.navigationSearchBar.inputPlaceholderTextColor
let placeholderColor = item.theme.list.itemSecondaryTextColor
let (_, searchBarApply) = searchBarNodeLayout(NSAttributedString(string: placeholder ?? "", font: searchBarFont, textColor: placeholderColor), CGSize(width: baseWidth - 20.0, height: 36.0), 1.0, placeholderColor, nextIsPinned ? item.theme.chatList.pinnedSearchBarColor : item.theme.chatList.regularSearchBarColor, backgroundColor, .immediate)

View File

@ -83,12 +83,6 @@ public struct ContainerViewLayout: Equatable {
public extension ContainerViewLayout {
func insets(options: ContainerViewLayoutInsetOptions) -> UIEdgeInsets {
var insets = self.intrinsicInsets
if self.inSlideOver {
let onScreenNavigationHeight = self.deviceMetrics.onScreenNavigationHeight(inLandscape: false) ?? 0.0
if insets.bottom > 0.0 && abs(insets.bottom - onScreenNavigationHeight) < 0.1 {
insets.bottom = 0.0
}
}
if let statusBarHeight = self.statusBarHeight, options.contains(.statusBar) {
insets.top += statusBarHeight
}

View File

@ -25,6 +25,11 @@ public enum DeviceMetrics: CaseIterable, Equatable {
}
public init(screenSize: CGSize, statusBarHeight: CGFloat, onScreenNavigationHeight: CGFloat?) {
var screenSize = screenSize
if screenSize.width > screenSize.height {
screenSize = CGSize(width: screenSize.height, height: screenSize.width)
}
let additionalSize = CGSize(width: screenSize.width, height: screenSize.height + 20.0)
for device in DeviceMetrics.allCases {
if let _ = onScreenNavigationHeight, device.onScreenNavigationHeight(inLandscape: false) == nil {
@ -33,7 +38,7 @@ public enum DeviceMetrics: CaseIterable, Equatable {
let width = device.screenSize.width
let height = device.screenSize.height
if ((screenSize.width.isEqual(to: width) && screenSize.height.isEqual(to: height)) || screenSize.height.isEqual(to: width) && screenSize.width.isEqual(to: height)) || ((additionalSize.width.isEqual(to: width) && additionalSize.height.isEqual(to: height)) || additionalSize.height.isEqual(to: width) && additionalSize.width.isEqual(to: height)) {
if ((screenSize.width.isEqual(to: width) && screenSize.height.isEqual(to: height)) || (additionalSize.width.isEqual(to: width) && additionalSize.height.isEqual(to: height))) {
self = device
return
}

View File

@ -15,6 +15,22 @@ public class EditableTextNode: ASEditableTextNode {
self.textView.reloadInputViews()
}
}
public var isRTL: Bool {
if let text = self.textView.text, !text.isEmpty {
let tagger = NSLinguisticTagger(tagSchemes: [.language], options: 0)
tagger.string = text
let lang = tagger.tag(at: 0, scheme: .language, tokenRange: nil, sentenceRange: nil)
if let lang = lang?.rawValue, lang.contains("he") || lang.contains("ar") || lang.contains("fa") {
return true
} else {
return false
}
} else {
return false
}
}
}
public extension UITextView {

View File

@ -85,6 +85,7 @@ private struct UpdatingLayout {
}
}
private let defaultStatusBarHeight: CGFloat = 20.0
private let statusBarHiddenInLandscape: Bool = UIDevice.current.userInterfaceIdiom == .phone
private func inputHeightOffsetForLayout(_ layout: WindowLayout) -> CGFloat {
@ -280,7 +281,7 @@ public final class WindowKeyboardGestureRecognizerDelegate: NSObject, UIGestureR
public class Window1 {
public let hostView: WindowHostView
private let deviceMetrics: DeviceMetrics
private var deviceMetrics: DeviceMetrics
private let statusBarHost: StatusBarHost?
private let statusBarManager: StatusBarManager?
@ -342,7 +343,7 @@ public class Window1 {
self.volumeControlStatusBarNode.isHidden = true
let boundsSize = self.hostView.eventView.bounds.size
self.deviceMetrics = DeviceMetrics(screenSize: UIScreen.main.bounds.size, statusBarHeight: statusBarHost?.statusBarFrame.height ?? 20.0, onScreenNavigationHeight: self.hostView.onScreenNavigationHeight)
self.deviceMetrics = DeviceMetrics(screenSize: UIScreen.main.bounds.size, statusBarHeight: statusBarHost?.statusBarFrame.height ?? defaultStatusBarHeight, onScreenNavigationHeight: self.hostView.onScreenNavigationHeight)
self.statusBarHost = statusBarHost
let statusBarHeight: CGFloat
@ -439,7 +440,7 @@ public class Window1 {
self.statusBarChangeObserver = NotificationCenter.default.addObserver(forName: UIApplication.willChangeStatusBarFrameNotification, object: nil, queue: OperationQueue.main, using: { [weak self] notification in
if let strongSelf = self {
let statusBarHeight: CGFloat = max(20.0, (notification.userInfo?[UIApplication.statusBarFrameUserInfoKey] as? NSValue)?.cgRectValue.height ?? 20.0)
let statusBarHeight: CGFloat = max(defaultStatusBarHeight, (notification.userInfo?[UIApplication.statusBarFrameUserInfoKey] as? NSValue)?.cgRectValue.height ?? defaultStatusBarHeight)
let transition: ContainedViewLayoutTransition = .animated(duration: 0.35, curve: .easeInOut)
strongSelf.updateLayout { $0.update(statusBarHeight: statusBarHeight, transition: transition, overrideTransition: false) }
@ -947,6 +948,11 @@ public class Window1 {
} else {
statusBarHeight = self.deviceMetrics.statusBarHeight
}
if self.deviceMetrics.type == .tablet, let onScreenNavigationHeight = self.hostView.onScreenNavigationHeight, onScreenNavigationHeight != self.deviceMetrics.onScreenNavigationHeight(inLandscape: false) {
self.deviceMetrics = DeviceMetrics(screenSize: UIScreen.main.bounds.size, statusBarHeight: statusBarHeight ?? defaultStatusBarHeight, onScreenNavigationHeight: onScreenNavigationHeight)
}
let statusBarWasHidden = self.statusBarHidden
if statusBarHiddenInLandscape && isLandscape {
statusBarHeight = nil

View File

@ -39,7 +39,7 @@ class ChatDocumentGalleryItem: GalleryItem {
}
if let location = self.location {
node._title.set(.single("\(location.index + 1) \(self.presentationData.strings.Common_of) \(location.count)"))
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").0))
}
node.setMessage(self.message)
@ -48,7 +48,7 @@ class ChatDocumentGalleryItem: GalleryItem {
func updateNode(node: GalleryItemNode) {
if let node = node as? ChatDocumentGalleryItemNode, let location = self.location {
node._title.set(.single("\(location.index + 1) \(self.presentationData.strings.Common_of) \(location.count)"))
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").0))
node.setMessage(self.message)
}
}

View File

@ -40,7 +40,7 @@ class ChatExternalFileGalleryItem: GalleryItem {
}
if let location = self.location {
node._title.set(.single("\(location.index + 1) \(self.presentationData.strings.Common_of) \(location.count)"))
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").0))
}
node.setMessage(self.message)
@ -49,7 +49,7 @@ class ChatExternalFileGalleryItem: GalleryItem {
func updateNode(node: GalleryItemNode) {
if let node = node as? ChatExternalFileGalleryItemNode, let location = self.location {
node._title.set(.single("\(location.index + 1) \(self.presentationData.strings.Common_of) \(location.count)"))
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").0))
node.setMessage(self.message)
}
}

View File

@ -112,7 +112,7 @@ class ChatImageGalleryItem: GalleryItem {
}
if let location = self.location {
node._title.set(.single("\(location.index + 1) \(self.presentationData.strings.Common_of) \(location.count)"))
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").0))
}
node.setMessage(self.message)
@ -122,7 +122,7 @@ class ChatImageGalleryItem: GalleryItem {
func updateNode(node: GalleryItemNode) {
if let node = node as? ChatImageGalleryItemNode, let location = self.location {
node._title.set(.single("\(location.index + 1) \(self.presentationData.strings.Common_of) \(location.count)"))
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").0))
node.setMessage(self.message)
}

View File

@ -55,7 +55,7 @@ public class UniversalVideoGalleryItem: GalleryItem {
let node = UniversalVideoGalleryItemNode(context: self.context, presentationData: self.presentationData, performAction: self.performAction, openActionOptions: self.openActionOptions)
if let indexData = self.indexData {
node._title.set(.single("\(indexData.position + 1) \(self.presentationData.strings.Common_of) \(indexData.totalCount)"))
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(indexData.position + 1)", "\(indexData.totalCount)").0))
}
node.setupItem(self)
@ -66,7 +66,7 @@ public class UniversalVideoGalleryItem: GalleryItem {
public func updateNode(node: GalleryItemNode) {
if let node = node as? UniversalVideoGalleryItemNode {
if let indexData = self.indexData {
node._title.set(.single("\(indexData.position + 1) \(self.presentationData.strings.Common_of) \(indexData.totalCount)"))
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(indexData.position + 1)", "\(indexData.totalCount)").0))
}
node.setupItem(self)

View File

@ -60,7 +60,7 @@ class InstantImageGalleryItem: GalleryItem {
node.setImage(imageReference: self.imageReference)
if let location = self.location {
node._title.set(.single("\(location.position + 1) \(self.presentationData.strings.Common_of) \(location.totalCount)"))
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.position + 1)", "\(location.totalCount)").0))
}
node.setCaption(self.caption, credit: self.credit)
@ -71,7 +71,7 @@ class InstantImageGalleryItem: GalleryItem {
func updateNode(node: GalleryItemNode) {
if let node = node as? InstantImageGalleryItemNode {
if let location = self.location {
node._title.set(.single("\(location.position + 1) \(self.presentationData.strings.Common_of) \(location.totalCount)"))
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.position + 1)", "\(location.totalCount)").0))
}
node.setCaption(self.caption, credit: self.credit)

View File

@ -215,11 +215,11 @@ func importLegacyPreferences(accountManager: AccountManager, account: TemporaryA
}
switch autoNightPreferences.mode {
case TGPresentationAutoNightModeSunsetSunrise:
settings.automaticThemeSwitchSetting = AutomaticThemeSwitchSetting(trigger: .timeBased(setting: .automatic(latitude: Double(autoNightPreferences.latitude), longitude: Double(autoNightPreferences.longitude), localizedName: autoNightPreferences.cachedLocationName)), theme: nightTheme)
settings.automaticThemeSwitchSetting = AutomaticThemeSwitchSetting(trigger: .timeBased(setting: .automatic(latitude: Double(autoNightPreferences.latitude), longitude: Double(autoNightPreferences.longitude), localizedName: autoNightPreferences.cachedLocationName)), theme: .builtin(nightTheme))
case TGPresentationAutoNightModeScheduled:
settings.automaticThemeSwitchSetting = AutomaticThemeSwitchSetting(trigger: .timeBased(setting: .manual(fromSeconds: autoNightPreferences.scheduleStart, toSeconds: autoNightPreferences.scheduleEnd)), theme: nightTheme)
settings.automaticThemeSwitchSetting = AutomaticThemeSwitchSetting(trigger: .timeBased(setting: .manual(fromSeconds: autoNightPreferences.scheduleStart, toSeconds: autoNightPreferences.scheduleEnd)), theme: .builtin(nightTheme))
case TGPresentationAutoNightModeBrightness:
settings.automaticThemeSwitchSetting = AutomaticThemeSwitchSetting(trigger: .brightness(threshold: Double(autoNightPreferences.brightnessThreshold)), theme: nightTheme)
settings.automaticThemeSwitchSetting = AutomaticThemeSwitchSetting(trigger: .brightness(threshold: Double(autoNightPreferences.brightnessThreshold)), theme: .builtin(nightTheme))
default:
break
}

View File

@ -340,8 +340,9 @@ private final class LegacyComponentsGlobalsProviderImpl: NSObject, LegacyCompone
let theme = presentationTheme.list
let navigationBar = presentationTheme.rootController.navigationBar
let tabBar = presentationTheme.rootController.tabBar
return TGMediaAssetsPallete(dark: presentationTheme.overallDarkAppearance, backgroundColor: theme.plainBackgroundColor, selectionColor: theme.itemHighlightedBackgroundColor, separatorColor: theme.itemPlainSeparatorColor, textColor: theme.itemPrimaryTextColor, secondaryTextColor: theme.controlSecondaryColor, accentColor: theme.itemAccentColor, barBackgroundColor: navigationBar.backgroundColor, barSeparatorColor: navigationBar.separatorColor, navigationTitleColor: navigationBar.primaryTextColor, badge: generateStretchableFilledCircleImage(diameter: 22.0, color: navigationBar.accentTextColor), badgeTextColor: navigationBar.backgroundColor, sendIconImage: PresentationResourcesChat.chatInputPanelSendButtonImage(presentationTheme), maybeAccentColor: navigationBar.accentTextColor)
return TGMediaAssetsPallete(dark: presentationTheme.overallDarkAppearance, backgroundColor: theme.plainBackgroundColor, selectionColor: theme.itemHighlightedBackgroundColor, separatorColor: theme.itemPlainSeparatorColor, textColor: theme.itemPrimaryTextColor, secondaryTextColor: theme.controlSecondaryColor, accentColor: theme.itemAccentColor, barBackgroundColor: tabBar.backgroundColor, barSeparatorColor: tabBar.separatorColor, navigationTitleColor: navigationBar.primaryTextColor, badge: generateStretchableFilledCircleImage(diameter: 22.0, color: navigationBar.accentTextColor), badgeTextColor: navigationBar.backgroundColor, sendIconImage: PresentationResourcesChat.chatInputPanelSendButtonImage(presentationTheme), maybeAccentColor: navigationBar.accentTextColor)
}
func checkButtonPallete() -> TGCheckButtonPallete! {

View File

@ -740,12 +740,6 @@ NSString *suffix = @"";
if ([platform hasPrefix:@"iPad"]) return UIDeviceUnknowniPad;
if ([platform hasPrefix:@"AppleTV"]) return UIDeviceUnknownAppleTV;
#define IPAD_PRO_3G_NAMESTRING @"iPad Pro 12.9 (3rd gen)"
#define IPAD_PRO_11_NAMESTRING @"iPad Pro 11"
#define IPAD_PRO_6G_NAMESTRING @"iPad (6th gen)"
#define IPAD_PRO_10_5_NAMESTRING @"iPad Pro 10.5"
#define IPAD_PRO_12_9_NAMESTRING @"iPad Pro 12.9"
// Simulator thanks Jordan Breeding
if ([platform hasSuffix:@"86"] || [platform isEqual:@"x86_64"])
{

View File

@ -36,7 +36,7 @@ class SecureIdDocumentGalleryItem: GalleryItem {
node.setResource(secureIdContext: self.secureIdContext, resource: self.resource)
node._title.set(.single("\(self.location.position + 1) \(self.strings.Common_of) \(self.location.totalCount)"))
node._title.set(.single(self.strings.Items_NOfM("\(self.location.position + 1)", "\(self.location.totalCount)").0))
node.setCaption(self.caption)
node.delete = self.delete
@ -46,7 +46,7 @@ class SecureIdDocumentGalleryItem: GalleryItem {
func updateNode(node: GalleryItemNode) {
if let node = node as? SecureIdDocumentGalleryItemNode {
node._title.set(.single("\(self.location.position + 1) \(self.strings.Common_of) \(self.location.totalCount)"))
node._title.set(.single(self.strings.Items_NOfM("\(self.location.position + 1)", "\(self.location.totalCount)").0))
node.setCaption(self.caption)
node.delete = self.delete

View File

@ -59,7 +59,7 @@ class PeerAvatarImageGalleryItem: GalleryItem {
let node = PeerAvatarImageGalleryItemNode(context: self.context, presentationData: self.presentationData, peer: self.peer)
if let indexData = self.entry.indexData {
node._title.set(.single("\(indexData.position + 1) \(self.presentationData.strings.Common_of) \(indexData.totalCount)"))
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(indexData.position + 1)", "\(indexData.totalCount)").0))
}
node.setEntry(self.entry)
@ -71,7 +71,7 @@ class PeerAvatarImageGalleryItem: GalleryItem {
func updateNode(node: GalleryItemNode) {
if let node = node as? PeerAvatarImageGalleryItemNode {
if let indexData = self.entry.indexData {
node._title.set(.single("\(indexData.position + 1) \(self.presentationData.strings.Common_of) \(indexData.totalCount)"))
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(indexData.position + 1)", "\(indexData.totalCount)").0))
}
node.setEntry(self.entry)

View File

@ -379,7 +379,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
let _ = enqueueMessages(account: context.account, peerId: context.account.peerId, messages: [message]).start()
if let navigateToChat = navigateToChat {
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.EditTheme_ThemeTemplateAlert, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Settings_SavedMessages, action: {
presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.EditTheme_ThemeTemplateAlertTitle, text: presentationData.strings.EditTheme_ThemeTemplateAlertText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Settings_SavedMessages, action: {
completion()
navigateToChat(context.account.peerId)
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
@ -524,8 +524,18 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
return state
}
if case .slugOccupied = error {
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.EditTheme_ErrorLinkTaken, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
var errorText: String?
switch error {
case .slugOccupied:
errorText = presentationData.strings.EditTheme_ErrorLinkTaken
case .slugInvalid:
errorText = presentationData.strings.EditTheme_ErrorInvalidCharacters
default:
break
}
if let errorText = errorText {
presentControllerImpl?(textAlertController(context: context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
}
})
}

View File

@ -11,6 +11,7 @@ import TelegramStringFormatting
import AccountContext
import DeviceLocationManager
import Geocoding
import WallpaperResources
private enum TriggerMode {
case none
@ -24,14 +25,16 @@ private enum TimeBasedManualField {
}
private final class ThemeAutoNightSettingsControllerArguments {
let context: AccountContext
let updateMode: (TriggerMode) -> Void
let updateTimeBasedAutomatic: (Bool) -> Void
let openTimeBasedManual: (TimeBasedManualField) -> Void
let updateTimeBasedAutomaticLocation: () -> Void
let updateAutomaticBrightness: (Double) -> Void
let updateTheme: (PresentationBuiltinThemeReference) -> Void
let updateTheme: (PresentationThemeReference) -> Void
init(updateMode: @escaping (TriggerMode) -> Void, updateTimeBasedAutomatic: @escaping (Bool) -> Void, openTimeBasedManual: @escaping (TimeBasedManualField) -> Void, updateTimeBasedAutomaticLocation: @escaping () -> Void, updateAutomaticBrightness: @escaping (Double) -> Void, updateTheme: @escaping (PresentationBuiltinThemeReference) -> Void) {
init(context: AccountContext, updateMode: @escaping (TriggerMode) -> Void, updateTimeBasedAutomatic: @escaping (Bool) -> Void, openTimeBasedManual: @escaping (TimeBasedManualField) -> Void, updateTimeBasedAutomaticLocation: @escaping () -> Void, updateAutomaticBrightness: @escaping (Double) -> Void, updateTheme: @escaping (PresentationThemeReference) -> Void) {
self.context = context
self.updateMode = updateMode
self.updateTimeBasedAutomatic = updateTimeBasedAutomatic
self.openTimeBasedManual = openTimeBasedManual
@ -61,8 +64,7 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
case settingInfo(PresentationTheme, String)
case themeHeader(PresentationTheme, String)
case themeNightBlue(PresentationTheme, String, Bool)
case themeNight(PresentationTheme, String, Bool)
case themeItem(PresentationTheme, PresentationStrings, [PresentationThemeReference], PresentationThemeReference, [Int64: PresentationThemeAccentColor])
var section: ItemListSectionId {
switch self {
@ -70,7 +72,7 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
return ThemeAutoNightSettingsControllerSection.mode.rawValue
case .settingsHeader, .timeBasedAutomaticLocation, .timeBasedAutomaticLocationValue, .timeBasedManualFrom, .timeBasedManualTo, .brightnessValue, .settingInfo:
return ThemeAutoNightSettingsControllerSection.settings.rawValue
case .themeHeader, .themeNightBlue, .themeNight:
case .themeHeader, .themeItem:
return ThemeAutoNightSettingsControllerSection.theme.rawValue
}
}
@ -99,10 +101,8 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
return 9
case .themeHeader:
return 10
case .themeNightBlue:
case .themeItem:
return 11
case .themeNight:
return 12
}
}
@ -174,14 +174,8 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
} else {
return false
}
case let .themeNightBlue(lhsTheme, lhsTitle, lhsValue):
if case let .themeNightBlue(rhsTheme, rhsTitle, rhsValue) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsValue == rhsValue {
return true
} else {
return false
}
case let .themeNight(lhsTheme, lhsTitle, lhsValue):
if case let .themeNight(rhsTheme, rhsTitle, rhsValue) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsValue == rhsValue {
case let .themeItem(lhsTheme, lhsStrings, lhsThemes, lhsCurrentTheme, lhsThemeAccentColors):
if case let .themeItem(rhsTheme, rhsStrings, rhsThemes, rhsCurrentTheme, rhsThemeAccentColors) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsThemes == rhsThemes, lhsCurrentTheme == rhsCurrentTheme, lhsThemeAccentColors == rhsThemeAccentColors {
return true
} else {
return false
@ -233,19 +227,16 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
case let .themeHeader(theme, title):
return ItemListSectionHeaderItem(theme: theme, text: title, sectionId: self.section)
case let .themeNightBlue(theme, title, value):
return ItemListCheckboxItem(theme: theme, title: title, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: {
arguments.updateTheme(.nightAccent)
})
case let .themeNight(theme, title, value):
return ItemListCheckboxItem(theme: theme, title: title, style: .left, checked: value, zeroSeparatorInsets: false, sectionId: self.section, action: {
arguments.updateTheme(.night)
case let .themeItem(theme, strings, themes, currentTheme, themeSpecificAccentColors):
return ThemeSettingsThemeItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, themes: themes, themeSpecificAccentColors: themeSpecificAccentColors, currentTheme: currentTheme, updatedTheme: { theme in
arguments.updateTheme(theme)
}, longTapped: { _ in
})
}
}
}
private func themeAutoNightSettingsControllerEntries(theme: PresentationTheme, strings: PresentationStrings, switchSetting: AutomaticThemeSwitchSetting, dateTimeFormat: PresentationDateTimeFormat) -> [ThemeAutoNightSettingsControllerEntry] {
private func themeAutoNightSettingsControllerEntries(theme: PresentationTheme, strings: PresentationStrings, settings: PresentationThemeSettings, switchSetting: AutomaticThemeSwitchSetting, availableThemes: [PresentationThemeReference], dateTimeFormat: PresentationDateTimeFormat) -> [ThemeAutoNightSettingsControllerEntry] {
var entries: [ThemeAutoNightSettingsControllerEntry] = []
let activeTriggerMode: TriggerMode
@ -297,8 +288,7 @@ private func themeAutoNightSettingsControllerEntries(theme: PresentationTheme, s
break
case .timeBased, .brightness:
entries.append(.themeHeader(theme, strings.AutoNightTheme_PreferredTheme))
entries.append(.themeNightBlue(theme, strings.Appearance_ThemeCarouselTintedNight, switchSetting.theme == .nightAccent))
entries.append(.themeNight(theme, strings.Appearance_ThemeCarouselNewNight, switchSetting.theme == .night))
entries.append(.themeItem(theme, strings, availableThemes, switchSetting.theme, settings.themeSpecificAccentColors))
}
return entries
@ -389,7 +379,7 @@ public func themeAutoNightSettingsController(context: AccountContext) -> ViewCon
updateLocationDisposable.set(disposable)
}
let arguments = ThemeAutoNightSettingsControllerArguments(updateMode: { mode in
let arguments = ThemeAutoNightSettingsControllerArguments(context: context, updateMode: { mode in
var updateLocation = false
updateSettings { settings in
var settings = settings
@ -508,19 +498,51 @@ public func themeAutoNightSettingsController(context: AccountContext) -> ViewCon
}
}))
}, updateTheme: { theme in
updateSettings { settings in
var settings = settings
settings.theme = theme
return settings
let presentationTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: theme, accentColor: nil, serviceBackgroundColor: .black, baseColor: nil)
let resolvedWallpaper: Signal<TelegramWallpaper?, NoError>
if case let .file(file) = presentationTheme.chat.defaultWallpaper, file.id == 0 {
resolvedWallpaper = cachedWallpaper(account: context.account, slug: file.slug, settings: file.settings)
|> map { wallpaper -> TelegramWallpaper? in
return wallpaper?.wallpaper
}
} else {
resolvedWallpaper = .single(nil)
}
let _ = (resolvedWallpaper
|> mapToSignal { resolvedWallpaper -> Signal<Void, NoError> in
var updatedTheme = theme
if case let .cloud(info) = theme {
updatedTheme = .cloud(PresentationCloudTheme(theme: info.theme, resolvedWallpaper: resolvedWallpaper))
}
updateSettings { settings in
var settings = settings
settings.theme = updatedTheme
return settings
}
return .complete()
}).start()
})
let signal = combineLatest(context.sharedContext.presentationData |> deliverOnMainQueue, sharedData |> deliverOnMainQueue, stagingSettingsPromise.get() |> deliverOnMainQueue)
|> map { presentationData, sharedData, stagingSettings -> (ItemListControllerState, (ItemListNodeState<ThemeAutoNightSettingsControllerEntry>, ThemeAutoNightSettingsControllerEntry.ItemGenerationArguments)) in
let cloudThemes = Promise<[TelegramTheme]>()
let updatedCloudThemes = telegramThemes(postbox: context.account.postbox, network: context.account.network, accountManager: context.sharedContext.accountManager)
cloudThemes.set(updatedCloudThemes)
let signal = combineLatest(context.sharedContext.presentationData |> deliverOnMainQueue, sharedData |> deliverOnMainQueue, cloudThemes.get() |> deliverOnMainQueue, stagingSettingsPromise.get() |> deliverOnMainQueue)
|> map { presentationData, sharedData, cloudThemes, stagingSettings -> (ItemListControllerState, (ItemListNodeState<ThemeAutoNightSettingsControllerEntry>, ThemeAutoNightSettingsControllerEntry.ItemGenerationArguments)) in
let settings = (sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings] as? PresentationThemeSettings) ?? PresentationThemeSettings.defaultSettings
let defaultThemes: [PresentationThemeReference] = [.builtin(.night), .builtin(.nightAccent)]
let cloudThemes: [PresentationThemeReference] = cloudThemes.map { .cloud(PresentationCloudTheme(theme: $0, resolvedWallpaper: nil)) }
var availableThemes = defaultThemes
availableThemes.append(contentsOf: cloudThemes)
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.AutoNightTheme_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let listState = ItemListNodeState(entries: themeAutoNightSettingsControllerEntries(theme: presentationData.theme, strings: presentationData.strings, switchSetting: stagingSettings ?? settings.automaticThemeSwitchSetting, dateTimeFormat: presentationData.dateTimeFormat), style: .blocks, animateChanges: false)
let listState = ItemListNodeState(entries: themeAutoNightSettingsControllerEntries(theme: presentationData.theme, strings: presentationData.strings, settings: settings, switchSetting: stagingSettings ?? settings.automaticThemeSwitchSetting, availableThemes: availableThemes, dateTimeFormat: presentationData.dateTimeFormat), style: .blocks, animateChanges: false)
return (controllerState, (listState, arguments))
}

View File

@ -101,7 +101,7 @@ private final class ThemeAutoNightTimeSelectionActionSheetItemNode: ActionSheetI
self.pickerView.datePickerMode = .time
self.pickerView.timeZone = TimeZone(secondsFromGMT: 0)
self.pickerView.date = Date(timeIntervalSince1970: Double(currentValue))
self.pickerView.locale = localeWithStrings(strings)
self.pickerView.locale = Locale.current
self.pickerView.setValue(theme.primaryTextColor, forKey: "textColor")

View File

@ -98,11 +98,11 @@ private let colors: [UInt32: String] = [
0x80b3c4: "Glacier",
0xfebaad: "Melon",
0xc54b8c: "Mulberry",
0xa9c6c2: "Opal"
0xa9c6c2: "Opal",
0x54a5f8: "Blue"
]
private let adjectives = [
"Always",
"Ancient",
"Antique",
"Autumn",
@ -148,7 +148,6 @@ private let adjectives = [
"Frosty",
"Frozen",
"Gentle",
"Golden",
"Heavenly",
"Hyper",
"Icy",
@ -202,7 +201,6 @@ private let adjectives = [
"Twinkling",
"Ultimate",
"Ultra",
"Uptown",
"Velvety",
"Vibrant",
"Vintage",
@ -322,7 +320,6 @@ private extension UIColor {
}
}
func generateThemeName(accentColor: UIColor) -> String {
var nearest: (color: UInt32, distance: Int32)?
for (color, _) in colors {
@ -340,11 +337,7 @@ func generateThemeName(accentColor: UIColor) -> String {
if arc4random() % 2 == 0 {
return "\(adjectives[Int(arc4random()) % adjectives.count].capitalized) \(colorName)"
} else {
if false, arc4random() % 3 == 0 {
return "\(adjectives[Int(arc4random()) % adjectives.count].capitalized) \(colorName) \(subjectives[Int(arc4random()) % subjectives.count].capitalized)"
} else {
return "\(colorName) \(subjectives[Int(arc4random()) % subjectives.count].capitalized)"
}
return "\(colorName) \(subjectives[Int(arc4random()) % subjectives.count].capitalized)"
}
} else {
return ""

View File

@ -24,13 +24,12 @@ public final class ThemePreviewController: ViewController {
private let previewTheme: PresentationTheme
private let source: ThemePreviewSource
private let theme = Promise<TelegramTheme?>()
private let presentationTheme = Promise<PresentationTheme>()
private var controllerNode: ThemePreviewControllerNode {
return self.displayNode as! ThemePreviewControllerNode
}
private let titleView: CounterContollerTitleView
private var didPlayPresentationAnimation = false
private var presentationData: PresentationData
@ -45,8 +44,7 @@ public final class ThemePreviewController: ViewController {
self.source = source
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.titleView = CounterContollerTitleView(theme: self.previewTheme)
self.presentationTheme.set(.single(previewTheme))
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.previewTheme, presentationStrings: self.presentationData.strings))
@ -61,24 +59,45 @@ public final class ThemePreviewController: ViewController {
return .single(nil)
})
themeName = previewTheme.name.string
self.presentationTheme.set(.single(self.previewTheme)
|> then(
self.theme.get()
|> mapToSignal { theme in
if let file = theme?.file {
return telegramThemeData(account: context.account, accountManager: context.sharedContext.accountManager, resource: file.resource)
|> mapToSignal { data -> Signal<PresentationTheme, NoError> in
guard let data = data, let presentationTheme = makePresentationTheme(data: data) else {
return .complete()
}
return .single(presentationTheme)
}
} else {
return .complete()
}
}
))
} else {
self.theme.set(.single(nil))
themeName = previewTheme.name.string
}
self.titleView.title = CounterContollerTitle(title: themeName, counter: " ")
let titleView = CounterContollerTitleView(theme: self.previewTheme)
titleView.title = CounterContollerTitle(title: themeName, counter: " ")
self.navigationItem.titleView = titleView
self.statusBar.statusBarStyle = self.previewTheme.rootController.statusBarStyle.style
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: self.previewTheme.rootController.navigationBar.accentTextColor), style: .plain, target: self, action: #selector(self.actionPressed))
self.disposable = (self.theme.get()
|> deliverOnMainQueue).start(next: { [weak self] theme in
self.disposable = (combineLatest(self.theme.get(), self.presentationTheme.get())
|> deliverOnMainQueue).start(next: { [weak self] theme, presentationTheme in
if let strongSelf = self, let theme = theme {
strongSelf.titleView.title = CounterContollerTitle(title: themeName, counter: strongSelf.presentationData.strings.Theme_UsersCount(max(1, theme.installCount)))
let titleView = CounterContollerTitleView(theme: strongSelf.previewTheme)
titleView.title = CounterContollerTitle(title: themeName, counter: strongSelf.presentationData.strings.Theme_UsersCount(max(1, theme.installCount)))
strongSelf.navigationItem.titleView = titleView
strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationTheme: presentationTheme, presentationStrings: strongSelf.presentationData.strings))
}
})

View File

@ -29,7 +29,7 @@ private func generateMaskImage(color: UIColor) -> UIImage? {
final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
private let context: AccountContext
private let previewTheme: PresentationTheme
private var previewTheme: PresentationTheme
private var presentationData: PresentationData
public let wallpaperPromise = Promise<TelegramWallpaper>()
@ -87,12 +87,15 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.instantChatBackgroundNode.displaysAsynchronously = false
self.instantChatBackgroundNode.image = chatControllerBackgroundImage(theme: previewTheme, wallpaper: previewTheme.chat.defaultWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
self.instantChatBackgroundNode.motionEnabled = previewTheme.chat.defaultWallpaper.settings?.motion ?? false
self.instantChatBackgroundNode.view.contentMode = .scaleAspectFill
self.remoteChatBackgroundNode = TransformImageNode()
self.remoteChatBackgroundNode.backgroundColor = previewTheme.chatList.backgroundColor
self.remoteChatBackgroundNode.view.contentMode = .scaleAspectFill
self.blurredNode = BlurredImageNode()
self.blurredNode.clipsToBounds = true
self.blurredNode.blurView.contentMode = .scaleAspectFill
self.toolbarNode = WallpaperGalleryToolbarNode(theme: self.previewTheme, strings: self.presentationData.strings)
@ -263,6 +266,27 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.pageControlNode.setPage(0.0)
}
func updateTheme(_ theme: PresentationTheme) {
self.previewTheme = theme
self.backgroundColor = self.previewTheme.list.plainBackgroundColor
self.pageControlNode.dotColor = self.previewTheme.chatList.unreadBadgeActiveBackgroundColor
self.pageControlNode.inactiveDotColor = self.previewTheme.list.pageIndicatorInactiveColor
self.chatListBackgroundNode.backgroundColor = self.previewTheme.chatList.backgroundColor
self.maskNode.image = generateMaskImage(color: self.previewTheme.chatList.backgroundColor)
if case let .color(value) = self.previewTheme.chat.defaultWallpaper {
self.instantChatBackgroundNode.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
}
self.toolbarNode.updateThemeAndStrings(theme: self.previewTheme, strings: self.presentationData.strings)
if let (layout, navigationBarHeight) = self.validLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate)
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let bounds = scrollView.bounds
if !bounds.width.isZero {

View File

@ -36,7 +36,7 @@ func themeDisplayName(strings: PresentationStrings, reference: PresentationTheme
private final class ThemeSettingsControllerArguments {
let context: AccountContext
let selectTheme: (PresentationThemeReference) -> Void
let updateTheme: (PresentationThemeReference) -> Void
let selectFontSize: (PresentationFontSize) -> Void
let openWallpaperSettings: () -> Void
let selectAccentColor: (PresentationThemeAccentColor) -> Void
@ -48,9 +48,9 @@ private final class ThemeSettingsControllerArguments {
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, editTheme: @escaping (PresentationCloudTheme) -> Void) {
init(context: AccountContext, updateTheme: @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.updateTheme = updateTheme
self.selectFontSize = selectFontSize
self.openWallpaperSettings = openWallpaperSettings
self.selectAccentColor = selectAccentColor
@ -290,14 +290,14 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
})
case let .themeListHeader(theme, text):
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, _):
return ThemeSettingsThemeItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, themes: themes, themeSpecificAccentColors: themeSpecificAccentColors, currentTheme: currentTheme, updatedTheme: { theme in
if case let .cloud(theme) = theme, theme.theme.file == nil {
if theme.theme.isCreator {
arguments.editTheme(theme)
}
} else {
arguments.selectTheme(theme)
arguments.updateTheme(theme)
}
}, longTapped: { theme in
arguments.presentThemeMenu(theme, theme.index == currentTheme.index)
@ -324,11 +324,6 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
}
}
private struct ThemeSettingsState: Equatable {
init() {
}
}
private func themeSettingsControllerEntries(presentationData: PresentationData, theme: PresentationTheme, themeReference: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], availableThemes: [PresentationThemeReference], autoNightSettings: AutomaticThemeSwitchSetting, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, largeEmoji: Bool, disableAnimations: Bool, availableAppIcons: [PresentationAppIcon], currentAppIconName: String?) -> [ThemeSettingsControllerEntry] {
var entries: [ThemeSettingsControllerEntry] = []
@ -342,19 +337,17 @@ private func themeSettingsControllerEntries(presentationData: PresentationData,
}
entries.append(.wallpaper(presentationData.theme, strings.Settings_ChatBackground))
if theme.name == .builtin(.day) || theme.name == .builtin(.dayClassic) {
let title: String
switch autoNightSettings.trigger {
case .none:
title = strings.AutoNightTheme_Disabled
case .timeBased:
title = strings.AutoNightTheme_Scheduled
case .brightness:
title = strings.AutoNightTheme_Automatic
}
entries.append(.autoNightTheme(presentationData.theme, strings.Appearance_AutoNightTheme, title))
let title: String
switch autoNightSettings.trigger {
case .none:
title = strings.AutoNightTheme_Disabled
case .timeBased:
title = strings.AutoNightTheme_Scheduled
case .brightness:
title = strings.AutoNightTheme_Automatic
}
entries.append(.autoNightTheme(presentationData.theme, strings.Appearance_AutoNightTheme, title))
entries.append(.fontSizeHeader(presentationData.theme, strings.Appearance_TextSize.uppercased()))
entries.append(.fontSize(presentationData.theme, fontSize))
@ -373,18 +366,11 @@ private func themeSettingsControllerEntries(presentationData: PresentationData,
}
public func themeSettingsController(context: AccountContext, focusOnItemTag: ThemeSettingsEntryTag? = nil) -> ViewController {
let initialState = ThemeSettingsState()
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
let stateValue = Atomic(value: initialState)
let updateState: ((ThemeSettingsState) -> ThemeSettingsState) -> Void = { f in
statePromise.set(stateValue.modify { f($0) })
}
var pushControllerImpl: ((ViewController) -> Void)?
var presentControllerImpl: ((ViewController, Any?) -> Void)?
var getNavigationControllerImpl: (() -> NavigationController?)?
var selectThemeImpl: ((PresentationThemeReference) -> Void)?
var updateThemeImpl: ((PresentationThemeReference) -> Void)?
var moreImpl: (() -> Void)?
let _ = telegramWallpapers(postbox: context.account.postbox, network: context.account.network).start()
@ -405,8 +391,8 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
let updatedCloudThemes = telegramThemes(postbox: context.account.postbox, network: context.account.network, accountManager: context.sharedContext.accountManager)
cloudThemes.set(updatedCloudThemes)
let arguments = ThemeSettingsControllerArguments(context: context, selectTheme: { theme in
selectThemeImpl?(theme)
let arguments = ThemeSettingsControllerArguments(context: context, updateTheme: { theme in
updateThemeImpl?(theme)
}, selectFontSize: { size in
let _ = updatePresentationThemeSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: size, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
@ -488,7 +474,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
} else {
newTheme = .builtin(.nightAccent)
}
selectThemeImpl?(newTheme)
updateThemeImpl?(newTheme)
}
let _ = deleteThemeInteractively(account: context.account, accountManager: context.sharedContext.accountManager, theme: theme.theme).start()
@ -516,8 +502,8 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
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())
|> map { presentationData, sharedData, cloudThemes, availableAppIcons, currentAppIconName, state -> (ItemListControllerState, (ItemListNodeState<ThemeSettingsControllerEntry>, ThemeSettingsControllerEntry.ItemGenerationArguments)) in
let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings]), cloudThemes.get(), availableAppIcons, currentAppIconName.get())
|> map { presentationData, sharedData, cloudThemes, availableAppIcons, currentAppIconName -> (ItemListControllerState, (ItemListNodeState<ThemeSettingsControllerEntry>, ThemeSettingsControllerEntry.ItemGenerationArguments)) in
let settings = (sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings] as? PresentationThemeSettings) ?? PresentationThemeSettings.defaultSettings
let fontSize = settings.fontSize
@ -565,7 +551,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
getNavigationControllerImpl = { [weak controller] in
return controller?.navigationController as? NavigationController
}
selectThemeImpl = { theme in
updateThemeImpl = { theme in
let presentationTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: theme, accentColor: nil, serviceBackgroundColor: .black, baseColor: nil)
let resolvedWallpaper: Signal<TelegramWallpaper?, NoError>
@ -590,7 +576,6 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
if case let .cloud(info) = theme {
updatedTheme = .cloud(PresentationCloudTheme(theme: info.theme, resolvedWallpaper: resolvedWallpaper))
}
return (context.sharedContext.accountManager.transaction { transaction -> Void in
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in
let current: PresentationThemeSettings

View File

@ -160,19 +160,12 @@ final class WallpaperColorPanelNode: ASDisplayNode, UITextFieldDelegate {
}
@objc internal func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.count > 1 {
if string.count <= 6 {
var updated = textField.text ?? ""
updated.replaceSubrange(updated.index(updated.startIndex, offsetBy: range.lowerBound) ..< updated.index(updated.startIndex, offsetBy: range.upperBound), with: string)
if updated.count <= 6 && updated.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted) == nil {
textField.text = updated.uppercased()
}
}
return false
} else if string.count == 1 {
return (textField.text ?? "").count < 6 && string.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted) == nil
var updated = textField.text ?? ""
updated.replaceSubrange(updated.index(updated.startIndex, offsetBy: range.lowerBound) ..< updated.index(updated.startIndex, offsetBy: range.upperBound), with: string)
if updated.count <= 6 && updated.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted) == nil {
textField.text = updated.uppercased()
}
return true
return false
}
@objc func textFieldTextChanged(_ sender: UITextField) {

View File

@ -66,8 +66,8 @@ final class WallpaperGalleryToolbarNode: ASDisplayNode {
self.separatorNode.backgroundColor = theme.rootController.tabBar.separatorColor
self.topSeparatorNode.backgroundColor = theme.rootController.tabBar.separatorColor
self.cancelButton.setTitle(strings.Common_Cancel, with: Font.regular(17.0), with: theme.rootController.navigationBar.primaryTextColor, for: [])
self.doneButton.setTitle(strings.Wallpaper_Set, with: Font.regular(17.0), with: theme.rootController.navigationBar.primaryTextColor, for: [])
self.cancelButton.setTitle(strings.Common_Cancel, with: Font.regular(17.0), with: theme.list.itemPrimaryTextColor, for: [])
self.doneButton.setTitle(strings.Wallpaper_Set, with: Font.regular(17.0), with: theme.list.itemPrimaryTextColor, for: [])
}
func updateLayout(size: CGSize, layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {

View File

@ -455,7 +455,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
if let (item, previousItem, nextItem, order, type, _) = self.playlistStateAndType, !mediaAccessoryPanelHidden {
let panelHeight = MediaNavigationAccessoryHeaderNode.minimizedHeight
let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: navigationHeight.isZero ? -panelHeight : (navigationHeight + additionalHeight + UIScreenPixel)), size: CGSize(width: layout.size.width, height: panelHeight))
let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: navigationHeight.isZero ? -panelHeight : (navigationHeight + additionalHeight)), size: CGSize(width: layout.size.width, height: panelHeight))
if let (mediaAccessoryPanel, mediaType) = self.mediaAccessoryPanel, mediaType == type {
transition.updateFrame(layer: mediaAccessoryPanel.layer, frame: panelFrame)
mediaAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: transition)
@ -615,7 +615,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
if let dismissingPanel = self.dismissingPanel {
self.displayNode.insertSubnode(mediaAccessoryPanel, aboveSubnode: dismissingPanel)
} else if let navigationBar = self.navigationBar {
self.displayNode.insertSubnode(mediaAccessoryPanel, aboveSubnode: navigationBar)
self.displayNode.insertSubnode(mediaAccessoryPanel, belowSubnode: navigationBar)
} else {
self.displayNode.addSubnode(mediaAccessoryPanel)
}

View File

@ -32,6 +32,7 @@ public enum AddressNameAvailability: Equatable {
public enum AddressNameDomain {
case account
case peer(PeerId)
case theme(TelegramTheme)
}
public func checkAddressNameFormat(_ value: String, canEmpty: Bool = false) -> AddressNameFormatError? {
@ -106,6 +107,20 @@ public func addressNameAvailability(account: Account, domain: AddressNameDomain,
} else {
return .single(.invalid)
}
case .theme:
return account.network.request(Api.functions.account.createTheme(slug: name, title: "", document: .inputDocumentEmpty))
|> map { _ -> AddressNameAvailability in
return .available
}
|> `catch` { error -> Signal<AddressNameAvailability, NoError> in
if error.errorDescription == "THEME_SLUG_OCCUPIED" {
return .single(.taken)
} else if error.errorDescription == "THEME_SLUG_INVALID" {
return .single(.invalid)
} else {
return .single(.available)
}
}
}
} |> switchToLatest
}
@ -154,6 +169,15 @@ public func updateAddressName(account: Account, domain: AddressNameDomain, name:
} else {
return .fail(.generic)
}
case let .theme(theme):
let flags: Int32 = 1 << 0
return account.network.request(Api.functions.account.updateTheme(flags: flags, format: telegramThemeFormat, theme: .inputTheme(id: theme.id, accessHash: theme.accessHash), slug: nil, title: nil, document: nil))
|> mapError { _ -> UpdateAddressNameError in
return .generic
}
|> map { _ in
return Void()
}
}
} |> mapError { _ -> UpdateAddressNameError in return .generic } |> switchToLatest
}

View File

@ -26,16 +26,16 @@ final class CachedThemesConfiguration: PostboxCoding {
}
#if os(macOS)
private let themeFormat = "macos"
private let themeFileExtension = "palette"
let telegramThemeFormat = "macos"
let telegramThemeFileExtension = "palette"
#else
private let themeFormat = "ios"
private let themeFileExtension = "tgios-theme"
let telegramThemeFormat = "ios"
let telegramThemeFileExtension = "tgios-theme"
#endif
public func telegramThemes(postbox: Postbox, network: Network, accountManager: AccountManager, forceUpdate: Bool = false) -> Signal<[TelegramTheme], NoError> {
let fetch: ([TelegramTheme]?, Int32?) -> Signal<[TelegramTheme], NoError> = { current, hash in
network.request(Api.functions.account.getThemes(format: themeFormat, hash: hash ?? 0))
network.request(Api.functions.account.getThemes(format: telegramThemeFormat, hash: hash ?? 0))
|> retryRequest
|> mapToSignal { result -> Signal<([TelegramTheme], Int32), NoError> in
switch result {
@ -109,7 +109,7 @@ public enum GetThemeError {
}
public func getTheme(account: Account, slug: String) -> Signal<TelegramTheme, GetThemeError> {
return account.network.request(Api.functions.account.getTheme(format: themeFormat, theme: .inputThemeSlug(slug: slug), documentId: 0))
return account.network.request(Api.functions.account.getTheme(format: telegramThemeFormat, theme: .inputThemeSlug(slug: slug), documentId: 0))
|> mapError { error -> GetThemeError in
if error.errorDescription == "THEME_FORMAT_INVALID" {
return .unsupported
@ -137,7 +137,7 @@ private func checkThemeUpdated(network: Network, theme: TelegramTheme) -> Signal
guard let file = theme.file, let fileId = file.id?.id else {
return .fail(.generic)
}
return network.request(Api.functions.account.getTheme(format: themeFormat, theme: .inputTheme(id: theme.id, accessHash: theme.accessHash), documentId: fileId))
return network.request(Api.functions.account.getTheme(format: telegramThemeFormat, theme: .inputTheme(id: theme.id, accessHash: theme.accessHash), documentId: fileId))
|> mapError { _ -> GetThemeError in return .generic }
|> map { theme -> ThemeUpdatedResult in
if let theme = TelegramTheme(apiTheme: theme) {
@ -192,7 +192,7 @@ private func installTheme(account: Account, theme: TelegramTheme?, autoNight: Bo
inputTheme = nil
}
return account.network.request(Api.functions.account.installTheme(flags: flags, format: themeFormat, theme: inputTheme))
return account.network.request(Api.functions.account.installTheme(flags: flags, format: telegramThemeFormat, theme: inputTheme))
|> `catch` { _ -> Signal<Api.Bool, NoError> in
return .complete()
}
@ -240,8 +240,8 @@ private func uploadedThemeThumbnail(postbox: Postbox, network: Network, data: Da
}
private func uploadTheme(account: Account, resource: MediaResource, thumbnailData: Data? = nil) -> Signal<UploadThemeResult, UploadThemeError> {
let fileName = "theme.\(themeFileExtension)"
let mimeType = "application/x-tgtheme-\(themeFormat)"
let fileName = "theme.\(telegramThemeFileExtension)"
let mimeType = "application/x-tgtheme-\(telegramThemeFormat)"
let uploadedThumbnail: Signal<UploadedThemeData?, UploadThemeError>
if let thumbnailData = thumbnailData {
@ -387,7 +387,7 @@ public func updateTheme(account: Account, accountManager: AccountManager, theme:
inputDocument = nil
}
return account.network.request(Api.functions.account.updateTheme(flags: flags, format: themeFormat, theme: .inputTheme(id: theme.id, accessHash: theme.accessHash), slug: slug, title: title, document: inputDocument))
return account.network.request(Api.functions.account.updateTheme(flags: flags, format: telegramThemeFormat, theme: .inputTheme(id: theme.id, accessHash: theme.accessHash), slug: slug, title: title, document: inputDocument))
|> mapError { error in
if error.errorDescription == "THEME_SLUG_INVALID" {
return .slugInvalid

View File

@ -100,6 +100,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
)
let intro = PresentationThemeIntro(
statusBarStyle: .white,
startButtonColor: accentColor,
dotColor: UIColor(rgb: 0x5e5e5e)
)

View File

@ -76,6 +76,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
)
let intro = PresentationThemeIntro(
statusBarStyle: .white,
startButtonColor: accentColor,
dotColor: mainSecondaryColor
)

View File

@ -83,6 +83,7 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
)
let intro = PresentationThemeIntro(
statusBarStyle: .black,
startButtonColor: UIColor(rgb: 0x2ca5e0),
dotColor: UIColor(rgb: 0xd9d9d9)
)
@ -294,8 +295,8 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
)
let historyNavigation = PresentationThemeChatHistoryNavigation(
fillColor: .white,
strokeColor: UIColor(rgb: 0x000000, alpha: 0.15),
fillColor: UIColor(rgb: 0xf7f7f7),
strokeColor: UIColor(rgb: 0xb1b1b1),
foregroundColor: UIColor(rgb: 0x88888d),
badgeBackgroundColor: accentColor,
badgeStrokeColor: accentColor,

View File

@ -254,7 +254,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager) -
let parameters = AutomaticThemeSwitchParameters(settings: themeSettings.automaticThemeSwitchSetting)
if automaticThemeShouldSwitchNow(parameters, currentTheme: themeSettings.theme) {
effectiveTheme = .builtin(themeSettings.automaticThemeSwitchSetting.theme)
effectiveTheme = themeSettings.automaticThemeSwitchSetting.theme
} else {
effectiveTheme = themeSettings.theme
}
@ -301,7 +301,7 @@ private enum PreparedAutomaticThemeSwitchTrigger {
private struct AutomaticThemeSwitchParameters {
let trigger: PreparedAutomaticThemeSwitchTrigger
let theme: PresentationBuiltinThemeReference
let theme: PresentationThemeReference
init(settings: AutomaticThemeSwitchSetting) {
let trigger: PreparedAutomaticThemeSwitchTrigger
@ -330,17 +330,6 @@ private struct AutomaticThemeSwitchParameters {
}
private func automaticThemeShouldSwitchNow(_ parameters: AutomaticThemeSwitchParameters, currentTheme: PresentationThemeReference) -> Bool {
switch currentTheme {
case let .builtin(builtin):
switch builtin {
case .nightAccent, .night:
return false
default:
break
}
default:
return false
}
switch parameters.trigger {
case .none:
return false
@ -514,7 +503,7 @@ public func updatedPresentationData(accountManager: AccountManager, applicationI
var effectiveChatWallpaper: TelegramWallpaper = currentWallpaper
if shouldSwitch {
let automaticTheme: PresentationThemeReference = .builtin(themeSettings.automaticThemeSwitchSetting.theme)
let automaticTheme = themeSettings.automaticThemeSwitchSetting.theme
if let themeSpecificWallpaper = themeSettings.themeSpecificChatWallpapers[automaticTheme.index] {
effectiveChatWallpaper = themeSpecificWallpaper
}

View File

@ -19,10 +19,12 @@ public final class PresentationThemeGradientColors {
}
public final class PresentationThemeIntro {
public let statusBarStyle: PresentationThemeStatusBarStyle
public let startButtonColor: UIColor
public let dotColor: UIColor
public init(startButtonColor: UIColor, dotColor: UIColor) {
public init(statusBarStyle: PresentationThemeStatusBarStyle, startButtonColor: UIColor, dotColor: UIColor) {
self.statusBarStyle = statusBarStyle
self.startButtonColor = startButtonColor
self.dotColor = dotColor
}

View File

@ -237,18 +237,21 @@ extension PresentationThemeGradientColors: Codable {
extension PresentationThemeIntro: Codable {
enum CodingKeys: String, CodingKey {
case statusBar
case startButton
case dot
}
public convenience init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
self.init(startButtonColor: try decodeColor(values, .startButton),
self.init(statusBarStyle: try values.decode(PresentationThemeStatusBarStyle.self, forKey: .statusBar),
startButtonColor: try decodeColor(values, .startButton),
dotColor: try decodeColor(values, .dot))
}
public func encode(to encoder: Encoder) throws {
var values = encoder.container(keyedBy: CodingKeys.self)
try values.encode(self.statusBarStyle, forKey: .statusBar)
try encodeColor(&values, self.startButtonColor, .startButton)
try encodeColor(&values, self.dotColor, .dot)
}

View File

@ -2,18 +2,36 @@ import Foundation
public enum ArabicNumeralStringType {
case western
case eastern
case arabic
case persian
}
public func normalizeArabicNumeralString(_ string: String, type: ArabicNumeralStringType) -> String {
var string = string
let numerals = ["٠": "0", "١": "1", "٢": "2", "٣": "3", "٤": "4", "٥": "5", "٦": "6", "٧": "7", "٨": "8", "٩": "9"]
for (easternNumeral, westernNumeral) in numerals {
let numerals = [
("0", "٠", "۰"),
("1", "١", "۱"),
("2", "٢", "۲"),
("3", "٣", "۳"),
("4", "٤", "۴"),
("5", "٥", "۵"),
("6", "٦", "۶"),
("7", "٧", "۷"),
("8", "٨", "۸"),
("9", "٩", "۹"),
]
for (western, arabic, persian) in numerals {
switch type {
case .western:
string = string.replacingOccurrences(of: easternNumeral, with: westernNumeral)
case .eastern:
string = string.replacingOccurrences(of: westernNumeral, with: easternNumeral)
string = string.replacingOccurrences(of: arabic, with: western)
string = string.replacingOccurrences(of: persian, with: western)
case .arabic:
string = string.replacingOccurrences(of: western, with: arabic)
string = string.replacingOccurrences(of: persian, with: arabic)
case .persian:
string = string.replacingOccurrences(of: western, with: persian)
string = string.replacingOccurrences(of: arabic, with: persian)
}
}

View File

@ -114,7 +114,7 @@ public func stringForUserPresence(strings: PresentationStrings, day: RelativeTim
let dayString: String
switch day {
case .today:
dayString = strings.LastSeen_AtDate(strings.Time_TodayAt(stringForShortTimestamp(hours: hours, minutes: minutes, dateTimeFormat: dateTimeFormat)).0).0
dayString = strings.LastSeen_TodayAt(stringForShortTimestamp(hours: hours, minutes: minutes, dateTimeFormat: dateTimeFormat)).0
case .yesterday:
dayString = strings.LastSeen_YesterdayAt(stringForShortTimestamp(hours: hours, minutes: minutes, dateTimeFormat: dateTimeFormat)).0
}

View File

@ -38,7 +38,7 @@ final class AuthorizationSequenceAwaitingAccountResetController: ViewController
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
self.statusBar.statusBarStyle = theme.rootController.statusBarStyle.style
self.statusBar.statusBarStyle = theme.intro.statusBarStyle.style
self.attemptNavigation = { _ in
return false

View File

@ -47,7 +47,7 @@ final class AuthorizationSequenceCodeEntryController: ViewController {
self.hasActiveInput = true
self.statusBar.statusBarStyle = theme.rootController.statusBarStyle.style
self.statusBar.statusBarStyle = theme.intro.statusBarStyle.style
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed))

View File

@ -54,7 +54,7 @@ final class AuthorizationSequencePasswordEntryController: ViewController {
self.hasActiveInput = true
self.statusBar.statusBarStyle = theme.rootController.statusBarStyle.style
self.statusBar.statusBarStyle = theme.intro.statusBarStyle.style
self.attemptNavigation = { _ in
return false

View File

@ -42,7 +42,7 @@ final class AuthorizationSequencePasswordRecoveryController: ViewController {
self.hasActiveInput = true
self.statusBar.statusBarStyle = theme.rootController.statusBarStyle.style
self.statusBar.statusBarStyle = theme.intro.statusBarStyle.style
self.attemptNavigation = { _ in
return false

View File

@ -61,7 +61,7 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
self.hasActiveInput = true
self.statusBar.statusBarStyle = theme.rootController.statusBarStyle.style
self.statusBar.statusBarStyle = theme.intro.statusBarStyle.style
self.attemptNavigation = { _ in
return false
}

View File

@ -47,7 +47,7 @@ final class AuthorizationSequenceSignUpController: ViewController {
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
self.statusBar.statusBarStyle = self.theme.rootController.statusBarStyle.style
self.statusBar.statusBarStyle = theme.intro.statusBarStyle.style
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed))

View File

@ -74,7 +74,7 @@ final class AuthorizationSequenceSplashController: ViewController {
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
self.statusBar.statusBarStyle = theme.rootController.statusBarStyle.style
self.statusBar.statusBarStyle = theme.intro.statusBarStyle.style
self.controller.startMessaging = { [weak self] in
self?.activateLocalization("en")

View File

@ -526,6 +526,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
if let messages = strongSelf.chatDisplayNode.historyNode.messageGroupInCurrentHistoryView(message.id) {
(strongSelf.view.window as? WindowHost)?.cancelInteractiveKeyboardGestures()
strongSelf.chatDisplayNode.cancelInteractiveKeyboardGestures()
var updatedMessages = messages
for i in 0 ..< updatedMessages.count {
if updatedMessages[i].id == message.id {
@ -1553,10 +1554,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let controller = ChatScheduleTimeController(context: strongSelf.context, mode: mode, minimalTime: strongSelf.presentationInterfaceState.slowmodeState?.timeout, completion: { [weak self] scheduleTime in
if let strongSelf = self {
strongSelf.chatDisplayNode.sendCurrentMessage(scheduleTime: scheduleTime)
if !strongSelf.presentationInterfaceState.isScheduledMessages {
strongSelf.openScheduledMessages()
}
strongSelf.chatDisplayNode.sendCurrentMessage(scheduleTime: scheduleTime, completion: { [weak self] in
if let strongSelf = self, !strongSelf.presentationInterfaceState.isScheduledMessages {
strongSelf.openScheduledMessages()
}
})
}
})
strongSelf.chatDisplayNode.dismissInput()

View File

@ -2057,7 +2057,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
}
func sendCurrentMessage(silentPosting: Bool? = nil, scheduleTime: Int32? = nil) {
func sendCurrentMessage(silentPosting: Bool? = nil, scheduleTime: Int32? = nil, completion: @escaping () -> Void = {}) {
if let textInputPanelNode = self.inputPanelNode as? ChatTextInputPanelNode {
if textInputPanelNode.textInputNode?.isFirstResponder() ?? false {
Keyboard.applyAutocorrection()
@ -2114,6 +2114,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
textInputPanelNode.text = ""
strongSelf.requestUpdateChatInterfaceState(false, true, { $0.withUpdatedReplyMessageId(nil).withUpdatedForwardMessageIds(nil).withUpdatedComposeDisableUrlPreview(nil) })
strongSelf.ignoreUpdateHeight = false
completion()
}
})

View File

@ -145,8 +145,8 @@ final class ChatInfoTitlePanelNode: ChatTitleAccessoryPanelNode {
let panelHeight: CGFloat = 55.0
if themeUpdated {
self.separatorNode.backgroundColor = interfaceState.theme.rootController.navigationBar.separatorColor
self.backgroundColor = interfaceState.theme.rootController.navigationBar.backgroundColor
self.backgroundColor = interfaceState.theme.chat.historyNavigation.fillColor
self.separatorNode.backgroundColor = interfaceState.theme.chat.historyNavigation.strokeColor
}
let updatedButtons: [ChatInfoTitleButton]
@ -157,8 +157,6 @@ final class ChatInfoTitlePanelNode: ChatTitleAccessoryPanelNode {
} else {
updatedButtons = []
}
/*case .group:
updatedButtons = groupButtons()*/
}
var buttonsUpdated = false
@ -182,7 +180,7 @@ final class ChatInfoTitlePanelNode: ChatTitleAccessoryPanelNode {
let buttonNode = ChatInfoTitlePanelButtonNode()
buttonNode.laysOutHorizontally = false
buttonNode.setup(text: button.title(interfaceState.strings), color: interfaceState.theme.rootController.navigationBar.accentTextColor, icon: button.icon(interfaceState.theme))
buttonNode.setup(text: button.title(interfaceState.strings), color: interfaceState.theme.chat.inputPanel.panelControlAccentColor, icon: button.icon(interfaceState.theme))
buttonNode.addTarget(self, action: #selector(self.buttonPressed(_:)), forControlEvents: [.touchUpInside])
self.addSubnode(buttonNode)

View File

@ -373,6 +373,24 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
})))
}
if data.messageActions.options.contains(.sendScheduledNow) {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.ScheduledMessages_SendNow, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Resend"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in
controllerInteraction.sendScheduledMessagesNow(selectAll ? messages.map { $0.id } : [message.id])
f(.dismissWithoutContent)
})))
}
if data.messageActions.options.contains(.editScheduledTime) {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.ScheduledMessages_EditTime, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Schedule"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in
controllerInteraction.editScheduledMessagesTime(selectAll ? messages.map { $0.id } : [message.id])
f(.dismissWithoutContent)
})))
}
let resourceAvailable: Bool
if let resourceStatus = data.resourceStatus, case .Local = resourceStatus {
resourceAvailable = true
@ -425,24 +443,6 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
})))
}
if data.messageActions.options.contains(.sendScheduledNow) {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.ScheduledMessages_SendNow, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Resend"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in
controllerInteraction.sendScheduledMessagesNow(selectAll ? messages.map { $0.id } : [message.id])
f(.dismissWithoutContent)
})))
}
if data.messageActions.options.contains(.editScheduledTime) {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.ScheduledMessages_EditTime, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Schedule"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in
controllerInteraction.editScheduledMessagesTime(selectAll ? messages.map { $0.id } : [message.id])
f(.dismissWithoutContent)
})))
}
if data.canEdit {
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_MessageDialogEdit, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.actionSheet.primaryTextColor)
@ -738,8 +738,14 @@ func chatAvailableMessageActionsImpl(postbox: Postbox, accountPeerId: PeerId, me
}
if id.namespace == Namespaces.Message.ScheduledCloud {
optionsMap[id]!.insert(.sendScheduledNow)
optionsMap[id]!.insert(.editScheduledTime)
optionsMap[id]!.insert(.deleteLocally)
if let peer = transaction.getPeer(id.peerId), let channel = peer as? TelegramChannel, !channel.hasPermission(.editAllMessages) {
} else {
optionsMap[id]!.insert(.editScheduledTime)
}
if let peer = transaction.getPeer(id.peerId), let channel = peer as? TelegramChannel, !channel.hasPermission(.deleteAllMessages) {
} else {
optionsMap[id]!.insert(.deleteLocally)
}
} else if id.peerId == accountPeerId {
if !(message.flags.isSending || message.flags.contains(.Failed)) {
optionsMap[id]!.insert(.forward)

View File

@ -190,6 +190,15 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
}
return .waitForSingleTap
}
recognizer.longTap = { [weak self] point, recognizer in
guard let strongSelf = self else {
return
}
//strongSelf.reactionRecognizer?.cancel()
if strongSelf.gestureRecognized(gesture: .longTap, location: point, recognizer: recognizer) {
recognizer.cancel()
}
}
self.view.addGestureRecognizer(recognizer)
let replyRecognizer = ChatSwipeToReplyRecognizer(target: self, action: #selector(self.swipeToReplyGesture(_:)))
@ -736,129 +745,135 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
switch recognizer.state {
case .ended:
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
switch gesture {
case .tap:
if let avatarNode = self.accessoryItemNode as? ChatMessageAvatarAccessoryItemNode, avatarNode.frame.contains(location) {
if let item = self.item, let author = item.content.firstMessage.author {
var openPeerId = item.effectiveAuthorId ?? author.id
var navigate: ChatControllerInteractionNavigateToPeer
if item.content.firstMessage.id.peerId == item.context.account.peerId {
navigate = .chat(textInputState: nil, subject: nil)
} else {
navigate = .info
}
for attribute in item.content.firstMessage.attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {
openPeerId = attribute.messageId.peerId
navigate = .chat(textInputState: nil, subject: .message(attribute.messageId))
}
}
if item.effectiveAuthorId?.namespace == Namespaces.Peer.Empty {
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, avatarNode.frame)
} else {
if let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
if case .member = channel.participationStatus {
} else {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
return
}
}
item.controllerInteraction.openPeer(openPeerId, navigate, item.message)
}
}
return
}
if let viaBotNode = self.viaBotNode, viaBotNode.frame.contains(location) {
if let item = self.item {
for attribute in item.message.attributes {
if let attribute = attribute as? InlineBotMessageAttribute {
var botAddressName: String?
if let peerId = attribute.peerId, let botPeer = item.message.peers[peerId], let addressName = botPeer.addressName {
botAddressName = addressName
} else {
botAddressName = attribute.title
}
if let botAddressName = botAddressName {
item.controllerInteraction.updateInputState { textInputState in
return ChatTextInputState(inputText: NSAttributedString(string: "@" + botAddressName + " "))
}
item.controllerInteraction.updateInputMode { _ in
return .text
}
}
return
}
}
}
}
if let replyInfoNode = self.replyInfoNode, replyInfoNode.frame.contains(location) {
if let item = self.item {
for attribute in item.message.attributes {
if let attribute = attribute as? ReplyMessageAttribute {
item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId)
return
}
}
}
}
if let item = self.item, self.imageNode.frame.contains(location) {
if self.telegramFile != nil {
let _ = item.controllerInteraction.openMessage(item.message, .default)
} else if let _ = self.emojiFile {
var startTime: Signal<Double, NoError>
if self.animationNode.playIfNeeded() {
startTime = .single(0.0)
} else {
startTime = self.animationNode.status
|> map { $0.timestamp }
|> take(1)
|> deliverOnMainQueue
}
if let text = self.item?.message.text, let firstScalar = text.unicodeScalars.first, firstScalar.value == 0x2764 {
let _ = startTime.start(next: { [weak self] time in
guard let strongSelf = self else {
return
}
let heartbeatHaptic: ChatMessageHeartbeatHaptic
if let current = strongSelf.heartbeatHaptic {
heartbeatHaptic = current
} else {
heartbeatHaptic = ChatMessageHeartbeatHaptic()
heartbeatHaptic.enabled = true
strongSelf.heartbeatHaptic = heartbeatHaptic
}
if !heartbeatHaptic.active {
heartbeatHaptic.start(time: time)
}
})
}
}
return
}
self.item?.controllerInteraction.clickThroughMessage()
case .longTap, .doubleTap:
if let item = self.item, self.imageNode.frame.contains(location) {
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.imageNode.frame, nil)
}
case .hold:
break
}
let _ = self.gestureRecognized(gesture: gesture, location: location, recognizer: nil)
}
default:
break
}
}
private func gestureRecognized(gesture: TapLongTapOrDoubleTapGesture, location: CGPoint, recognizer: TapLongTapOrDoubleTapGestureRecognizer?) -> Bool {
switch gesture {
case .tap:
if let avatarNode = self.accessoryItemNode as? ChatMessageAvatarAccessoryItemNode, avatarNode.frame.contains(location) {
if let item = self.item, let author = item.content.firstMessage.author {
var openPeerId = item.effectiveAuthorId ?? author.id
var navigate: ChatControllerInteractionNavigateToPeer
if item.content.firstMessage.id.peerId == item.context.account.peerId {
navigate = .chat(textInputState: nil, subject: nil)
} else {
navigate = .info
}
for attribute in item.content.firstMessage.attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {
openPeerId = attribute.messageId.peerId
navigate = .chat(textInputState: nil, subject: .message(attribute.messageId))
}
}
if item.effectiveAuthorId?.namespace == Namespaces.Peer.Empty {
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, avatarNode.frame)
} else {
if let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
if case .member = channel.participationStatus {
} else {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
return true
}
}
item.controllerInteraction.openPeer(openPeerId, navigate, item.message)
}
}
return true
}
if let viaBotNode = self.viaBotNode, viaBotNode.frame.contains(location) {
if let item = self.item {
for attribute in item.message.attributes {
if let attribute = attribute as? InlineBotMessageAttribute {
var botAddressName: String?
if let peerId = attribute.peerId, let botPeer = item.message.peers[peerId], let addressName = botPeer.addressName {
botAddressName = addressName
} else {
botAddressName = attribute.title
}
if let botAddressName = botAddressName {
item.controllerInteraction.updateInputState { textInputState in
return ChatTextInputState(inputText: NSAttributedString(string: "@" + botAddressName + " "))
}
item.controllerInteraction.updateInputMode { _ in
return .text
}
}
return true
}
}
}
}
if let replyInfoNode = self.replyInfoNode, replyInfoNode.frame.contains(location) {
if let item = self.item {
for attribute in item.message.attributes {
if let attribute = attribute as? ReplyMessageAttribute {
item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId)
return true
}
}
}
}
if let item = self.item, self.imageNode.frame.contains(location) {
if self.telegramFile != nil {
let _ = item.controllerInteraction.openMessage(item.message, .default)
} else if let _ = self.emojiFile {
var startTime: Signal<Double, NoError>
if self.animationNode.playIfNeeded() {
startTime = .single(0.0)
} else {
startTime = self.animationNode.status
|> map { $0.timestamp }
|> take(1)
|> deliverOnMainQueue
}
if let text = self.item?.message.text, let firstScalar = text.unicodeScalars.first, firstScalar.value == 0x2764 {
let _ = startTime.start(next: { [weak self] time in
guard let strongSelf = self else {
return
}
let heartbeatHaptic: ChatMessageHeartbeatHaptic
if let current = strongSelf.heartbeatHaptic {
heartbeatHaptic = current
} else {
heartbeatHaptic = ChatMessageHeartbeatHaptic()
heartbeatHaptic.enabled = true
strongSelf.heartbeatHaptic = heartbeatHaptic
}
if !heartbeatHaptic.active {
heartbeatHaptic.start(time: time)
}
})
}
}
return true
}
self.item?.controllerInteraction.clickThroughMessage()
case .longTap, .doubleTap:
if let item = self.item, self.imageNode.frame.contains(location) {
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.imageNode.frame, nil)
return false
}
case .hold:
break
}
return true
}
@objc func shareButtonPressed() {
if let item = self.item {
if item.content.firstMessage.id.peerId == item.context.account.peerId {

View File

@ -402,8 +402,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
if let (media, flags) = mediaAndFlags {
if let file = media as? TelegramMediaFile {
if file.mimeType == "application/x-tgtheme-ios", let size = file.size, size < 16 * 1024 {
let automaticDownload = true
let (_, initialImageWidth, refineLayout) = contentImageLayout(context, presentationData.theme.theme, presentationData.strings, presentationData.dateTimeFormat, message, file, automaticDownload ? .full : .none, associatedData.automaticDownloadPeerType, .constrained(CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height)), layoutConstants, contentMode)
let (_, initialImageWidth, refineLayout) = contentImageLayout(context, presentationData.theme.theme, presentationData.strings, presentationData.dateTimeFormat, message, file, .full, associatedData.automaticDownloadPeerType, .constrained(CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height)), layoutConstants, contentMode)
initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right
refineContentImageLayout = refineLayout
} else if file.isInstantVideo {
@ -521,7 +520,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
if let count = webpageGalleryMediaCount {
additionalImageBadgeContent = .text(inset: 0.0, backgroundColor: presentationData.theme.theme.chat.message.mediaDateAndStatusFillColor, foregroundColor: presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor, text: NSAttributedString(string: "1 \(presentationData.strings.Common_of) \(count)"))
additionalImageBadgeContent = .text(inset: 0.0, backgroundColor: presentationData.theme.theme.chat.message.mediaDateAndStatusFillColor, foregroundColor: presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor, text: NSAttributedString(string: presentationData.strings.Items_NOfM("1", "\(count)").0))
skipStandardStatus = imageMode
} else if let mediaBadge = mediaBadge {
additionalImageBadgeContent = .text(inset: 0.0, backgroundColor: presentationData.theme.theme.chat.message.mediaDateAndStatusFillColor, foregroundColor: presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor, text: NSAttributedString(string: mediaBadge))

View File

@ -832,7 +832,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
}
strongSelf.fetchDisposable.set(visibilityAwareFetchSignal.start())
}
} else if case .prefetch = automaticDownload, message.id.namespace != Namespaces.Message.SecretIncoming {
} else if case .prefetch = automaticDownload, message.id.namespace != Namespaces.Message.SecretIncoming && message.id.namespace != Namespaces.Message.Local {
if let file = media as? TelegramMediaFile {
let fetchSignal = preloadVideoResource(postbox: context.account.postbox, resourceReference: AnyMediaReference.message(message: MessageReference(message), media: file).resourceReference(file.resource), duration: 4.0)
let visibilityAwareFetchSignal = strongSelf.visibilityPromise.get()

View File

@ -80,6 +80,15 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
return .waitForSingleTap
}
recognizer.longTap = { [weak self] point, recognizer in
guard let strongSelf = self else {
return
}
//strongSelf.reactionRecognizer?.cancel()
if strongSelf.gestureRecognized(gesture: .longTap, location: point, recognizer: recognizer) {
recognizer.cancel()
}
}
self.view.addGestureRecognizer(recognizer)
let replyRecognizer = ChatSwipeToReplyRecognizer(target: self, action: #selector(self.swipeToReplyGesture(_:)))
@ -577,97 +586,104 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
@objc func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
switch recognizer.state {
case .ended:
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
switch gesture {
case .tap:
if let avatarNode = self.accessoryItemNode as? ChatMessageAvatarAccessoryItemNode, avatarNode.frame.contains(location) {
if let item = self.item, let author = item.content.firstMessage.author {
var openPeerId = item.effectiveAuthorId ?? author.id
var navigate: ChatControllerInteractionNavigateToPeer
if item.content.firstMessage.id.peerId == item.context.account.peerId {
navigate = .chat(textInputState: nil, subject: nil)
} else {
navigate = .info
}
for attribute in item.content.firstMessage.attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {
openPeerId = attribute.messageId.peerId
navigate = .chat(textInputState: nil, subject: .message(attribute.messageId))
}
}
if item.effectiveAuthorId?.namespace == Namespaces.Peer.Empty {
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, avatarNode.frame)
} else {
if let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
if case .member = channel.participationStatus {
} else {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
return
}
}
item.controllerInteraction.openPeer(openPeerId, navigate, item.message)
}
}
return
}
if let viaBotNode = self.viaBotNode, viaBotNode.frame.contains(location) {
if let item = self.item {
for attribute in item.message.attributes {
if let attribute = attribute as? InlineBotMessageAttribute {
var botAddressName: String?
if let peerId = attribute.peerId, let botPeer = item.message.peers[peerId], let addressName = botPeer.addressName {
botAddressName = addressName
} else {
botAddressName = attribute.title
}
if let botAddressName = botAddressName {
item.controllerInteraction.updateInputState { textInputState in
return ChatTextInputState(inputText: NSAttributedString(string: "@" + botAddressName + " "))
}
item.controllerInteraction.updateInputMode { _ in
return .text
}
}
return
}
}
}
}
if let replyInfoNode = self.replyInfoNode, replyInfoNode.frame.contains(location) {
if let item = self.item {
for attribute in item.message.attributes {
if let attribute = attribute as? ReplyMessageAttribute {
item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId)
return
}
}
}
}
if let item = self.item, self.imageNode.frame.contains(location) {
let _ = item.controllerInteraction.openMessage(item.message, .default)
return
}
case .ended:
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
let _ = self.gestureRecognized(gesture: gesture, location: location, recognizer: nil)
}
default:
break
}
}
private func gestureRecognized(gesture: TapLongTapOrDoubleTapGesture, location: CGPoint, recognizer: TapLongTapOrDoubleTapGestureRecognizer?) -> Bool {
switch gesture {
case .tap:
if let avatarNode = self.accessoryItemNode as? ChatMessageAvatarAccessoryItemNode, avatarNode.frame.contains(location) {
if let item = self.item, let author = item.content.firstMessage.author {
var openPeerId = item.effectiveAuthorId ?? author.id
var navigate: ChatControllerInteractionNavigateToPeer
self.item?.controllerInteraction.clickThroughMessage()
case .longTap, .doubleTap:
if let item = self.item, self.imageNode.frame.contains(location) {
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.imageNode.frame, nil)
if item.content.firstMessage.id.peerId == item.context.account.peerId {
navigate = .chat(textInputState: nil, subject: nil)
} else {
navigate = .info
}
for attribute in item.content.firstMessage.attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {
openPeerId = attribute.messageId.peerId
navigate = .chat(textInputState: nil, subject: .message(attribute.messageId))
}
case .hold:
break
}
if item.effectiveAuthorId?.namespace == Namespaces.Peer.Empty {
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, avatarNode.frame)
} else {
if let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
if case .member = channel.participationStatus {
} else {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
return true
}
}
item.controllerInteraction.openPeer(openPeerId, navigate, item.message)
}
}
return true
}
if let viaBotNode = self.viaBotNode, viaBotNode.frame.contains(location) {
if let item = self.item {
for attribute in item.message.attributes {
if let attribute = attribute as? InlineBotMessageAttribute {
var botAddressName: String?
if let peerId = attribute.peerId, let botPeer = item.message.peers[peerId], let addressName = botPeer.addressName {
botAddressName = addressName
} else {
botAddressName = attribute.title
}
if let botAddressName = botAddressName {
item.controllerInteraction.updateInputState { textInputState in
return ChatTextInputState(inputText: NSAttributedString(string: "@" + botAddressName + " "))
}
item.controllerInteraction.updateInputMode { _ in
return .text
}
}
return true
}
}
}
}
default:
if let replyInfoNode = self.replyInfoNode, replyInfoNode.frame.contains(location) {
if let item = self.item {
for attribute in item.message.attributes {
if let attribute = attribute as? ReplyMessageAttribute {
item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId)
return true
}
}
}
}
if let item = self.item, self.imageNode.frame.contains(location) {
let _ = item.controllerInteraction.openMessage(item.message, .default)
return true
}
self.item?.controllerInteraction.clickThroughMessage()
case .longTap, .doubleTap:
if let item = self.item, self.imageNode.frame.contains(location) {
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.imageNode.frame, recognizer)
return false
}
case .hold:
break
}
return true
}
@objc func shareButtonPressed() {

View File

@ -110,8 +110,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
self.theme = interfaceState.theme
self.closeButton.setImage(PresentationResourcesChat.chatInputPanelCloseIconImage(interfaceState.theme), for: [])
self.lineNode.image = PresentationResourcesChat.chatInputPanelVerticalSeparatorLineImage(interfaceState.theme)
self.backgroundColor = interfaceState.theme.rootController.navigationBar.backgroundColor
self.separatorNode.backgroundColor = interfaceState.theme.rootController.navigationBar.separatorColor
self.backgroundColor = interfaceState.theme.chat.historyNavigation.fillColor
self.separatorNode.backgroundColor = interfaceState.theme.chat.historyNavigation.strokeColor
}
var messageUpdated = false

View File

@ -92,8 +92,8 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
self.theme = interfaceState.theme
self.closeButton.setImage(PresentationResourcesChat.chatInputPanelEncircledCloseIconImage(interfaceState.theme), for: [])
self.backgroundColor = interfaceState.theme.rootController.navigationBar.backgroundColor
self.separatorNode.backgroundColor = interfaceState.theme.rootController.navigationBar.separatorColor
self.backgroundColor = interfaceState.theme.chat.historyNavigation.fillColor
self.separatorNode.backgroundColor = interfaceState.theme.chat.historyNavigation.strokeColor
}
let panelHeight: CGFloat = 40.0

View File

@ -34,8 +34,8 @@ final class ChatRequestInProgressTitlePanelNode: ChatTitleAccessoryPanelNode {
if interfaceState.theme !== self.theme {
self.theme = interfaceState.theme
self.backgroundColor = interfaceState.theme.rootController.navigationBar.backgroundColor
self.separatorNode.backgroundColor = interfaceState.theme.rootController.navigationBar.separatorColor
self.backgroundColor = interfaceState.theme.chat.historyNavigation.fillColor
self.separatorNode.backgroundColor = interfaceState.theme.chat.historyNavigation.strokeColor
}
let panelHeight: CGFloat = 40.0

View File

@ -125,7 +125,7 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel
let pickerView = UIDatePicker()
pickerView.timeZone = TimeZone(secondsFromGMT: 0)
pickerView.datePickerMode = .dateAndTime
pickerView.locale = localeWithStrings(self.presentationData.strings)
pickerView.locale = Locale.current
pickerView.timeZone = TimeZone.current
pickerView.minuteInterval = 1
pickerView.setValue(self.presentationData.theme.actionSheet.primaryTextColor, forKey: "textColor")

View File

@ -128,7 +128,7 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
if let currentId = results.currentId, let index = results.messageIndices.firstIndex(where: { $0.id == currentId }) {
let adjustedIndex = results.messageIndices.count - 1 - index
resultIndex = index
resultsText = NSAttributedString(string: "\(adjustedIndex + 1) \(interfaceState.strings.Common_of) \(displayTotalCount)", font: labelFont, textColor: interfaceState.theme.chat.inputPanel.primaryTextColor)
resultsText = NSAttributedString(string: interfaceState.strings.Items_NOfM("\(adjustedIndex + 1)", "\(displayTotalCount)").0, font: labelFont, textColor: interfaceState.theme.chat.inputPanel.primaryTextColor)
} else {
resultsText = NSAttributedString(string: interfaceState.strings.Conversation_SearchNoResults, font: labelFont, textColor: interfaceState.theme.chat.inputPanel.primaryTextColor)
}

View File

@ -384,9 +384,14 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
self.messageBackgroundNode.layer.animateBounds(from: fromFrame, to: self.messageBackgroundNode.bounds, duration: duration, timingFunction: kCAMediaTimingFunctionSpring)
self.messageBackgroundNode.layer.animatePosition(from: CGPoint(x: (initialWidth - self.messageClipNode.bounds.width) / 2.0, y: delta), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
let textOffset = self.textInputNode.textView.contentSize.height - self.textInputNode.textView.contentOffset.y - self.textInputNode.textView.frame.height
self.fromMessageTextNode.layer.animatePosition(from: CGPoint(x: 0.0, y: delta * 2.0 + textOffset), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
self.toMessageTextNode.layer.animatePosition(from: CGPoint(x: 0.0, y: delta * 2.0 + textOffset), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
var textXOffset: CGFloat = 0.0
let textYOffset = self.textInputNode.textView.contentSize.height - self.textInputNode.textView.contentOffset.y - self.textInputNode.textView.frame.height
if self.textInputNode.textView.numberOfLines == 1 && self.textInputNode.isRTL {
textXOffset = initialWidth - self.messageClipNode.bounds.width
}
self.fromMessageTextNode.layer.animatePosition(from: CGPoint(x: textXOffset, y: delta * 2.0 + textYOffset), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
self.toMessageTextNode.layer.animatePosition(from: CGPoint(x: textXOffset, y: delta * 2.0 + textYOffset), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
let springDuration: Double = 0.42
let springDamping: CGFloat = 104.0
@ -480,9 +485,13 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
self.messageBackgroundNode.layer.animateBounds(from: self.messageBackgroundNode.bounds, to: toFrame, duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
self.messageBackgroundNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: (initialWidth - self.messageClipNode.bounds.width) / 2.0, y: delta), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
let textOffset = self.textInputNode.textView.contentSize.height - self.textInputNode.textView.contentOffset.y - self.textInputNode.textView.frame.height
self.fromMessageTextNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: delta * 2.0 + textOffset), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
self.toMessageTextNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: delta * 2.0 + textOffset), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
var textXOffset: CGFloat = 0.0
let textYOffset = self.textInputNode.textView.contentSize.height - self.textInputNode.textView.contentOffset.y - self.textInputNode.textView.frame.height
if self.textInputNode.textView.numberOfLines == 1 && self.textInputNode.isRTL {
textXOffset = initialWidth - self.messageClipNode.bounds.width
}
self.fromMessageTextNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: textXOffset, y: delta * 2.0 + textYOffset), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
self.toMessageTextNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: textXOffset, y: delta * 2.0 + textYOffset), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
} else {
completedBubble = true
}
@ -560,9 +569,11 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
messageFrame.size.width = ceil(layout.size.width - messageFrame.origin.x - sendButtonFrame.width - layout.safeInsets.left - layout.safeInsets.right + 8.0)
}
var messageOriginDelta: CGFloat = 0.0
if self.textInputNode.textView.numberOfLines == 1 || self.textInputNode.textView.attributedText.string.isEmpty {
let textWidth = min(self.toMessageTextNode.textView.sizeThatFits(layout.size).width + 36.0, messageFrame.width)
messageFrame.origin.x += messageFrame.width - textWidth
messageOriginDelta = messageFrame.width - textWidth
messageFrame.origin.x += messageOriginDelta
messageFrame.size.width = textWidth
}
@ -586,6 +597,11 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
var textFrame = self.textFieldFrame
textFrame.origin = CGPoint(x: 13.0, y: 6.0 - UIScreenPixel)
textFrame.size.height = self.textInputNode.textView.contentSize.height
if self.textInputNode.isRTL {
textFrame.origin.x -= messageOriginDelta
}
self.fromMessageTextNode.frame = textFrame
self.toMessageTextNode.frame = textFrame

View File

@ -42,8 +42,8 @@ final class ChatToastAlertPanelNode: ChatTitleAccessoryPanelNode {
let panelHeight: CGFloat = 40.0
self.textColor = interfaceState.theme.rootController.navigationBar.primaryTextColor
self.backgroundColor = interfaceState.theme.rootController.navigationBar.backgroundColor
self.separatorNode.backgroundColor = interfaceState.theme.rootController.navigationBar.separatorColor
self.backgroundColor = interfaceState.theme.chat.historyNavigation.fillColor
self.separatorNode.backgroundColor = interfaceState.theme.chat.historyNavigation.strokeColor
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel)))

View File

@ -282,7 +282,32 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil))
present(controller, nil)
let _ = (signal
var cancelImpl: (() -> Void)?
let progressSignal = Signal<Never, NoError> { subscriber in
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: {
cancelImpl?()
}))
present(controller, nil)
return ActionDisposable { [weak controller] in
Queue.mainQueue().async() {
controller?.dismiss()
}
}
}
|> runOn(Queue.mainQueue())
|> delay(0.35, queue: Queue.mainQueue())
let disposable = MetaDisposable()
let progressDisposable = progressSignal.start()
cancelImpl = {
disposable.set(nil)
}
disposable.set((signal
|> afterDisposed {
Queue.mainQueue().async {
progressDisposable.dispose()
}
}
|> deliverOnMainQueue).start(next: { [weak controller] dataAndTheme in
controller?.dismiss()
@ -300,7 +325,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
}
present(textAlertController(context: context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
controller?.dismiss()
})
}))
dismissInput()
}
}

View File

@ -48,18 +48,15 @@ public final class TelegramRootController: NavigationController {
if presentationData.chatWallpaper != strongSelf.presentationData.chatWallpaper {
let navigationDetailsBackgroundMode: NavigationEmptyDetailsBackgoundMode?
switch presentationData.chatWallpaper {
case .color:
let image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/EmptyMasterDetailIcon"), color: presentationData.theme.chatList.messageTextColor.withAlphaComponent(0.2))
navigationDetailsBackgroundMode = image != nil ? .image(image!) : nil
default:
let image = chatControllerBackgroundImage(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper, mediaBox: strongSelf.context.account.postbox.mediaBox, knockoutMode: strongSelf.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
navigationDetailsBackgroundMode = image != nil ? .wallpaper(image!) : nil
case .color:
let image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/EmptyMasterDetailIcon"), color: presentationData.theme.chatList.messageTextColor.withAlphaComponent(0.2))
navigationDetailsBackgroundMode = image != nil ? .image(image!) : nil
default:
navigationDetailsBackgroundMode = chatControllerBackgroundImage(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper, mediaBox: strongSelf.context.sharedContext.accountManager.mediaBox, knockoutMode: strongSelf.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper).flatMap(NavigationEmptyDetailsBackgoundMode.wallpaper)
}
strongSelf.updateBackgroundDetailsMode(navigationDetailsBackgroundMode, transition: .immediate)
}
let previousTheme = strongSelf.presentationData.theme
strongSelf.presentationData = presentationData
if previousTheme !== presentationData.theme {

View File

@ -66,6 +66,10 @@ final class ThemeUpdateManagerImpl: ThemeUpdateManager {
validIds.insert(themeSettings.theme.index)
themes[themeSettings.theme.index] = (themeSettings.theme, false)
}
if case .cloud = themeSettings.automaticThemeSwitchSetting.theme, themeSettings.automaticThemeSwitchSetting.trigger != .none {
validIds.insert(themeSettings.automaticThemeSwitchSetting.theme.index)
themes[themeSettings.automaticThemeSwitchSetting.theme.index] = (themeSettings.automaticThemeSwitchSetting.theme, true)
}
if previousIds != validIds {
for id in validIds {
@ -126,20 +130,25 @@ final class ThemeUpdateManagerImpl: ThemeUpdateManager {
current = PresentationThemeSettings.defaultSettings
}
let chatWallpaper: TelegramWallpaper
if let themeSpecificWallpaper = current.themeSpecificChatWallpapers[updatedTheme.index] {
chatWallpaper = themeSpecificWallpaper
} else if let presentationTheme = presentationTheme {
if case let .cloud(info) = updatedTheme, let resolvedWallpaper = info.resolvedWallpaper {
chatWallpaper = resolvedWallpaper
} else {
chatWallpaper = presentationTheme.chat.defaultWallpaper
}
var chatWallpaper = current.chatWallpaper
var automaticThemeSwitchSetting = current.automaticThemeSwitchSetting
if isAutoNight {
automaticThemeSwitchSetting.theme = updatedTheme
} else {
chatWallpaper = current.chatWallpaper
if let themeSpecificWallpaper = current.themeSpecificChatWallpapers[updatedTheme.index] {
chatWallpaper = themeSpecificWallpaper
} else if let presentationTheme = presentationTheme {
if case let .cloud(info) = updatedTheme, let resolvedWallpaper = info.resolvedWallpaper {
chatWallpaper = resolvedWallpaper
} else {
chatWallpaper = presentationTheme.chat.defaultWallpaper
}
} else {
chatWallpaper = current.chatWallpaper
}
}
return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: updatedTheme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: updatedTheme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
})
}).start()
}

View File

@ -271,21 +271,27 @@ public enum AutomaticThemeSwitchTrigger: PostboxCoding, Equatable {
public struct AutomaticThemeSwitchSetting: PostboxCoding, Equatable {
public var trigger: AutomaticThemeSwitchTrigger
public var theme: PresentationBuiltinThemeReference
public var theme: PresentationThemeReference
public init(trigger: AutomaticThemeSwitchTrigger, theme: PresentationBuiltinThemeReference) {
public init(trigger: AutomaticThemeSwitchTrigger, theme: PresentationThemeReference) {
self.trigger = trigger
self.theme = theme
}
public init(decoder: PostboxDecoder) {
self.trigger = decoder.decodeObjectForKey("trigger", decoder: { AutomaticThemeSwitchTrigger(decoder: $0) }) as! AutomaticThemeSwitchTrigger
self.theme = PresentationBuiltinThemeReference(rawValue: decoder.decodeInt32ForKey("theme", orElse: 0))!
if let theme = decoder.decodeObjectForKey("theme_v2", decoder: { PresentationThemeReference(decoder: $0) }) as? PresentationThemeReference {
self.theme = theme
} else if let legacyValue = decoder.decodeOptionalInt32ForKey("theme") {
self.theme = .builtin(PresentationBuiltinThemeReference(rawValue: legacyValue) ?? .nightAccent)
} else {
self.theme = .builtin(.nightAccent)
}
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeObject(self.trigger, forKey: "trigger")
encoder.encodeInt32(self.theme.rawValue, forKey: "theme")
encoder.encodeObject(self.theme, forKey: "theme_v2")
}
}
@ -443,7 +449,7 @@ public struct PresentationThemeSettings: PreferencesEntry {
}
public static var defaultSettings: PresentationThemeSettings {
return PresentationThemeSettings(chatWallpaper: .builtin(WallpaperSettings()), theme: .builtin(.dayClassic), themeSpecificAccentColors: [:], themeSpecificChatWallpapers: [:], fontSize: .regular, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting(trigger: .none, theme: .nightAccent), largeEmoji: true, disableAnimations: true)
return PresentationThemeSettings(chatWallpaper: .builtin(WallpaperSettings()), theme: .builtin(.dayClassic), themeSpecificAccentColors: [:], themeSpecificChatWallpapers: [:], fontSize: .regular, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting(trigger: .none, theme: .builtin(.nightAccent)), largeEmoji: true, disableAnimations: true)
}
public init(chatWallpaper: TelegramWallpaper, theme: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], themeSpecificChatWallpapers: [Int64: TelegramWallpaper], fontSize: PresentationFontSize, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting, largeEmoji: Bool, disableAnimations: Bool) {
@ -499,7 +505,7 @@ public struct PresentationThemeSettings: PreferencesEntry {
}
self.fontSize = PresentationFontSize(rawValue: decoder.decodeInt32ForKey("f", orElse: PresentationFontSize.regular.rawValue)) ?? .regular
self.automaticThemeSwitchSetting = (decoder.decodeObjectForKey("automaticThemeSwitchSetting", decoder: { AutomaticThemeSwitchSetting(decoder: $0) }) as? AutomaticThemeSwitchSetting) ?? AutomaticThemeSwitchSetting(trigger: .none, theme: .nightAccent)
self.automaticThemeSwitchSetting = (decoder.decodeObjectForKey("automaticThemeSwitchSetting", decoder: { AutomaticThemeSwitchSetting(decoder: $0) }) as? AutomaticThemeSwitchSetting) ?? AutomaticThemeSwitchSetting(trigger: .none, theme: .builtin(.nightAccent))
self.largeEmoji = decoder.decodeBoolForKey("largeEmoji", orElse: true)
self.disableAnimations = decoder.decodeBoolForKey("disableAnimations", orElse: true)
}