Reduce Motion option to disable animations of chats/contacts/calls lists and chat bubbles

Fixed crash on profile photo removal
Fixed several auto-download issues
Fixed network usage calculation for videos
Fixed several UI issues
This commit is contained in:
Ilya Laktyushin 2018-10-06 00:52:13 +03:00
parent 606e607de4
commit ab5881de15
92 changed files with 3114 additions and 2738 deletions

View File

@ -37,6 +37,7 @@
09874E582107A4C300E190B8 /* VimeoEmbedImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09874E3A21075BF400E190B8 /* VimeoEmbedImplementation.swift */; }; 09874E582107A4C300E190B8 /* VimeoEmbedImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09874E3A21075BF400E190B8 /* VimeoEmbedImplementation.swift */; };
09874E592107BD4100E190B8 /* GenericEmbedImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09874E4021075C1700E190B8 /* GenericEmbedImplementation.swift */; }; 09874E592107BD4100E190B8 /* GenericEmbedImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09874E4021075C1700E190B8 /* GenericEmbedImplementation.swift */; };
09AE3823214C110900850BFD /* LegacySecureIdScanController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09AE3822214C110800850BFD /* LegacySecureIdScanController.swift */; }; 09AE3823214C110900850BFD /* LegacySecureIdScanController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09AE3822214C110800850BFD /* LegacySecureIdScanController.swift */; };
09C3466D2167D63A00B76780 /* Accessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09C3466C2167D63A00B76780 /* Accessibility.swift */; };
09C500242142BA6400EF253E /* ItemListWebsiteItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09C500232142BA6400EF253E /* ItemListWebsiteItem.swift */; }; 09C500242142BA6400EF253E /* ItemListWebsiteItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09C500232142BA6400EF253E /* ItemListWebsiteItem.swift */; };
09FE756D2153F5F900A3120F /* CallRouteActionSheetItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FE756C2153F5F900A3120F /* CallRouteActionSheetItem.swift */; }; 09FE756D2153F5F900A3120F /* CallRouteActionSheetItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FE756C2153F5F900A3120F /* CallRouteActionSheetItem.swift */; };
D007019C2029E8F2006B9E34 /* LegqacyICloudFileController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D007019B2029E8F2006B9E34 /* LegqacyICloudFileController.swift */; }; D007019C2029E8F2006B9E34 /* LegqacyICloudFileController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D007019B2029E8F2006B9E34 /* LegqacyICloudFileController.swift */; };
@ -1057,6 +1058,7 @@
09874E4221075C3000E190B8 /* VKEmbedImplementation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VKEmbedImplementation.swift; sourceTree = "<group>"; }; 09874E4221075C3000E190B8 /* VKEmbedImplementation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VKEmbedImplementation.swift; sourceTree = "<group>"; };
09874E4421075C3F00E190B8 /* StreamableEmbedImplementation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StreamableEmbedImplementation.swift; sourceTree = "<group>"; }; 09874E4421075C3F00E190B8 /* StreamableEmbedImplementation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StreamableEmbedImplementation.swift; sourceTree = "<group>"; };
09AE3822214C110800850BFD /* LegacySecureIdScanController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacySecureIdScanController.swift; sourceTree = "<group>"; }; 09AE3822214C110800850BFD /* LegacySecureIdScanController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacySecureIdScanController.swift; sourceTree = "<group>"; };
09C3466C2167D63A00B76780 /* Accessibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Accessibility.swift; sourceTree = "<group>"; };
09C500232142BA6400EF253E /* ItemListWebsiteItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemListWebsiteItem.swift; sourceTree = "<group>"; }; 09C500232142BA6400EF253E /* ItemListWebsiteItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemListWebsiteItem.swift; sourceTree = "<group>"; };
09FE756C2153F5F900A3120F /* CallRouteActionSheetItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallRouteActionSheetItem.swift; sourceTree = "<group>"; }; 09FE756C2153F5F900A3120F /* CallRouteActionSheetItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallRouteActionSheetItem.swift; sourceTree = "<group>"; };
D00219051DDD1C9E00BE708A /* ImageContainingNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageContainingNode.swift; sourceTree = "<group>"; }; D00219051DDD1C9E00BE708A /* ImageContainingNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageContainingNode.swift; sourceTree = "<group>"; };
@ -4312,6 +4314,7 @@
D044A0F220BDA05800326FAC /* ThrottledValue.swift */, D044A0F220BDA05800326FAC /* ThrottledValue.swift */,
D0EC55A2210231D600D1992C /* SearchPeerMembers.swift */, D0EC55A2210231D600D1992C /* SearchPeerMembers.swift */,
D0192D45210F4F940005FA10 /* FixSearchableListNodeScrolling.swift */, D0192D45210F4F940005FA10 /* FixSearchableListNodeScrolling.swift */,
09C3466C2167D63A00B76780 /* Accessibility.swift */,
); );
name = Utils; name = Utils;
sourceTree = "<group>"; sourceTree = "<group>";
@ -4917,6 +4920,7 @@
D0EC6D2D1EB9F58800EBF1C3 /* TapLongTapOrDoubleTapGestureRecognizer.swift in Sources */, D0EC6D2D1EB9F58800EBF1C3 /* TapLongTapOrDoubleTapGestureRecognizer.swift in Sources */,
D0AF7C461ED84BC500CD8E0F /* LanguageSelectionController.swift in Sources */, D0AF7C461ED84BC500CD8E0F /* LanguageSelectionController.swift in Sources */,
D0B69C3C20EBD8C8003632C7 /* CheckDeviceAccess.swift in Sources */, D0B69C3C20EBD8C8003632C7 /* CheckDeviceAccess.swift in Sources */,
09C3466D2167D63A00B76780 /* Accessibility.swift in Sources */,
D0FA08C020483F9600DD23FC /* ExtractVideoData.swift in Sources */, D0FA08C020483F9600DD23FC /* ExtractVideoData.swift in Sources */,
D0BE30492061C0F500FBE6D8 /* SecureIdAuthHeaderNode.swift in Sources */, D0BE30492061C0F500FBE6D8 /* SecureIdAuthHeaderNode.swift in Sources */,
D0EC6D2E1EB9F58800EBF1C3 /* ImageNode.swift in Sources */, D0EC6D2E1EB9F58800EBF1C3 /* ImageNode.swift in Sources */,

View File

@ -0,0 +1,49 @@
import SwiftSignalKit
import UIKit
func currentReduceMotionEnabled() -> Bool {
return UIAccessibility.isReduceMotionEnabled
}
func reduceMotionEnabled() -> Signal<Bool, NoError> {
return Signal { subscriber in
subscriber.putNext(UIAccessibility.isReduceMotionEnabled)
let observer = NotificationCenter.default.addObserver(forName: NSNotification.Name.UIAccessibilityReduceMotionStatusDidChange, object: nil, queue: .main, using: { _ in
subscriber.putNext(UIAccessibility.isReduceMotionEnabled)
})
return ActionDisposable {
Queue.mainQueue().async {
NotificationCenter.default.removeObserver(observer)
}
}
} |> runOn(Queue.mainQueue())
}
func boldTextEnabled() -> Signal<Bool, NoError> {
return Signal { subscriber in
subscriber.putNext(UIAccessibility.isBoldTextEnabled)
let observer = NotificationCenter.default.addObserver(forName: NSNotification.Name.UIAccessibilityBoldTextStatusDidChange, object: nil, queue: .main, using: { _ in
subscriber.putNext(UIAccessibility.isBoldTextEnabled)
})
return ActionDisposable {
Queue.mainQueue().async {
NotificationCenter.default.removeObserver(observer)
}
}
} |> runOn(Queue.mainQueue())
}
private func checkButtonShapes() -> Bool {
let button = UIButton()
button.setTitle("title", for: .normal)
if let attributes = button.titleLabel?.attributedText?.attributes(at: 0, effectiveRange: nil), let _ = attributes[NSAttributedStringKey.underlineStyle] {
return true
} else {
return false
}
}

View File

@ -10,7 +10,7 @@ import CoreTelephony
public final class AuthorizationSequenceController: NavigationController { public final class AuthorizationSequenceController: NavigationController {
static func navigationBarTheme(_ theme: AuthorizationTheme) -> NavigationBarTheme { static func navigationBarTheme(_ theme: AuthorizationTheme) -> NavigationBarTheme {
return NavigationBarTheme(buttonColor: theme.accentColor, primaryTextColor: .black, backgroundColor: .clear, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) return NavigationBarTheme(buttonColor: theme.accentColor, disabledButtonColor: UIColor(rgb: 0xd0d0d0), primaryTextColor: .black, backgroundColor: .clear, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear)
} }
private var account: UnauthorizedAccount private var account: UnauthorizedAccount

View File

@ -186,7 +186,7 @@ final class AuthorizationSequenceCountrySelectionController: ViewController {
self.strings = strings self.strings = strings
self.displayCodes = displayCodes self.displayCodes = displayCodes
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(buttonColor: theme.searchBar.accent, primaryTextColor: theme.searchBar.primaryText, backgroundColor: theme.searchBar.background, separatorColor: theme.searchBar.separator, badgeBackgroundColor: theme.searchBar.accent, badgeStrokeColor: .clear, badgeTextColor: theme.searchBar.background), strings: NavigationBarStrings(presentationStrings: strings))) super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(buttonColor: theme.searchBar.accent, disabledButtonColor: UIColor(rgb: 0xd0d0d0), primaryTextColor: theme.searchBar.primaryText, backgroundColor: theme.searchBar.background, separatorColor: theme.searchBar.separator, badgeBackgroundColor: theme.searchBar.accent, badgeStrokeColor: .clear, badgeTextColor: theme.searchBar.background), strings: NavigationBarStrings(presentationStrings: strings)))
self.statusBar.statusBarStyle = theme.statusBar.style self.statusBar.statusBarStyle = theme.statusBar.style

View File

@ -470,7 +470,7 @@ func autodownloadMediaCategoryController(account: Account, category: AutomaticDo
case .cellular: case .cellular:
settings.peers.channels.voiceMessage.cellular = !settings.peers.channels.voiceMessage.cellular settings.peers.channels.voiceMessage.cellular = !settings.peers.channels.voiceMessage.cellular
case .wifi: case .wifi:
settings.peers.channels.voiceMessage.wifi = !settings.peers.channels.file.wifi settings.peers.channels.voiceMessage.wifi = !settings.peers.channels.voiceMessage.wifi
} }
} }
case .videoMessage: case .videoMessage:
@ -501,7 +501,7 @@ func autodownloadMediaCategoryController(account: Account, category: AutomaticDo
case .cellular: case .cellular:
settings.peers.channels.videoMessage.cellular = !settings.peers.channels.videoMessage.cellular settings.peers.channels.videoMessage.cellular = !settings.peers.channels.videoMessage.cellular
case .wifi: case .wifi:
settings.peers.channels.videoMessage.wifi = !settings.peers.channels.file.wifi settings.peers.channels.videoMessage.wifi = !settings.peers.channels.videoMessage.wifi
} }
} }
} }

View File

@ -25,6 +25,8 @@ final class CallControllerNode: ASDisplayNode {
private let buttonsNode: CallControllerButtonsNode private let buttonsNode: CallControllerButtonsNode
private var keyPreviewNode: CallControllerKeyPreviewNode? private var keyPreviewNode: CallControllerKeyPreviewNode?
private var debugNode: CallDebugNode?
private var keyTextData: (Data, String)? private var keyTextData: (Data, String)?
private let keyButtonNode: HighlightableButtonNode private let keyButtonNode: HighlightableButtonNode
@ -433,16 +435,47 @@ final class CallControllerNode: ASDisplayNode {
} }
} }
final private class CallControllerDebugNode: ASDisplayNode { final private class CallDebugNode: ASDisplayNode {
private let disposable = MetaDisposable() private let disposable = MetaDisposable()
private let dimNode: ASDisplayNode
private let textNode: ASTextNode
override init() { override init() {
super.init() self.dimNode = ASDisplayNode()
self.dimNode.isLayerBacked = true
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.8)
self.textNode = ASTextNode()
self.textNode.isUserInteractionEnabled = false
super.init()
} }
deinit { deinit {
disposable.dispose() disposable.dispose()
} }
override func didLoad() {
super.didLoad()
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
self.view.addGestureRecognizer(tapRecognizer)
}
@objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
}
override func layout() {
super.layout()
let size = self.bounds.size
self.dimNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.width))
//let labelSize = labelNode.measure(CGSize(width: , height: 100.0))
//textNode.frame = CGRect(origin: CGPoint(x: floor(size.width, y: 81.0), size: labelSize)
}
} }

View File

@ -121,7 +121,7 @@ public final class CallListController: ViewController {
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
if self.isNodeLoaded { if self.isNodeLoaded {
self.controllerNode.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat) self.controllerNode.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, disableAnimations: self.presentationData.disableAnimations)
} }
} }

View File

@ -69,19 +69,20 @@ struct CallListNodeState: Equatable {
let theme: PresentationTheme let theme: PresentationTheme
let strings: PresentationStrings let strings: PresentationStrings
let dateTimeFormat: PresentationDateTimeFormat let dateTimeFormat: PresentationDateTimeFormat
let disableAnimations: Bool
let editing: Bool let editing: Bool
let messageIdWithRevealedOptions: MessageId? let messageIdWithRevealedOptions: MessageId?
func withUpdatedPresentationData(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat) -> CallListNodeState { func withUpdatedPresentationData(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, disableAnimations: Bool) -> CallListNodeState {
return CallListNodeState(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, editing: self.editing, messageIdWithRevealedOptions: self.messageIdWithRevealedOptions) return CallListNodeState(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, disableAnimations: disableAnimations, editing: self.editing, messageIdWithRevealedOptions: self.messageIdWithRevealedOptions)
} }
func withUpdatedEditing(_ editing: Bool) -> CallListNodeState { func withUpdatedEditing(_ editing: Bool) -> CallListNodeState {
return CallListNodeState(theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, editing: editing, messageIdWithRevealedOptions: self.messageIdWithRevealedOptions) return CallListNodeState(theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, disableAnimations: self.disableAnimations, editing: editing, messageIdWithRevealedOptions: self.messageIdWithRevealedOptions)
} }
func withUpdatedMessageIdWithRevealedOptions(_ messageIdWithRevealedOptions: MessageId?) -> CallListNodeState { func withUpdatedMessageIdWithRevealedOptions(_ messageIdWithRevealedOptions: MessageId?) -> CallListNodeState {
return CallListNodeState(theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, editing: self.editing, messageIdWithRevealedOptions: messageIdWithRevealedOptions) return CallListNodeState(theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, disableAnimations: self.disableAnimations, editing: self.editing, messageIdWithRevealedOptions: messageIdWithRevealedOptions)
} }
static func ==(lhs: CallListNodeState, rhs: CallListNodeState) -> Bool { static func ==(lhs: CallListNodeState, rhs: CallListNodeState) -> Bool {
@ -198,7 +199,7 @@ final class CallListControllerNode: ASDisplayNode {
self.openInfo = openInfo self.openInfo = openInfo
self.emptyStateUpdated = emptyStateUpdated self.emptyStateUpdated = emptyStateUpdated
self.currentState = CallListNodeState(theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, editing: false, messageIdWithRevealedOptions: nil) self.currentState = CallListNodeState(theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, disableAnimations: presentationData.disableAnimations, editing: false, messageIdWithRevealedOptions: nil)
self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true) self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true)
self.listNode = ListView() self.listNode = ListView()
@ -322,7 +323,7 @@ final class CallListControllerNode: ASDisplayNode {
} }
} }
return preparedCallListNodeViewTransition(from: previous, to: processedView, reason: reason, account: account, scrollPosition: update.scrollPosition) return preparedCallListNodeViewTransition(from: previous, to: processedView, reason: reason, disableAnimations: state.disableAnimations, account: account, scrollPosition: update.scrollPosition)
|> map({ mappedCallListNodeViewListTransition(account: account, showSettings: showSettings, nodeInteraction: nodeInteraction, transition: $0) }) |> map({ mappedCallListNodeViewListTransition(account: account, showSettings: showSettings, nodeInteraction: nodeInteraction, transition: $0) })
|> runOn(prepareOnMainQueue ? Queue.mainQueue() : viewProcessingQueue) |> runOn(prepareOnMainQueue ? Queue.mainQueue() : viewProcessingQueue)
} }
@ -371,8 +372,8 @@ final class CallListControllerNode: ASDisplayNode {
self.emptyStateDisposable.dispose() self.emptyStateDisposable.dispose()
} }
func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat) { func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, disableAnimations: Bool) {
if theme !== self.currentState.theme || strings !== self.currentState.strings { if theme !== self.currentState.theme || strings !== self.currentState.strings || disableAnimations != self.currentState.disableAnimations {
switch self.mode { switch self.mode {
case .tab: case .tab:
self.backgroundColor = theme.chatList.backgroundColor self.backgroundColor = theme.chatList.backgroundColor
@ -385,7 +386,7 @@ final class CallListControllerNode: ASDisplayNode {
self.updateEmptyPlaceholder(theme: theme, strings: strings, type: self.currentLocationAndType.type, hidden: self.emptyTextNode.isHidden) self.updateEmptyPlaceholder(theme: theme, strings: strings, type: self.currentLocationAndType.type, hidden: self.emptyTextNode.isHidden)
self.updateState { self.updateState {
return $0.withUpdatedPresentationData(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat) return $0.withUpdatedPresentationData(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, disableAnimations: disableAnimations)
} }
} }
} }

View File

@ -45,7 +45,7 @@ enum CallListNodeViewScrollPosition {
case index(index: MessageIndex, position: ListViewScrollPosition, directionHint: ListViewScrollToItemDirectionHint, animated: Bool) case index(index: MessageIndex, position: ListViewScrollPosition, directionHint: ListViewScrollToItemDirectionHint, animated: Bool)
} }
func preparedCallListNodeViewTransition(from fromView: CallListNodeView?, to toView: CallListNodeView, reason: CallListNodeViewTransitionReason, account: Account, scrollPosition: CallListNodeViewScrollPosition?) -> Signal<CallListNodeViewTransition, NoError> { func preparedCallListNodeViewTransition(from fromView: CallListNodeView?, to toView: CallListNodeView, reason: CallListNodeViewTransitionReason, disableAnimations: Bool, account: Account, scrollPosition: CallListNodeViewScrollPosition?) -> Signal<CallListNodeViewTransition, NoError> {
return Signal { subscriber in return Signal { subscriber in
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromView?.filteredEntries ?? [], rightList: toView.filteredEntries) let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromView?.filteredEntries ?? [], rightList: toView.filteredEntries)
@ -74,7 +74,9 @@ func preparedCallListNodeViewTransition(from fromView: CallListNodeView?, to toV
let _ = options.insert(.Synchronous) let _ = options.insert(.Synchronous)
case .interactiveChanges: case .interactiveChanges:
let _ = options.insert(.AnimateAlpha) let _ = options.insert(.AnimateAlpha)
let _ = options.insert(.AnimateInsertion) if !disableAnimations {
let _ = options.insert(.AnimateInsertion)
}
for (index, _, _) in indicesAndItems.sorted(by: { $0.0 > $1.0 }) { for (index, _, _) in indicesAndItems.sorted(by: { $0.0 > $1.0 }) {
let adjustedIndex = updatedCount - 1 - index let adjustedIndex = updatedCount - 1 - index

View File

@ -98,7 +98,11 @@ final class ChangePhoneNumberController: ViewController {
} }
@objc func nextPressed() { @objc func nextPressed() {
let (_, number) = self.controllerNode.codeAndNumber let (code, number) = self.controllerNode.codeAndNumber
var phoneNumber = number
if let code = code {
phoneNumber = "\(code)\(phoneNumber)"
}
if !number.isEmpty { if !number.isEmpty {
self.inProgress = true self.inProgress = true
self.requestDisposable.set((requestChangeAccountPhoneNumberVerification(account: self.account, phoneNumber: self.controllerNode.currentNumber) |> deliverOnMainQueue).start(next: { [weak self] next in self.requestDisposable.set((requestChangeAccountPhoneNumberVerification(account: self.account, phoneNumber: self.controllerNode.currentNumber) |> deliverOnMainQueue).start(next: { [weak self] next in
@ -119,7 +123,7 @@ final class ChangePhoneNumberController: ViewController {
case .invalidPhoneNumber: case .invalidPhoneNumber:
text = presentationData.strings.Login_InvalidPhoneError text = presentationData.strings.Login_InvalidPhoneError
case .phoneNumberOccupied: case .phoneNumberOccupied:
text = presentationData.strings.ChangePhone_ErrorOccupied(number).0 text = presentationData.strings.ChangePhone_ErrorOccupied(formatPhoneNumber(phoneNumber)).0
case .generic: case .generic:
text = presentationData.strings.Login_UnknownError text = presentationData.strings.Login_UnknownError
} }

View File

@ -2,6 +2,7 @@ import Foundation
import AsyncDisplayKit import AsyncDisplayKit
import Display import Display
import TelegramCore import TelegramCore
import CoreTelephony
private func generateCountryButtonBackground(color: UIColor, strokeColor: UIColor) -> UIImage? { private func generateCountryButtonBackground(color: UIColor, strokeColor: UIColor) -> UIImage? {
return generateImage(CGSize(width: 45.0, height: 44.0 + 6.0), rotatedContext: { size, context in return generateImage(CGSize(width: 45.0, height: 44.0 + 6.0), rotatedContext: { size, context in
@ -149,7 +150,7 @@ final class ChangePhoneNumberControllerNode: ASDisplayNode {
self.phoneInputNode.countryCodeUpdated = { [weak self] code in self.phoneInputNode.countryCodeUpdated = { [weak self] code in
if let strongSelf = self { if let strongSelf = self {
if let code = Int(code), let (coutnryId, countryName) = countryCodeToIdAndName[code] { if let code = Int(code), let (_, countryName) = countryCodeToIdAndName[code] {
strongSelf.countryButton.setTitle(countryName, with: Font.regular(17.0), with: strongSelf.presentationData.theme.list.itemPrimaryTextColor, for: []) strongSelf.countryButton.setTitle(countryName, with: Font.regular(17.0), with: strongSelf.presentationData.theme.list.itemPrimaryTextColor, for: [])
} else { } else {
strongSelf.countryButton.setTitle(strongSelf.presentationData.strings.Login_CountryCode, with: Font.regular(17.0), with: strongSelf.presentationData.theme.list.itemPrimaryTextColor, for: []) strongSelf.countryButton.setTitle(strongSelf.presentationData.strings.Login_CountryCode, with: Font.regular(17.0), with: strongSelf.presentationData.theme.list.itemPrimaryTextColor, for: [])
@ -157,7 +158,29 @@ final class ChangePhoneNumberControllerNode: ASDisplayNode {
} }
} }
self.phoneInputNode.number = "+1" var countryId: String? = nil
let networkInfo = CTTelephonyNetworkInfo()
if let carrier = networkInfo.subscriberCellularProvider {
countryId = carrier.isoCountryCode
}
if countryId == nil {
countryId = (Locale.current as NSLocale).object(forKey: .countryCode) as? String
}
var countryCodeAndId: (Int32, String) = (1, "US")
if let countryId = countryId {
let normalizedId = countryId.uppercased()
for (code, idAndName) in countryCodeToIdAndName {
if idAndName.0 == normalizedId {
countryCodeAndId = (Int32(code), idAndName.0.uppercased())
break
}
}
}
self.phoneInputNode.number = "+\(countryCodeAndId.0)"
} }
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {

View File

@ -564,7 +564,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
text = presentationData.strings.Channel_NotificationLoading text = presentationData.strings.Channel_NotificationLoading
} }
entries.append(.privateLink(presentationData.theme, text, link)) entries.append(.privateLink(presentationData.theme, text, link))
entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.Group_Username_CreatePrivateLinkHelp)) entries.append(.privateLinkInfo(presentationData.theme, presentationData.strings.GroupInfo_InviteLink_Help))
switch mode { switch mode {
case .initialSetup: case .initialSetup:
break break
@ -867,9 +867,6 @@ public func channelVisibilityController(account: Account, peerId: PeerId, mode:
invokeAction() invokeAction()
} }
}) })
} else { } else {
switch mode { switch mode {
case .initialSetup: case .initialSetup:
@ -926,7 +923,13 @@ public func channelVisibilityController(account: Account, peerId: PeerId, mode:
} }
} }
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(isGroup ? presentationData.strings.GroupInfo_GroupType : presentationData.strings.Channel_TypeSetup_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false) let title: String
if case .privateLink = mode {
title = presentationData.strings.GroupInfo_InviteLink_Title
} else {
title = isGroup ? presentationData.strings.GroupInfo_GroupType : presentationData.strings.Channel_TypeSetup_Title
}
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
let listState = ItemListNodeState(entries: channelVisibilityControllerEntries(presentationData: presentationData, mode: mode, view: view, publicChannelsToRevoke: publicChannelsToRevoke, state: state), style: .blocks, crossfadeState: crossfade, animateChanges: false) let listState = ItemListNodeState(entries: channelVisibilityControllerEntries(presentationData: presentationData, mode: mode, view: view, publicChannelsToRevoke: publicChannelsToRevoke, state: state), style: .blocks, crossfadeState: crossfade, animateChanges: false)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))

View File

@ -13,14 +13,12 @@ private let messageFixedFont: UIFont = UIFont(name: "Menlo-Regular", size: 16.0)
final class ChatBotInfoItem: ListViewItem { final class ChatBotInfoItem: ListViewItem {
fileprivate let text: String fileprivate let text: String
fileprivate let controllerInteraction: ChatControllerInteraction fileprivate let controllerInteraction: ChatControllerInteraction
fileprivate let theme: ChatPresentationThemeData fileprivate let presentationData: ChatPresentationData
fileprivate let strings: PresentationStrings
init(text: String, controllerInteraction: ChatControllerInteraction, theme: ChatPresentationThemeData, strings: PresentationStrings) { init(text: String, controllerInteraction: ChatControllerInteraction, presentationData: ChatPresentationData) {
self.text = text self.text = text
self.controllerInteraction = controllerInteraction self.controllerInteraction = controllerInteraction
self.theme = theme self.presentationData = presentationData
self.strings = strings
} }
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, () -> Void)) -> Void) { func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, () -> Void)) -> Void) {
@ -77,6 +75,8 @@ final class ChatBotInfoItemNode: ListViewItemNode {
private var theme: ChatPresentationThemeData? private var theme: ChatPresentationThemeData?
private var item: ChatBotInfoItem?
init() { init() {
self.offsetContainer = ASDisplayNode() self.offsetContainer = ASDisplayNode()
@ -106,10 +106,12 @@ final class ChatBotInfoItemNode: ListViewItemNode {
let currentTextAndEntities = self.currentTextAndEntities let currentTextAndEntities = self.currentTextAndEntities
let currentTheme = self.theme let currentTheme = self.theme
return { [weak self] item, params in return { [weak self] item, params in
self?.item = item
var updatedBackgroundImage: UIImage? var updatedBackgroundImage: UIImage?
if currentTheme != item.theme { if currentTheme != item.presentationData.theme {
let principalGraphics = PresentationResourcesChat.principalGraphics(item.theme.theme, wallpaper: !item.theme.wallpaper.isEmpty) //let principalGraphics = PresentationResourcesChat.principalGraphics(item.presentationData.theme.theme, wallpaper: !item.presentationData.theme.wallpaper.isEmpty)
updatedBackgroundImage = PresentationResourcesChat.chatInfoItemBackgroundImage(item.theme.theme, wallpaper: !item.theme.wallpaper.isEmpty) updatedBackgroundImage = PresentationResourcesChat.chatInfoItemBackgroundImage(item.presentationData.theme.theme, wallpaper: !item.presentationData.theme.wallpaper.isEmpty)
} }
var updatedTextAndEntities: (String, [MessageTextEntity]) var updatedTextAndEntities: (String, [MessageTextEntity])
@ -123,7 +125,7 @@ final class ChatBotInfoItemNode: ListViewItemNode {
updatedTextAndEntities = (item.text, generateTextEntities(item.text, enabledTypes: .all)) updatedTextAndEntities = (item.text, generateTextEntities(item.text, enabledTypes: .all))
} }
let attributedText = stringWithAppliedEntities(updatedTextAndEntities.0, entities: updatedTextAndEntities.1, baseColor: item.theme.theme.chat.bubble.infoPrimaryTextColor, linkColor: item.theme.theme.chat.bubble.infoLinkTextColor, baseFont: messageFont, linkFont: messageFont, boldFont: messageBoldFont, italicFont: messageItalicFont, fixedFont: messageFixedFont) let attributedText = stringWithAppliedEntities(updatedTextAndEntities.0, entities: updatedTextAndEntities.1, baseColor: item.presentationData.theme.theme.chat.bubble.infoPrimaryTextColor, linkColor: item.presentationData.theme.theme.chat.bubble.infoLinkTextColor, baseFont: messageFont, linkFont: messageFont, boldFont: messageBoldFont, italicFont: messageItalicFont, fixedFont: messageFixedFont)
let horizontalEdgeInset: CGFloat = 10.0 + params.leftInset let horizontalEdgeInset: CGFloat = 10.0 + params.leftInset
let horizontalContentInset: CGFloat = 12.0 let horizontalContentInset: CGFloat = 12.0
@ -138,7 +140,7 @@ final class ChatBotInfoItemNode: ListViewItemNode {
let itemLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: textLayout.size.height + verticalItemInset * 2.0 + verticalContentInset * 2.0 + 4.0), insets: UIEdgeInsets()) let itemLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: textLayout.size.height + verticalItemInset * 2.0 + verticalContentInset * 2.0 + 4.0), insets: UIEdgeInsets())
return (itemLayout, { _ in return (itemLayout, { _ in
if let strongSelf = self { if let strongSelf = self {
strongSelf.theme = item.theme strongSelf.theme = item.presentationData.theme
if let updatedBackgroundImage = updatedBackgroundImage { if let updatedBackgroundImage = updatedBackgroundImage {
strongSelf.backgroundNode.image = updatedBackgroundImage strongSelf.backgroundNode.image = updatedBackgroundImage
@ -238,4 +240,11 @@ final class ChatBotInfoItemNode: ListViewItemNode {
break break
} }
} }
override public var wantsScrollDynamics: Bool {
if let disableAnimations = self.item?.presentationData.disableAnimations {
return !disableAnimations
}
return true
}
} }

View File

@ -94,7 +94,7 @@ final class ChatBotStartInputPanelNode: ChatInputPanelNode {
let buttonSize = self.button.measure(CGSize(width: width - 80.0, height: 100.0)) let buttonSize = self.button.measure(CGSize(width: width - 80.0, height: 100.0))
let panelHeight: CGFloat = 45.0 let panelHeight = defaultHeight(metrics: metrics)
self.button.frame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - buttonSize.width) / 2.0), y: floor((panelHeight - buttonSize.height) / 2.0)), size: buttonSize) self.button.frame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - buttonSize.width) / 2.0), y: floor((panelHeight - buttonSize.height) / 2.0)), size: buttonSize)
@ -105,6 +105,6 @@ final class ChatBotStartInputPanelNode: ChatInputPanelNode {
} }
override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
return 45.0 return defaultHeight(metrics: metrics)
} }
} }

View File

@ -129,7 +129,7 @@ final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
} }
} }
let panelHeight: CGFloat = 45.0 let panelHeight = defaultHeight(metrics: metrics)
let buttonSize = self.button.bounds.size let buttonSize = self.button.bounds.size
self.button.frame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - buttonSize.width) / 2.0), y: floor((panelHeight - buttonSize.height) / 2.0)), size: buttonSize) self.button.frame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - buttonSize.width) / 2.0), y: floor((panelHeight - buttonSize.height) / 2.0)), size: buttonSize)
@ -141,6 +141,6 @@ final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
} }
override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
return 45.0 return defaultHeight(metrics: metrics)
} }
} }

View File

@ -211,9 +211,9 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, fontSize: self.presentationData.fontSize, accountPeerId: account.peerId, mode: mode, chatLocation: chatLocation) self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, fontSize: self.presentationData.fontSize, accountPeerId: account.peerId, mode: mode, chatLocation: chatLocation)
var enableMediaAccessoryPanel = false var mediaAccessoryPanelVisibility = MediaAccessoryPanelVisibility.none
if case .standard = mode { if case .standard = mode {
enableMediaAccessoryPanel = true mediaAccessoryPanelVisibility = .specific(size: .compact)
} else { } else {
locationBroadcastPanelSource = .none locationBroadcastPanelSource = .none
} }
@ -224,7 +224,7 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin
default: default:
navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData) navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData)
} }
super.init(account: account, navigationBarPresentationData: navigationBarPresentationData, enableMediaAccessoryPanel: enableMediaAccessoryPanel, locationBroadcastPanelSource: locationBroadcastPanelSource) super.init(account: account, navigationBarPresentationData: navigationBarPresentationData, mediaAccessoryPanelVisibility: mediaAccessoryPanelVisibility, locationBroadcastPanelSource: locationBroadcastPanelSource)
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
@ -2848,6 +2848,8 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin
self.validLayout = layout self.validLayout = layout
self.chatTitleView?.layoutMetrics = layout.metrics
self.chatDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop in self.chatDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop in
self.chatDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop) self.chatDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop)
}) })
@ -3621,7 +3623,7 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin
} }
private func enqueueMediaMessages(signals: [Any]?) { private func enqueueMediaMessages(signals: [Any]?) {
if case let .peer(peerId) = self.chatLocation { if case .peer = self.chatLocation {
self.enqueueMediaMessageDisposable.set((legacyAssetPickerEnqueueMessages(account: self.account, signals: signals!) |> deliverOnMainQueue).start(next: { [weak self] messages in self.enqueueMediaMessageDisposable.set((legacyAssetPickerEnqueueMessages(account: self.account, signals: signals!) |> deliverOnMainQueue).start(next: { [weak self] messages in
if let strongSelf = self { if let strongSelf = self {
let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId

View File

@ -298,7 +298,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
} }
} }
if case let .peer(peerId) = strongSelf.chatLocation { if case .peer = strongSelf.chatLocation {
strongSelf.sendMessages(messages) strongSelf.sendMessages(messages)
} }
} }
@ -309,11 +309,12 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.textInputPanelNode?.pasteImages = { [weak self] images in self.textInputPanelNode?.pasteImages = { [weak self] images in
self?.displayPasteMenu(images) self?.displayPasteMenu(images)
} }
self.textInputPanelNode?.pasteData = { [weak self] data in
//self?.sendGifData(data)
}
self.textInputPanelNode?.displayAttachmentMenu = { [weak self] in self.textInputPanelNode?.displayAttachmentMenu = { [weak self] in
self?.displayAttachmentMenu() self?.displayAttachmentMenu()
} }
self.textInputPanelNode?.updateActivity = { [weak self] in self.textInputPanelNode?.updateActivity = { [weak self] in
self?.updateTypingActivity(true) self?.updateTypingActivity(true)
} }
@ -639,7 +640,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
} }
if case .standard(true) = self.chatPresentationInterfaceState.mode { if case .standard(true) = self.chatPresentationInterfaceState.mode {
inputPanelSize = CGSize(width: layout.size.width, height: 47.0) self.inputPanelNode = nil
inputPanelSize = CGSize(width: layout.size.width, height: 0.0)
} }
if let inputMediaNode = self.inputMediaNode, inputMediaNode != self.inputNode { if let inputMediaNode = self.inputMediaNode, inputMediaNode != self.inputNode {
@ -649,7 +651,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
transition.updateFrame(node: self.titleAccessoryPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: 56.0))) transition.updateFrame(node: self.titleAccessoryPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: 56.0)))
var titleAccessoryPanelFrame: CGRect? var titleAccessoryPanelFrame: CGRect?
if let titleAccessoryPanelNode = self.titleAccessoryPanelNode, let panelHeight = titleAccessoryPanelHeight { if let _ = self.titleAccessoryPanelNode, let panelHeight = titleAccessoryPanelHeight {
titleAccessoryPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: panelHeight)) titleAccessoryPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: panelHeight))
insets.top += panelHeight insets.top += panelHeight
} }
@ -855,8 +857,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
listInsets.left += 6.0 listInsets.left += 6.0
listInsets.right += 6.0 listInsets.right += 6.0
listInsets.top += 6.0 listInsets.top += 6.0
containerInsets.bottom += 6.0
//listInsets.bottom += 6.0
} }
} }
@ -1675,7 +1675,6 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
} }
if let menuHeight = menuHeight { if let menuHeight = menuHeight {
if let _ = self.controllerInteraction.contextHighlightedState?.messageStableId, let (menuController, node, frame) = displayContextMenuController { if let _ = self.controllerInteraction.contextHighlightedState?.messageStableId, let (menuController, node, frame) = displayContextMenuController {
self.controllerInteraction.presentController(menuController, ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self] in self.controllerInteraction.presentController(menuController, ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self] in
if let strongSelf = self { if let strongSelf = self {
var bounds = strongSelf.bounds var bounds = strongSelf.bounds

View File

@ -8,13 +8,11 @@ import TelegramCore
private let messageFont = Font.medium(14.0) private let messageFont = Font.medium(14.0)
final class ChatEmptyItem: ListViewItem { final class ChatEmptyItem: ListViewItem {
fileprivate let theme: PresentationTheme fileprivate let presentationData: ChatPresentationData
fileprivate let strings: PresentationStrings
fileprivate let tagMask: MessageTags? fileprivate let tagMask: MessageTags?
init(theme: PresentationTheme, strings: PresentationStrings, tagMask: MessageTags?) { init(presentationData: ChatPresentationData, tagMask: MessageTags?) {
self.theme = theme self.presentationData = presentationData
self.strings = strings
self.tagMask = tagMask self.tagMask = tagMask
} }
@ -71,6 +69,8 @@ final class ChatEmptyItemNode: ListViewItemNode {
private var theme: PresentationTheme? private var theme: PresentationTheme?
private var item: ChatEmptyItem?
init(rotated: Bool) { init(rotated: Bool) {
self.rotated = rotated self.rotated = rotated
self.offsetContainer = ASDisplayNode() self.offsetContainer = ASDisplayNode()
@ -98,28 +98,30 @@ final class ChatEmptyItemNode: ListViewItemNode {
let makeTextLayout = TextNode.asyncLayout(self.textNode) let makeTextLayout = TextNode.asyncLayout(self.textNode)
let currentTheme = self.theme let currentTheme = self.theme
return { [weak self] item, params in return { [weak self] item, params in
self?.item = item
let width = params.width let width = params.width
var updatedBackgroundImage: UIImage? var updatedBackgroundImage: UIImage?
let iconImage: UIImage? = PresentationResourcesChat.chatEmptyItemIconImage(item.theme) let iconImage: UIImage? = PresentationResourcesChat.chatEmptyItemIconImage(item.presentationData.theme.theme)
if currentTheme !== item.theme { if currentTheme !== item.presentationData.theme {
updatedBackgroundImage = PresentationResourcesChat.chatEmptyItemBackgroundImage(item.theme) updatedBackgroundImage = PresentationResourcesChat.chatEmptyItemBackgroundImage(item.presentationData.theme.theme)
} }
let attributedText: NSAttributedString let attributedText: NSAttributedString
if let tagMask = item.tagMask { if let tagMask = item.tagMask {
let text: String let text: String
if tagMask == .photoOrVideo { if tagMask == .photoOrVideo {
text = item.strings.SharedMedia_EmptyText text = item.presentationData.strings.SharedMedia_EmptyText
} else if tagMask == .file { } else if tagMask == .file {
text = item.strings.SharedMedia_EmptyFilesText text = item.presentationData.strings.SharedMedia_EmptyFilesText
} else { } else {
text = "" text = ""
} }
attributedText = NSAttributedString(string: text, font: messageFont, textColor: item.theme.list.itemSecondaryTextColor, paragraphAlignment: .center) attributedText = NSAttributedString(string: text, font: messageFont, textColor: item.presentationData.theme.theme.list.itemSecondaryTextColor, paragraphAlignment: .center)
} else { } else {
attributedText = NSAttributedString(string: item.strings.Conversation_EmptyPlaceholder, font: messageFont, textColor: item.theme.chat.serviceMessage.serviceMessagePrimaryTextColor, paragraphAlignment: .center) attributedText = NSAttributedString(string: item.presentationData.strings.Conversation_EmptyPlaceholder, font: messageFont, textColor: item.presentationData.theme.theme.chat.serviceMessage.serviceMessagePrimaryTextColor, paragraphAlignment: .center)
} }
let horizontalEdgeInset: CGFloat = 10.0 let horizontalEdgeInset: CGFloat = 10.0
@ -144,7 +146,7 @@ final class ChatEmptyItemNode: ListViewItemNode {
let itemLayout = ListViewItemNodeLayout(contentSize: CGSize(width: width, height: imageSize.height + imageSpacing + textLayout.size.height + verticalItemInset * 2.0 + verticalContentInset * 2.0 + 4.0), insets: UIEdgeInsets()) let itemLayout = ListViewItemNodeLayout(contentSize: CGSize(width: width, height: imageSize.height + imageSpacing + textLayout.size.height + verticalItemInset * 2.0 + verticalContentInset * 2.0 + 4.0), insets: UIEdgeInsets())
return (itemLayout, { _ in return (itemLayout, { _ in
if let strongSelf = self { if let strongSelf = self {
strongSelf.theme = item.theme strongSelf.theme = item.presentationData.theme.theme
if let updatedBackgroundImage = updatedBackgroundImage { if let updatedBackgroundImage = updatedBackgroundImage {
strongSelf.backgroundNode.image = updatedBackgroundImage strongSelf.backgroundNode.image = updatedBackgroundImage
@ -181,4 +183,11 @@ final class ChatEmptyItemNode: ListViewItemNode {
override func animateRemoved(_ currentTimestamp: Double, duration: Double) { override func animateRemoved(_ currentTimestamp: Double, duration: Double) {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration * 0.5, removeOnCompletion: false) self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration * 0.5, removeOnCompletion: false)
} }
override public var wantsScrollDynamics: Bool {
if let disableAnimations = self.item?.presentationData.disableAnimations {
return !disableAnimations
}
return true
}
} }

View File

@ -58,7 +58,7 @@ final class ChatFeedNavigationInputPanelNode: ChatInputPanelNode {
let buttonSize = self.button.measure(CGSize(width: width - leftInset - rightInset - 80.0, height: 100.0)) let buttonSize = self.button.measure(CGSize(width: width - leftInset - rightInset - 80.0, height: 100.0))
let panelHeight: CGFloat = 45.0 let panelHeight = defaultHeight(metrics: metrics)
self.button.frame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - buttonSize.width) / 2.0), y: floor((panelHeight - buttonSize.height) / 2.0)), size: buttonSize) self.button.frame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - buttonSize.width) / 2.0), y: floor((panelHeight - buttonSize.height) / 2.0)), size: buttonSize)
@ -66,7 +66,7 @@ final class ChatFeedNavigationInputPanelNode: ChatInputPanelNode {
} }
override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
return 45.0 return defaultHeight(metrics: metrics)
} }
} }

View File

@ -74,7 +74,7 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
if let maxReadIndex = view.maxReadIndex, includeUnreadEntry { if let maxReadIndex = view.maxReadIndex, includeUnreadEntry {
var i = 0 var i = 0
let unreadEntry: ChatHistoryEntry = .UnreadEntry(maxReadIndex, presentationData.theme, presentationData.strings) let unreadEntry: ChatHistoryEntry = .UnreadEntry(maxReadIndex, presentationData)
for entry in entries { for entry in entries {
if entry > unreadEntry { if entry > unreadEntry {
if i == 0, case .HoleEntry = entry { if i == 0, case .HoleEntry = entry {
@ -97,7 +97,7 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
} }
} }
if let cachedPeerData = cachedPeerData as? CachedUserData, let botInfo = cachedPeerData.botInfo, !botInfo.description.isEmpty { if let cachedPeerData = cachedPeerData as? CachedUserData, let botInfo = cachedPeerData.botInfo, !botInfo.description.isEmpty {
entries.insert(.ChatInfoEntry(botInfo.description, presentationData.theme, presentationData.strings), at: 0) entries.insert(.ChatInfoEntry(botInfo.description, presentationData), at: 0)
} else if view.entries.isEmpty && includeEmptyEntry { } else if view.entries.isEmpty && includeEmptyEntry {
//entries.insert(.EmptyChatInfoEntry(presentationData.theme, presentationData.strings, view.tagMask), at: 0) //entries.insert(.EmptyChatInfoEntry(presentationData.theme, presentationData.strings, view.tagMask), at: 0)
} }

View File

@ -27,9 +27,9 @@ enum ChatHistoryEntry: Identifiable, Comparable {
case HoleEntry(MessageHistoryHole, PresentationTheme, PresentationStrings) case HoleEntry(MessageHistoryHole, PresentationTheme, PresentationStrings)
case MessageEntry(Message, ChatPresentationData, Bool, MessageHistoryEntryMonthLocation?, ChatHistoryMessageSelection, Bool) case MessageEntry(Message, ChatPresentationData, Bool, MessageHistoryEntryMonthLocation?, ChatHistoryMessageSelection, Bool)
case MessageGroupEntry(MessageGroupInfo, [(Message, Bool, ChatHistoryMessageSelection, Bool)], ChatPresentationData) case MessageGroupEntry(MessageGroupInfo, [(Message, Bool, ChatHistoryMessageSelection, Bool)], ChatPresentationData)
case UnreadEntry(MessageIndex, ChatPresentationThemeData, PresentationStrings) case UnreadEntry(MessageIndex, ChatPresentationData)
case ChatInfoEntry(String, ChatPresentationThemeData, PresentationStrings) case ChatInfoEntry(String, ChatPresentationData)
case EmptyChatInfoEntry(PresentationTheme, PresentationStrings, MessageTags?) case EmptyChatInfoEntry(ChatPresentationData, MessageTags?)
case SearchEntry(PresentationTheme, PresentationStrings) case SearchEntry(PresentationTheme, PresentationStrings)
var stableId: UInt64 { var stableId: UInt64 {
@ -59,7 +59,7 @@ enum ChatHistoryEntry: Identifiable, Comparable {
return MessageIndex(message) return MessageIndex(message)
case let .MessageGroupEntry(_, messages, _): case let .MessageGroupEntry(_, messages, _):
return MessageIndex(messages[messages.count - 1].0) return MessageIndex(messages[messages.count - 1].0)
case let .UnreadEntry(index, _, _): case let .UnreadEntry(index, _):
return index return index
case .ChatInfoEntry: case .ChatInfoEntry:
return MessageIndex.absoluteLowerBound() return MessageIndex.absoluteLowerBound()
@ -173,20 +173,20 @@ enum ChatHistoryEntry: Identifiable, Comparable {
} else { } else {
return false return false
} }
case let .UnreadEntry(lhsIndex, lhsTheme, lhsStrings): case let .UnreadEntry(lhsIndex, lhsPresentationData):
if case let .UnreadEntry(rhsIndex, rhsTheme, rhsStrings) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings { if case let .UnreadEntry(rhsIndex, rhsPresentationData) = rhs, lhsIndex == rhsIndex, lhsPresentationData === rhsPresentationData {
return true return true
} else { } else {
return false return false
} }
case let .ChatInfoEntry(lhsText, lhsTheme, lhsStrings): case let .ChatInfoEntry(lhsText, lhsPresentationData):
if case let .ChatInfoEntry(rhsText, rhsTheme, rhsStrings) = rhs, lhsText == rhsText, lhsTheme == rhsTheme, lhsStrings === rhsStrings { if case let .ChatInfoEntry(rhsText, rhsPresentationData) = rhs, lhsText == rhsText, lhsPresentationData === rhsPresentationData {
return true return true
} else { } else {
return false return false
} }
case let .EmptyChatInfoEntry(lhsTheme, lhsStrings, lhsTagMask): case let .EmptyChatInfoEntry(lhsPresentationData, lhsTagMask):
if case let .EmptyChatInfoEntry(rhsTheme, rhsStrings, rhsTagMask) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsTagMask == rhsTagMask { if case let .EmptyChatInfoEntry(rhsPresentationData, rhsTagMask) = rhs, lhsPresentationData === rhsPresentationData, lhsTagMask == rhsTagMask {
return true return true
} else { } else {
return false return false

View File

@ -254,7 +254,7 @@ public final class ChatHistoryGridNode: GridNode, ChatHistoryNode {
self.chatPresentationDataPromise.set(account.telegramApplicationContext.presentationData |> map { presentationData in self.chatPresentationDataPromise.set(account.telegramApplicationContext.presentationData |> map { presentationData in
return ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper), fontSize: presentationData.fontSize, strings: self.presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat) return ChatPresentationData(theme: ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper), fontSize: presentationData.fontSize, strings: self.presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, disableAnimations: presentationData.disableAnimations)
}) })
self.floatingSections = true self.floatingSections = true

View File

@ -172,12 +172,12 @@ private func mappedInsertEntries(account: Account, chatLocation: ChatLocation, a
item = ListMessageHoleItem() item = ListMessageHoleItem()
} }
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint)
case let .UnreadEntry(_, theme, strings): case let .UnreadEntry(_, presentationData):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index, theme: theme, strings: strings), directionHint: entry.directionHint) return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index, presentationData: presentationData), directionHint: entry.directionHint)
case let .ChatInfoEntry(text, theme, strings): case let .ChatInfoEntry(text, presentationData):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(text: text, controllerInteraction: controllerInteraction, theme: theme, strings: strings), directionHint: entry.directionHint) return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(text: text, controllerInteraction: controllerInteraction, presentationData: presentationData), directionHint: entry.directionHint)
case let .EmptyChatInfoEntry(theme, strings, tagMask): case let .EmptyChatInfoEntry(presentationData, tagMask):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatEmptyItem(theme: theme, strings: strings, tagMask: tagMask), directionHint: entry.directionHint) return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatEmptyItem(presentationData: presentationData, tagMask: tagMask), directionHint: entry.directionHint)
case let .SearchEntry(theme, strings): case let .SearchEntry(theme, strings):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, placeholder: strings.Common_Search, activate: { return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, placeholder: strings.Common_Search, activate: {
controllerInteraction.openSearch() controllerInteraction.openSearch()
@ -217,12 +217,12 @@ private func mappedUpdateEntries(account: Account, chatLocation: ChatLocation, a
item = ListMessageHoleItem() item = ListMessageHoleItem()
} }
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint)
case let .UnreadEntry(_, theme, strings): case let .UnreadEntry(_, presentationData):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index, theme: theme, strings: strings), directionHint: entry.directionHint) return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatUnreadItem(index: entry.entry.index, presentationData: presentationData), directionHint: entry.directionHint)
case let .ChatInfoEntry(text, theme, strings): case let .ChatInfoEntry(text, presentationData):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(text: text, controllerInteraction: controllerInteraction, theme: theme, strings: strings), directionHint: entry.directionHint) return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatBotInfoItem(text: text, controllerInteraction: controllerInteraction, presentationData: presentationData), directionHint: entry.directionHint)
case let .EmptyChatInfoEntry(theme, strings, tagMask): case let .EmptyChatInfoEntry(presentationData, tagMask):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatEmptyItem(theme: theme, strings: strings, tagMask: tagMask), directionHint: entry.directionHint) return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatEmptyItem(presentationData: presentationData, tagMask: tagMask), directionHint: entry.directionHint)
case let .SearchEntry(theme, strings): case let .SearchEntry(theme, strings):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, placeholder: strings.Common_Search, activate: { return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, placeholder: strings.Common_Search, activate: {
controllerInteraction.openSearch() controllerInteraction.openSearch()
@ -261,7 +261,7 @@ private func extractAssociatedData(chatLocation: ChatLocation, view: MessageHist
for entry in view.additionalData { for entry in view.additionalData {
if case let .peer(_, value) = entry { if case let .peer(_, value) = entry {
if let channel = value as? TelegramChannel, case .group = channel.info { if let channel = value as? TelegramChannel, case .group = channel.info {
automaticMediaDownloadPeerType = .channel automaticMediaDownloadPeerType = .group
} }
break break
} }
@ -357,7 +357,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
self.currentPresentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } self.currentPresentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
self.chatPresentationDataPromise = Promise(ChatPresentationData(theme: ChatPresentationThemeData(theme: self.currentPresentationData.theme, wallpaper: self.currentPresentationData.chatWallpaper), fontSize: self.currentPresentationData.fontSize, strings: self.currentPresentationData.strings, dateTimeFormat: self.currentPresentationData.dateTimeFormat)) self.chatPresentationDataPromise = Promise(ChatPresentationData(theme: ChatPresentationThemeData(theme: self.currentPresentationData.theme, wallpaper: self.currentPresentationData.chatWallpaper), fontSize: self.currentPresentationData.fontSize, strings: self.currentPresentationData.strings, dateTimeFormat: self.currentPresentationData.dateTimeFormat, disableAnimations: self.currentPresentationData.disableAnimations))
super.init() super.init()
@ -672,19 +672,22 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
let previousTheme = strongSelf.currentPresentationData.theme let previousTheme = strongSelf.currentPresentationData.theme
let previousStrings = strongSelf.currentPresentationData.strings let previousStrings = strongSelf.currentPresentationData.strings
let previousWallpaper = strongSelf.currentPresentationData.chatWallpaper let previousWallpaper = strongSelf.currentPresentationData.chatWallpaper
let previousDisableAnimations = strongSelf.currentPresentationData.disableAnimations
strongSelf.currentPresentationData = presentationData strongSelf.currentPresentationData = presentationData
if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings || previousWallpaper != presentationData.chatWallpaper { if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings || previousWallpaper != presentationData.chatWallpaper || previousDisableAnimations != presentationData.disableAnimations {
let themeData = ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper) let themeData = ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper)
let chatPresentationData = ChatPresentationData(theme: themeData, fontSize: presentationData.fontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, disableAnimations: presentationData.disableAnimations)
strongSelf.forEachItemHeaderNode { itemHeaderNode in strongSelf.forEachItemHeaderNode { itemHeaderNode in
if let dateNode = itemHeaderNode as? ChatMessageDateHeaderNode { if let dateNode = itemHeaderNode as? ChatMessageDateHeaderNode {
dateNode.updateThemeAndStrings(theme: themeData, strings: presentationData.strings) dateNode.updatePresentationData(chatPresentationData)
} else if let dateNode = itemHeaderNode as? ListMessageDateHeaderNode { } else if let dateNode = itemHeaderNode as? ListMessageDateHeaderNode {
dateNode.updateThemeAndStrings(theme: presentationData.theme, strings: presentationData.strings) dateNode.updateThemeAndStrings(theme: presentationData.theme, strings: presentationData.strings)
} }
} }
strongSelf.chatPresentationDataPromise.set(.single(ChatPresentationData(theme: themeData, fontSize: presentationData.fontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat))) strongSelf.chatPresentationDataPromise.set(.single(chatPresentationData))
} }
} }
}) })

View File

@ -15,4 +15,12 @@ class ChatInputPanelNode: ASDisplayNode {
func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
return 0.0 return 0.0
} }
func defaultHeight(metrics: LayoutMetrics) -> CGFloat {
if case .regular = metrics.widthClass, case .regular = metrics.heightClass {
return 49.0
} else {
return 45.0
}
}
} }

View File

@ -27,6 +27,8 @@ private let tabImageUnread = tabImageNone.flatMap({ image in
}) })
public class ChatListController: TelegramController, UIViewControllerPreviewingDelegate { public class ChatListController: TelegramController, UIViewControllerPreviewingDelegate {
private var validLayout: ContainerViewLayout?
private let account: Account private let account: Account
private let controlsHistoryPreload: Bool private let controlsHistoryPreload: Bool
@ -67,7 +69,7 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD
self.titleView = NetworkStatusTitleView(theme: self.presentationData.theme) self.titleView = NetworkStatusTitleView(theme: self.presentationData.theme)
super.init(account: account, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), enableMediaAccessoryPanel: true, locationBroadcastPanelSource: .summary) super.init(account: account, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), mediaAccessoryPanelVisibility: .always, locationBroadcastPanelSource: .summary)
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBar.style.style self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBar.style.style
@ -270,12 +272,12 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
if self.isNodeLoaded { if self.isNodeLoaded {
self.chatListDisplayNode.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder) self.chatListDisplayNode.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: self.presentationData.disableAnimations)
} }
} }
override public func loadDisplayNode() { override public func loadDisplayNode() {
self.displayNode = ChatListControllerNode(account: self.account, groupId: self.groupId, controlsHistoryPreload: self.controlsHistoryPreload, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder) self.displayNode = ChatListControllerNode(account: self.account, groupId: self.groupId, controlsHistoryPreload: self.controlsHistoryPreload, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: self.presentationData.disableAnimations)
self.chatListDisplayNode.navigationBar = self.navigationBar self.chatListDisplayNode.navigationBar = self.navigationBar
@ -525,6 +527,8 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition) super.containerLayoutUpdated(layout, transition: transition)
self.validLayout = layout
self.chatListDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition) self.chatListDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition)
} }
@ -589,6 +593,10 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD
} }
public func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? { public func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
guard let layout = self.validLayout, case .compact = layout.metrics.widthClass else {
return nil
}
let boundsSize = self.view.bounds.size let boundsSize = self.view.bounds.size
var contentSize = CGSize(width: boundsSize.width, height: boundsSize.height - (boundsSize.height > boundsSize.width ? 50.0 : 10.0)) var contentSize = CGSize(width: boundsSize.width, height: boundsSize.height - (boundsSize.height > boundsSize.width ? 50.0 : 10.0))
if (boundsSize.width.isEqual(to: 375.0) && boundsSize.height.isEqual(to: 812.0)) { if (boundsSize.width.isEqual(to: 375.0) && boundsSize.height.isEqual(to: 812.0)) {

View File

@ -23,10 +23,10 @@ class ChatListControllerNode: ASDisplayNode {
var themeAndStrings: (PresentationTheme, PresentationStrings, dateTimeFormat: PresentationDateTimeFormat) var themeAndStrings: (PresentationTheme, PresentationStrings, dateTimeFormat: PresentationDateTimeFormat)
init(account: Account, groupId: PeerGroupId?, controlsHistoryPreload: Bool, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder) { init(account: Account, groupId: PeerGroupId?, controlsHistoryPreload: Bool, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool) {
self.account = account self.account = account
self.groupId = groupId self.groupId = groupId
self.chatListNode = ChatListNode(account: account, groupId: groupId, controlsHistoryPreload: controlsHistoryPreload, mode: .chatList, theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder) self.chatListNode = ChatListNode(account: account, groupId: groupId, controlsHistoryPreload: controlsHistoryPreload, mode: .chatList, theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, disableAnimations: disableAnimations)
self.themeAndStrings = (theme, strings, dateTimeFormat) self.themeAndStrings = (theme, strings, dateTimeFormat)
@ -61,11 +61,11 @@ class ChatListControllerNode: ASDisplayNode {
} }
} }
func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder) { func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool) {
self.themeAndStrings = (theme, strings, dateTimeFormat) self.themeAndStrings = (theme, strings, dateTimeFormat)
self.backgroundColor = theme.chatList.backgroundColor self.backgroundColor = theme.chatList.backgroundColor
self.chatListNode.updateThemeAndStrings(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder) self.chatListNode.updateThemeAndStrings(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, disableAnimations: disableAnimations)
self.searchDisplayController?.updateThemeAndStrings(theme: theme, strings: strings) self.searchDisplayController?.updateThemeAndStrings(theme: theme, strings: strings)
self.chatListEmptyNode?.updateThemeAndStrings(theme: theme, strings: strings) self.chatListEmptyNode?.updateThemeAndStrings(theme: theme, strings: strings)
} }

View File

@ -164,27 +164,29 @@ private enum RevealOptionKey: Int32 {
private let itemHeight: CGFloat = 76.0 private let itemHeight: CGFloat = 76.0
private func revealOptions(strings: PresentationStrings, theme: PresentationTheme, isPinned: Bool?, isMuted: Bool?, hasPeerGroupId: Bool?, canDelete: Bool) -> [ItemListRevealOption] { private func revealOptions(strings: PresentationStrings, theme: PresentationTheme, isPinned: Bool?, isMuted: Bool?, hasPeerGroupId: Bool?, canDelete: Bool, isEditing: Bool) -> [ItemListRevealOption] {
var options: [ItemListRevealOption] = [] var options: [ItemListRevealOption] = []
if let isPinned = isPinned { if !isEditing {
if isPinned { if let isPinned = isPinned {
options.append(ItemListRevealOption(key: RevealOptionKey.unpin.rawValue, title: strings.DialogList_Unpin, icon: unpinIcon, color: theme.list.itemDisclosureActions.neutral1.fillColor, textColor: theme.list.itemDisclosureActions.neutral1.foregroundColor)) if isPinned {
} else { options.append(ItemListRevealOption(key: RevealOptionKey.unpin.rawValue, title: strings.DialogList_Unpin, icon: unpinIcon, color: theme.list.itemDisclosureActions.neutral1.fillColor, textColor: theme.list.itemDisclosureActions.neutral1.foregroundColor))
options.append(ItemListRevealOption(key: RevealOptionKey.pin.rawValue, title: strings.DialogList_Pin, icon: pinIcon, color: theme.list.itemDisclosureActions.neutral1.fillColor, textColor: theme.list.itemDisclosureActions.neutral1.foregroundColor)) } else {
options.append(ItemListRevealOption(key: RevealOptionKey.pin.rawValue, title: strings.DialogList_Pin, icon: pinIcon, color: theme.list.itemDisclosureActions.neutral1.fillColor, textColor: theme.list.itemDisclosureActions.neutral1.foregroundColor))
}
} }
} if let isMuted = isMuted {
if let isMuted = isMuted { if isMuted {
if isMuted { options.append(ItemListRevealOption(key: RevealOptionKey.unmute.rawValue, title: strings.Conversation_Unmute, icon: unmuteIcon, color: theme.list.itemDisclosureActions.neutral2.fillColor, textColor: theme.list.itemDisclosureActions.neutral2.foregroundColor))
options.append(ItemListRevealOption(key: RevealOptionKey.unmute.rawValue, title: strings.Conversation_Unmute, icon: unmuteIcon, color: theme.list.itemDisclosureActions.neutral2.fillColor, textColor: theme.list.itemDisclosureActions.neutral2.foregroundColor)) } else {
} else { options.append(ItemListRevealOption(key: RevealOptionKey.mute.rawValue, title: strings.Conversation_Mute, icon: muteIcon, color: theme.list.itemDisclosureActions.neutral2.fillColor, textColor: theme.list.itemDisclosureActions.neutral2.foregroundColor))
options.append(ItemListRevealOption(key: RevealOptionKey.mute.rawValue, title: strings.Conversation_Mute, icon: muteIcon, color: theme.list.itemDisclosureActions.neutral2.fillColor, textColor: theme.list.itemDisclosureActions.neutral2.foregroundColor)) }
} }
} if let hasPeerGroupId = hasPeerGroupId {
if let hasPeerGroupId = hasPeerGroupId { if hasPeerGroupId {
if hasPeerGroupId { options.append(ItemListRevealOption(key: RevealOptionKey.ungroup.rawValue, title: "Ungroup", icon: ungroupIcon, color: theme.list.itemAccentColor, textColor: theme.list.itemDisclosureActions.neutral2.foregroundColor))
options.append(ItemListRevealOption(key: RevealOptionKey.ungroup.rawValue, title: "Ungroup", icon: ungroupIcon, color: theme.list.itemAccentColor, textColor: theme.list.itemDisclosureActions.neutral2.foregroundColor)) } else {
} else { options.append(ItemListRevealOption(key: RevealOptionKey.group.rawValue, title: "Group", icon: groupIcon, color: theme.list.itemAccentColor, textColor: theme.list.itemDisclosureActions.neutral2.foregroundColor))
options.append(ItemListRevealOption(key: RevealOptionKey.group.rawValue, title: "Group", icon: groupIcon, color: theme.list.itemAccentColor, textColor: theme.list.itemDisclosureActions.neutral2.foregroundColor)) }
} }
} }
if canDelete { if canDelete {
@ -709,8 +711,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
isPinned = item.index.pinningIndex != nil isPinned = item.index.pinningIndex != nil
} }
if item.enableContextActions && !item.editing && !isAd { if item.enableContextActions && !isAd {
peerRevealOptions = revealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isPinned: isPinned, isMuted: item.account.peerId != item.index.messageIndex.id.peerId ? (currentMutedIconImage != nil) : nil, hasPeerGroupId: hasPeerGroupId, canDelete: true) peerRevealOptions = revealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isPinned: isPinned, isMuted: item.account.peerId != item.index.messageIndex.id.peerId ? (currentMutedIconImage != nil) : nil, hasPeerGroupId: hasPeerGroupId, canDelete: true, isEditing: item.editing)
if itemPeer.peerId != item.account.peerId { if itemPeer.peerId != item.account.peerId {
peerLeftRevealOptions = leftRevealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isUnread: unreadCount.unread) peerLeftRevealOptions = leftRevealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isUnread: unreadCount.unread)
} else { } else {
@ -723,8 +725,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
case .groupReference: case .groupReference:
let isPinned = item.index.pinningIndex != nil let isPinned = item.index.pinningIndex != nil
if item.enableContextActions && !item.editing { if item.enableContextActions {
peerRevealOptions = revealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isPinned: isPinned, isMuted: nil, hasPeerGroupId: nil, canDelete: false) peerRevealOptions = revealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isPinned: isPinned, isMuted: nil, hasPeerGroupId: nil, canDelete: false, isEditing: item.editing)
} else { } else {
peerRevealOptions = [] peerRevealOptions = []
} }

View File

@ -333,12 +333,12 @@ final class ChatListNode: ListView {
var isEmptyUpdated: ((Bool) -> Void)? var isEmptyUpdated: ((Bool) -> Void)?
private var wasEmpty: Bool? private var wasEmpty: Bool?
init(account: Account, groupId: PeerGroupId?, controlsHistoryPreload: Bool, mode: ChatListNodeMode, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder) { init(account: Account, groupId: PeerGroupId?, controlsHistoryPreload: Bool, mode: ChatListNodeMode, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool) {
self.account = account self.account = account
self.controlsHistoryPreload = controlsHistoryPreload self.controlsHistoryPreload = controlsHistoryPreload
self.mode = mode self.mode = mode
self.currentState = ChatListNodeState(presentationData: ChatListPresentationData(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder), editing: false, peerIdWithRevealedOptions: nil, peerInputActivities: nil) self.currentState = ChatListNodeState(presentationData: ChatListPresentationData(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, disableAnimations: disableAnimations), editing: false, peerIdWithRevealedOptions: nil, peerInputActivities: nil)
self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true) self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true)
self.theme = theme self.theme = theme
@ -475,7 +475,7 @@ final class ChatListNode: ListView {
} }
} }
return preparedChatListNodeViewTransition(from: previous, to: processedView, reason: reason, account: account, scrollPosition: updatedScrollPosition) return preparedChatListNodeViewTransition(from: previous, to: processedView, reason: reason, disableAnimations: state.presentationData.disableAnimations, account: account, scrollPosition: updatedScrollPosition)
|> map({ mappedChatListNodeViewListTransition(account: account, nodeInteraction: nodeInteraction, peerGroupId: groupId, mode: mode, transition: $0) }) |> map({ mappedChatListNodeViewListTransition(account: account, nodeInteraction: nodeInteraction, peerGroupId: groupId, mode: mode, transition: $0) })
|> runOn(prepareOnMainQueue ? Queue.mainQueue() : viewProcessingQueue) |> runOn(prepareOnMainQueue ? Queue.mainQueue() : viewProcessingQueue)
} }
@ -768,7 +768,7 @@ final class ChatListNode: ListView {
self.activityStatusesDisposable?.dispose() self.activityStatusesDisposable?.dispose()
} }
func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder) { func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool) {
if theme !== self.currentState.presentationData.theme || strings !== self.currentState.presentationData.strings || dateTimeFormat != self.currentState.presentationData.dateTimeFormat { if theme !== self.currentState.presentationData.theme || strings !== self.currentState.presentationData.strings || dateTimeFormat != self.currentState.presentationData.dateTimeFormat {
self.theme = theme self.theme = theme
@ -777,7 +777,7 @@ final class ChatListNode: ListView {
} }
self.updateState { self.updateState {
return $0.withUpdatedPresentationData(ChatListPresentationData(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder)) return $0.withUpdatedPresentationData(ChatListPresentationData(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, disableAnimations: disableAnimations))
} }
} }
} }

View File

@ -6,12 +6,14 @@ final class ChatListPresentationData {
let dateTimeFormat: PresentationDateTimeFormat let dateTimeFormat: PresentationDateTimeFormat
let nameSortOrder: PresentationPersonNameOrder let nameSortOrder: PresentationPersonNameOrder
let nameDisplayOrder: PresentationPersonNameOrder let nameDisplayOrder: PresentationPersonNameOrder
let disableAnimations: Bool
init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder) { init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool) {
self.theme = theme self.theme = theme
self.strings = strings self.strings = strings
self.dateTimeFormat = dateTimeFormat self.dateTimeFormat = dateTimeFormat
self.nameSortOrder = nameSortOrder self.nameSortOrder = nameSortOrder
self.nameDisplayOrder = nameDisplayOrder self.nameDisplayOrder = nameDisplayOrder
self.disableAnimations = disableAnimations
} }
} }

View File

@ -507,7 +507,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
self.openMessage = openMessage self.openMessage = openMessage
self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
self.presentationDataPromise = Promise(ChatListPresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder)) self.presentationDataPromise = Promise(ChatListPresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: self.presentationData.disableAnimations))
self.recentListNode = ListView() self.recentListNode = ListView()
self.listNode = ListView() self.listNode = ListView()

View File

@ -154,7 +154,7 @@ final class ChatListSearchRecentPeersNode: ASDisplayNode {
case .disabled: case .disabled:
return .single(([], [:])) return .single(([], [:]))
case let .peers(peers): case let .peers(peers):
return combineLatest(peers.map {account.postbox.peerView(id: $0.id)}) |> mapToSignal { peerViews -> Signal<([Peer], [PeerId: UnreadSearchBadge]), NoError> in return combineLatest(peers.filter { !$0.isDeleted }.map {account.postbox.peerView(id: $0.id)}) |> mapToSignal { peerViews -> Signal<([Peer], [PeerId: UnreadSearchBadge]), NoError> in
return account.postbox.unreadMessageCountsView(items: peerViews.map {.peer($0.peerId)}) |> map { values in return account.postbox.unreadMessageCountsView(items: peerViews.map {.peer($0.peerId)}) |> map { values in
var peers:[Peer] = [] var peers:[Peer] = []

View File

@ -44,7 +44,7 @@ enum ChatListNodeViewScrollPosition {
case index(index: ChatListIndex, position: ListViewScrollPosition, directionHint: ListViewScrollToItemDirectionHint, animated: Bool) case index(index: ChatListIndex, position: ListViewScrollPosition, directionHint: ListViewScrollToItemDirectionHint, animated: Bool)
} }
func preparedChatListNodeViewTransition(from fromView: ChatListNodeView?, to toView: ChatListNodeView, reason: ChatListNodeViewTransitionReason, account: Account, scrollPosition: ChatListNodeViewScrollPosition?) -> Signal<ChatListNodeViewTransition, NoError> { func preparedChatListNodeViewTransition(from fromView: ChatListNodeView?, to toView: ChatListNodeView, reason: ChatListNodeViewTransitionReason, disableAnimations: Bool, account: Account, scrollPosition: ChatListNodeViewScrollPosition?) -> Signal<ChatListNodeViewTransition, NoError> {
return Signal { subscriber in return Signal { subscriber in
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromView?.filteredEntries ?? [], rightList: toView.filteredEntries) let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromView?.filteredEntries ?? [], rightList: toView.filteredEntries)
@ -98,7 +98,9 @@ func preparedChatListNodeViewTransition(from fromView: ChatListNodeView?, to toV
let _ = options.insert(.AnimateCrossfade) let _ = options.insert(.AnimateCrossfade)
} else { } else {
let _ = options.insert(.AnimateAlpha) let _ = options.insert(.AnimateAlpha)
let _ = options.insert(.AnimateInsertion) if !disableAnimations {
let _ = options.insert(.AnimateInsertion)
}
} }
case .reload: case .reload:
break break

View File

@ -30,6 +30,8 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
self.emptyNode = ImmediateTextNode() self.emptyNode = ImmediateTextNode()
self.emptyNode.isLayerBacked = true self.emptyNode.isLayerBacked = true
self.emptyNode.attributedText = NSAttributedString(string: strings.Conversation_EmptyGifPanelPlaceholder, font: Font.regular(15.0), textColor: theme.chat.inputMediaPanel.stickersSectionTextColor) self.emptyNode.attributedText = NSAttributedString(string: strings.Conversation_EmptyGifPanelPlaceholder, font: Font.regular(15.0), textColor: theme.chat.inputMediaPanel.stickersSectionTextColor)
self.emptyNode.textAlignment = .center
self.emptyNode.maximumNumberOfLines = 3
super.init() super.init()

View File

@ -116,7 +116,7 @@ final class ChatMessageActionSheetControllerNode: ViewControllerTracingNode {
self.addSubnode(self.inputDimNode) self.addSubnode(self.inputDimNode)
self.addSubnode(self.itemsShadowNode) self.addSubnode(self.itemsShadowNode)
self.addSubnode(self.itemsContainerNode) self.addSubnode(self.itemsContainerNode)
for actionNode in actionNodes { for actionNode in actionNodes {
self.itemsContainerNode.addSubnode(actionNode) self.itemsContainerNode.addSubnode(actionNode)
actionNode.addTarget(self, action: #selector(actionPressed(_:)), forControlEvents: .touchUpInside) actionNode.addTarget(self, action: #selector(actionPressed(_:)), forControlEvents: .touchUpInside)
@ -205,7 +205,7 @@ final class ChatMessageActionSheetControllerNode: ViewControllerTracingNode {
} }
} }
if let associatedController = self.associatedController { if let associatedController = self.associatedController {
let subpoint = self.view.convert(point, to: associatedController.view) let subpoint = self.view.convert(point, to: nil) // temporary fix of iPad landscape+keyboard issue. Point converts to portrait coordinate system otherwise
if let result = associatedController.view.hitTest(subpoint, with: event) { if let result = associatedController.view.hitTest(subpoint, with: event) {
return result return result
} }

View File

@ -1040,7 +1040,13 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
} else { } else {
minimalContentSize = layoutConstants.bubble.minimumSize minimalContentSize = layoutConstants.bubble.minimumSize
} }
let layoutBubbleSize = CGSize(width: max(contentSize.width, headerSize.width) + layoutConstants.bubble.contentInsets.left + layoutConstants.bubble.contentInsets.right, height: max(minimalContentSize.height, headerSize.height + contentSize.height + layoutConstants.bubble.contentInsets.top + layoutConstants.bubble.contentInsets.bottom)) let calculatedBubbleHeight = headerSize.height + contentSize.height + layoutConstants.bubble.contentInsets.top + layoutConstants.bubble.contentInsets.bottom
let layoutBubbleSize = CGSize(width: max(contentSize.width, headerSize.width) + layoutConstants.bubble.contentInsets.left + layoutConstants.bubble.contentInsets.right, height: max(minimalContentSize.height, calculatedBubbleHeight))
var contentVerticalOffset: CGFloat = 0.0
if minimalContentSize.height > calculatedBubbleHeight + 2.0 {
contentVerticalOffset = floorToScreenPixels((minimalContentSize.height - calculatedBubbleHeight) / 2.0)
}
let backgroundFrame: CGRect let backgroundFrame: CGRect
let contentOrigin: CGPoint let contentOrigin: CGPoint
@ -1048,12 +1054,12 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
switch alignment { switch alignment {
case .none: case .none:
backgroundFrame = CGRect(origin: CGPoint(x: incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset) : (params.width - params.rightInset - layoutBubbleSize.width - layoutConstants.bubble.edgeInset), y: 0.0), size: layoutBubbleSize) backgroundFrame = CGRect(origin: CGPoint(x: incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + avatarInset) : (params.width - params.rightInset - layoutBubbleSize.width - layoutConstants.bubble.edgeInset), y: 0.0), size: layoutBubbleSize)
contentOrigin = CGPoint(x: backgroundFrame.origin.x + (incoming ? layoutConstants.bubble.contentInsets.left : layoutConstants.bubble.contentInsets.right), y: backgroundFrame.origin.y + layoutConstants.bubble.contentInsets.top + headerSize.height) contentOrigin = CGPoint(x: backgroundFrame.origin.x + (incoming ? layoutConstants.bubble.contentInsets.left : layoutConstants.bubble.contentInsets.right), y: backgroundFrame.origin.y + layoutConstants.bubble.contentInsets.top + headerSize.height + contentVerticalOffset)
contentUpperRightCorner = CGPoint(x: backgroundFrame.maxX - (incoming ? layoutConstants.bubble.contentInsets.right : layoutConstants.bubble.contentInsets.left), y: backgroundFrame.origin.y + layoutConstants.bubble.contentInsets.top + headerSize.height) contentUpperRightCorner = CGPoint(x: backgroundFrame.maxX - (incoming ? layoutConstants.bubble.contentInsets.right : layoutConstants.bubble.contentInsets.left), y: backgroundFrame.origin.y + layoutConstants.bubble.contentInsets.top + headerSize.height)
case .center: case .center:
let availableWidth = params.width - params.leftInset - params.rightInset let availableWidth = params.width - params.leftInset - params.rightInset
backgroundFrame = CGRect(origin: CGPoint(x: params.leftInset + floor((availableWidth - layoutBubbleSize.width) / 2.0), y: 0.0), size: layoutBubbleSize) backgroundFrame = CGRect(origin: CGPoint(x: params.leftInset + floor((availableWidth - layoutBubbleSize.width) / 2.0), y: 0.0), size: layoutBubbleSize)
contentOrigin = CGPoint(x: backgroundFrame.minX + floor(layoutConstants.bubble.contentInsets.right + layoutConstants.bubble.contentInsets.left) / 2.0, y: backgroundFrame.minY + layoutConstants.bubble.contentInsets.top + headerSize.height) contentOrigin = CGPoint(x: backgroundFrame.minX + floor(layoutConstants.bubble.contentInsets.right + layoutConstants.bubble.contentInsets.left) / 2.0, y: backgroundFrame.minY + layoutConstants.bubble.contentInsets.top + headerSize.height + contentVerticalOffset)
contentUpperRightCorner = CGPoint(x: backgroundFrame.maxX - (incoming ? layoutConstants.bubble.contentInsets.right : layoutConstants.bubble.contentInsets.left), y: backgroundFrame.origin.y + layoutConstants.bubble.contentInsets.top + headerSize.height) contentUpperRightCorner = CGPoint(x: backgroundFrame.maxX - (incoming ? layoutConstants.bubble.contentInsets.right : layoutConstants.bubble.contentInsets.left), y: backgroundFrame.origin.y + layoutConstants.bubble.contentInsets.top + headerSize.height)
} }
@ -1755,9 +1761,11 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
switch item.content { switch item.content {
case let .message(message, _, _, _): case let .message(message, _, _, _):
for media in message.media { for media in message.media {
if media is TelegramMediaAction { if let action = media as? TelegramMediaAction {
canHaveSelection = false if case .phoneCall = action.action { } else {
break canHaveSelection = false
break
}
} }
} }
default: default:
@ -1883,50 +1891,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
} }
} }
private func performMessageButtonAction(button: ReplyMarkupButton) {
if let item = self.item {
switch button.action {
case .text:
item.controllerInteraction.sendMessage(button.title)
case let .url(url):
item.controllerInteraction.openUrl(url, true, nil)
case .requestMap:
item.controllerInteraction.shareCurrentLocation()
case .requestPhone:
item.controllerInteraction.shareAccountContact()
case .openWebApp:
item.controllerInteraction.requestMessageActionCallback(item.message.id, nil, true)
case let .callback(data):
item.controllerInteraction.requestMessageActionCallback(item.message.id, data, false)
case let .switchInline(samePeer, query):
var botPeer: Peer?
var found = false
for attribute in item.message.attributes {
if let attribute = attribute as? InlineBotMessageAttribute {
if let peerId = attribute.peerId {
botPeer = item.message.peers[peerId]
found = true
}
}
}
if !found {
botPeer = item.message.author
}
var peerId: PeerId?
if samePeer {
peerId = item.message.id.peerId
}
if let botPeer = botPeer, let addressName = botPeer.addressName {
item.controllerInteraction.activateSwitchInline(peerId, "@\(addressName) \(query)")
}
case .payment:
item.controllerInteraction.openCheckoutOrReceipt(item.message.id)
}
}
}
@objc func shareButtonPressed() { @objc func shareButtonPressed() {
if let item = self.item { if let item = self.item {
if item.content.firstMessage.id.peerId == item.account.peerId { if item.content.firstMessage.id.peerId == item.account.peerId {

View File

@ -17,13 +17,12 @@ final class ChatMessageDateHeader: ListViewItemHeader {
private let roundedTimestamp: Int32 private let roundedTimestamp: Int32
let id: Int64 let id: Int64
let theme: ChatPresentationThemeData let presentationData: ChatPresentationData
let strings: PresentationStrings let action: ((Int32) -> Void)?
let action:((Int32)->Void)?
init(timestamp: Int32, theme: ChatPresentationThemeData, strings: PresentationStrings, action:((Int32)->Void)? = nil) { init(timestamp: Int32, presentationData: ChatPresentationData, action:((Int32) -> Void)? = nil) {
self.timestamp = timestamp self.timestamp = timestamp
self.theme = theme self.presentationData = presentationData
self.strings = strings
self.action = action self.action = action
if timestamp == Int32.max { if timestamp == Int32.max {
self.roundedTimestamp = timestamp / (granularity) * (granularity) self.roundedTimestamp = timestamp / (granularity) * (granularity)
@ -38,7 +37,7 @@ final class ChatMessageDateHeader: ListViewItemHeader {
let height: CGFloat = 34.0 let height: CGFloat = 34.0
func node() -> ListViewItemHeaderNode { func node() -> ListViewItemHeaderNode {
return ChatMessageDateHeaderNode(localTimestamp: self.roundedTimestamp, theme: self.theme, strings: self.strings, action: self.action) return ChatMessageDateHeaderNode(localTimestamp: self.roundedTimestamp, presentationData: self.presentationData, action: self.action)
} }
} }
@ -81,17 +80,16 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
let stickBackgroundNode: ASImageNode let stickBackgroundNode: ASImageNode
private let localTimestamp: Int32 private let localTimestamp: Int32
private var theme: ChatPresentationThemeData private var presentationData: ChatPresentationData
private var strings: PresentationStrings
private var flashingOnScrolling = false private var flashingOnScrolling = false
private var stickDistanceFactor: CGFloat = 0.0 private var stickDistanceFactor: CGFloat = 0.0
private var action:((Int32)->Void)? = nil private var action:((Int32) -> Void)? = nil
init(localTimestamp: Int32, theme: ChatPresentationThemeData, strings: PresentationStrings, action:((Int32)->Void)? = nil) { init(localTimestamp: Int32, presentationData: ChatPresentationData, action:((Int32) -> Void)? = nil) {
self.presentationData = presentationData
self.localTimestamp = localTimestamp self.localTimestamp = localTimestamp
self.theme = theme
self.strings = strings
self.action = action self.action = action
self.labelNode = TextNode() self.labelNode = TextNode()
@ -115,7 +113,7 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
let graphics = PresentationResourcesChat.principalGraphics(theme.theme, wallpaper: !theme.wallpaper.isEmpty) let graphics = PresentationResourcesChat.principalGraphics(presentationData.theme.theme, wallpaper: !presentationData.theme.wallpaper.isEmpty)
self.backgroundNode.image = graphics.dateStaticBackground self.backgroundNode.image = graphics.dateStaticBackground
self.stickBackgroundNode.image = graphics.dateFloatingBackground self.stickBackgroundNode.image = graphics.dateFloatingBackground
@ -139,15 +137,15 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
let text: String let text: String
if timeinfo.tm_year == timeinfoNow.tm_year { if timeinfo.tm_year == timeinfoNow.tm_year {
if timeinfo.tm_yday == timeinfoNow.tm_yday { if timeinfo.tm_yday == timeinfoNow.tm_yday {
text = strings.Weekday_Today text = presentationData.strings.Weekday_Today
} else { } else {
text = strings.Date_ChatDateHeader(monthAtIndex(Int(timeinfo.tm_mon), strings: strings), "\(timeinfo.tm_mday)").0 text = presentationData.strings.Date_ChatDateHeader(monthAtIndex(Int(timeinfo.tm_mon), strings: presentationData.strings), "\(timeinfo.tm_mday)").0
} }
} else { } else {
text = strings.Date_ChatDateHeaderYear(monthAtIndex(Int(timeinfo.tm_mon), strings: strings), "\(timeinfo.tm_mday)", "\(1900 + timeinfo.tm_year)").0 text = presentationData.strings.Date_ChatDateHeaderYear(monthAtIndex(Int(timeinfo.tm_mon), strings: presentationData.strings), "\(timeinfo.tm_mday)", "\(1900 + timeinfo.tm_year)").0
} }
let attributedString = NSAttributedString(string: text, font: titleFont, textColor: theme.theme.chat.serviceMessage.dateTextColor) let attributedString = NSAttributedString(string: text, font: titleFont, textColor: presentationData.theme.theme.chat.serviceMessage.dateTextColor)
let labelLayout = TextNode.asyncLayout(self.labelNode) let labelLayout = TextNode.asyncLayout(self.labelNode)
let (size, apply) = labelLayout(TextNodeLayoutArguments(attributedString: attributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 320.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let (size, apply) = labelLayout(TextNodeLayoutArguments(attributedString: attributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 320.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
@ -164,18 +162,14 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
} }
override func didLoad() { override func didLoad() {
super.didLoad() super.didLoad()
self.view.addGestureRecognizer(ListViewTapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) self.view.addGestureRecognizer(ListViewTapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
} }
func updateThemeAndStrings(theme: ChatPresentationThemeData, strings: PresentationStrings) { func updatePresentationData(_ presentationData: ChatPresentationData) {
self.theme = theme let graphics = PresentationResourcesChat.principalGraphics(presentationData.theme.theme, wallpaper: !presentationData.theme.wallpaper.isEmpty)
self.strings = strings
let graphics = PresentationResourcesChat.principalGraphics(theme.theme, wallpaper: !theme.wallpaper.isEmpty)
self.backgroundNode.image = graphics.dateStaticBackground self.backgroundNode.image = graphics.dateStaticBackground
self.stickBackgroundNode.image = graphics.dateFloatingBackground self.stickBackgroundNode.image = graphics.dateFloatingBackground
@ -261,4 +255,8 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
action?(self.localTimestamp) action?(self.localTimestamp)
} }
} }
override public var wantsScrollDynamics: Bool {
return !self.presentationData.disableAnimations
}
} }

View File

@ -8,11 +8,10 @@ import TelegramCore
class ChatMessageInstantVideoItemNode: ChatMessageItemView { class ChatMessageInstantVideoItemNode: ChatMessageItemView {
private let interactiveVideoNode: ChatMessageInteractiveInstantVideoNode private let interactiveVideoNode: ChatMessageInteractiveInstantVideoNode
private var selectionNode: ChatMessageSelectionNode?
private var swipeToReplyNode: ChatMessageSwipeToReplyNode? private var swipeToReplyNode: ChatMessageSwipeToReplyNode?
private var swipeToReplyFeedback: HapticFeedback? private var swipeToReplyFeedback: HapticFeedback?
private var selectionNode: ChatMessageSelectionNode?
private var appliedItem: ChatMessageItem? private var appliedItem: ChatMessageItem?
private var forwardInfoNode: ChatMessageForwardInfoNode? private var forwardInfoNode: ChatMessageForwardInfoNode?
@ -21,6 +20,8 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
private var replyInfoNode: ChatMessageReplyInfoNode? private var replyInfoNode: ChatMessageReplyInfoNode?
private var replyBackgroundNode: ASImageNode? private var replyBackgroundNode: ASImageNode?
private var actionButtonsNode: ChatMessageActionButtonsNode?
private var currentSwipeToReplyTranslation: CGFloat = 0.0 private var currentSwipeToReplyTranslation: CGFloat = 0.0
override var visibility: ListViewItemNodeVisibility { override var visibility: ListViewItemNodeVisibility {
@ -76,9 +77,12 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
let makeForwardInfoLayout = ChatMessageForwardInfoNode.asyncLayout(self.forwardInfoNode) let makeForwardInfoLayout = ChatMessageForwardInfoNode.asyncLayout(self.forwardInfoNode)
let currentForwardBackgroundNode = self.forwardBackgroundNode let currentForwardBackgroundNode = self.forwardBackgroundNode
let actionButtonsLayout = ChatMessageActionButtonsNode.asyncLayout(self.actionButtonsNode)
let currentItem = self.appliedItem let currentItem = self.appliedItem
return { item, params, mergedTop, mergedBottom, dateHeaderAtBottom in return { item, params, mergedTop, mergedBottom, dateHeaderAtBottom in
let baseWidth = params.width - params.leftInset - params.rightInset
let incoming = item.message.effectivelyIncoming(item.account.peerId) let incoming = item.message.effectivelyIncoming(item.account.peerId)
let avatarInset: CGFloat let avatarInset: CGFloat
@ -124,6 +128,9 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
var replyInfoApply: (CGSize, () -> ChatMessageReplyInfoNode)? var replyInfoApply: (CGSize, () -> ChatMessageReplyInfoNode)?
var updatedReplyBackgroundNode: ASImageNode? var updatedReplyBackgroundNode: ASImageNode?
var replyBackgroundImage: UIImage? var replyBackgroundImage: UIImage?
var replyMarkup: ReplyMarkupMessageAttribute?
var inlineBotNameString: String?
for attribute in item.message.attributes { for attribute in item.message.attributes {
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] { if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
let availableWidth = max(60.0, params.width - params.leftInset - params.rightInset - videoLayout.contentSize.width - 20.0 - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left) let availableWidth = max(60.0, params.width - params.leftInset - params.rightInset - videoLayout.contentSize.width - 20.0 - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left)
@ -136,6 +143,14 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
} }
replyBackgroundImage = PresentationResourcesChat.chatServiceBubbleFillImage(item.presentationData.theme.theme) replyBackgroundImage = PresentationResourcesChat.chatServiceBubbleFillImage(item.presentationData.theme.theme)
break break
} else if let attribute = attribute as? InlineBotMessageAttribute {
if let peerId = attribute.peerId, let bot = item.message.peers[peerId] as? TelegramUser {
inlineBotNameString = bot.username
} else {
inlineBotNameString = attribute.title
}
} else if let attribute = attribute as? ReplyMarkupMessageAttribute, attribute.flags.contains(.inline), !attribute.rows.isEmpty {
replyMarkup = attribute
} }
} }
@ -173,7 +188,25 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
forwardBackgroundImage = PresentationResourcesChat.chatServiceBubbleFillImage(item.presentationData.theme.theme) forwardBackgroundImage = PresentationResourcesChat.chatServiceBubbleFillImage(item.presentationData.theme.theme)
} }
return (ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: videoLayout.contentSize.height), insets: layoutInsets), { [weak self] animation in var maxContentWidth = videoLayout.contentSize.width
var actionButtonsFinalize: ((CGFloat) -> (CGSize, (_ animated: Bool) -> ChatMessageActionButtonsNode))?
if let replyMarkup = replyMarkup {
let (minWidth, buttonsLayout) = actionButtonsLayout(item.account, item.presentationData.theme.theme, item.presentationData.strings, replyMarkup, item.message, maxContentWidth)
maxContentWidth = max(maxContentWidth, minWidth)
actionButtonsFinalize = buttonsLayout
}
var actionButtonsSizeAndApply: (CGSize, (Bool) -> ChatMessageActionButtonsNode)?
if let actionButtonsFinalize = actionButtonsFinalize {
actionButtonsSizeAndApply = actionButtonsFinalize(maxContentWidth)
}
var layoutSize = CGSize(width: params.width, height: videoLayout.contentSize.height)
if let actionButtonsSizeAndApply = actionButtonsSizeAndApply {
layoutSize.height += actionButtonsSizeAndApply.0.height
}
return (ListViewItemNodeLayout(contentSize: layoutSize, insets: layoutInsets), { [weak self] animation in
if let strongSelf = self { if let strongSelf = self {
strongSelf.appliedItem = item strongSelf.appliedItem = item
@ -241,6 +274,35 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
forwardInfoNode.removeFromSupernode() forwardInfoNode.removeFromSupernode()
strongSelf.forwardInfoNode = nil strongSelf.forwardInfoNode = nil
} }
if let actionButtonsSizeAndApply = actionButtonsSizeAndApply {
var animated = false
if let _ = strongSelf.actionButtonsNode {
if case .System = animation {
animated = true
}
}
let actionButtonsNode = actionButtonsSizeAndApply.1(animated)
let previousFrame = actionButtonsNode.frame
let actionButtonsFrame = CGRect(origin: CGPoint(x: videoFrame.minX, y: videoFrame.maxY), size: actionButtonsSizeAndApply.0)
actionButtonsNode.frame = actionButtonsFrame
if actionButtonsNode !== strongSelf.actionButtonsNode {
strongSelf.actionButtonsNode = actionButtonsNode
actionButtonsNode.buttonPressed = { button in
if let strongSelf = self {
strongSelf.performMessageButtonAction(button: button)
}
}
strongSelf.addSubnode(actionButtonsNode)
} else {
if case let .System(duration) = animation {
actionButtonsNode.layer.animateFrame(from: previousFrame, to: actionButtonsFrame, duration: duration, timingFunction: kCAMediaTimingFunctionSpring)
}
}
} else if let actionButtonsNode = strongSelf.actionButtonsNode {
actionButtonsNode.removeFromSupernode()
strongSelf.actionButtonsNode = nil
}
} }
}) })
} }

View File

@ -279,11 +279,8 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
self.effectiveAuthorId = effectiveAuthor?.id self.effectiveAuthorId = effectiveAuthor?.id
self.header = ChatMessageDateHeader(timestamp: content.index.timestamp, theme: presentationData.theme, strings: presentationData.strings, action: { timestamp in self.header = ChatMessageDateHeader(timestamp: content.index.timestamp, presentationData: presentationData, action: { timestamp in
//
var calendar = NSCalendar.current var calendar = NSCalendar.current
calendar.timeZone = TimeZone(abbreviation: "UTC")! calendar.timeZone = TimeZone(abbreviation: "UTC")!
let date = Date(timeIntervalSince1970: TimeInterval(timestamp)) let date = Date(timeIntervalSince1970: TimeInterval(timestamp))
let components = calendar.dateComponents([.year, .month, .day], from: date) let components = calendar.dateComponents([.year, .month, .day], from: date)
@ -291,21 +288,6 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
if let date = calendar.date(from: components) { if let date = calendar.date(from: components) {
controllerInteraction.navigateToFirstDateMessage(Int32(date.timeIntervalSince1970)) controllerInteraction.navigateToFirstDateMessage(Int32(date.timeIntervalSince1970))
} }
/*
unsigned unitFlags = NSCalendarUnitDay| NSCalendarUnitYear | NSCalendarUnitMonth;
NSDateComponents *components = [cal components:unitFlags fromDate:date];
NSDateComponents *comps = [[NSDateComponents alloc] init];
comps.day = day;
comps.year = components.year;
comps.month = components.month;
return [cal dateFromComponents:comps];
*/
// item.chatInteraction?.jumpToDate(CalendarUtils.monthDay(components.day!, date: date))
}) })
if displayAuthorInfo { if displayAuthorInfo {
@ -330,7 +312,6 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
} }
} }
self.accessoryItem = accessoryItem self.accessoryItem = accessoryItem
} }
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, () -> Void)) -> Void) { public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, () -> Void)) -> Void) {

View File

@ -2,6 +2,7 @@ import Foundation
import AsyncDisplayKit import AsyncDisplayKit
import Display import Display
import Postbox import Postbox
import TelegramCore
struct ChatMessageItemWidthFill { struct ChatMessageItemWidthFill {
let compactInset: CGFloat let compactInset: CGFloat
@ -190,4 +191,55 @@ public class ChatMessageItemView: ListViewItemNode {
return nil return nil
} }
} }
func performMessageButtonAction(button: ReplyMarkupButton) {
if let item = self.item {
switch button.action {
case .text:
item.controllerInteraction.sendMessage(button.title)
case let .url(url):
item.controllerInteraction.openUrl(url, true, nil)
case .requestMap:
item.controllerInteraction.shareCurrentLocation()
case .requestPhone:
item.controllerInteraction.shareAccountContact()
case .openWebApp:
item.controllerInteraction.requestMessageActionCallback(item.message.id, nil, true)
case let .callback(data):
item.controllerInteraction.requestMessageActionCallback(item.message.id, data, false)
case let .switchInline(samePeer, query):
var botPeer: Peer?
var found = false
for attribute in item.message.attributes {
if let attribute = attribute as? InlineBotMessageAttribute {
if let peerId = attribute.peerId {
botPeer = item.message.peers[peerId]
found = true
}
}
}
if !found {
botPeer = item.message.author
}
var peerId: PeerId?
if samePeer {
peerId = item.message.id.peerId
}
if let botPeer = botPeer, let addressName = botPeer.addressName {
item.controllerInteraction.activateSwitchInline(peerId, "@\(addressName) \(query)")
}
case .payment:
item.controllerInteraction.openCheckoutOrReceipt(item.message.id)
}
}
}
override public var wantsScrollDynamics: Bool {
if let disableAnimations = self.item?.presentationData.disableAnimations {
return !disableAnimations
}
return true
}
} }

View File

@ -76,7 +76,8 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
textColor = titleColor textColor = titleColor
} }
var leftInset: CGFloat = 10.0 var leftInset: CGFloat = 11.0
let spacing: CGFloat = 2.0
var overlayIcon: UIImage? var overlayIcon: UIImage?
@ -112,7 +113,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
var applyImage: (() -> TransformImageNode)? var applyImage: (() -> TransformImageNode)?
if let imageDimensions = imageDimensions { if let imageDimensions = imageDimensions {
leftInset += 36.0 leftInset += 32.0
let boundingSize = CGSize(width: 30.0, height: 30.0) let boundingSize = CGSize(width: 30.0, height: 30.0)
var radius: CGFloat = 2.0 var radius: CGFloat = 2.0
var imageSize = imageDimensions.aspectFilled(boundingSize) var imageSize = imageDimensions.aspectFilled(boundingSize)
@ -151,7 +152,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
let (titleLayout, titleApply) = titleNodeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: titleString, font: titleFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: contrainedTextSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let (titleLayout, titleApply) = titleNodeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: titleString, font: titleFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: contrainedTextSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let (textLayout, textApply) = textNodeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: textString, font: textFont, textColor: textColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: contrainedTextSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let (textLayout, textApply) = textNodeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: textString, font: textFont, textColor: textColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: contrainedTextSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let size = CGSize(width: max(titleLayout.size.width, textLayout.size.width) + leftInset, height: titleLayout.size.height + textLayout.size.height) let size = CGSize(width: max(titleLayout.size.width, textLayout.size.width) + leftInset, height: titleLayout.size.height + textLayout.size.height + 2 * spacing)
return (size, { return (size, {
let node: ChatMessageReplyInfoNode let node: ChatMessageReplyInfoNode
@ -186,8 +187,8 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
node.addSubnode(imageNode) node.addSubnode(imageNode)
node.imageNode = imageNode node.imageNode = imageNode
} }
imageFrame = CGRect(origin: CGPoint(x: 8.0, y: 3.0), size: CGSize(width: 30.0, height: 30.0)) imageNode.frame = CGRect(origin: CGPoint(x: 8.0, y: 4.0 + UIScreenPixel), size: CGSize(width: 30.0, height: 30.0))
imageNode.frame = CGRect(origin: CGPoint(x: 8.0, y: 3.0), size: CGSize(width: 30.0, height: 30.0)) imageFrame = imageNode.frame
if let updateImageSignal = updateImageSignal { if let updateImageSignal = updateImageSignal {
imageNode.setSignal(updateImageSignal) imageNode.setSignal(updateImageSignal)
@ -216,11 +217,11 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
node.overlayIconNode = nil node.overlayIconNode = nil
} }
titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: titleLayout.size) titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: spacing), size: titleLayout.size)
textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: titleLayout.size.height), size: textLayout.size) textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: titleNode.frame.maxY + spacing), size: textLayout.size)
node.lineNode.image = lineImage node.lineNode.image = lineImage
node.lineNode.frame = CGRect(origin: CGPoint(x: 1.0, y: 3.0), size: CGSize(width: 2.0, height: max(0.0, size.height - 4.0))) node.lineNode.frame = CGRect(origin: CGPoint(x: 1.0, y: 3.0), size: CGSize(width: 2.0, height: max(0.0, size.height - 5.0)))
node.contentNode.frame = CGRect(origin: CGPoint(), size: size) node.contentNode.frame = CGRect(origin: CGPoint(), size: size)

View File

@ -116,7 +116,7 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, maxHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, maxHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
self.validLayout = (width, leftInset, rightInset, maxHeight, metrics) self.validLayout = (width, leftInset, rightInset, maxHeight, metrics)
let panelHeight: CGFloat = 45.0 let panelHeight = defaultHeight(metrics: metrics)
if self.presentationInterfaceState != interfaceState { if self.presentationInterfaceState != interfaceState {
self.presentationInterfaceState = interfaceState self.presentationInterfaceState = interfaceState
@ -186,6 +186,6 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
} }
override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
return 45.0 return defaultHeight(metrics: metrics)
} }
} }

View File

@ -124,8 +124,8 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
if let strongSelf = self, let item = strongSelf.item { if let strongSelf = self, let item = strongSelf.item {
if let webPage = strongSelf.webPage, case let .Loaded(content) = webPage.content, let image = content.image, let instantPage = content.instantPage { if let webPage = strongSelf.webPage, case let .Loaded(content) = webPage.content, let image = content.image, let instantPage = content.instantPage {
var isGallery = false var isGallery = false
switch websiteType(of: content) { switch instantPageType(of: content) {
case .instagram, .twitter: case .album:
let count = instantPageGalleryMedia(webpageId: webPage.webpageId, page: instantPage, galleryMedia: image).count let count = instantPageGalleryMedia(webpageId: webPage.webpageId, page: instantPage, galleryMedia: image).count
if count > 1 { if count > 1 {
isGallery = true isGallery = true
@ -331,8 +331,8 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
} }
if let webPage = self.webPage, case let .Loaded(content) = webPage.content, content.instantPage != nil { if let webPage = self.webPage, case let .Loaded(content) = webPage.content, content.instantPage != nil {
switch instantPageType(of: content) { switch websiteType(of: content) {
case .album: case .instagram, .twitter:
return .none return .none
default: default:
return .instantPage return .instantPage

View File

@ -53,17 +53,19 @@ public final class ChatPresentationData {
let fontSize: PresentationFontSize let fontSize: PresentationFontSize
let strings: PresentationStrings let strings: PresentationStrings
let dateTimeFormat: PresentationDateTimeFormat let dateTimeFormat: PresentationDateTimeFormat
let disableAnimations: Bool
let messageFont: UIFont let messageFont: UIFont
let messageBoldFont: UIFont let messageBoldFont: UIFont
let messageItalicFont: UIFont let messageItalicFont: UIFont
let messageFixedFont: UIFont let messageFixedFont: UIFont
init(theme: ChatPresentationThemeData, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat) { init(theme: ChatPresentationThemeData, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, disableAnimations: Bool) {
self.theme = theme self.theme = theme
self.fontSize = fontSize self.fontSize = fontSize
self.strings = strings self.strings = strings
self.dateTimeFormat = dateTimeFormat self.dateTimeFormat = dateTimeFormat
self.disableAnimations = disableAnimations
let baseFontSize = fontSize.baseDisplaySize let baseFontSize = fontSize.baseDisplaySize
self.messageFont = UIFont.systemFont(ofSize: baseFontSize) self.messageFont = UIFont.systemFont(ofSize: baseFontSize)

View File

@ -26,7 +26,7 @@ final class ChatRecentActionsController: TelegramController {
self.titleView = ChatRecentActionsTitleView(color: self.presentationData.theme.rootController.navigationBar.primaryTextColor) self.titleView = ChatRecentActionsTitleView(color: self.presentationData.theme.rootController.navigationBar.primaryTextColor)
super.init(account: account, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), enableMediaAccessoryPanel: true, locationBroadcastPanelSource: .none) super.init(account: account, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), mediaAccessoryPanelVisibility: .specific(size: .compact), locationBroadcastPanelSource: .none)
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBar.style.style self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBar.style.style

View File

@ -97,7 +97,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
self.state = ChatRecentActionsControllerState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, fontSize: self.presentationData.fontSize) self.state = ChatRecentActionsControllerState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, fontSize: self.presentationData.fontSize)
self.chatPresentationDataPromise = Promise(ChatPresentationData(theme: ChatPresentationThemeData(theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper), fontSize: self.presentationData.fontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat)) self.chatPresentationDataPromise = Promise(ChatPresentationData(theme: ChatPresentationThemeData(theme: self.presentationData.theme, wallpaper: self.presentationData.chatWallpaper), fontSize: self.presentationData.fontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, disableAnimations: self.presentationData.disableAnimations))
self.context = ChannelAdminEventLogContext(postbox: self.account.postbox, network: self.account.network, peerId: self.peer.id) self.context = ChannelAdminEventLogContext(postbox: self.account.postbox, network: self.account.network, peerId: self.peer.id)

View File

@ -131,7 +131,7 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode {
} }
} }
let panelHeight: CGFloat = 45.0 let panelHeight = defaultHeight(metrics: metrics)
transition.updateFrame(node: self.deleteButton, frame: CGRect(origin: CGPoint(x: leftInset, y: -1.0), size: CGSize(width: 48.0, height: panelHeight))) transition.updateFrame(node: self.deleteButton, frame: CGRect(origin: CGPoint(x: leftInset, y: -1.0), size: CGSize(width: 48.0, height: panelHeight)))
transition.updateFrame(node: self.sendButton, frame: CGRect(origin: CGPoint(x: width - rightInset - 43.0 - UIScreenPixel, y: -UIScreenPixel), size: CGSize(width: 44.0, height: panelHeight))) transition.updateFrame(node: self.sendButton, frame: CGRect(origin: CGPoint(x: width - rightInset - 43.0 - UIScreenPixel, y: -UIScreenPixel), size: CGSize(width: 44.0, height: panelHeight)))
@ -158,7 +158,7 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode {
} }
override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
return 45.0 return defaultHeight(metrics: metrics)
} }
} }

View File

@ -33,7 +33,7 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode {
} }
} }
let panelHeight: CGFloat = 45.0 let panelHeight = defaultHeight(metrics: metrics)
let textSize = self.textNode.updateLayout(CGSize(width: width - leftInset - rightInset - 8.0 * 2.0, height: panelHeight)) let textSize = self.textNode.updateLayout(CGSize(width: width - leftInset - rightInset - 8.0 * 2.0, height: panelHeight))
self.textNode.frame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - textSize.width) / 2.0), y: floor((panelHeight - textSize.height) / 2.0)), size: textSize) self.textNode.frame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - textSize.width) / 2.0), y: floor((panelHeight - textSize.height) / 2.0)), size: textSize)
@ -42,6 +42,6 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode {
} }
override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
return 45.0 return defaultHeight(metrics: metrics)
} }
} }

View File

@ -104,7 +104,12 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
} }
} }
let panelHeight: CGFloat = 45.0 let panelHeight: CGFloat
if case .regular = metrics.widthClass {
panelHeight = 49.0
} else {
panelHeight = 45.0
}
transition.updateFrame(node: self.downButton, frame: CGRect(origin: CGPoint(x: leftInset + 12.0, y: 0.0), size: CGSize(width: 40.0, height: panelHeight))) transition.updateFrame(node: self.downButton, frame: CGRect(origin: CGPoint(x: leftInset + 12.0, y: 0.0), size: CGSize(width: 40.0, height: panelHeight)))
transition.updateFrame(node: self.upButton, frame: CGRect(origin: CGPoint(x: leftInset + 12.0 + 43.0, y: 0.0), size: CGSize(width: 40.0, height: panelHeight))) transition.updateFrame(node: self.upButton, frame: CGRect(origin: CGPoint(x: leftInset + 12.0 + 43.0, y: 0.0), size: CGSize(width: 40.0, height: panelHeight)))
@ -154,6 +159,6 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
} }
override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
return 45.0 return defaultHeight(metrics: metrics)
} }
} }

View File

@ -112,8 +112,12 @@ private final class AccessoryItemIconButton: HighlightableButton {
private func calclulateTextFieldMinHeight(_ presentationInterfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { private func calclulateTextFieldMinHeight(_ presentationInterfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
let baseFontSize = max(17.0, presentationInterfaceState.fontSize.baseDisplaySize) let baseFontSize = max(17.0, presentationInterfaceState.fontSize.baseDisplaySize)
let result: CGFloat var result: CGFloat
if baseFontSize.isEqual(to: 17.0) { if baseFontSize.isEqual(to: 26.0) {
result = 42.0
} else if baseFontSize.isEqual(to: 23.0) {
result = 38.0
} else if baseFontSize.isEqual(to: 17.0) {
result = 31.0 result = 31.0
} else if baseFontSize.isEqual(to: 19.0) { } else if baseFontSize.isEqual(to: 19.0) {
result = 33.0 result = 33.0
@ -122,6 +126,11 @@ private func calclulateTextFieldMinHeight(_ presentationInterfaceState: ChatPres
} else { } else {
result = 31.0 result = 31.0
} }
if case .regular = metrics.widthClass {
result = max(33.0, result)
}
return result return result
} }
@ -178,6 +187,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
var displayAttachmentMenu: () -> Void = { } var displayAttachmentMenu: () -> Void = { }
var sendMessage: () -> Void = { } var sendMessage: () -> Void = { }
var pasteImages: ([UIImage]) -> Void = { _ in } var pasteImages: ([UIImage]) -> Void = { _ in }
var pasteData: (Data) -> Void = { _ in }
var updateHeight: () -> Void = { } var updateHeight: () -> Void = { }
var updateActivity: () -> Void = { } var updateActivity: () -> Void = { }
@ -482,7 +492,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
let updatedMaxHeight = (CGFloat(maxNumberOfLines) * 22.0 + 10.0) let updatedMaxHeight = (CGFloat(maxNumberOfLines) * 22.0 + 10.0)
textFieldHeight = min(updatedMaxHeight, unboundTextFieldHeight) textFieldHeight = max(textFieldMinHeight, min(updatedMaxHeight, unboundTextFieldHeight))
} else { } else {
textFieldHeight = textFieldMinHeight textFieldHeight = textFieldMinHeight
} }
@ -1296,26 +1306,28 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
} }
@objc func editableTextNodeShouldPaste(_ editableTextNode: ASEditableTextNode) -> Bool { @objc func editableTextNodeShouldPaste(_ editableTextNode: ASEditableTextNode) -> Bool {
let pasteboard = UIPasteboard.general
var images: [UIImage] = [] var images: [UIImage] = []
var text: String? if let gifData = pasteboard.data(forPasteboardType: "com.compuserve.gif") {
self.pasteData(gifData)
for item in UIPasteboard.general.items {
if let image = item[kUTTypeJPEG as String] as? UIImage {
images.append(image)
} else if let image = item[kUTTypePNG as String] as? UIImage {
images.append(image)
} else if let image = item[kUTTypeGIF as String] as? UIImage {
images.append(image)
}
}
if !images.isEmpty {
self.pasteImages(images)
return false return false
} else { } else {
return true for item in UIPasteboard.general.items {
if let image = item[kUTTypeJPEG as String] as? UIImage {
images.append(image)
} else if let image = item[kUTTypePNG as String] as? UIImage {
images.append(image)
} else if let image = item[kUTTypeGIF as String] as? UIImage {
images.append(image)
}
}
if !images.isEmpty {
self.pasteImages(images)
return false
}
} }
return true
} }
@objc func sendButtonPressed() { @objc func sendButtonPressed() {

View File

@ -185,39 +185,55 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
} }
} }
private func updateNetworkStatusNode(networkState: AccountNetworkState, metrics: LayoutMetrics) {
var isOnline = false
if case .online = networkState {
isOnline = true
}
if isOnline || metrics.widthClass == .regular {
self.contentContainer.isHidden = false
if let networkStatusNode = self.networkStatusNode {
self.networkStatusNode = nil
networkStatusNode.removeFromSupernode()
}
} else {
self.contentContainer.isHidden = true
let statusNode: ChatTitleNetworkStatusNode
if let current = self.networkStatusNode {
statusNode = current
} else {
statusNode = ChatTitleNetworkStatusNode(theme: self.theme)
self.networkStatusNode = statusNode
self.insertSubview(statusNode.view, belowSubview: self.button.view)
}
switch self.networkState {
case .waitingForNetwork:
statusNode.title = self.strings.State_WaitingForNetwork
case .connecting:
statusNode.title = self.strings.State_Connecting
case .updating:
statusNode.title = self.strings.State_Updating
case .online:
break
}
}
self.setNeedsLayout()
}
var networkState: AccountNetworkState = .online(proxy: nil) { var networkState: AccountNetworkState = .online(proxy: nil) {
didSet { didSet {
if self.networkState != oldValue { if self.networkState != oldValue {
if case .online = self.networkState { updateNetworkStatusNode(networkState: self.networkState, metrics: self.layoutMetrics)
self.contentContainer.isHidden = false }
if let networkStatusNode = self.networkStatusNode { }
self.networkStatusNode = nil }
networkStatusNode.removeFromSupernode()
} var layoutMetrics: LayoutMetrics = LayoutMetrics() {
} else { didSet {
self.contentContainer.isHidden = true if self.layoutMetrics != oldValue {
let statusNode: ChatTitleNetworkStatusNode updateNetworkStatusNode(networkState: self.networkState, metrics: self.layoutMetrics)
if let current = self.networkStatusNode {
statusNode = current
} else {
statusNode = ChatTitleNetworkStatusNode(theme: self.theme)
self.networkStatusNode = statusNode
self.insertSubview(statusNode.view, belowSubview: self.button.view)
}
switch self.networkState {
case .waitingForNetwork:
statusNode.title = self.strings.State_WaitingForNetwork
case .connecting:
statusNode.title = self.strings.State_Connecting
case .updating:
statusNode.title = self.strings.State_Updating
case .online:
break
}
}
self.setNeedsLayout()
} }
} }
} }

View File

@ -103,7 +103,7 @@ final class ChatUnblockInputPanelNode: ChatInputPanelNode {
let buttonSize = self.button.measure(CGSize(width: width - leftInset - rightInset - 80.0, height: 100.0)) let buttonSize = self.button.measure(CGSize(width: width - leftInset - rightInset - 80.0, height: 100.0))
let panelHeight: CGFloat = 45.0 let panelHeight = defaultHeight(metrics: metrics)
self.button.frame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - buttonSize.width) / 2.0), y: floor((panelHeight - buttonSize.height) / 2.0)), size: buttonSize) self.button.frame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - buttonSize.width) / 2.0), y: floor((panelHeight - buttonSize.height) / 2.0)), size: buttonSize)
@ -114,6 +114,6 @@ final class ChatUnblockInputPanelNode: ChatInputPanelNode {
} }
override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
return 45.0 return defaultHeight(metrics: metrics)
} }
} }

View File

@ -9,15 +9,13 @@ private let titleFont = UIFont.systemFont(ofSize: 13.0)
class ChatUnreadItem: ListViewItem { class ChatUnreadItem: ListViewItem {
let index: MessageIndex let index: MessageIndex
let theme: ChatPresentationThemeData let presentationData: ChatPresentationData
let strings: PresentationStrings
let header: ChatMessageDateHeader let header: ChatMessageDateHeader
init(index: MessageIndex, theme: ChatPresentationThemeData, strings: PresentationStrings) { init(index: MessageIndex, presentationData: ChatPresentationData) {
self.index = index self.index = index
self.theme = theme self.presentationData = presentationData
self.strings = strings self.header = ChatMessageDateHeader(timestamp: index.timestamp, presentationData: presentationData)
self.header = ChatMessageDateHeader(timestamp: index.timestamp, theme: theme, strings: strings)
} }
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, () -> Void)) -> Void) { func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, () -> Void)) -> Void) {
@ -110,18 +108,18 @@ class ChatUnreadItemNode: ListViewItemNode {
return { item, params, dateAtBottom in return { item, params, dateAtBottom in
var updatedBackgroundImage: UIImage? var updatedBackgroundImage: UIImage?
if currentTheme != item.theme { if currentTheme != item.presentationData.theme {
updatedBackgroundImage = PresentationResourcesChat.chatUnreadBarBackgroundImage(item.theme.theme) updatedBackgroundImage = PresentationResourcesChat.chatUnreadBarBackgroundImage(item.presentationData.theme.theme)
} }
let (size, apply) = labelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.strings.Conversation_UnreadMessages, font: titleFont, textColor: item.theme.theme.chat.serviceMessage.unreadBarTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let (size, apply) = labelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Conversation_UnreadMessages, font: titleFont, textColor: item.presentationData.theme.theme.chat.serviceMessage.unreadBarTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let backgroundSize = CGSize(width: params.width, height: 25.0) let backgroundSize = CGSize(width: params.width, height: 25.0)
return (ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: 25.0), insets: UIEdgeInsets(top: 6.0 + (dateAtBottom ? layoutConstants.timestampHeaderHeight : 0.0), left: 0.0, bottom: 5.0, right: 0.0)), { [weak self] in return (ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: 25.0), insets: UIEdgeInsets(top: 6.0 + (dateAtBottom ? layoutConstants.timestampHeaderHeight : 0.0), left: 0.0, bottom: 5.0, right: 0.0)), { [weak self] in
if let strongSelf = self { if let strongSelf = self {
strongSelf.item = item strongSelf.item = item
strongSelf.theme = item.theme strongSelf.theme = item.presentationData.theme
if let updatedBackgroundImage = updatedBackgroundImage { if let updatedBackgroundImage = updatedBackgroundImage {
strongSelf.backgroundNode.image = updatedBackgroundImage strongSelf.backgroundNode.image = updatedBackgroundImage
@ -149,4 +147,11 @@ class ChatUnreadItemNode: ListViewItemNode {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
} }
override public var wantsScrollDynamics: Bool {
if let disableAnimations = self.item?.presentationData.disableAnimations {
return !disableAnimations
}
return true
}
} }

View File

@ -76,9 +76,17 @@ final class ChatVideoGalleryItemScrubberView: UIView {
} }
func setStatusSignal(_ status: Signal<MediaPlayerStatus, NoError>?) { func setStatusSignal(_ status: Signal<MediaPlayerStatus, NoError>?) {
self.scrubberNode.status = status let mappedStatus: Signal<MediaPlayerStatus, NoError>?
self.leftTimestampNode.status = status if let status = status {
self.rightTimestampNode.status = status mappedStatus = combineLatest(status, self.scrubberNode.scrubbingTimestamp) |> map { status, scrubbingTimestamp -> MediaPlayerStatus in
return MediaPlayerStatus(generationTimestamp: status.generationTimestamp, duration: status.duration, dimensions: status.dimensions, timestamp: scrubbingTimestamp ?? status.timestamp, baseRate: status.baseRate, seekId: status.seekId, status: status.status)
}
} else {
mappedStatus = nil
}
self.scrubberNode.status = mappedStatus
self.leftTimestampNode.status = mappedStatus
self.rightTimestampNode.status = mappedStatus
} }
func setBufferingStatusSignal(_ status: Signal<(IndexSet, Int)?, NoError>?) { func setBufferingStatusSignal(_ status: Signal<(IndexSet, Int)?, NoError>?) {

View File

@ -11,7 +11,7 @@ public extension TabBarControllerTheme {
public extension NavigationBarTheme { public extension NavigationBarTheme {
public convenience init(rootControllerTheme: PresentationTheme) { public convenience init(rootControllerTheme: PresentationTheme) {
let theme = rootControllerTheme.rootController.navigationBar let theme = rootControllerTheme.rootController.navigationBar
self.init(buttonColor: theme.buttonColor, primaryTextColor: theme.primaryTextColor, backgroundColor: theme.backgroundColor, separatorColor: theme.separatorColor, badgeBackgroundColor: theme.badgeBackgroundColor, badgeStrokeColor: theme.badgeStrokeColor, badgeTextColor: theme.badgeTextColor) self.init(buttonColor: theme.buttonColor, disabledButtonColor: theme.disabledButtonColor, primaryTextColor: theme.primaryTextColor, backgroundColor: theme.backgroundColor, separatorColor: theme.separatorColor, badgeBackgroundColor: theme.badgeBackgroundColor, badgeStrokeColor: theme.badgeStrokeColor, badgeTextColor: theme.badgeTextColor)
} }
} }

View File

@ -565,7 +565,7 @@ final class ContactListNode: ASDisplayNode {
private var presentationData: PresentationData private var presentationData: PresentationData
private var presentationDataDisposable: Disposable? private var presentationDataDisposable: Disposable?
private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, PresentationPersonNameOrder)> private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, PresentationPersonNameOrder, Bool)>
init(account: Account, presentation: ContactListPresentation, filters: [ContactListFilter] = [.excludeSelf], selectionState: ContactListNodeGroupSelectionState? = nil) { init(account: Account, presentation: ContactListPresentation, filters: [ContactListFilter] = [.excludeSelf], selectionState: ContactListNodeGroupSelectionState? = nil) {
self.account = account self.account = account
@ -576,7 +576,7 @@ final class ContactListNode: ASDisplayNode {
self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings, self.presentationData.dateTimeFormat, self.presentationData.nameSortOrder, self.presentationData.nameDisplayOrder)) self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings, self.presentationData.dateTimeFormat, self.presentationData.nameSortOrder, self.presentationData.nameDisplayOrder, self.presentationData.disableAnimations))
super.init() super.init()
@ -722,7 +722,7 @@ final class ContactListNode: ASDisplayNode {
let entries = contactListNodeEntries(accountPeer: view.accountPeer, peers: peers, presences: view.peerPresences, presentation: presentation, selectionState: selectionState, theme: themeAndStrings.0, strings: themeAndStrings.1, dateTimeFormat: themeAndStrings.2, sortOrder: themeAndStrings.3, displayOrder: themeAndStrings.4, disabledPeerIds: disabledPeerIds) let entries = contactListNodeEntries(accountPeer: view.accountPeer, peers: peers, presences: view.peerPresences, presentation: presentation, selectionState: selectionState, theme: themeAndStrings.0, strings: themeAndStrings.1, dateTimeFormat: themeAndStrings.2, sortOrder: themeAndStrings.3, displayOrder: themeAndStrings.4, disabledPeerIds: disabledPeerIds)
let previous = previousEntries.swap(entries) let previous = previousEntries.swap(entries)
let animated: Bool let animated: Bool
if let previous = previous { if let previous = previous, !themeAndStrings.5 {
animated = (entries.count - previous.count) < 20 animated = (entries.count - previous.count) < 20
} else { } else {
animated = false animated = false
@ -747,12 +747,13 @@ final class ContactListNode: ASDisplayNode {
if let strongSelf = self { if let strongSelf = self {
let previousTheme = strongSelf.presentationData.theme let previousTheme = strongSelf.presentationData.theme
let previousStrings = strongSelf.presentationData.strings let previousStrings = strongSelf.presentationData.strings
let previousDisableAnimations = strongSelf.presentationData.disableAnimations
strongSelf.presentationData = presentationData strongSelf.presentationData = presentationData
if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings { if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings || previousDisableAnimations != presentationData.disableAnimations {
strongSelf.backgroundColor = presentationData.theme.chatList.backgroundColor strongSelf.backgroundColor = presentationData.theme.chatList.backgroundColor
strongSelf.themeAndStringsPromise.set(.single((presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameSortOrder, presentationData.nameDisplayOrder))) strongSelf.themeAndStringsPromise.set(.single((presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameSortOrder, presentationData.nameDisplayOrder, presentationData.disableAnimations)))
strongSelf.listNode.forEachAccessoryItemNode({ accessoryItemNode in strongSelf.listNode.forEachAccessoryItemNode({ accessoryItemNode in
if let accessoryItemNode = accessoryItemNode as? ContactsSectionHeaderAccessoryItemNode { if let accessoryItemNode = accessoryItemNode as? ContactsSectionHeaderAccessoryItemNode {

View File

@ -153,7 +153,7 @@ class ContactMultiselectionController: ViewController {
} }
override func loadDisplayNode() { override func loadDisplayNode() {
self.displayNode = ContactMultiselectionControllerNode(account: self.account, options: self.options, filters: filters) self.displayNode = ContactMultiselectionControllerNode(account: self.account, mode: self.mode, options: self.options, filters: filters)
self._listReady.set(self.contactsNode.contactListNode.ready) self._listReady.set(self.contactsNode.contactListNode.ready)
self.contactsNode.dismiss = { [weak self] in self.contactsNode.dismiss = { [weak self] in

View File

@ -44,12 +44,20 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
private var presentationData: PresentationData private var presentationData: PresentationData
private var presentationDataDisposable: Disposable? private var presentationDataDisposable: Disposable?
init(account: Account, options: [ContactListAdditionalOption], filters: [ContactListFilter]) { init(account: Account, mode: ContactMultiselectionControllerMode, options: [ContactListAdditionalOption], filters: [ContactListFilter]) {
self.account = account self.account = account
self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
let placeholder: String
switch mode {
case .peerSelection:
placeholder = self.presentationData.strings.Contacts_SearchLabel
default:
placeholder = self.presentationData.strings.Compose_TokenListPlaceholder
}
self.contactListNode = ContactListNode(account: account, presentation: .natural(displaySearch: false, options: options), filters: filters, selectionState: ContactListNodeGroupSelectionState()) self.contactListNode = ContactListNode(account: account, presentation: .natural(displaySearch: false, options: options), filters: filters, selectionState: ContactListNodeGroupSelectionState())
self.tokenListNode = EditableTokenListNode(theme: EditableTokenListNodeTheme(backgroundColor: self.presentationData.theme.rootController.navigationBar.backgroundColor, separatorColor: self.presentationData.theme.rootController.navigationBar.separatorColor, placeholderTextColor: self.presentationData.theme.list.itemPlaceholderTextColor, primaryTextColor: self.presentationData.theme.list.itemPrimaryTextColor, selectedTextColor: self.presentationData.theme.list.itemAccentColor, keyboardColor: self.presentationData.theme.chatList.searchBarKeyboardColor), placeholder: self.presentationData.strings.Compose_TokenListPlaceholder) self.tokenListNode = EditableTokenListNode(theme: EditableTokenListNodeTheme(backgroundColor: self.presentationData.theme.rootController.navigationBar.backgroundColor, separatorColor: self.presentationData.theme.rootController.navigationBar.separatorColor, placeholderTextColor: self.presentationData.theme.list.itemPlaceholderTextColor, primaryTextColor: self.presentationData.theme.list.itemPrimaryTextColor, selectedTextColor: self.presentationData.theme.list.itemAccentColor, keyboardColor: self.presentationData.theme.chatList.searchBarKeyboardColor), placeholder: placeholder)
super.init() super.init()

View File

@ -24,6 +24,7 @@ private let rootTabBar = PresentationThemeRootTabBar(
private let rootNavigationBar = PresentationThemeRootNavigationBar( private let rootNavigationBar = PresentationThemeRootNavigationBar(
buttonColor: accentColor, buttonColor: accentColor,
disabledButtonColor: UIColor(rgb: 0x5b646f),
primaryTextColor: UIColor(rgb: 0xffffff), primaryTextColor: UIColor(rgb: 0xffffff),
secondaryTextColor: UIColor(rgb: 0x8B9197), secondaryTextColor: UIColor(rgb: 0x8B9197),
controlColor: UIColor(rgb: 0x8B9197), controlColor: UIColor(rgb: 0x8B9197),

View File

@ -24,6 +24,7 @@ private let rootTabBar = PresentationThemeRootTabBar(
private let rootNavigationBar = PresentationThemeRootNavigationBar( private let rootNavigationBar = PresentationThemeRootNavigationBar(
buttonColor: accentColor, buttonColor: accentColor,
disabledButtonColor: UIColor(rgb: 0x525252),
primaryTextColor: accentColor, primaryTextColor: accentColor,
secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5),
controlColor: accentColor, controlColor: accentColor,

View File

@ -24,6 +24,7 @@ private func makeDefaultPresentationTheme(accentColor: UIColor, day: Bool) -> Pr
let rootNavigationBar = PresentationThemeRootNavigationBar( let rootNavigationBar = PresentationThemeRootNavigationBar(
buttonColor: accentColor, buttonColor: accentColor,
disabledButtonColor: UIColor(rgb: 0xd0d0d0),
primaryTextColor: .black, primaryTextColor: .black,
secondaryTextColor: UIColor(rgb: 0x787878), secondaryTextColor: UIColor(rgb: 0x787878),
controlColor: UIColor(rgb: 0x7e8791), controlColor: UIColor(rgb: 0x7e8791),

View File

@ -42,7 +42,7 @@ final class DeleteChatInputPanelNode: ChatInputPanelNode {
let buttonSize = self.button.measure(CGSize(width: width - leftInset - rightInset - 10.0, height: 100.0)) let buttonSize = self.button.measure(CGSize(width: width - leftInset - rightInset - 10.0, height: 100.0))
let panelHeight: CGFloat = 45.0 let panelHeight = defaultHeight(metrics: metrics)
self.button.frame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - buttonSize.width) / 2.0), y: floor((panelHeight - buttonSize.height) / 2.0)), size: buttonSize) self.button.frame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - buttonSize.width) / 2.0), y: floor((panelHeight - buttonSize.height) / 2.0)), size: buttonSize)
@ -50,6 +50,6 @@ final class DeleteChatInputPanelNode: ChatInputPanelNode {
} }
override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
return 45.0 return defaultHeight(metrics: metrics)
} }
} }

View File

@ -293,90 +293,11 @@ func editSettingsController(account: Account, currentName: ItemListAvatarAndName
var avatarGalleryTransitionArguments: ((AvatarGalleryEntry) -> GalleryTransitionArguments?)? var avatarGalleryTransitionArguments: ((AvatarGalleryEntry) -> GalleryTransitionArguments?)?
let avatarAndNameInfoContext = ItemListAvatarAndNameInfoItemContext() let avatarAndNameInfoContext = ItemListAvatarAndNameInfoItemContext()
var updateHiddenAvatarImpl: (() -> Void)? var updateHiddenAvatarImpl: (() -> Void)?
var changeProfilePhotoImpl: (() -> Void)?
let wallpapersPromise = Promise<[TelegramWallpaper]>() let wallpapersPromise = Promise<[TelegramWallpaper]>()
wallpapersPromise.set(telegramWallpapers(postbox: account.postbox, network: account.network)) wallpapersPromise.set(telegramWallpapers(postbox: account.postbox, network: account.network))
let changeProfilePhotoImpl: () -> Void = {
let _ = (account.postbox.transaction { transaction -> Peer? in
return transaction.getPeer(account.peerId)
} |> deliverOnMainQueue).start(next: { peer in
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme)
legacyController.statusBar.statusBarStyle = .Ignore
let emptyController = LegacyEmptyController(context: legacyController.context)!
let navigationController = makeLegacyNavigationController(rootController: emptyController)
navigationController.setNavigationBarHidden(true, animated: false)
navigationController.navigationBar.transform = CGAffineTransform(translationX: -1000.0, y: 0.0)
legacyController.bind(controller: navigationController)
presentControllerImpl?(legacyController, nil)
var hasPhotos = false
if let peer = peer, !peer.profileImageRepresentations.isEmpty {
hasPhotos = true
}
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasDeleteButton: hasPhotos, personalPhoto: true, saveEditedPhotos: false, saveCapturedMedia: false)!
let _ = currentAvatarMixin.swap(mixin)
mixin.didFinishWithImage = { image in
if let image = image {
if let data = UIImageJPEGRepresentation(image, 0.6) {
let resource = LocalFileMediaResource(fileId: arc4random64())
account.postbox.mediaBox.storeResourceData(resource.id, data: data)
let representation = TelegramMediaImageRepresentation(dimensions: CGSize(width: 640.0, height: 640.0), resource: resource)
updateState {
$0.withUpdatedUpdatingAvatar(.image(representation))
}
updateAvatarDisposable.set((updateAccountPhoto(account: account, resource: resource) |> deliverOnMainQueue).start(next: { result in
switch result {
case .complete:
updateState {
$0.withUpdatedUpdatingAvatar(nil)
}
case .progress:
break
}
}))
}
}
}
mixin.didFinishWithDelete = {
let _ = currentAvatarMixin.swap(nil)
updateState {
if let profileImage = peer?.smallProfileImage {
return $0.withUpdatedUpdatingAvatar(.image(profileImage))
} else {
return $0.withUpdatedUpdatingAvatar(.none)
}
}
updateAvatarDisposable.set((updateAccountPhoto(account: account, resource: nil) |> deliverOnMainQueue).start(next: { result in
switch result {
case .complete:
updateState {
$0.withUpdatedUpdatingAvatar(nil)
}
case .progress:
break
}
}))
}
mixin.didDismiss = { [weak legacyController] in
let _ = currentAvatarMixin.swap(nil)
legacyController?.dismiss()
}
let menuController = mixin.present()
if let menuController = menuController {
menuController.customRemoveFromParentViewController = { [weak legacyController] in
legacyController?.dismiss()
}
}
})
}
let arguments = EditSettingsItemArguments(account: account, accountManager: accountManager, avatarAndNameInfoContext: avatarAndNameInfoContext, avatarTapAction: { let arguments = EditSettingsItemArguments(account: account, accountManager: accountManager, avatarAndNameInfoContext: avatarAndNameInfoContext, avatarTapAction: {
var updating = false var updating = false
updateState { updateState {
@ -388,7 +309,7 @@ func editSettingsController(account: Account, currentName: ItemListAvatarAndName
return return
} }
changeProfilePhotoImpl() changeProfilePhotoImpl?()
}, pushController: { controller in }, pushController: { controller in
pushControllerImpl?(controller) pushControllerImpl?(controller)
}, presentController: { controller in }, presentController: { controller in
@ -500,6 +421,88 @@ func editSettingsController(account: Account, currentName: ItemListAvatarAndName
} }
} }
} }
changeProfilePhotoImpl = { [weak controller] in
let _ = (account.postbox.transaction { transaction -> Peer? in
return transaction.getPeer(account.peerId)
} |> deliverOnMainQueue).start(next: { peer in
controller?.view.endEditing(true)
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme)
legacyController.statusBar.statusBarStyle = .Ignore
let emptyController = LegacyEmptyController(context: legacyController.context)!
let navigationController = makeLegacyNavigationController(rootController: emptyController)
navigationController.setNavigationBarHidden(true, animated: false)
navigationController.navigationBar.transform = CGAffineTransform(translationX: -1000.0, y: 0.0)
legacyController.bind(controller: navigationController)
presentControllerImpl?(legacyController, nil)
var hasPhotos = false
if let peer = peer, !peer.profileImageRepresentations.isEmpty {
hasPhotos = true
}
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasDeleteButton: hasPhotos, personalPhoto: true, saveEditedPhotos: false, saveCapturedMedia: false)!
let _ = currentAvatarMixin.swap(mixin)
mixin.didFinishWithImage = { image in
if let image = image {
if let data = UIImageJPEGRepresentation(image, 0.6) {
let resource = LocalFileMediaResource(fileId: arc4random64())
account.postbox.mediaBox.storeResourceData(resource.id, data: data)
let representation = TelegramMediaImageRepresentation(dimensions: CGSize(width: 640.0, height: 640.0), resource: resource)
updateState {
$0.withUpdatedUpdatingAvatar(.image(representation))
}
updateAvatarDisposable.set((updateAccountPhoto(account: account, resource: resource) |> deliverOnMainQueue).start(next: { result in
switch result {
case .complete:
updateState {
$0.withUpdatedUpdatingAvatar(nil)
}
case .progress:
break
}
}))
}
}
}
mixin.didFinishWithDelete = {
let _ = currentAvatarMixin.swap(nil)
updateState {
if let profileImage = peer?.smallProfileImage {
return $0.withUpdatedUpdatingAvatar(.image(profileImage))
} else {
return $0.withUpdatedUpdatingAvatar(.none)
}
}
updateAvatarDisposable.set((updateAccountPhoto(account: account, resource: nil) |> deliverOnMainQueue).start(next: { result in
switch result {
case .complete:
updateState {
$0.withUpdatedUpdatingAvatar(nil)
}
case .progress:
break
}
}))
}
mixin.didDismiss = { [weak legacyController] in
let _ = currentAvatarMixin.swap(nil)
legacyController?.dismiss()
}
let menuController = mixin.present()
if let menuController = menuController {
menuController.customRemoveFromParentViewController = { [weak legacyController] in
legacyController?.dismiss()
}
}
})
}
return controller return controller
} }

View File

@ -231,8 +231,8 @@ enum GalleryControllerItemSource {
} }
class GalleryController: ViewController { class GalleryController: ViewController {
static let darkNavigationTheme = NavigationBarTheme(buttonColor: .white, primaryTextColor: .white, backgroundColor: UIColor(white: 0.0, alpha: 0.6), separatorColor: UIColor(white: 0.0, alpha: 0.8), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) static let darkNavigationTheme = NavigationBarTheme(buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: UIColor(white: 0.0, alpha: 0.6), separatorColor: UIColor(white: 0.0, alpha: 0.8), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear)
static let lightNavigationTheme = NavigationBarTheme(buttonColor: UIColor(rgb: 0x007ee5), primaryTextColor: .black, backgroundColor: UIColor(red: 0.968626451, green: 0.968626451, blue: 0.968626451, alpha: 1.0), separatorColor: UIColor(red: 0.6953125, green: 0.6953125, blue: 0.6953125, alpha: 1.0), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) static let lightNavigationTheme = NavigationBarTheme(buttonColor: UIColor(rgb: 0x007ee5), disabledButtonColor: UIColor(rgb: 0xd0d0d0), primaryTextColor: .black, backgroundColor: UIColor(red: 0.968626451, green: 0.968626451, blue: 0.968626451, alpha: 1.0), separatorColor: UIColor(red: 0.6953125, green: 0.6953125, blue: 0.6953125, alpha: 1.0), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear)
private var galleryNode: GalleryControllerNode { private var galleryNode: GalleryControllerNode {
return self.displayNode as! GalleryControllerNode return self.displayNode as! GalleryControllerNode

View File

@ -155,8 +155,6 @@ final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate {
} }
} }
var removedNodes: [GalleryItemNode] = []
if !transaction.deleteItems.isEmpty || !transaction.insertItems.isEmpty { if !transaction.deleteItems.isEmpty || !transaction.insertItems.isEmpty {
let deleteItems = transaction.deleteItems.sorted() let deleteItems = transaction.deleteItems.sorted()
@ -164,8 +162,8 @@ final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate {
self.items.remove(at: deleteItemIndex) self.items.remove(at: deleteItemIndex)
for i in 0 ..< self.itemNodes.count { for i in 0 ..< self.itemNodes.count {
if self.itemNodes[i].index == deleteItemIndex { if self.itemNodes[i].index == deleteItemIndex {
removedNodes.append(self.itemNodes[i])
self.removeVisibleItemNode(internalIndex: i) self.removeVisibleItemNode(internalIndex: i)
break
} }
} }
} }

View File

@ -735,10 +735,6 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa
entries.append(.info(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer: peer, cachedData: view.cachedData, state: infoState, updatingAvatar: state.updatingAvatar)) entries.append(.info(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer: peer, cachedData: view.cachedData, state: infoState, updatingAvatar: state.updatingAvatar))
} }
if canEditGroupInfo {
entries.append(GroupInfoEntry.setGroupPhoto(presentationData.theme, presentationData.strings.GroupInfo_SetGroupPhoto))
}
let peerNotificationSettings: TelegramPeerNotificationSettings = (view.notificationSettings as? TelegramPeerNotificationSettings) ?? TelegramPeerNotificationSettings.defaultSettings let peerNotificationSettings: TelegramPeerNotificationSettings = (view.notificationSettings as? TelegramPeerNotificationSettings) ?? TelegramPeerNotificationSettings.defaultSettings
let notificationsText: String let notificationsText: String
switch peerNotificationSettings.muteState { switch peerNotificationSettings.muteState {
@ -751,6 +747,10 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa
} }
if let editingState = state.editingState { if let editingState = state.editingState {
if canEditGroupInfo {
entries.append(GroupInfoEntry.setGroupPhoto(presentationData.theme, presentationData.strings.GroupInfo_SetGroupPhoto))
}
if let group = view.peers[view.peerId] as? TelegramGroup { if let group = view.peers[view.peerId] as? TelegramGroup {
if case .creator = group.role { if case .creator = group.role {
entries.append(.adminManagement(presentationData.theme, presentationData.strings.GroupInfo_ChatAdmins)) entries.append(.adminManagement(presentationData.theme, presentationData.strings.GroupInfo_ChatAdmins))

View File

@ -26,14 +26,14 @@ final class HashtagSearchController: TelegramController {
self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
super.init(account: account, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), enableMediaAccessoryPanel: true, locationBroadcastPanelSource: .none) super.init(account: account, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), mediaAccessoryPanelVisibility: .specific(size: .compact), locationBroadcastPanelSource: .none)
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBar.style.style self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBar.style.style
self.title = query self.title = query
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder) let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: self.presentationData.disableAnimations)
let location: SearchMessagesLocation = .general let location: SearchMessagesLocation = .general
let search = searchMessages(account: account, location: location, query: query) let search = searchMessages(account: account, location: location, query: query)

View File

@ -453,7 +453,12 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite
statusColor = item.theme.list.itemPrimaryTextColor statusColor = item.theme.list.itemPrimaryTextColor
} }
let (statusNodeLayout, statusNodeApply) = layoutStatusNode(TextNodeLayoutArguments(attributedString: NSAttributedString(string: statusText, font: statusFont, textColor: statusColor), backgroundColor: nil, maximumNumberOfLines: 3, truncationType: .end, constrainedSize: CGSize(width: baseWidth - 20, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) var availableStatusWidth = baseWidth - 20
if item.call != nil {
availableStatusWidth -= 44.0
}
let (statusNodeLayout, statusNodeApply) = layoutStatusNode(TextNodeLayoutArguments(attributedString: NSAttributedString(string: statusText, font: statusFont, textColor: statusColor), backgroundColor: nil, maximumNumberOfLines: 3, truncationType: .end, constrainedSize: CGSize(width: availableStatusWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let separatorHeight = UIScreenPixel let separatorHeight = UIScreenPixel
@ -660,6 +665,7 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite
inputFirstField.textColor = item.theme.list.itemPrimaryTextColor inputFirstField.textColor = item.theme.list.itemPrimaryTextColor
inputFirstField.keyboardAppearance = item.theme.chatList.searchBarKeyboardColor.keyboardAppearance inputFirstField.keyboardAppearance = item.theme.chatList.searchBarKeyboardColor.keyboardAppearance
inputFirstField.autocorrectionType = .no inputFirstField.autocorrectionType = .no
inputFirstField.returnKeyType = .next
inputFirstField.attributedPlaceholder = NSAttributedString(string: item.strings.UserInfo_FirstNamePlaceholder, font: Font.regular(17.0), textColor: item.theme.list.itemPlaceholderTextColor) inputFirstField.attributedPlaceholder = NSAttributedString(string: item.strings.UserInfo_FirstNamePlaceholder, font: Font.regular(17.0), textColor: item.theme.list.itemPlaceholderTextColor)
inputFirstField.attributedText = NSAttributedString(string: firstName, font: Font.regular(17.0), textColor: item.theme.list.itemPrimaryTextColor) inputFirstField.attributedText = NSAttributedString(string: firstName, font: Font.regular(17.0), textColor: item.theme.list.itemPrimaryTextColor)
strongSelf.inputFirstField = inputFirstField strongSelf.inputFirstField = inputFirstField
@ -675,6 +681,7 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite
inputSecondField.textColor = item.theme.list.itemPrimaryTextColor inputSecondField.textColor = item.theme.list.itemPrimaryTextColor
inputSecondField.keyboardAppearance = item.theme.chatList.searchBarKeyboardColor.keyboardAppearance inputSecondField.keyboardAppearance = item.theme.chatList.searchBarKeyboardColor.keyboardAppearance
inputSecondField.autocorrectionType = .no inputSecondField.autocorrectionType = .no
inputSecondField.returnKeyType = .done
inputSecondField.attributedPlaceholder = NSAttributedString(string: item.strings.UserInfo_LastNamePlaceholder, font: Font.regular(17.0), textColor: item.theme.list.itemPlaceholderTextColor) inputSecondField.attributedPlaceholder = NSAttributedString(string: item.strings.UserInfo_LastNamePlaceholder, font: Font.regular(17.0), textColor: item.theme.list.itemPlaceholderTextColor)
inputSecondField.attributedText = NSAttributedString(string: lastName, font: Font.regular(17.0), textColor: item.theme.list.itemPrimaryTextColor) inputSecondField.attributedText = NSAttributedString(string: lastName, font: Font.regular(17.0), textColor: item.theme.list.itemPrimaryTextColor)
strongSelf.inputSecondField = inputSecondField strongSelf.inputSecondField = inputSecondField

View File

@ -161,6 +161,7 @@ class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerDelega
@objc func revealTapGesture(_ recognizer: UITapGestureRecognizer) { @objc func revealTapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state { if case .ended = recognizer.state {
self.updateRevealOffsetInternal(offset: 0.0, transition: .animated(duration: 0.3, curve: .spring)) self.updateRevealOffsetInternal(offset: 0.0, transition: .animated(duration: 0.3, curve: .spring))
self.revealOptionsInteractivelyClosed()
} }
} }

View File

@ -173,12 +173,17 @@ final class MediaPlayerScrubbingNode: ASDisplayNode {
private var playbackStatusValue: MediaPlayerPlaybackStatus? private var playbackStatusValue: MediaPlayerPlaybackStatus?
private var scrubbingBeginTimestamp: Double? private var scrubbingBeginTimestamp: Double?
private var scrubbingTimestamp: Double? private var scrubbingTimestampValue: Double?
var playbackStatusUpdated: ((MediaPlayerPlaybackStatus?) -> Void)? var playbackStatusUpdated: ((MediaPlayerPlaybackStatus?) -> Void)?
var playerStatusUpdated: ((MediaPlayerStatus?) -> Void)? var playerStatusUpdated: ((MediaPlayerStatus?) -> Void)?
var seek: ((Double) -> Void)? var seek: ((Double) -> Void)?
private let _scrubbingTimestamp = Promise<Double?>(nil)
var scrubbingTimestamp: Signal<Double?, NoError> {
return self._scrubbingTimestamp.get()
}
var ignoreSeekId: Int? var ignoreSeekId: Int?
var enableScrubbing: Bool = true { var enableScrubbing: Bool = true {
@ -361,7 +366,8 @@ final class MediaPlayerScrubbingNode: ASDisplayNode {
if let strongSelf = self { if let strongSelf = self {
if let statusValue = strongSelf.statusValue, Double(0.0).isLess(than: statusValue.duration) { if let statusValue = strongSelf.statusValue, Double(0.0).isLess(than: statusValue.duration) {
strongSelf.scrubbingBeginTimestamp = statusValue.timestamp strongSelf.scrubbingBeginTimestamp = statusValue.timestamp
strongSelf.scrubbingTimestamp = statusValue.timestamp strongSelf.scrubbingTimestampValue = statusValue.timestamp
strongSelf._scrubbingTimestamp.set(.single(strongSelf.scrubbingTimestampValue))
strongSelf.updateProgress() strongSelf.updateProgress()
} }
} }
@ -369,7 +375,8 @@ final class MediaPlayerScrubbingNode: ASDisplayNode {
handleNodeContainer.updateScrubbing = { [weak self] addedFraction in handleNodeContainer.updateScrubbing = { [weak self] addedFraction in
if let strongSelf = self { if let strongSelf = self {
if let statusValue = strongSelf.statusValue, let scrubbingBeginTimestamp = strongSelf.scrubbingBeginTimestamp, Double(0.0).isLess(than: statusValue.duration) { if let statusValue = strongSelf.statusValue, let scrubbingBeginTimestamp = strongSelf.scrubbingBeginTimestamp, Double(0.0).isLess(than: statusValue.duration) {
strongSelf.scrubbingTimestamp = scrubbingBeginTimestamp + statusValue.duration * Double(addedFraction) strongSelf.scrubbingTimestampValue = max(0.0, min(statusValue.duration, scrubbingBeginTimestamp + statusValue.duration * Double(addedFraction)))
strongSelf._scrubbingTimestamp.set(.single(strongSelf.scrubbingTimestampValue))
strongSelf.updateProgress() strongSelf.updateProgress()
} }
} }
@ -377,13 +384,14 @@ final class MediaPlayerScrubbingNode: ASDisplayNode {
handleNodeContainer.endScrubbing = { [weak self] apply in handleNodeContainer.endScrubbing = { [weak self] apply in
if let strongSelf = self { if let strongSelf = self {
strongSelf.scrubbingBeginTimestamp = nil strongSelf.scrubbingBeginTimestamp = nil
let scrubbingTimestamp = strongSelf.scrubbingTimestamp let scrubbingTimestampValue = strongSelf.scrubbingTimestampValue
strongSelf.scrubbingTimestamp = nil strongSelf.scrubbingTimestampValue = nil
if let scrubbingTimestamp = scrubbingTimestamp, apply { strongSelf._scrubbingTimestamp.set(.single(nil))
if let scrubbingTimestampValue = scrubbingTimestampValue, apply {
if let statusValue = strongSelf.statusValue { if let statusValue = strongSelf.statusValue {
strongSelf.ignoreSeekId = statusValue.seekId strongSelf.ignoreSeekId = statusValue.seekId
} }
strongSelf.seek?(scrubbingTimestamp) strongSelf.seek?(scrubbingTimestampValue)
} }
strongSelf.updateProgress() strongSelf.updateProgress()
} }
@ -404,7 +412,7 @@ final class MediaPlayerScrubbingNode: ASDisplayNode {
if let strongSelf = self { if let strongSelf = self {
if let statusValue = strongSelf.statusValue, Double(0.0).isLess(than: statusValue.duration) { if let statusValue = strongSelf.statusValue, Double(0.0).isLess(than: statusValue.duration) {
strongSelf.scrubbingBeginTimestamp = statusValue.timestamp strongSelf.scrubbingBeginTimestamp = statusValue.timestamp
strongSelf.scrubbingTimestamp = statusValue.timestamp strongSelf.scrubbingTimestampValue = statusValue.timestamp
strongSelf.updateProgress() strongSelf.updateProgress()
} }
} }
@ -412,7 +420,7 @@ final class MediaPlayerScrubbingNode: ASDisplayNode {
handleNodeContainer.updateScrubbing = { [weak self] addedFraction in handleNodeContainer.updateScrubbing = { [weak self] addedFraction in
if let strongSelf = self { if let strongSelf = self {
if let statusValue = strongSelf.statusValue, let scrubbingBeginTimestamp = strongSelf.scrubbingBeginTimestamp, Double(0.0).isLess(than: statusValue.duration) { if let statusValue = strongSelf.statusValue, let scrubbingBeginTimestamp = strongSelf.scrubbingBeginTimestamp, Double(0.0).isLess(than: statusValue.duration) {
strongSelf.scrubbingTimestamp = scrubbingBeginTimestamp + statusValue.duration * Double(addedFraction) strongSelf.scrubbingTimestampValue = scrubbingBeginTimestamp + statusValue.duration * Double(addedFraction)
strongSelf.updateProgress() strongSelf.updateProgress()
} }
} }
@ -420,10 +428,10 @@ final class MediaPlayerScrubbingNode: ASDisplayNode {
handleNodeContainer.endScrubbing = { [weak self] apply in handleNodeContainer.endScrubbing = { [weak self] apply in
if let strongSelf = self { if let strongSelf = self {
strongSelf.scrubbingBeginTimestamp = nil strongSelf.scrubbingBeginTimestamp = nil
let scrubbingTimestamp = strongSelf.scrubbingTimestamp let scrubbingTimestampValue = strongSelf.scrubbingTimestampValue
strongSelf.scrubbingTimestamp = nil strongSelf.scrubbingTimestampValue = nil
if let scrubbingTimestamp = scrubbingTimestamp, apply { if let scrubbingTimestampValue = scrubbingTimestampValue, apply {
strongSelf.seek?(scrubbingTimestamp) strongSelf.seek?(scrubbingTimestampValue)
} }
strongSelf.updateProgress() strongSelf.updateProgress()
} }
@ -529,8 +537,8 @@ final class MediaPlayerScrubbingNode: ASDisplayNode {
if case .buffering(true, _) = statusValue.status { if case .buffering(true, _) = statusValue.status {
initialBuffering = true initialBuffering = true
} else if Double(0.0).isLess(than: statusValue.duration) { } else if Double(0.0).isLess(than: statusValue.duration) {
if let scrubbingTimestamp = self.scrubbingTimestamp { if let scrubbingTimestampValue = self.scrubbingTimestampValue {
timestampAndDuration = (max(0.0, min(scrubbingTimestamp, statusValue.duration)), statusValue.duration) timestampAndDuration = (max(0.0, min(scrubbingTimestampValue, statusValue.duration)), statusValue.duration)
} else { } else {
timestampAndDuration = (statusValue.timestamp, statusValue.duration) timestampAndDuration = (statusValue.timestamp, statusValue.duration)
} }
@ -539,7 +547,7 @@ final class MediaPlayerScrubbingNode: ASDisplayNode {
if let (timestamp, duration) = timestampAndDuration { if let (timestamp, duration) = timestampAndDuration {
let progress = CGFloat(timestamp / duration) let progress = CGFloat(timestamp / duration)
if let _ = scrubbingTimestamp { if let _ = scrubbingTimestampValue {
let fromRect = CGRect(origin: backgroundFrame.origin, size: CGSize(width: 0.0, height: backgroundFrame.size.height)) let fromRect = CGRect(origin: backgroundFrame.origin, size: CGSize(width: 0.0, height: backgroundFrame.size.height))
let toRect = CGRect(origin: backgroundFrame.origin, size: CGSize(width: backgroundFrame.size.width, height: backgroundFrame.size.height)) let toRect = CGRect(origin: backgroundFrame.origin, size: CGSize(width: backgroundFrame.size.width, height: backgroundFrame.size.height))
@ -641,8 +649,8 @@ final class MediaPlayerScrubbingNode: ASDisplayNode {
let timestampAndDuration: (timestamp: Double, duration: Double)? let timestampAndDuration: (timestamp: Double, duration: Double)?
if let statusValue = self.statusValue, Double(0.0).isLess(than: statusValue.duration) { if let statusValue = self.statusValue, Double(0.0).isLess(than: statusValue.duration) {
if let scrubbingTimestamp = self.scrubbingTimestamp { if let scrubbingTimestampValue = self.scrubbingTimestampValue {
timestampAndDuration = (max(0.0, min(scrubbingTimestamp, statusValue.duration)), statusValue.duration) timestampAndDuration = (max(0.0, min(scrubbingTimestampValue, statusValue.duration)), statusValue.duration)
} else { } else {
timestampAndDuration = (statusValue.timestamp, statusValue.duration) timestampAndDuration = (statusValue.timestamp, statusValue.duration)
} }
@ -652,7 +660,7 @@ final class MediaPlayerScrubbingNode: ASDisplayNode {
if let (timestamp, duration) = timestampAndDuration { if let (timestamp, duration) = timestampAndDuration {
let progress = CGFloat(timestamp / duration) let progress = CGFloat(timestamp / duration)
if let _ = scrubbingTimestamp { if let _ = scrubbingTimestampValue {
let fromRect = CGRect(origin: backgroundFrame.origin, size: CGSize(width: 0.0, height: backgroundFrame.size.height)) let fromRect = CGRect(origin: backgroundFrame.origin, size: CGSize(width: 0.0, height: backgroundFrame.size.height))
let toRect = CGRect(origin: backgroundFrame.origin, size: CGSize(width: backgroundFrame.size.width, height: backgroundFrame.size.height)) let toRect = CGRect(origin: backgroundFrame.origin, size: CGSize(width: backgroundFrame.size.width, height: backgroundFrame.size.height))

View File

@ -100,10 +100,12 @@ private func allOpenInOptions(applicationContext: TelegramApplicationContext, it
let lat = location.latitude let lat = location.latitude
let lon = location.longitude let lon = location.longitude
if let venue = location.venue, let venueId = venue.id, let provider = venue.provider, provider == "foursquare" { if !withDirections {
options.append(OpenInOption(application: .other(title: "Foursquare", identifier: 306934924, scheme: "foursquare"), action: { if let venue = location.venue, let venueId = venue.id, let provider = venue.provider, provider == "foursquare" {
return .openUrl(url: "foursquare://venues/\(venueId)") options.append(OpenInOption(application: .other(title: "Foursquare", identifier: 306934924, scheme: "foursquare"), action: {
})) return .openUrl(url: "foursquare://venues/\(venueId)")
}))
}
} }
options.append(OpenInOption(application: .maps, action: { options.append(OpenInOption(application: .maps, action: {
@ -147,31 +149,33 @@ private func allOpenInOptions(applicationContext: TelegramApplicationContext, it
return .openUrl(url: "lyft://ridetype?id=lyft&destination[latitude]=\(lat)&destination[longitude]=\(lon)") return .openUrl(url: "lyft://ridetype?id=lyft&destination[latitude]=\(lat)&destination[longitude]=\(lon)")
})) }))
options.append(OpenInOption(application: .other(title: "Citymapper", identifier: 469463298, scheme: "citymapper"), action: {
let endName: String
let endAddress: String
if let title = location.venue?.title.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed), title.count > 0 {
endName = title
} else {
endName = ""
}
if let address = location.venue?.address?.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed), address.count > 0 {
endAddress = address
} else {
endAddress = ""
}
return .openUrl(url: "citymapper://directions?endcoord=\(lat),\(lon)&endname=\(endName)&endaddress=\(endAddress)")
}))
if withDirections { if withDirections {
options.append(OpenInOption(application: .other(title: "Citymapper", identifier: 469463298, scheme: "citymapper"), action: {
let endName: String
let endAddress: String
if let title = location.venue?.title.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed), title.count > 0 {
endName = title
} else {
endName = ""
}
if let address = location.venue?.address?.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed), address.count > 0 {
endAddress = address
} else {
endAddress = ""
}
return .openUrl(url: "citymapper://directions?endcoord=\(lat),\(lon)&endname=\(endName)&endaddress=\(endAddress)")
}))
options.append(OpenInOption(application: .other(title: "Yandex.Navi", identifier: 474500851, scheme: "yandexnavi"), action: { options.append(OpenInOption(application: .other(title: "Yandex.Navi", identifier: 474500851, scheme: "yandexnavi"), action: {
return .openUrl(url: "yandexnavi://build_route_on_map?lat_to=\(lat)&lon_to=\(lon)") return .openUrl(url: "yandexnavi://build_route_on_map?lat_to=\(lat)&lon_to=\(lon)")
})) }))
} }
options.append(OpenInOption(application: .other(title: "HERE Maps", identifier: 955837609, scheme: "here-location"), action: { if !withDirections {
return .openUrl(url: "here-location://\(lat),\(lon)") options.append(OpenInOption(application: .other(title: "HERE Maps", identifier: 955837609, scheme: "here-location"), action: {
})) return .openUrl(url: "here-location://\(lat),\(lon)")
}))
}
options.append(OpenInOption(application: .other(title: "Waze", identifier: 323229106, scheme: "waze"), action: { options.append(OpenInOption(application: .other(title: "Waze", identifier: 323229106, scheme: "waze"), action: {
let url = "waze://?ll=\(lat),\(lon)" let url = "waze://?ll=\(lat),\(lon)"

View File

@ -41,10 +41,10 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec
self.requestDismiss = requestDismiss self.requestDismiss = requestDismiss
self.requestShare = requestShare self.requestShare = requestShare
if case .reversed = initialOrder { if case .regular = initialOrder {
self.currentIsReversed = true
} else {
self.currentIsReversed = false self.currentIsReversed = false
} else {
self.currentIsReversed = true
} }
var openMessageImpl: ((MessageId) -> Bool)? var openMessageImpl: ((MessageId) -> Bool)?
@ -125,8 +125,10 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec
self.controlsNode.updateOrder = { [weak self] order in self.controlsNode.updateOrder = { [weak self] order in
if let strongSelf = self { if let strongSelf = self {
var reversed = false let reversed: Bool
if case .reversed = order { if case .regular = order {
reversed = false
} else {
reversed = true reversed = true
} }
if reversed != strongSelf.currentIsReversed { if reversed != strongSelf.currentIsReversed {

View File

@ -177,9 +177,9 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
self.addSubnode(self.separatorNode) self.addSubnode(self.separatorNode)
let mappedStatus = status |> map { value -> MediaPlayerStatus in let mappedStatus = combineLatest(status, self.scrubberNode.scrubbingTimestamp) |> map { value, scrubbingTimestamp -> MediaPlayerStatus in
if let value = value { if let value = value {
return value.status return MediaPlayerStatus(generationTimestamp: value.status.generationTimestamp, duration: value.status.duration, dimensions: value.status.dimensions, timestamp: scrubbingTimestamp ?? value.status.timestamp, baseRate: value.status.baseRate, seekId: value.status.seekId, status: value.status.status)
} else { } else {
return MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .paused) return MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .paused)
} }
@ -222,16 +222,23 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
displayData = value.item.displayData displayData = value.item.displayData
let baseColor: UIColor
if strongSelf.theme.list.itemPrimaryTextColor.isEqual(strongSelf.theme.list.itemAccentColor) {
baseColor = strongSelf.theme.list.controlSecondaryColor
} else {
baseColor = strongSelf.theme.list.itemPrimaryTextColor
}
if value.order != strongSelf.currentOrder { if value.order != strongSelf.currentOrder {
strongSelf.updateOrder?(value.order) strongSelf.updateOrder?(value.order)
strongSelf.currentOrder = value.order strongSelf.currentOrder = value.order
switch value.order { switch value.order {
case .regular: case .regular:
strongSelf.orderButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/OrderReverse"), color: strongSelf.theme.list.itemPrimaryTextColor) strongSelf.orderButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/OrderReverse"), color: baseColor)
case .reversed: case .reversed:
strongSelf.orderButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/OrderReverse"), color: strongSelf.theme.list.itemAccentColor) strongSelf.orderButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/OrderReverse"), color: strongSelf.theme.list.itemAccentColor)
case .random: case .random:
strongSelf.orderButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/OrderRandom"), color: strongSelf.theme.list.itemPrimaryTextColor) strongSelf.orderButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/OrderRandom"), color: strongSelf.theme.list.itemAccentColor)
} }
} }
if value.looping != strongSelf.currentLooping { if value.looping != strongSelf.currentLooping {
@ -239,15 +246,9 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
switch value.looping { switch value.looping {
case .none: case .none:
let baseColor: UIColor
if strongSelf.theme.list.itemPrimaryTextColor.isEqual(strongSelf.theme.list.itemAccentColor) {
baseColor = strongSelf.theme.list.controlSecondaryColor
} else {
baseColor = strongSelf.theme.list.itemPrimaryTextColor
}
strongSelf.loopingButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Repeat"), color: baseColor) strongSelf.loopingButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Repeat"), color: baseColor)
case .item: case .item:
strongSelf.loopingButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/RepeatOne"), color: strongSelf.theme.list.itemPrimaryTextColor) strongSelf.loopingButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/RepeatOne"), color: strongSelf.theme.list.itemAccentColor)
case .all: case .all:
strongSelf.loopingButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Repeat"), color: strongSelf.theme.list.itemAccentColor) strongSelf.loopingButton.icon = generateTintedImage(image: UIImage(bundleImageName: "GlobalMusicPlayer/Repeat"), color: strongSelf.theme.list.itemAccentColor)
} }

View File

@ -47,7 +47,7 @@ public class PeerMediaCollectionController: TelegramController {
self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
self.interfaceState = PeerMediaCollectionInterfaceState(theme: self.presentationData.theme, strings: self.presentationData.strings) self.interfaceState = PeerMediaCollectionInterfaceState(theme: self.presentationData.theme, strings: self.presentationData.strings)
super.init(account: account, navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: self.presentationData.theme).withUpdatedSeparatorColor(self.presentationData.theme.rootController.navigationBar.backgroundColor), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)), enableMediaAccessoryPanel: true, locationBroadcastPanelSource: .none) super.init(account: account, navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: self.presentationData.theme).withUpdatedSeparatorColor(self.presentationData.theme.rootController.navigationBar.backgroundColor), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)), mediaAccessoryPanelVisibility: .specific(size: .compact), locationBroadcastPanelSource: .none)
self.title = self.presentationData.strings.SharedMedia_TitleAll self.title = self.presentationData.strings.SharedMedia_TitleAll
@ -515,7 +515,7 @@ public class PeerMediaCollectionController: TelegramController {
self.validLayout = layout self.validLayout = layout
self.mediaCollectionDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets in self.mediaCollectionDisplayNode.containerLayoutUpdated(layout, navigationBarHeightAndPrimaryHeight: (self.navigationHeight, self.primaryNavigationHeight), transition: transition, listViewTransaction: { updateSizeAndInsets in
self.mediaCollectionDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets) self.mediaCollectionDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets)
}) })
} }

View File

@ -191,7 +191,11 @@ class PeerMediaCollectionControllerNode: ASDisplayNode {
self.candidateHistoryNodeReadyDisposable.dispose() self.candidateHistoryNodeReadyDisposable.dispose()
} }
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition, listViewTransaction: (ListViewUpdateSizeAndInsets) -> Void) { func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeightAndPrimaryHeight: (CGFloat, CGFloat), transition: ContainedViewLayoutTransition, listViewTransaction: (ListViewUpdateSizeAndInsets) -> Void) {
let navigationBarHeight = navigationBarHeightAndPrimaryHeight.0
let primaryNavigationBarHeight = navigationBarHeightAndPrimaryHeight.1
let navigationBarHeightDelta = (navigationBarHeight - primaryNavigationBarHeight)
self.containerLayout = (layout, navigationBarHeight) self.containerLayout = (layout, navigationBarHeight)
var vanillaInsets = layout.insets(options: []) var vanillaInsets = layout.insets(options: [])
@ -207,19 +211,19 @@ class PeerMediaCollectionControllerNode: ASDisplayNode {
if let searchDisplayController = self.searchDisplayController { if let searchDisplayController = self.searchDisplayController {
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition) searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
if !searchDisplayController.isDeactivating { if !searchDisplayController.isDeactivating {
vanillaInsets.top += layout.statusBarHeight ?? 0.0 vanillaInsets.top += (layout.statusBarHeight ?? 0.0) - navigationBarHeightDelta
} }
} }
let sectionsHeight = self.sectionsNode.updateLayout(width: layout.size.width, additionalInset: additionalInset, transition: transition, interfaceState: self.mediaCollectionInterfaceState) let sectionsHeight = self.sectionsNode.updateLayout(width: layout.size.width, additionalInset: additionalInset, transition: transition, interfaceState: self.mediaCollectionInterfaceState)
var sectionOffset: CGFloat = 0.0 var sectionOffset: CGFloat = 0.0
if navigationBarHeight.isZero { if primaryNavigationBarHeight.isZero {
sectionOffset = -sectionsHeight sectionOffset = -sectionsHeight - navigationBarHeightDelta
} }
transition.updateFrame(node: self.sectionsNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight + sectionOffset), size: CGSize(width: layout.size.width, height: sectionsHeight))) transition.updateFrame(node: self.sectionsNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight + sectionOffset), size: CGSize(width: layout.size.width, height: sectionsHeight)))
var insets = vanillaInsets var insets = vanillaInsets
if !navigationBarHeight.isZero { if !primaryNavigationBarHeight.isZero {
insets.top += sectionsHeight insets.top += sectionsHeight
} }

View File

@ -62,7 +62,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
self.segmentedControl.tintColor = self.presentationData.theme.rootController.navigationBar.accentTextColor self.segmentedControl.tintColor = self.presentationData.theme.rootController.navigationBar.accentTextColor
self.segmentedControl.selectedSegmentIndex = 0 self.segmentedControl.selectedSegmentIndex = 0
self.chatListNode = ChatListNode(account: account, groupId: nil, controlsHistoryPreload: false, mode: .peers(filter: filter), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder) self.chatListNode = ChatListNode(account: account, groupId: nil, controlsHistoryPreload: false, mode: .peers(filter: filter), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: presentationData.disableAnimations)
super.init() super.init()
@ -107,7 +107,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
private func updateThemeAndStrings() { private func updateThemeAndStrings() {
self.searchDisplayController?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings) self.searchDisplayController?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings)
self.chatListNode.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder) self.chatListNode.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: self.presentationData.disableAnimations)
} }
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {

View File

@ -34,8 +34,9 @@ public final class PresentationData: Equatable {
public let dateTimeFormat: PresentationDateTimeFormat public let dateTimeFormat: PresentationDateTimeFormat
public let nameDisplayOrder: PresentationPersonNameOrder public let nameDisplayOrder: PresentationPersonNameOrder
public let nameSortOrder: PresentationPersonNameOrder public let nameSortOrder: PresentationPersonNameOrder
public let disableAnimations: Bool
public init(strings: PresentationStrings, theme: PresentationTheme, chatWallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, nameSortOrder: PresentationPersonNameOrder) { public init(strings: PresentationStrings, theme: PresentationTheme, chatWallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, nameSortOrder: PresentationPersonNameOrder, disableAnimations: Bool) {
self.strings = strings self.strings = strings
self.theme = theme self.theme = theme
self.chatWallpaper = chatWallpaper self.chatWallpaper = chatWallpaper
@ -43,10 +44,11 @@ public final class PresentationData: Equatable {
self.dateTimeFormat = dateTimeFormat self.dateTimeFormat = dateTimeFormat
self.nameDisplayOrder = nameDisplayOrder self.nameDisplayOrder = nameDisplayOrder
self.nameSortOrder = nameSortOrder self.nameSortOrder = nameSortOrder
self.disableAnimations = disableAnimations
} }
public static func ==(lhs: PresentationData, rhs: PresentationData) -> Bool { public static func ==(lhs: PresentationData, rhs: PresentationData) -> Bool {
return lhs.strings === rhs.strings && lhs.theme === rhs.theme && lhs.chatWallpaper == rhs.chatWallpaper && lhs.fontSize == rhs.fontSize && lhs.dateTimeFormat == rhs.dateTimeFormat return lhs.strings === rhs.strings && lhs.theme === rhs.theme && lhs.chatWallpaper == rhs.chatWallpaper && lhs.fontSize == rhs.fontSize && lhs.dateTimeFormat == rhs.dateTimeFormat && lhs.disableAnimations == rhs.disableAnimations
} }
} }
@ -271,7 +273,7 @@ public func currentPresentationDataAndSettings(postbox: Postbox) -> Signal<Initi
let dateTimeFormat = currentDateTimeFormat() let dateTimeFormat = currentDateTimeFormat()
let nameDisplayOrder = currentPersonNameDisplayOrder() let nameDisplayOrder = currentPersonNameDisplayOrder()
let nameSortOrder = currentPersonNameSortOrder() let nameSortOrder = currentPersonNameSortOrder()
return InitialPresentationDataAndSettings(presentationData: PresentationData(strings: stringsValue, theme: themeValue, chatWallpaper: effectiveChatWallpaper, fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder), automaticMediaDownloadSettings: automaticMediaDownloadSettings, loggingSettings: loggingSettings, callListSettings: callListSettings, inAppNotificationSettings: inAppNotificationSettings, mediaInputSettings: mediaInputSettings, experimentalUISettings: experimentalUISettings) return InitialPresentationDataAndSettings(presentationData: PresentationData(strings: stringsValue, theme: themeValue, chatWallpaper: effectiveChatWallpaper, fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: currentReduceMotionEnabled() || themeSettings.disableAnimations), automaticMediaDownloadSettings: automaticMediaDownloadSettings, loggingSettings: loggingSettings, callListSettings: callListSettings, inAppNotificationSettings: inAppNotificationSettings, mediaInputSettings: mediaInputSettings, experimentalUISettings: experimentalUISettings)
} }
} }
@ -342,8 +344,8 @@ private func automaticThemeShouldSwitch(_ settings: AutomaticThemeSwitchSetting,
public func updatedPresentationData(postbox: Postbox) -> Signal<PresentationData, NoError> { public func updatedPresentationData(postbox: Postbox) -> Signal<PresentationData, NoError> {
let preferencesKey = PostboxViewKey.preferences(keys: Set([ApplicationSpecificPreferencesKeys.presentationThemeSettings, PreferencesKeys.localizationSettings])) let preferencesKey = PostboxViewKey.preferences(keys: Set([ApplicationSpecificPreferencesKeys.presentationThemeSettings, PreferencesKeys.localizationSettings]))
return postbox.combinedView(keys: [preferencesKey]) return combineLatest(postbox.combinedView(keys: [preferencesKey]), reduceMotionEnabled())
|> mapToSignal { view -> Signal<PresentationData, NoError> in |> mapToSignal { view, disableAnimations -> Signal<PresentationData, NoError> in
let themeSettings: PresentationThemeSettings let themeSettings: PresentationThemeSettings
if let current = (view.views[preferencesKey] as! PreferencesView).values[ApplicationSpecificPreferencesKeys.presentationThemeSettings] as? PresentationThemeSettings { if let current = (view.views[preferencesKey] as! PreferencesView).values[ApplicationSpecificPreferencesKeys.presentationThemeSettings] as? PresentationThemeSettings {
themeSettings = current themeSettings = current
@ -361,7 +363,7 @@ public func updatedPresentationData(postbox: Postbox) -> Signal<PresentationData
effectiveTheme = .builtin(themeSettings.automaticThemeSwitchSetting.theme) effectiveTheme = .builtin(themeSettings.automaticThemeSwitchSetting.theme)
switch themeSettings.automaticThemeSwitchSetting.theme { switch themeSettings.automaticThemeSwitchSetting.theme {
case .nightAccent: case .nightAccent:
effectiveChatWallpaper = .color(0x18222D) effectiveChatWallpaper = .color(0x18222d)
case .nightGrayscale: case .nightGrayscale:
effectiveChatWallpaper = .color(0x000000) effectiveChatWallpaper = .color(0x000000)
default: default:
@ -402,7 +404,7 @@ public func updatedPresentationData(postbox: Postbox) -> Signal<PresentationData
let nameDisplayOrder = currentPersonNameDisplayOrder() let nameDisplayOrder = currentPersonNameDisplayOrder()
let nameSortOrder = currentPersonNameSortOrder() let nameSortOrder = currentPersonNameSortOrder()
return PresentationData(strings: stringsValue, theme: themeValue, chatWallpaper: effectiveChatWallpaper, fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder) return PresentationData(strings: stringsValue, theme: themeValue, chatWallpaper: effectiveChatWallpaper, fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: disableAnimations || themeSettings.disableAnimations)
} }
} }
} }
@ -413,5 +415,5 @@ public func defaultPresentationData() -> PresentationData {
let nameSortOrder = currentPersonNameSortOrder() let nameSortOrder = currentPersonNameSortOrder()
let themeSettings = PresentationThemeSettings.defaultSettings let themeSettings = PresentationThemeSettings.defaultSettings
return PresentationData(strings: defaultPresentationStrings, theme: defaultPresentationTheme, chatWallpaper: .builtin, fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder) return PresentationData(strings: defaultPresentationStrings, theme: defaultPresentationTheme, chatWallpaper: .builtin, fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations)
} }

File diff suppressed because it is too large Load Diff

View File

@ -67,6 +67,7 @@ public final class PresentationThemeRootNavigationStatusBar {
public final class PresentationThemeRootNavigationBar { public final class PresentationThemeRootNavigationBar {
public let buttonColor: UIColor public let buttonColor: UIColor
public let disabledButtonColor: UIColor
public let primaryTextColor: UIColor public let primaryTextColor: UIColor
public let secondaryTextColor: UIColor public let secondaryTextColor: UIColor
public let controlColor: UIColor public let controlColor: UIColor
@ -77,8 +78,9 @@ public final class PresentationThemeRootNavigationBar {
public let badgeStrokeColor: UIColor public let badgeStrokeColor: UIColor
public let badgeTextColor: UIColor public let badgeTextColor: UIColor
public init(buttonColor: UIColor, primaryTextColor: UIColor, secondaryTextColor: UIColor, controlColor: UIColor, accentTextColor: UIColor, backgroundColor: UIColor, separatorColor: UIColor, badgeBackgroundColor: UIColor, badgeStrokeColor: UIColor, badgeTextColor: UIColor) { public init(buttonColor: UIColor, disabledButtonColor: UIColor, primaryTextColor: UIColor, secondaryTextColor: UIColor, controlColor: UIColor, accentTextColor: UIColor, backgroundColor: UIColor, separatorColor: UIColor, badgeBackgroundColor: UIColor, badgeStrokeColor: UIColor, badgeTextColor: UIColor) {
self.buttonColor = buttonColor self.buttonColor = buttonColor
self.disabledButtonColor = disabledButtonColor
self.primaryTextColor = primaryTextColor self.primaryTextColor = primaryTextColor
self.secondaryTextColor = secondaryTextColor self.secondaryTextColor = secondaryTextColor
self.controlColor = controlColor self.controlColor = controlColor

View File

@ -145,6 +145,7 @@ public struct PresentationThemeSettings: PreferencesEntry {
public var themeAccentColor: Int32? public var themeAccentColor: Int32?
public var fontSize: PresentationFontSize public var fontSize: PresentationFontSize
public var automaticThemeSwitchSetting: AutomaticThemeSwitchSetting public var automaticThemeSwitchSetting: AutomaticThemeSwitchSetting
public var disableAnimations: Bool
public var relatedResources: [MediaResourceId] { public var relatedResources: [MediaResourceId] {
switch self.chatWallpaper { switch self.chatWallpaper {
@ -156,15 +157,16 @@ public struct PresentationThemeSettings: PreferencesEntry {
} }
public static var defaultSettings: PresentationThemeSettings { public static var defaultSettings: PresentationThemeSettings {
return PresentationThemeSettings(chatWallpaper: .builtin, theme: .builtin(.dayClassic), themeAccentColor: nil, fontSize: .regular, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting(trigger: .none, theme: .nightAccent)) return PresentationThemeSettings(chatWallpaper: .builtin, theme: .builtin(.dayClassic), themeAccentColor: nil, fontSize: .regular, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting(trigger: .none, theme: .nightAccent), disableAnimations: false)
} }
public init(chatWallpaper: TelegramWallpaper, theme: PresentationThemeReference, themeAccentColor: Int32?, fontSize: PresentationFontSize, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting) { public init(chatWallpaper: TelegramWallpaper, theme: PresentationThemeReference, themeAccentColor: Int32?, fontSize: PresentationFontSize, automaticThemeSwitchSetting: AutomaticThemeSwitchSetting, disableAnimations: Bool) {
self.chatWallpaper = chatWallpaper self.chatWallpaper = chatWallpaper
self.theme = theme self.theme = theme
self.themeAccentColor = themeAccentColor self.themeAccentColor = themeAccentColor
self.fontSize = fontSize self.fontSize = fontSize
self.automaticThemeSwitchSetting = automaticThemeSwitchSetting self.automaticThemeSwitchSetting = automaticThemeSwitchSetting
self.disableAnimations = disableAnimations
} }
public init(decoder: PostboxDecoder) { public init(decoder: PostboxDecoder) {
@ -173,6 +175,7 @@ public struct PresentationThemeSettings: PreferencesEntry {
self.themeAccentColor = decoder.decodeOptionalInt32ForKey("themeAccentColor") self.themeAccentColor = decoder.decodeOptionalInt32ForKey("themeAccentColor")
self.fontSize = PresentationFontSize(rawValue: decoder.decodeInt32ForKey("f", orElse: PresentationFontSize.regular.rawValue)) ?? .regular 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: .nightAccent)
self.disableAnimations = decoder.decodeBoolForKey("disableAnimations", orElse: false)
} }
public func encode(_ encoder: PostboxEncoder) { public func encode(_ encoder: PostboxEncoder) {
@ -185,6 +188,7 @@ public struct PresentationThemeSettings: PreferencesEntry {
} }
encoder.encodeInt32(self.fontSize.rawValue, forKey: "f") encoder.encodeInt32(self.fontSize.rawValue, forKey: "f")
encoder.encodeObject(self.automaticThemeSwitchSetting, forKey: "automaticThemeSwitchSetting") encoder.encodeObject(self.automaticThemeSwitchSetting, forKey: "automaticThemeSwitchSetting")
encoder.encodeBool(self.disableAnimations, forKey: "disableAnimations")
} }
public func isEqual(to: PreferencesEntry) -> Bool { public func isEqual(to: PreferencesEntry) -> Bool {
@ -196,7 +200,7 @@ public struct PresentationThemeSettings: PreferencesEntry {
} }
public static func ==(lhs: PresentationThemeSettings, rhs: PresentationThemeSettings) -> Bool { public static func ==(lhs: PresentationThemeSettings, rhs: PresentationThemeSettings) -> Bool {
return lhs.chatWallpaper == rhs.chatWallpaper && lhs.theme == rhs.theme && lhs.themeAccentColor == rhs.themeAccentColor && lhs.fontSize == rhs.fontSize && lhs.automaticThemeSwitchSetting == rhs.automaticThemeSwitchSetting return lhs.chatWallpaper == rhs.chatWallpaper && lhs.theme == rhs.theme && lhs.themeAccentColor == rhs.themeAccentColor && lhs.fontSize == rhs.fontSize && lhs.automaticThemeSwitchSetting == rhs.automaticThemeSwitchSetting && lhs.disableAnimations == rhs.disableAnimations
} }
} }

View File

@ -51,9 +51,9 @@ private func screenRecordingActive() -> Signal<Bool, NoError> {
} }
func screenCaptureEvents() -> Signal<ScreenCaptureEvent, NoError> { func screenCaptureEvents() -> Signal<ScreenCaptureEvent, NoError> {
return Signal { susbcriber in return Signal { subscriber in
let observer = NotificationCenter.default.addObserver(forName: NSNotification.Name.UIApplicationUserDidTakeScreenshot, object: nil, queue: .main, using: { _ in let observer = NotificationCenter.default.addObserver(forName: NSNotification.Name.UIApplicationUserDidTakeScreenshot, object: nil, queue: .main, using: { _ in
susbcriber.putNext(.still) subscriber.putNext(.still)
}) })
var previous = false var previous = false
@ -61,7 +61,7 @@ func screenCaptureEvents() -> Signal<ScreenCaptureEvent, NoError> {
if value != previous { if value != previous {
previous = value previous = value
if value { if value {
susbcriber.putNext(.video) subscriber.putNext(.video)
} }
} }
}) })

View File

@ -152,7 +152,7 @@ class SearchBarNode: ASDisplayNode, UITextFieldDelegate {
private let iconNode: ASImageNode private let iconNode: ASImageNode
private let textField: SearchBarTextField private let textField: SearchBarTextField
private let clearButton: HighlightableButtonNode private let clearButton: HighlightableButtonNode
private let cancelButton: ASButtonNode private let cancelButton: HighlightableButtonNode
var placeholderString: NSAttributedString? { var placeholderString: NSAttributedString? {
get { get {
@ -262,7 +262,7 @@ class SearchBarNode: ASDisplayNode, UITextFieldDelegate {
self.textField.keyboardAppearance = .dark self.textField.keyboardAppearance = .dark
} }
self.cancelButton = ASButtonNode() self.cancelButton = HighlightableButtonNode()
self.cancelButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0) self.cancelButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0)
self.cancelButton.setAttributedTitle(NSAttributedString(string: strings.Common_Cancel, font: Font.regular(17.0), textColor: theme.accent), for: []) self.cancelButton.setAttributedTitle(NSAttributedString(string: strings.Common_Cancel, font: Font.regular(17.0), textColor: theme.accent), for: [])
self.cancelButton.displaysAsynchronously = false self.cancelButton.displaysAsynchronously = false

View File

@ -64,7 +64,7 @@ final class SecretChatHandshakeStatusInputPanelNode: ChatInputPanelNode {
let buttonSize = self.button.measure(CGSize(width: width - 10.0, height: 100.0)) let buttonSize = self.button.measure(CGSize(width: width - 10.0, height: 100.0))
let panelHeight: CGFloat = 45.0 let panelHeight = defaultHeight(metrics: metrics)
self.button.frame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - buttonSize.width) / 2.0), y: floor((panelHeight - buttonSize.height) / 2.0)), size: buttonSize) self.button.frame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - rightInset - buttonSize.width) / 2.0), y: floor((panelHeight - buttonSize.height) / 2.0)), size: buttonSize)
@ -72,6 +72,6 @@ final class SecretChatHandshakeStatusInputPanelNode: ChatInputPanelNode {
} }
override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { override func minimalHeight(interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
return 45.0 return defaultHeight(metrics: metrics)
} }
} }

View File

@ -99,14 +99,12 @@ final class SecureIdAuthController: ViewController {
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBar.style.style self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBar.style.style
self.title = self.presentationData.strings.Passport_Title self.title = self.presentationData.strings.Passport_Title
let leftButtonTitle: String
switch mode { switch mode {
case .form: case .form:
leftButtonTitle = self.presentationData.strings.Common_Cancel self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed))
case .list: case .list:
leftButtonTitle = self.presentationData.strings.Common_Done self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.cancelPressed))
} }
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: leftButtonTitle, style: .plain, target: self, action: #selector(self.cancelPressed))
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationInfoIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.infoPressed)) self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationInfoIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.infoPressed))
self.challengeDisposable.set((twoStepAuthData(account.network) self.challengeDisposable.set((twoStepAuthData(account.network)

View File

@ -2873,17 +2873,23 @@ final class SecureIdDocumentFormControllerNode: FormControllerNode<SecureIdDocum
if let frontSideDocument = innerState.frontSideDocument { if let frontSideDocument = innerState.frontSideDocument {
entries.append(SecureIdDocumentGalleryEntry(index: Int32(index), resource: frontSideDocument.resource, location: SecureIdDocumentGalleryEntryLocation(position: Int32(index), totalCount: totalCount), error: "")) entries.append(SecureIdDocumentGalleryEntry(index: Int32(index), resource: frontSideDocument.resource, location: SecureIdDocumentGalleryEntryLocation(position: Int32(index), totalCount: totalCount), error: ""))
centralIndex = index if document.id == frontSideDocument.id {
centralIndex = index
}
index += 1 index += 1
} }
if let backSideDocument = innerState.backSideDocument { if let backSideDocument = innerState.backSideDocument {
entries.append(SecureIdDocumentGalleryEntry(index: Int32(index), resource: backSideDocument.resource, location: SecureIdDocumentGalleryEntryLocation(position: Int32(index), totalCount: totalCount), error: "")) entries.append(SecureIdDocumentGalleryEntry(index: Int32(index), resource: backSideDocument.resource, location: SecureIdDocumentGalleryEntryLocation(position: Int32(index), totalCount: totalCount), error: ""))
centralIndex = index if document.id == backSideDocument.id {
centralIndex = index
}
index += 1 index += 1
} }
if let selfieDocument = innerState.selfieDocument { if let selfieDocument = innerState.selfieDocument {
entries.append(SecureIdDocumentGalleryEntry(index: Int32(index), resource: selfieDocument.resource, location: SecureIdDocumentGalleryEntryLocation(position: Int32(index), totalCount: totalCount), error: "")) entries.append(SecureIdDocumentGalleryEntry(index: Int32(index), resource: selfieDocument.resource, location: SecureIdDocumentGalleryEntryLocation(position: Int32(index), totalCount: totalCount), error: ""))
centralIndex = index if document.id == selfieDocument.id {
centralIndex = index
}
index += 1 index += 1
} }
if let _ = innerState.documents.index(where: { $0.id == document.id }) { if let _ = innerState.documents.index(where: { $0.id == document.id }) {

View File

@ -259,11 +259,8 @@ public final class ShareController: ViewController {
for entry in view.0.entries.reversed() { for entry in view.0.entries.reversed() {
switch entry { switch entry {
case let .MessageEntry(_, _, _, _, _, renderedPeer, _): case let .MessageEntry(_, _, _, _, _, renderedPeer, _):
if let peer = renderedPeer.chatMainPeer, peer.id != accountPeer.id { if let peer = renderedPeer.chatMainPeer, peer.id != accountPeer.id, canSendMessagesToPeer(peer) {
if let user = peer as? TelegramUser, (user.firstName ?? "").isEmpty, (user.lastName ?? "").isEmpty { peers.append(peer)
} else if canSendMessagesToPeer(peer) {
peers.append(peer)
}
} }
default: default:
break break

View File

@ -4,6 +4,12 @@ import TelegramCore
import SwiftSignalKit import SwiftSignalKit
import Postbox import Postbox
enum MediaAccessoryPanelVisibility {
case none
case specific(size: ContainerViewLayoutSizeClass)
case always
}
enum LocationBroadcastPanelSource { enum LocationBroadcastPanelSource {
case none case none
case summary case summary
@ -36,7 +42,7 @@ private func presentLiveLocationController(account: Account, peerId: PeerId, con
public class TelegramController: ViewController { public class TelegramController: ViewController {
private let account: Account private let account: Account
let enableMediaAccessoryPanel: Bool let mediaAccessoryPanelVisibility: MediaAccessoryPanelVisibility
let locationBroadcastPanelSource: LocationBroadcastPanelSource let locationBroadcastPanelSource: LocationBroadcastPanelSource
private var mediaStatusDisposable: Disposable? private var mediaStatusDisposable: Disposable?
@ -70,18 +76,22 @@ public class TelegramController: ViewController {
return height return height
} }
init(account: Account, navigationBarPresentationData: NavigationBarPresentationData?, enableMediaAccessoryPanel: Bool, locationBroadcastPanelSource: LocationBroadcastPanelSource) { public var primaryNavigationHeight: CGFloat {
return super.navigationHeight
}
init(account: Account, navigationBarPresentationData: NavigationBarPresentationData?, mediaAccessoryPanelVisibility: MediaAccessoryPanelVisibility, locationBroadcastPanelSource: LocationBroadcastPanelSource) {
self.account = account self.account = account
self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
self.enableMediaAccessoryPanel = enableMediaAccessoryPanel self.mediaAccessoryPanelVisibility = mediaAccessoryPanelVisibility
self.locationBroadcastPanelSource = locationBroadcastPanelSource self.locationBroadcastPanelSource = locationBroadcastPanelSource
super.init(navigationBarPresentationData: navigationBarPresentationData) super.init(navigationBarPresentationData: navigationBarPresentationData)
if enableMediaAccessoryPanel { if case .none = mediaAccessoryPanelVisibility {} else {
self.mediaStatusDisposable = (account.telegramApplicationContext.mediaManager.globalMediaPlayerState self.mediaStatusDisposable = (account.telegramApplicationContext.mediaManager.globalMediaPlayerState
|> deliverOnMainQueue).start(next: { [weak self] playlistStateAndType in |> deliverOnMainQueue).start(next: { [weak self] playlistStateAndType in
if let strongSelf = self, strongSelf.enableMediaAccessoryPanel { if let strongSelf = self {
if !arePlaylistItemsEqual(strongSelf.playlistStateAndType?.0, playlistStateAndType?.0.item) || if !arePlaylistItemsEqual(strongSelf.playlistStateAndType?.0, playlistStateAndType?.0.item) ||
strongSelf.playlistStateAndType?.1 != playlistStateAndType?.0.order || strongSelf.playlistStateAndType?.2 != playlistStateAndType?.1 { strongSelf.playlistStateAndType?.1 != playlistStateAndType?.0.order || strongSelf.playlistStateAndType?.2 != playlistStateAndType?.1 {
var previousVoiceItem: SharedMediaPlaylistItem? var previousVoiceItem: SharedMediaPlaylistItem?
@ -352,7 +362,17 @@ public class TelegramController: ViewController {
} }
} }
if let (item, _, type) = self.playlistStateAndType { let mediaAccessoryPanelHidden: Bool
switch self.mediaAccessoryPanelVisibility {
case .always:
mediaAccessoryPanelHidden = false
case .none:
mediaAccessoryPanelHidden = true
case let .specific(size):
mediaAccessoryPanelHidden = size != layout.metrics.widthClass
}
if let (item, _, type) = self.playlistStateAndType, !mediaAccessoryPanelHidden {
let panelHeight = MediaNavigationAccessoryHeaderNode.minimizedHeight 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 + UIScreenPixel)), size: CGSize(width: layout.size.width, height: panelHeight))
if let (mediaAccessoryPanel, mediaType) = self.mediaAccessoryPanel, mediaType == type { if let (mediaAccessoryPanel, mediaType) = self.mediaAccessoryPanel, mediaType == type {

View File

@ -14,8 +14,9 @@ public class TermsOfServiceControllerTheme {
public let itemSeparator: UIColor public let itemSeparator: UIColor
public let primary: UIColor public let primary: UIColor
public let accent: UIColor public let accent: UIColor
public let disabled: UIColor
public init(statusBarStyle: StatusBarStyle, navigationBackground: UIColor, navigationSeparator: UIColor, listBackground: UIColor, itemBackground: UIColor, itemSeparator: UIColor, primary: UIColor, accent: UIColor) { public init(statusBarStyle: StatusBarStyle, navigationBackground: UIColor, navigationSeparator: UIColor, listBackground: UIColor, itemBackground: UIColor, itemSeparator: UIColor, primary: UIColor, accent: UIColor, disabled: UIColor) {
self.statusBarStyle = statusBarStyle self.statusBarStyle = statusBarStyle
self.navigationBackground = navigationBackground self.navigationBackground = navigationBackground
self.navigationSeparator = navigationSeparator self.navigationSeparator = navigationSeparator
@ -24,16 +25,17 @@ public class TermsOfServiceControllerTheme {
self.itemSeparator = itemSeparator self.itemSeparator = itemSeparator
self.primary = primary self.primary = primary
self.accent = accent self.accent = accent
self.disabled = disabled
} }
} }
public extension TermsOfServiceControllerTheme { public extension TermsOfServiceControllerTheme {
convenience init(presentationTheme: PresentationTheme) { convenience init(presentationTheme: PresentationTheme) {
self.init(statusBarStyle: presentationTheme.rootController.statusBar.style.style, navigationBackground: presentationTheme.rootController.navigationBar.backgroundColor, navigationSeparator: presentationTheme.rootController.navigationBar.separatorColor, listBackground: presentationTheme.list.blocksBackgroundColor, itemBackground: presentationTheme.list.itemBlocksBackgroundColor, itemSeparator: presentationTheme.list.itemBlocksSeparatorColor, primary: presentationTheme.list.itemPrimaryTextColor, accent: presentationTheme.list.itemAccentColor) self.init(statusBarStyle: presentationTheme.rootController.statusBar.style.style, navigationBackground: presentationTheme.rootController.navigationBar.backgroundColor, navigationSeparator: presentationTheme.rootController.navigationBar.separatorColor, listBackground: presentationTheme.list.blocksBackgroundColor, itemBackground: presentationTheme.list.itemBlocksBackgroundColor, itemSeparator: presentationTheme.list.itemBlocksSeparatorColor, primary: presentationTheme.list.itemPrimaryTextColor, accent: presentationTheme.list.itemAccentColor, disabled: presentationTheme.rootController.navigationBar.disabledButtonColor)
} }
convenience init(authTheme: AuthorizationTheme) { convenience init(authTheme: AuthorizationTheme) {
self.init(statusBarStyle: authTheme.statusBarStyle, navigationBackground: authTheme.navigationBarBackgroundColor, navigationSeparator: authTheme.navigationBarSeparatorColor, listBackground: authTheme.listBackgroundColor, itemBackground: authTheme.backgroundColor, itemSeparator: authTheme.separatorColor, primary: authTheme.primaryColor, accent: authTheme.accentColor) self.init(statusBarStyle: authTheme.statusBarStyle, navigationBackground: authTheme.navigationBarBackgroundColor, navigationSeparator: authTheme.navigationBarSeparatorColor, listBackground: authTheme.listBackgroundColor, itemBackground: authTheme.backgroundColor, itemSeparator: authTheme.separatorColor, primary: authTheme.primaryColor, accent: authTheme.accentColor, disabled: authTheme.accentColor)
} }
var presentationTheme: PresentationTheme { var presentationTheme: PresentationTheme {
@ -93,7 +95,7 @@ public class TermsOfServiceController: ViewController {
self.decline = decline self.decline = decline
self.openUrl = openUrl self.openUrl = openUrl
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(buttonColor: self.theme.accent, primaryTextColor: self.theme.primary, backgroundColor: self.theme.navigationBackground, separatorColor: self.theme.navigationSeparator, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear), strings: NavigationBarStrings(back: strings.Common_Back, close: strings.Common_Close))) super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(buttonColor: self.theme.accent, disabledButtonColor: self.theme.disabled, primaryTextColor: self.theme.primary, backgroundColor: self.theme.navigationBackground, separatorColor: self.theme.navigationSeparator, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear), strings: NavigationBarStrings(back: strings.Common_Back, close: strings.Common_Close)))
self.statusBar.statusBarStyle = self.theme.statusBarStyle self.statusBar.statusBarStyle = self.theme.statusBarStyle

View File

@ -250,10 +250,10 @@ class ThemeGalleryController: ViewController {
} }
let _ = (updatePresentationThemeSettingsInteractively(postbox: strongSelf.account.postbox, { current in let _ = (updatePresentationThemeSettingsInteractively(postbox: strongSelf.account.postbox, { current in
if case .color(0x000000) = wallpaper { if case .color(0x000000) = wallpaper {
return PresentationThemeSettings(chatWallpaper: wallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: .regular, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting) return PresentationThemeSettings(chatWallpaper: wallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: .regular, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations)
} }
return PresentationThemeSettings(chatWallpaper: wallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting) return PresentationThemeSettings(chatWallpaper: wallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations)
}) |> deliverOnMainQueue).start(completed: { }) |> deliverOnMainQueue).start(completed: {
self?.dismiss(forceAway: true) self?.dismiss(forceAway: true)
}) })

View File

@ -99,10 +99,10 @@ final class ThemeGridController: ViewController {
let wallpaper: TelegramWallpaper = .image([TelegramMediaImageRepresentation(dimensions: image.size, resource: resource)]) let wallpaper: TelegramWallpaper = .image([TelegramMediaImageRepresentation(dimensions: image.size, resource: resource)])
let _ = (updatePresentationThemeSettingsInteractively(postbox: self.account.postbox, { current in let _ = (updatePresentationThemeSettingsInteractively(postbox: self.account.postbox, { current in
if case .color(0x000000) = wallpaper { if case .color(0x000000) = wallpaper {
return PresentationThemeSettings(chatWallpaper: wallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting) return PresentationThemeSettings(chatWallpaper: wallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations)
} }
return PresentationThemeSettings(chatWallpaper: wallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting) return PresentationThemeSettings(chatWallpaper: wallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations)
}) |> deliverOnMainQueue).start(completed: { [weak self] in }) |> deliverOnMainQueue).start(completed: { [weak self] in
let _ = (self?.navigationController as? NavigationController)?.popViewController(animated: true) let _ = (self?.navigationController as? NavigationController)?.popViewController(animated: true)
}) })

View File

@ -149,7 +149,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3)
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: item.strings.Appearance_PreviewReplyText, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: []) messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: item.strings.Appearance_PreviewReplyText, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
let chatPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: item.componentTheme, wallpaper: item.wallpaper), fontSize: item.fontSize, strings: item.strings, dateTimeFormat: item.dateTimeFormat) let chatPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: item.componentTheme, wallpaper: item.wallpaper), fontSize: item.fontSize, strings: item.strings, dateTimeFormat: item.dateTimeFormat, disableAnimations: false)
let item2: ChatMessageItem = ChatMessageItem(presentationData: chatPresentationData, account: item.account, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: nil, text: item.strings.Appearance_PreviewIncomingText, attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, isAdmin: false), disableDate: true) let item2: ChatMessageItem = ChatMessageItem(presentationData: chatPresentationData, account: item.account, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: nil, text: item.strings.Appearance_PreviewIncomingText, attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, isAdmin: false), disableDate: true)
let item1: ChatMessageItem = ChatMessageItem(presentationData: chatPresentationData, account: item.account, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: TelegramUser(id: item.account.peerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []), text: item.strings.Appearance_PreviewOutgoingText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, isAdmin: false), disableDate: true) let item1: ChatMessageItem = ChatMessageItem(presentationData: chatPresentationData, account: item.account, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false), controllerInteraction: controllerInteraction, content: .message(message: Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: TelegramUser(id: item.account.peerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []), text: item.strings.Appearance_PreviewOutgoingText, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), read: true, selection: .none, isAdmin: false), disableDate: true)

View File

@ -11,14 +11,16 @@ private final class ThemeSettingsControllerArguments {
let openWallpaperSettings: () -> Void let openWallpaperSettings: () -> Void
let openAccentColor: (Int32) -> Void let openAccentColor: (Int32) -> Void
let openAutoNightTheme: () -> Void let openAutoNightTheme: () -> Void
let disableAnimations: (Bool) -> Void
init(account: Account, selectTheme: @escaping (Int32) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, openAccentColor: @escaping (Int32) -> Void, openAutoNightTheme: @escaping () -> Void) { init(account: Account, selectTheme: @escaping (Int32) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, openAccentColor: @escaping (Int32) -> Void, openAutoNightTheme: @escaping () -> Void, disableAnimations: @escaping (Bool) -> Void) {
self.account = account self.account = account
self.selectTheme = selectTheme self.selectTheme = selectTheme
self.selectFontSize = selectFontSize self.selectFontSize = selectFontSize
self.openWallpaperSettings = openWallpaperSettings self.openWallpaperSettings = openWallpaperSettings
self.openAccentColor = openAccentColor self.openAccentColor = openAccentColor
self.openAutoNightTheme = openAutoNightTheme self.openAutoNightTheme = openAutoNightTheme
self.disableAnimations = disableAnimations
} }
} }
@ -36,12 +38,14 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
case wallpaper(PresentationTheme, String) case wallpaper(PresentationTheme, String)
case accentColor(PresentationTheme, String, Int32) case accentColor(PresentationTheme, String, Int32)
case autoNightTheme(PresentationTheme, String, String) case autoNightTheme(PresentationTheme, String, String)
case animationsItem(PresentationTheme, String, Bool)
case animationsInfo(PresentationTheme, String)
case themeListHeader(PresentationTheme, String) case themeListHeader(PresentationTheme, String)
case themeItem(PresentationTheme, String, Bool, Int32) case themeItem(PresentationTheme, String, Bool, Int32)
var section: ItemListSectionId { var section: ItemListSectionId {
switch self { switch self {
case .chatPreviewHeader, .chatPreview, .wallpaper, .accentColor, .autoNightTheme: case .chatPreviewHeader, .chatPreview, .wallpaper, .accentColor, .autoNightTheme, .animationsItem, .animationsInfo:
return ThemeSettingsControllerSection.chatPreview.rawValue return ThemeSettingsControllerSection.chatPreview.rawValue
case .themeListHeader, .themeItem: case .themeListHeader, .themeItem:
return ThemeSettingsControllerSection.themeList.rawValue return ThemeSettingsControllerSection.themeList.rawValue
@ -66,10 +70,14 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
return 5 return 5
case .autoNightTheme: case .autoNightTheme:
return 6 return 6
case .themeListHeader: case .animationsItem:
return 7 return 7
case .animationsInfo:
return 8
case .themeListHeader:
return 9
case let .themeItem(_, _, _, index): case let .themeItem(_, _, _, index):
return 8 + index return 10 + index
} }
} }
@ -93,18 +101,30 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
} else { } else {
return false return false
} }
case let .accentColor(lhsTheme, lhsText, lhsColor): case let .accentColor(lhsTheme, lhsText, lhsColor):
if case let .accentColor(rhsTheme, rhsText, rhsColor) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsColor == rhsColor { if case let .accentColor(rhsTheme, rhsText, rhsColor) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsColor == rhsColor {
return true return true
} else { } else {
return false return false
} }
case let .autoNightTheme(lhsTheme, lhsText, lhsValue): case let .autoNightTheme(lhsTheme, lhsText, lhsValue):
if case let .autoNightTheme(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { if case let .autoNightTheme(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true return true
} else { } else {
return false return false
} }
case let .animationsItem(lhsTheme, lhsTitle, lhsValue):
if case let .animationsItem(rhsTheme, rhsTitle, rhsValue) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsValue == rhsValue {
return true
} else {
return false
}
case let .animationsInfo(lhsTheme, lhsText):
if case let .animationsInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .themeListHeader(lhsTheme, lhsText): case let .themeListHeader(lhsTheme, lhsText):
if case let .themeListHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { if case let .themeListHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true return true
@ -160,6 +180,12 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
return ItemListDisclosureItem(theme: theme, icon: nil, title: text, label: value, labelStyle: .text, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { return ItemListDisclosureItem(theme: theme, icon: nil, title: text, label: value, labelStyle: .text, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: {
arguments.openAutoNightTheme() arguments.openAutoNightTheme()
}) })
case let .animationsItem(theme, title, value):
return ItemListSwitchItem(theme: theme, title: title, value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.disableAnimations(value)
})
case let .animationsInfo(theme, text):
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
case let .themeListHeader(theme, text): case let .themeListHeader(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section) return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .themeItem(theme, title, value, index): case let .themeItem(theme, title, value, index):
@ -170,7 +196,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
} }
} }
private func themeSettingsControllerEntries(presentationData: PresentationData, theme: PresentationTheme, themeAccentColor: Int32?, autoNightSettings: AutomaticThemeSwitchSetting, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat) -> [ThemeSettingsControllerEntry] { private func themeSettingsControllerEntries(presentationData: PresentationData, theme: PresentationTheme, themeAccentColor: Int32?, autoNightSettings: AutomaticThemeSwitchSetting, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, disableAnimations: Bool) -> [ThemeSettingsControllerEntry] {
var entries: [ThemeSettingsControllerEntry] = [] var entries: [ThemeSettingsControllerEntry] = []
entries.append(.fontSizeHeader(presentationData.theme, strings.Appearance_TextSize)) entries.append(.fontSizeHeader(presentationData.theme, strings.Appearance_TextSize))
@ -193,6 +219,11 @@ private func themeSettingsControllerEntries(presentationData: PresentationData,
} }
entries.append(.autoNightTheme(presentationData.theme, strings.Appearance_AutoNightTheme, title)) entries.append(.autoNightTheme(presentationData.theme, strings.Appearance_AutoNightTheme, title))
} }
if !UIAccessibility.isReduceMotionEnabled {
entries.append(.animationsItem(presentationData.theme, strings.Appearance_ReduceMotion, disableAnimations))
entries.append(.animationsInfo(presentationData.theme, strings.Appearance_ReduceMotionInfo))
}
entries.append(.themeListHeader(presentationData.theme, strings.Appearance_ColorTheme)) entries.append(.themeListHeader(presentationData.theme, strings.Appearance_ColorTheme))
entries.append(.themeItem(presentationData.theme, strings.Appearance_ThemeDayClassic, theme.name == .builtin(.dayClassic), 0)) entries.append(.themeItem(presentationData.theme, strings.Appearance_ThemeDayClassic, theme.name == .builtin(.dayClassic), 0))
entries.append(.themeItem(presentationData.theme, strings.Appearance_ThemeDay, theme.name == .builtin(.day), 1)) entries.append(.themeItem(presentationData.theme, strings.Appearance_ThemeDay, theme.name == .builtin(.day), 1))
@ -223,11 +254,11 @@ public func themeSettingsController(account: Account) -> ViewController {
wallpaper = .color(0x18222D) wallpaper = .color(0x18222D)
theme = .builtin(.nightAccent) theme = .builtin(.nightAccent)
} }
return PresentationThemeSettings(chatWallpaper: wallpaper, theme: theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting) return PresentationThemeSettings(chatWallpaper: wallpaper, theme: theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations)
}).start() }).start()
}, selectFontSize: { size in }, selectFontSize: { size in
let _ = updatePresentationThemeSettingsInteractively(postbox: account.postbox, { current in let _ = updatePresentationThemeSettingsInteractively(postbox: account.postbox, { current in
return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: size, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting) return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: size, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations)
}).start() }).start()
}, openWallpaperSettings: { }, openWallpaperSettings: {
pushControllerImpl?(ThemeGridController(account: account)) pushControllerImpl?(ThemeGridController(account: account))
@ -235,11 +266,15 @@ public func themeSettingsController(account: Account) -> ViewController {
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
presentControllerImpl?(ThemeAccentColorActionSheet(theme: presentationData.theme, strings: presentationData.strings, currentValue: color, applyValue: { color in presentControllerImpl?(ThemeAccentColorActionSheet(theme: presentationData.theme, strings: presentationData.strings, currentValue: color, applyValue: { color in
let _ = updatePresentationThemeSettingsInteractively(postbox: account.postbox, { current in let _ = updatePresentationThemeSettingsInteractively(postbox: account.postbox, { current in
return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeAccentColor: color, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting) return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeAccentColor: color, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: current.disableAnimations)
}).start() }).start()
})) }))
}, openAutoNightTheme: { }, openAutoNightTheme: {
pushControllerImpl?(themeAutoNightSettingsController(account: account)) pushControllerImpl?(themeAutoNightSettingsController(account: account))
}, disableAnimations: { disabled in
let _ = updatePresentationThemeSettingsInteractively(postbox: account.postbox, { current in
return PresentationThemeSettings(chatWallpaper: current.chatWallpaper, theme: current.theme, themeAccentColor: current.themeAccentColor, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, disableAnimations: disabled)
}).start()
}) })
let themeSettingsKey = ApplicationSpecificPreferencesKeys.presentationThemeSettings let themeSettingsKey = ApplicationSpecificPreferencesKeys.presentationThemeSettings
@ -256,6 +291,7 @@ public func themeSettingsController(account: Account) -> ViewController {
let wallpaper: TelegramWallpaper let wallpaper: TelegramWallpaper
let strings: PresentationStrings let strings: PresentationStrings
let dateTimeFormat: PresentationDateTimeFormat let dateTimeFormat: PresentationDateTimeFormat
let disableAnimations: Bool
let settings = (preferences.values[themeSettingsKey] as? PresentationThemeSettings) ?? PresentationThemeSettings.defaultSettings let settings = (preferences.values[themeSettingsKey] as? PresentationThemeSettings) ?? PresentationThemeSettings.defaultSettings
switch settings.theme { switch settings.theme {
@ -281,9 +317,10 @@ public func themeSettingsController(account: Account) -> ViewController {
} }
dateTimeFormat = presentationData.dateTimeFormat dateTimeFormat = presentationData.dateTimeFormat
disableAnimations = settings.disableAnimations
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Appearance_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: strings.Common_Back)) let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Appearance_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: strings.Common_Back))
let listState = ItemListNodeState(entries: themeSettingsControllerEntries(presentationData: presentationData, theme: theme, themeAccentColor: settings.themeAccentColor, autoNightSettings: settings.automaticThemeSwitchSetting, strings: presentationData.strings, wallpaper: wallpaper, fontSize: fontSize, dateTimeFormat: dateTimeFormat), style: .blocks, animateChanges: false) let listState = ItemListNodeState(entries: themeSettingsControllerEntries(presentationData: presentationData, theme: theme, themeAccentColor: settings.themeAccentColor, autoNightSettings: settings.automaticThemeSwitchSetting, strings: presentationData.strings, wallpaper: wallpaper, fontSize: fontSize, dateTimeFormat: dateTimeFormat, disableAnimations: disableAnimations), style: .blocks, animateChanges: false)
if previousTheme.swap(theme)?.name != theme.name { if previousTheme.swap(theme)?.name != theme.name {
presentControllerImpl?(ThemeSettingsCrossfadeController()) presentControllerImpl?(ThemeSettingsCrossfadeController())

View File

@ -33,7 +33,6 @@ final class WebEmbedVideoContent: UniversalVideoContent {
private final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoContentNode { private final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoContentNode {
private let webpageContent: TelegramMediaWebpageLoadedContent private let webpageContent: TelegramMediaWebpageLoadedContent
private let intrinsicDimensions: CGSize private let intrinsicDimensions: CGSize
private let approximateDuration: Int32
private let playbackCompletedListeners = Bag<() -> Void>() private let playbackCompletedListeners = Bag<() -> Void>()
@ -55,11 +54,6 @@ private final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoConte
return self._ready.get() return self._ready.get()
} }
private let _preloadCompleted = ValuePromise<Bool>()
var preloadCompleted: Signal<Bool, NoError> {
return self._preloadCompleted.get()
}
private let imageNode: TransformImageNode private let imageNode: TransformImageNode
private let playerNode: WebEmbedPlayerNode private let playerNode: WebEmbedPlayerNode
@ -67,7 +61,6 @@ private final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoConte
init(postbox: Postbox, audioSessionManager: ManagedAudioSession, webPage: TelegramMediaWebpage, webpageContent: TelegramMediaWebpageLoadedContent) { init(postbox: Postbox, audioSessionManager: ManagedAudioSession, webPage: TelegramMediaWebpage, webpageContent: TelegramMediaWebpageLoadedContent) {
self.webpageContent = webpageContent self.webpageContent = webpageContent
self.approximateDuration = Int32(webpageContent.duration ?? 0)
if let embedSize = webpageContent.embedSize { if let embedSize = webpageContent.embedSize {
self.intrinsicDimensions = embedSize self.intrinsicDimensions = embedSize
@ -86,8 +79,6 @@ private final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoConte
self.addSubnode(self.playerNode) self.addSubnode(self.playerNode)
self.addSubnode(self.imageNode) self.addSubnode(self.imageNode)
self._preloadCompleted.set(true)
if let image = webpageContent.image { if let image = webpageContent.image {
self.imageNode.setSignal(chatMessagePhoto(postbox: postbox, photoReference: .webPage(webPage: WebpageReference(webPage), media: image))) self.imageNode.setSignal(chatMessagePhoto(postbox: postbox, photoReference: .webPage(webPage: WebpageReference(webPage), media: image)))
self.imageNode.imageUpdated = { [weak self] in self.imageNode.imageUpdated = { [weak self] in
@ -116,9 +107,11 @@ private final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoConte
transition.updateFrame(node: self.imageNode, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(node: self.imageNode, frame: CGRect(origin: CGPoint(), size: size))
let makeImageLayout = self.imageNode.asyncLayout() if let image = webpageContent.image, let representation = image.representationForDisplayAtSize(self.intrinsicDimensions) {
let applyImageLayout = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: size, boundingSize: size, intrinsicInsets: UIEdgeInsets())) let makeImageLayout = self.imageNode.asyncLayout()
applyImageLayout() let applyImageLayout = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: representation.dimensions.aspectFilled(size), boundingSize: size, intrinsicInsets: UIEdgeInsets()))
applyImageLayout()
}
} }
func play() { func play() {