Various UI fixes

This commit is contained in:
Ilya Laktyushin 2019-09-02 21:30:09 +03:00
parent e4a472f246
commit 7d2e62fcce
85 changed files with 4488 additions and 4289 deletions

View file

@ -53,7 +53,7 @@ func unreadMessages(account: Account) -> Signal<[INMessage], NoError> {
}
if !isMuted && hasUnread {
signals.append(account.postbox.aroundMessageHistoryViewForLocation(.peer(index.messageIndex.id.peerId), anchor: .upperBound, count: 10, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: Set(), tagMask: nil, namespaces: .not([Namespaces.Message.ScheduledCloud, Namespaces.Message.ScheduledLocal]), orderStatistics: .combinedLocation)
signals.append(account.postbox.aroundMessageHistoryViewForLocation(.peer(index.messageIndex.id.peerId), anchor: .upperBound, count: 10, fixedCombinedReadStates: fixedCombinedReadStates, topTaggedMessageIdNamespaces: Set(), tagMask: nil, namespaces: .not(Namespaces.Message.allScheduled), orderStatistics: .combinedLocation)
|> take(1)
|> map { view -> [INMessage] in
var messages: [INMessage] = []

View file

@ -5242,18 +5242,12 @@
CURRENT_PROJECT_VERSION = 667;
DEVELOPMENT_TEAM = X834Q8SBVP;
ENABLE_BITCODE = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(BUILT_PRODUCTS_DIR)",
"$(PROJECT_DIR)/third-party",
);
FRAMEWORK_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)";
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/Watch/Bridge";
INFOPLIST_FILE = "Telegram-iOS/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/submodules/TelegramUI/third-party/opus/lib",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = (
"-ObjC",
@ -5286,18 +5280,12 @@
CURRENT_PROJECT_VERSION = 667;
DEVELOPMENT_TEAM = 6N38VWS5BX;
ENABLE_BITCODE = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(BUILT_PRODUCTS_DIR)",
"$(PROJECT_DIR)/third-party",
);
FRAMEWORK_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)";
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/Watch/Bridge";
INFOPLIST_FILE = "Telegram-iOS/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/submodules/TelegramUI/third-party/opus/lib",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"-ObjC",
"-lz",
@ -5382,18 +5370,12 @@
CURRENT_PROJECT_VERSION = 667;
DEVELOPMENT_TEAM = "";
ENABLE_BITCODE = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(BUILT_PRODUCTS_DIR)",
"$(PROJECT_DIR)/third-party",
);
FRAMEWORK_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)";
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/Watch/Bridge";
INFOPLIST_FILE = "Telegram-iOS/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/submodules/TelegramUI/third-party/opus/lib",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"-ObjC",
"-lz",
@ -6399,18 +6381,12 @@
CURRENT_PROJECT_VERSION = 667;
DEVELOPMENT_TEAM = 6N38VWS5BX;
ENABLE_BITCODE = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(BUILT_PRODUCTS_DIR)",
"$(PROJECT_DIR)/third-party",
);
FRAMEWORK_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)";
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/Watch/Bridge";
INFOPLIST_FILE = "Telegram-iOS/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/submodules/TelegramUI/third-party/opus/lib",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"-ObjC",
"-lz",
@ -6546,18 +6522,12 @@
CURRENT_PROJECT_VERSION = 667;
DEVELOPMENT_TEAM = X834Q8SBVP;
ENABLE_BITCODE = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(BUILT_PRODUCTS_DIR)",
"$(PROJECT_DIR)/third-party",
);
FRAMEWORK_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)";
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/Watch/Bridge";
INFOPLIST_FILE = "Telegram-iOS/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/submodules/TelegramUI/third-party/opus/lib",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"-ObjC",
"-lz",
@ -6702,18 +6672,12 @@
CURRENT_PROJECT_VERSION = 667;
DEVELOPMENT_TEAM = C67CF9S4VU;
ENABLE_BITCODE = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(BUILT_PRODUCTS_DIR)",
"$(PROJECT_DIR)/third-party",
);
FRAMEWORK_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)";
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/Watch/Bridge";
INFOPLIST_FILE = "Telegram-iOS/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/submodules/TelegramUI/third-party/opus/lib",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"-ObjC",
"-lz",
@ -7267,18 +7231,12 @@
CURRENT_PROJECT_VERSION = 667;
DEVELOPMENT_TEAM = C67CF9S4VU;
ENABLE_BITCODE = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(BUILT_PRODUCTS_DIR)",
"$(PROJECT_DIR)/third-party",
);
FRAMEWORK_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)";
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/Watch/Bridge";
INFOPLIST_FILE = "Telegram-iOS/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/submodules/TelegramUI/third-party/opus/lib",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"-ObjC",
"-lz",

View file

@ -41,6 +41,15 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D008599B1B28189D00EAF753"
BuildableName = "Telegram.app"
BlueprintName = "Telegram-iOS"
ReferencedContainer = "container:Telegram-iOS.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO">
@ -63,17 +72,6 @@
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D008599B1B28189D00EAF753"
BuildableName = "Telegram.app"
BlueprintName = "Telegram-iOS"
ReferencedContainer = "container:Telegram-iOS.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "DebugHockeyapp"
@ -85,6 +83,7 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
stopOnEveryUBSanitizerIssue = "YES"
migratedStopOnEveryIssue = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
queueDebuggingEnabled = "No">
@ -105,8 +104,6 @@
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "DebugHockeyapp"

View file

@ -4676,17 +4676,47 @@ Any member of this group will be able to see messages in the channel.";
"EditTheme.EditTitle" = "Edit Theme";
"EditTheme.Title" = "Theme Name";
"EditTheme.ShortLink" = "link";
"EditTheme.ShortLinkInfo" = "Your theme will be updated for all users each time you change it. Anyone can install it using this link.\n\nTheme links must be longer than 5 characters and can use a-z, 0-9 and underscores.";
"EditTheme.Preview" = "CHAT PREVIEW";
"EditTheme.UploadNewTheme" = "Create from File...";
"EditTheme.UploadEditedTheme" = "Update from File...";
"EditTheme.UploadNewInfo" = "This theme will be based on your current theme and wallpaper. Alternatively, you can use a custom theme file if you already have one.";
"EditTheme.UploadEditedInfo" = "You can select a new file to update the theme. It will be updated for all users.";
"EditTheme.ThemeTemplateAlert" = "New Theme Added\n\nPress and hold on your theme to edit it or get a sharing link. Users who install your theme will get automatic updates each time you change it.\n\nFor advanced editing purposes, you can find a file with your theme in Saved Messages.";
"EditTheme.FileReadError" = "Invalid theme file";
"EditTheme.Create.TopInfo" = "The theme will be based on your currently selected colors and wallpaper.";
"EditTheme.Create.BottomInfo" = "You can also use a manually edited custom theme file.";
"EditTheme.Expand.TopInfo" = "The theme will be based on your currently selected colors and wallpaper.";
"EditTheme.Expand.BottomInfo" = "You can also use a manually edited custom theme file.";
"EditTheme.Edit.TopInfo" = "Your theme will be updated for all users each time you change it. Anyone can install it using this link.\n\nTheme links must be at least **5** characters long and can use **a-z**, **0-9** and underscores.";
"EditTheme.Edit.BottomInfo" = "You can select a new file to update the theme. It will be updated for all users.";
"EditTheme.Create.Preview.IncomingReplyName" = "Bob";
"EditTheme.Create.Preview.IncomingReplyText" = "How does it work?";
"EditTheme.Create.Preview.IncomingText" = "Use your current colors";
"EditTheme.Create.Preview.OutgoingText" = "Or upload a theme file";
"EditTheme.Expand.Preview.IncomingReplyName" = "Bob";
"EditTheme.Expand.Preview.IncomingReplyText" = "How does it work?";
"EditTheme.Expand.Preview.IncomingText" = "Use your current colors";
"EditTheme.Expand.Preview.OutgoingText" = "Or upload a theme file";
"EditTheme.Edit.Preview.IncomingReplyName" = "Bob";
"EditTheme.Edit.Preview.IncomingReplyText" = "How does it work?";
"EditTheme.Edit.Preview.IncomingText" = "Use your current colors";
"EditTheme.Edit.Preview.OutgoingText" = "Or upload a theme file";
"EditTheme.ErrorLinkTaken" = "Sorry, this link is already taken";
"Wallpaper.ErrorNotFound" = "Sorry, this chat background doesn't seem to exist.";
"Theme.ErrorNotFound" = "Sorry, this color theme doesn't seem to exist.";
"Theme.Unsupported" = "Sorry, this color theme doesn't support your device yet.";
"Theme.UsersCount_1" = "%@ person is using this theme";
"Theme.UsersCount_2" = "%@ people are using this theme";
"Theme.UsersCount_3_10" = "%@ people are using this theme";
"Theme.UsersCount_any" = "%@ people are using this theme";
"Theme.UsersCount_many" = "%@ people are using this theme";
"Theme.UsersCount_0" = "%@ people are using this theme";
"Conversation.SendMessageErrorTooMuchScheduled" = "Sorry, you can not schedule more than 100 messages.";

View file

@ -704,7 +704,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode<BotCheckoutEntry>,
override func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
var updatedInsets = layout.intrinsicInsets
updatedInsets.bottom += BotCheckoutActionButton.diameter + 20.0
super.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, intrinsicInsets: updatedInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), navigationBarHeight: navigationBarHeight, transition: transition)
super.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: updatedInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), navigationBarHeight: navigationBarHeight, transition: transition)
let actionButtonFrame = CGRect(origin: CGPoint(x: 10.0, y: layout.size.height - 10.0 - BotCheckoutActionButton.diameter - layout.intrinsicInsets.bottom), size: CGSize(width: layout.size.width - 20.0, height: BotCheckoutActionButton.diameter))
transition.updateFrame(node: self.actionButton, frame: actionButtonFrame)

View file

@ -301,7 +301,7 @@ final class BotReceiptControllerNode: ItemListControllerNode<BotReceiptEntry> {
override func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
var updatedInsets = layout.intrinsicInsets
updatedInsets.bottom += BotCheckoutActionButton.diameter + 20.0
super.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, intrinsicInsets: updatedInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), navigationBarHeight: navigationBarHeight, transition: transition)
super.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: updatedInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), navigationBarHeight: navigationBarHeight, transition: transition)
let actionButtonFrame = CGRect(origin: CGPoint(x: 10.0, y: layout.size.height - 10.0 - BotCheckoutActionButton.diameter - layout.intrinsicInsets.bottom), size: CGSize(width: layout.size.width - 20.0, height: BotCheckoutActionButton.diameter))
transition.updateFrame(node: self.actionButton, frame: actionButtonFrame)

View file

@ -474,14 +474,16 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
switch item.style {
case .plain:
if strongSelf.backgroundNode.supernode != nil {
strongSelf.backgroundNode.removeFromSupernode()
if strongSelf.backgroundNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
}
if strongSelf.topStripeNode.supernode != nil {
strongSelf.topStripeNode.removeFromSupernode()
}
if strongSelf.bottomStripeNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 0)
if !last && strongSelf.bottomStripeNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 1)
} else if last && strongSelf.bottomStripeNode.supernode != nil {
strongSelf.bottomStripeNode.removeFromSupernode()
}
transition.updateFrameAdditive(node: strongSelf.bottomStripeNode, frame: CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight)))

View file

@ -1200,18 +1200,18 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
}
func previewingController(from sourceView: UIView, for location: CGPoint) -> (UIViewController, CGRect)? {
guard let layout = self.validLayout, case .compact = layout.metrics.widthClass else {
guard let layout = self.validLayout, case .phone = layout.deviceMetrics.type else {
return nil
}
let boundsSize = self.view.bounds.size
let contentSize: CGSize
if let metrics = DeviceMetrics.forScreenSize(layout.size) {
contentSize = metrics.previewingContentSize(inLandscape: boundsSize.width > boundsSize.height)
} else {
if case .unknown = layout.deviceMetrics {
contentSize = boundsSize
} else {
contentSize = layout.deviceMetrics.previewingContentSize(inLandscape: boundsSize.width > boundsSize.height)
}
if let searchController = self.chatListDisplayNode.searchDisplayController {
if let (view, bounds, action) = searchController.previewViewAndActionAtLocation(location) {
if let peerId = action as? PeerId, peerId.namespace != Namespaces.Peer.SecretChat {
@ -1221,7 +1221,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(peerId), subject: nil, botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
chatController.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, standardInputHeight: 216.0, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
chatController.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
return (chatController, sourceRect)
} else if let messageId = action as? MessageId, messageId.peerId.namespace != Namespaces.Peer.SecretChat {
var sourceRect = view.superview!.convert(view.frame, to: sourceView)
@ -1230,7 +1230,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(messageId.peerId), subject: .message(messageId), botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
chatController.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, standardInputHeight: 216.0, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
chatController.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
return (chatController, sourceRect)
}
}
@ -1253,14 +1253,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
if peer.peerId.namespace != Namespaces.Peer.SecretChat {
let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(peer.peerId), subject: nil, botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
chatController.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, standardInputHeight: 216.0, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
chatController.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
return (chatController, sourceRect)
} else {
return nil
}
case let .groupReference(groupId, _, _, _, _):
let chatListController = ChatListControllerImpl(context: self.context, groupId: groupId, controlsHistoryPreload: false, enableDebugActions: false)
chatListController.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, standardInputHeight: 216.0, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
chatListController.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
return (chatListController, sourceRect)
}
} else {

View file

@ -387,18 +387,18 @@ public class ContactsController: ViewController {
}
func previewingController(from sourceView: UIView, for location: CGPoint) -> (UIViewController, CGRect)? {
guard let layout = self.validLayout, case .compact = layout.metrics.widthClass else {
guard let layout = self.validLayout, case .phone = layout.deviceMetrics.type else {
return nil
}
let boundsSize = self.view.bounds.size
let contentSize: CGSize
if let metrics = DeviceMetrics.forScreenSize(layout.size) {
contentSize = metrics.previewingContentSize(inLandscape: boundsSize.width > boundsSize.height)
} else {
if case .unknown = layout.deviceMetrics {
contentSize = boundsSize
} else {
contentSize = layout.deviceMetrics.previewingContentSize(inLandscape: boundsSize.width > boundsSize.height)
}
var selectedNode: ContactsPeerItemNode?
if let searchController = self.contactsNode.searchDisplayController {
@ -434,7 +434,7 @@ public class ContactsController: ViewController {
if peer.id.namespace != Namespaces.Peer.SecretChat {
let chatController = self.context.sharedContext.makeChatController(context: self.context, chatLocation: .peer(peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
chatController.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, standardInputHeight: 216.0, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
chatController.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
return (chatController, sourceRect)
} else {
return nil

View file

@ -143,7 +143,7 @@ final class ContactsControllerNode: ASDisplayNode {
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
}
self.contactListNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition)
self.contactListNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition)
self.contactListNode.frame = CGRect(origin: CGPoint(), size: layout.size)
}

View file

@ -36,7 +36,7 @@ private let completionKey = "CAAnimationUtils_completion"
public let kCAMediaTimingFunctionSpring = "CAAnimationUtilsSpringCurve"
public extension CAAnimation {
public var completion: ((Bool) -> Void)? {
var completion: ((Bool) -> Void)? {
get {
if let delegate = self.delegate as? CALayerAnimationDelegate {
return delegate.completion
@ -54,7 +54,7 @@ public extension CAAnimation {
}
public extension CALayer {
public func makeAnimation(from: AnyObject, to: AnyObject, keyPath: String, timingFunction: String, duration: Double, delay: Double = 0.0, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) -> CAAnimation {
func makeAnimation(from: AnyObject, to: AnyObject, keyPath: String, timingFunction: String, duration: Double, delay: Double = 0.0, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) -> CAAnimation {
if timingFunction == kCAMediaTimingFunctionSpring {
let animation = makeSpringAnimation(keyPath)
animation.fromValue = from
@ -113,12 +113,12 @@ public extension CALayer {
}
}
public func animate(from: AnyObject, to: AnyObject, keyPath: String, timingFunction: String, duration: Double, delay: Double = 0.0, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
func animate(from: AnyObject, to: AnyObject, keyPath: String, timingFunction: String, duration: Double, delay: Double = 0.0, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
let animation = self.makeAnimation(from: from, to: to, keyPath: keyPath, timingFunction: timingFunction, duration: duration, delay: delay, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: additive, completion: completion)
self.add(animation, forKey: additive ? nil : keyPath)
}
public func animateGroup(_ animations: [CAAnimation], key: String) {
func animateGroup(_ animations: [CAAnimation], key: String) {
let animationGroup = CAAnimationGroup()
var timeOffset = 0.0
for animation in animations {
@ -130,7 +130,7 @@ public extension CALayer {
self.add(animationGroup, forKey: key)
}
public func animateKeyframes(values: [AnyObject], duration: Double, keyPath: String, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
func animateKeyframes(values: [AnyObject], duration: Double, keyPath: String, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
let k = Float(UIView.animationDurationFactor())
var speed: Float = 1.0
if k != 0 && k != 1 {
@ -160,7 +160,7 @@ public extension CALayer {
self.add(animation, forKey: keyPath)
}
public func animateSpring(from: AnyObject, to: AnyObject, keyPath: String, duration: Double, delay: Double = 0.0, initialVelocity: CGFloat = 0.0, damping: CGFloat = 88.0, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
func animateSpring(from: AnyObject, to: AnyObject, keyPath: String, duration: Double, delay: Double = 0.0, initialVelocity: CGFloat = 0.0, damping: CGFloat = 88.0, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
let animation: CABasicAnimation
if #available(iOS 9.0, *) {
animation = makeSpringBounceAnimation(keyPath, initialVelocity, damping)
@ -192,7 +192,7 @@ public extension CALayer {
self.add(animation, forKey: keyPath)
}
public func animateAdditive(from: NSValue, to: NSValue, keyPath: String, key: String, timingFunction: String, mediaTimingFunction: CAMediaTimingFunction? = nil, duration: Double, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
func animateAdditive(from: NSValue, to: NSValue, keyPath: String, key: String, timingFunction: String, mediaTimingFunction: CAMediaTimingFunction? = nil, duration: Double, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
let k = Float(UIView.animationDurationFactor())
var speed: Float = 1.0
if k != 0 && k != 1 {
@ -219,15 +219,15 @@ public extension CALayer {
self.add(animation, forKey: key)
}
public func animateAlpha(from: CGFloat, to: CGFloat, duration: Double, delay: Double = 0.0, timingFunction: String = CAMediaTimingFunctionName.easeInEaseOut.rawValue, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, completion: ((Bool) -> ())? = nil) {
func animateAlpha(from: CGFloat, to: CGFloat, duration: Double, delay: Double = 0.0, timingFunction: String = CAMediaTimingFunctionName.easeInEaseOut.rawValue, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, completion: ((Bool) -> ())? = nil) {
self.animate(from: NSNumber(value: Float(from)), to: NSNumber(value: Float(to)), keyPath: "opacity", timingFunction: timingFunction, duration: duration, delay: delay, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: removeOnCompletion, completion: completion)
}
public func animateScale(from: CGFloat, to: CGFloat, duration: Double, delay: Double = 0.0, timingFunction: String = CAMediaTimingFunctionName.easeInEaseOut.rawValue, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
func animateScale(from: CGFloat, to: CGFloat, duration: Double, delay: Double = 0.0, timingFunction: String = CAMediaTimingFunctionName.easeInEaseOut.rawValue, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
self.animate(from: NSNumber(value: Float(from)), to: NSNumber(value: Float(to)), keyPath: "transform.scale", timingFunction: timingFunction, duration: duration, delay: delay, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: removeOnCompletion, completion: completion)
}
public func animateRotation(from: CGFloat, to: CGFloat, duration: Double, delay: Double = 0.0, timingFunction: String = CAMediaTimingFunctionName.easeInEaseOut.rawValue, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
func animateRotation(from: CGFloat, to: CGFloat, duration: Double, delay: Double = 0.0, timingFunction: String = CAMediaTimingFunctionName.easeInEaseOut.rawValue, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
self.animate(from: NSNumber(value: Float(from)), to: NSNumber(value: Float(to)), keyPath: "transform.rotation.z", timingFunction: timingFunction, duration: duration, delay: delay, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: removeOnCompletion, completion: completion)
}
@ -251,27 +251,27 @@ public extension CALayer {
self.animate(from: NSValue(cgRect: from), to: NSValue(cgRect: to), keyPath: "bounds", timingFunction: timingFunction, duration: duration, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: additive, completion: completion)
}
public func animateBoundsOriginXAdditive(from: CGFloat, to: CGFloat, duration: Double, timingFunction: String = CAMediaTimingFunctionName.easeInEaseOut.rawValue, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
func animateBoundsOriginXAdditive(from: CGFloat, to: CGFloat, duration: Double, timingFunction: String = CAMediaTimingFunctionName.easeInEaseOut.rawValue, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
self.animate(from: from as NSNumber, to: to as NSNumber, keyPath: "bounds.origin.x", timingFunction: timingFunction, duration: duration, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: true, completion: completion)
}
public func animateBoundsOriginYAdditive(from: CGFloat, to: CGFloat, duration: Double, timingFunction: String = CAMediaTimingFunctionName.easeInEaseOut.rawValue, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
func animateBoundsOriginYAdditive(from: CGFloat, to: CGFloat, duration: Double, timingFunction: String = CAMediaTimingFunctionName.easeInEaseOut.rawValue, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
self.animate(from: from as NSNumber, to: to as NSNumber, keyPath: "bounds.origin.y", timingFunction: timingFunction, duration: duration, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: removeOnCompletion, additive: true, completion: completion)
}
public func animateBoundsOriginXAdditive(from: CGFloat, to: CGFloat, duration: Double, mediaTimingFunction: CAMediaTimingFunction) {
func animateBoundsOriginXAdditive(from: CGFloat, to: CGFloat, duration: Double, mediaTimingFunction: CAMediaTimingFunction) {
self.animate(from: from as NSNumber, to: to as NSNumber, keyPath: "bounds.origin.x", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: duration, mediaTimingFunction: mediaTimingFunction, additive: true)
}
public func animateBoundsOriginYAdditive(from: CGFloat, to: CGFloat, duration: Double, mediaTimingFunction: CAMediaTimingFunction) {
func animateBoundsOriginYAdditive(from: CGFloat, to: CGFloat, duration: Double, mediaTimingFunction: CAMediaTimingFunction) {
self.animate(from: from as NSNumber, to: to as NSNumber, keyPath: "bounds.origin.y", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: duration, mediaTimingFunction: mediaTimingFunction, additive: true)
}
public func animatePositionKeyframes(values: [CGPoint], duration: Double, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
func animatePositionKeyframes(values: [CGPoint], duration: Double, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
self.animateKeyframes(values: values.map { NSValue(cgPoint: $0) }, duration: duration, keyPath: "position")
}
public func animateFrame(from: CGRect, to: CGRect, duration: Double, timingFunction: String, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
func animateFrame(from: CGRect, to: CGRect, duration: Double, timingFunction: String, mediaTimingFunction: CAMediaTimingFunction? = nil, removeOnCompletion: Bool = true, additive: Bool = false, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
if from == to && !force {
if let completion = completion {
completion(true)
@ -321,7 +321,7 @@ public extension CALayer {
})
}
public func cancelAnimationsRecursive(key: String) {
func cancelAnimationsRecursive(key: String) {
self.removeAnimation(forKey: key)
if let sublayers = self.sublayers {
for layer in sublayers {

View file

@ -776,7 +776,7 @@ public extension ContainedViewLayoutTransition {
#if os(iOS)
public extension ContainedViewLayoutTransition {
public func animateView(_ f: @escaping () -> Void) {
func animateView(_ f: @escaping () -> Void) {
switch self {
case .immediate:
f()

View file

@ -35,32 +35,55 @@ public struct LayoutMetrics: Equatable {
}
}
public enum LayoutOrientation {
case portrait
case landscape
}
public struct ContainerViewLayout: Equatable {
public var size: CGSize
public var metrics: LayoutMetrics
public var deviceMetrics: DeviceMetrics
public var intrinsicInsets: UIEdgeInsets
public var safeInsets: UIEdgeInsets
public var statusBarHeight: CGFloat?
public var inputHeight: CGFloat?
public var standardInputHeight: CGFloat
public var inputHeightIsInteractivellyChanging: Bool
public var inVoiceOver: Bool
public init(size: CGSize, metrics: LayoutMetrics, intrinsicInsets: UIEdgeInsets, safeInsets: UIEdgeInsets, statusBarHeight: CGFloat?, inputHeight: CGFloat?, standardInputHeight: CGFloat, inputHeightIsInteractivellyChanging: Bool, inVoiceOver: Bool) {
public init(size: CGSize, metrics: LayoutMetrics, deviceMetrics: DeviceMetrics, intrinsicInsets: UIEdgeInsets, safeInsets: UIEdgeInsets, statusBarHeight: CGFloat?, inputHeight: CGFloat?, inputHeightIsInteractivellyChanging: Bool, inVoiceOver: Bool) {
self.size = size
self.metrics = metrics
self.deviceMetrics = deviceMetrics
self.intrinsicInsets = intrinsicInsets
self.safeInsets = safeInsets
self.statusBarHeight = statusBarHeight
self.inputHeight = inputHeight
self.standardInputHeight = standardInputHeight
self.inputHeightIsInteractivellyChanging = inputHeightIsInteractivellyChanging
self.inVoiceOver = inVoiceOver
}
public func insets(options: ContainerViewLayoutInsetOptions) -> UIEdgeInsets {
public func addedInsets(insets: UIEdgeInsets) -> ContainerViewLayout {
return ContainerViewLayout(size: self.size, metrics: self.metrics, deviceMetrics: self.deviceMetrics, intrinsicInsets: UIEdgeInsets(top: self.intrinsicInsets.top + insets.top, left: self.intrinsicInsets.left + insets.left, bottom: self.intrinsicInsets.bottom + insets.bottom, right: self.intrinsicInsets.right + insets.right), safeInsets: self.safeInsets, statusBarHeight: self.statusBarHeight, inputHeight: self.inputHeight, inputHeightIsInteractivellyChanging: self.inputHeightIsInteractivellyChanging, inVoiceOver: self.inVoiceOver)
}
public func withUpdatedSize(_ size: CGSize) -> ContainerViewLayout {
return ContainerViewLayout(size: size, metrics: self.metrics, deviceMetrics: self.deviceMetrics, intrinsicInsets: self.intrinsicInsets, safeInsets: self.safeInsets, statusBarHeight: self.statusBarHeight, inputHeight: self.inputHeight, inputHeightIsInteractivellyChanging: self.inputHeightIsInteractivellyChanging, inVoiceOver: self.inVoiceOver)
}
public func withUpdatedInputHeight(_ inputHeight: CGFloat?) -> ContainerViewLayout {
return ContainerViewLayout(size: self.size, metrics: self.metrics, deviceMetrics: self.deviceMetrics, intrinsicInsets: self.intrinsicInsets, safeInsets: self.safeInsets, statusBarHeight: self.statusBarHeight, inputHeight: inputHeight, inputHeightIsInteractivellyChanging: self.inputHeightIsInteractivellyChanging, inVoiceOver: self.inVoiceOver)
}
public func withUpdatedMetrics(_ metrics: LayoutMetrics) -> ContainerViewLayout {
return ContainerViewLayout(size: self.size, metrics: metrics, deviceMetrics: self.deviceMetrics, intrinsicInsets: self.intrinsicInsets, safeInsets: self.safeInsets, statusBarHeight: self.statusBarHeight, inputHeight: self.inputHeight, inputHeightIsInteractivellyChanging: self.inputHeightIsInteractivellyChanging, inVoiceOver: self.inVoiceOver)
}
}
public extension ContainerViewLayout {
func insets(options: ContainerViewLayoutInsetOptions) -> UIEdgeInsets {
var insets = self.intrinsicInsets
if let statusBarHeight = self.statusBarHeight , options.contains(.statusBar) {
if let statusBarHeight = self.statusBarHeight, options.contains(.statusBar) {
insets.top += statusBarHeight
}
if let inputHeight = self.inputHeight, options.contains(.input) {
@ -69,19 +92,11 @@ public struct ContainerViewLayout: Equatable {
return insets
}
public func addedInsets(insets: UIEdgeInsets) -> ContainerViewLayout {
return ContainerViewLayout(size: self.size, metrics: self.metrics, intrinsicInsets: UIEdgeInsets(top: self.intrinsicInsets.top + insets.top, left: self.intrinsicInsets.left + insets.left, bottom: self.intrinsicInsets.bottom + insets.bottom, right: self.intrinsicInsets.right + insets.right), safeInsets: self.safeInsets, statusBarHeight: self.statusBarHeight, inputHeight: self.inputHeight, standardInputHeight: self.standardInputHeight, inputHeightIsInteractivellyChanging: self.inputHeightIsInteractivellyChanging, inVoiceOver: self.inVoiceOver)
var orientation: LayoutOrientation {
return self.size.width > self.size.height ? .landscape : .portrait
}
public func withUpdatedSize(_ size: CGSize) -> ContainerViewLayout {
return ContainerViewLayout(size: size, metrics: self.metrics, intrinsicInsets: self.intrinsicInsets, safeInsets: self.safeInsets, statusBarHeight: self.statusBarHeight, inputHeight: self.inputHeight, standardInputHeight: self.standardInputHeight, inputHeightIsInteractivellyChanging: self.inputHeightIsInteractivellyChanging, inVoiceOver: self.inVoiceOver)
}
public func withUpdatedInputHeight(_ inputHeight: CGFloat?) -> ContainerViewLayout {
return ContainerViewLayout(size: self.size, metrics: self.metrics, intrinsicInsets: self.intrinsicInsets, safeInsets: self.safeInsets, statusBarHeight: self.statusBarHeight, inputHeight: inputHeight, standardInputHeight: self.standardInputHeight, inputHeightIsInteractivellyChanging: self.inputHeightIsInteractivellyChanging, inVoiceOver: self.inVoiceOver)
}
public func withUpdatedMetrics(_ metrics: LayoutMetrics) -> ContainerViewLayout {
return ContainerViewLayout(size: self.size, metrics: metrics, intrinsicInsets: self.intrinsicInsets, safeInsets: self.safeInsets, statusBarHeight: self.statusBarHeight, inputHeight: self.inputHeight, standardInputHeight: self.standardInputHeight, inputHeightIsInteractivellyChanging: self.inputHeightIsInteractivellyChanging, inVoiceOver: self.inVoiceOver)
var standardInputHeight: CGFloat {
return self.deviceMetrics.keyboardHeight(inLandscape: self.orientation == .landscape) + self.deviceMetrics.predictiveInputHeight(inLandscape: self.orientation == .landscape)
}
}

View file

@ -1,6 +1,11 @@
import UIKit
public enum DeviceMetrics: CaseIterable {
public enum DeviceType {
case phone
case tablet
}
public enum DeviceMetrics: CaseIterable, Equatable {
case iPhone4
case iPhone5
case iPhone6
@ -12,23 +17,39 @@ public enum DeviceMetrics: CaseIterable {
case iPadPro11Inch
case iPadPro
case iPadPro3rdGen
case unknown(screenSize: CGSize, statusBarHeight: CGFloat, onScreenNavigationHeight: CGFloat?)
public static func forScreenSize(_ size: CGSize, hintHasOnScreenNavigation: Bool = false) -> DeviceMetrics? {
let additionalSize = CGSize(width: size.width, height: size.height + 20.0)
public static var allCases: [DeviceMetrics] {
return [.iPhone4, .iPhone5, .iPhone6, .iPhone6Plus, .iPhoneX, .iPhoneXSMax,
.iPad, .iPadPro10Inch, .iPadPro11Inch, .iPadPro, .iPadPro3rdGen]
}
public init(screenSize: CGSize, statusBarHeight: CGFloat, onScreenNavigationHeight: CGFloat?) {
let additionalSize = CGSize(width: screenSize.width, height: screenSize.height + 20.0)
for device in DeviceMetrics.allCases {
if let _ = onScreenNavigationHeight, device.onScreenNavigationHeight(inLandscape: false) == nil {
continue
}
let width = device.screenSize.width
let height = device.screenSize.height
if ((size.width.isEqual(to: width) && size.height.isEqual(to: height)) || size.height.isEqual(to: width) && size.width.isEqual(to: height)) || ((additionalSize.width.isEqual(to: width) && additionalSize.height.isEqual(to: height)) || additionalSize.height.isEqual(to: width) && additionalSize.width.isEqual(to: height)) {
if hintHasOnScreenNavigation && device.onScreenNavigationHeight(inLandscape: false) == nil {
continue
}
return device
if ((screenSize.width.isEqual(to: width) && screenSize.height.isEqual(to: height)) || screenSize.height.isEqual(to: width) && screenSize.width.isEqual(to: height)) || ((additionalSize.width.isEqual(to: width) && additionalSize.height.isEqual(to: height)) || additionalSize.height.isEqual(to: width) && additionalSize.width.isEqual(to: height)) {
self = device
return
}
}
return nil
self = .unknown(screenSize: screenSize, statusBarHeight: statusBarHeight, onScreenNavigationHeight: onScreenNavigationHeight)
}
public var type: DeviceType {
switch self {
case .iPad, .iPadPro10Inch, .iPadPro11Inch, .iPadPro, .iPadPro3rdGen:
return .tablet
case let .unknown(screenSize, _, _) where screenSize.width >= 768.0 && screenSize.height >= 1024.0:
return .tablet
default:
return .phone
}
}
var screenSize: CGSize {
@ -53,10 +74,12 @@ public enum DeviceMetrics: CaseIterable {
return CGSize(width: 834.0, height: 1194.0)
case .iPadPro, .iPadPro3rdGen:
return CGSize(width: 1024.0, height: 1366.0)
case let .unknown(screenSize, _, _):
return screenSize
}
}
func safeAreaInsets(inLandscape: Bool) -> UIEdgeInsets {
func safeInsets(inLandscape: Bool) -> UIEdgeInsets {
switch self {
case .iPhoneX, .iPhoneXSMax:
return inLandscape ? UIEdgeInsets(top: 0.0, left: 44.0, bottom: 0.0, right: 44.0) : UIEdgeInsets(top: 44.0, left: 0.0, bottom: 0.0, right: 0.0)
@ -71,6 +94,8 @@ public enum DeviceMetrics: CaseIterable {
return inLandscape ? 21.0 : 34.0
case .iPadPro3rdGen, .iPadPro11Inch:
return 21.0
case let .unknown(_, _, onScreenNavigationHeight):
return onScreenNavigationHeight
default:
return nil
}
@ -82,12 +107,14 @@ public enum DeviceMetrics: CaseIterable {
return 44.0
case .iPadPro11Inch, .iPadPro3rdGen:
return 24.0
case let .unknown(_, statusBarHeight, _):
return statusBarHeight
default:
return 20.0
}
}
public func standardInputHeight(inLandscape: Bool) -> CGFloat {
public func keyboardHeight(inLandscape: Bool) -> CGFloat {
if inLandscape {
switch self {
case .iPhone4, .iPhone5:
@ -104,6 +131,8 @@ public enum DeviceMetrics: CaseIterable {
return 421.0
case .iPadPro3rdGen:
return 441.0
case .unknown:
return 216.0
}
} else {
switch self {
@ -123,6 +152,8 @@ public enum DeviceMetrics: CaseIterable {
return 328.0
case .iPadPro3rdGen:
return 348.0
case .unknown:
return 216.0
}
}
}
@ -134,6 +165,8 @@ public enum DeviceMetrics: CaseIterable {
return 37.0
case .iPad, .iPadPro10Inch, .iPadPro11Inch, .iPadPro, .iPadPro3rdGen:
return 50.0
case .unknown:
return 37.0
}
} else {
switch self {
@ -143,6 +176,8 @@ public enum DeviceMetrics: CaseIterable {
return 44.0
case .iPad, .iPadPro10Inch, .iPadPro11Inch, .iPadPro, .iPadPro3rdGen:
return 50.0
case .unknown:
return 44.0
}
}
}
@ -181,4 +216,13 @@ public enum DeviceMetrics: CaseIterable {
}
}
}
public var hasTopNotch: Bool {
switch self {
case .iPhoneX, .iPhoneXSMax:
return true
default:
return false
}
}
}

View file

@ -271,10 +271,10 @@ open class NavigationController: UINavigationController, ContainableController,
let masterWidth: CGFloat = max(320.0, floor(layout.size.width / 3.0))
let detailWidth: CGFloat = layout.size.width - masterWidth
if index == 0 {
return (CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: masterWidth, height: layout.size.height)), ContainerViewLayout(size: CGSize(width: masterWidth, height: layout.size.height), metrics: layout.metrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver))
return (CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: masterWidth, height: layout.size.height)), ContainerViewLayout(size: CGSize(width: masterWidth, height: layout.size.height), metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver))
} else {
let detailFrame = CGRect(origin: CGPoint(x: masterWidth, y: 0.0), size: CGSize(width: detailWidth, height: layout.size.height))
return (CGRect(origin: CGPoint(), size: detailFrame.size), ContainerViewLayout(size: CGSize(width: detailWidth, height: layout.size.height), metrics: LayoutMetrics(widthClass: .regular, heightClass: .regular), intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver))
return (CGRect(origin: CGPoint(), size: detailFrame.size), ContainerViewLayout(size: CGSize(width: detailWidth, height: layout.size.height), metrics: LayoutMetrics(widthClass: .regular, heightClass: .regular), deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver))
}
case .single:
var heightClass: ContainerViewLayoutSizeClass
@ -284,7 +284,7 @@ open class NavigationController: UINavigationController, ContainableController,
heightClass = .compact
}
return (CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: layout.size.height)), ContainerViewLayout(size: CGSize(width: layout.size.width, height: layout.size.height), metrics: LayoutMetrics(widthClass: .compact, heightClass: heightClass), intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver))
return (CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: layout.size.height)), ContainerViewLayout(size: CGSize(width: layout.size.width, height: layout.size.height), metrics: LayoutMetrics(widthClass: .compact, heightClass: heightClass), deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver))
}
}
@ -720,7 +720,7 @@ open class NavigationController: UINavigationController, ContainableController,
self.updateControllerLayouts(previousControllers: self._viewControllers, layout: layout, transition: transition)
if let presentedViewController = self.presentedViewController {
let containedLayout = ContainerViewLayout(size: layout.size, metrics: layout.metrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver)
let containedLayout = ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver)
if let presentedViewController = presentedViewController as? ContainableController {
presentedViewController.containerLayoutUpdated(containedLayout, transition: transition)

View file

@ -2,7 +2,7 @@ import Foundation
import UIKit
public extension CALayer {
public func addShakeAnimation(amplitude: CGFloat = 3.0, duration: Double = 0.3, count: Int = 4, decay: Bool = false) {
func addShakeAnimation(amplitude: CGFloat = 3.0, duration: Double = 0.3, count: Int = 4, decay: Bool = false) {
let k = Float(UIView.animationDurationFactor())
var speed: Float = 1.0
if k != 0 && k != 1 {

View file

@ -188,7 +188,7 @@ public extension UIColor {
}
public extension CGSize {
public func fitted(_ size: CGSize) -> CGSize {
func fitted(_ size: CGSize) -> CGSize {
var fittedSize = self
if fittedSize.width > size.width {
fittedSize = CGSize(width: size.width, height: floor((fittedSize.height * size.width / max(fittedSize.width, 1.0))))
@ -199,11 +199,11 @@ public extension CGSize {
return fittedSize
}
public func cropped(_ size: CGSize) -> CGSize {
func cropped(_ size: CGSize) -> CGSize {
return CGSize(width: min(size.width, self.width), height: min(size.height, self.height))
}
public func fittedToArea(_ area: CGFloat) -> CGSize {
func fittedToArea(_ area: CGFloat) -> CGSize {
if self.height < 1.0 || self.width < 1.0 {
return CGSize()
}
@ -213,22 +213,22 @@ public extension CGSize {
return CGSize(width: floor(width), height: floor(height))
}
public func aspectFilled(_ size: CGSize) -> CGSize {
func aspectFilled(_ size: CGSize) -> CGSize {
let scale = max(size.width / max(1.0, self.width), size.height / max(1.0, self.height))
return CGSize(width: floor(self.width * scale), height: floor(self.height * scale))
}
public func aspectFitted(_ size: CGSize) -> CGSize {
func aspectFitted(_ size: CGSize) -> CGSize {
let scale = min(size.width / max(1.0, self.width), size.height / max(1.0, self.height))
return CGSize(width: floor(self.width * scale), height: floor(self.height * scale))
}
public func aspectFittedOrSmaller(_ size: CGSize) -> CGSize {
func aspectFittedOrSmaller(_ size: CGSize) -> CGSize {
let scale = min(1.0, min(size.width / max(1.0, self.width), size.height / max(1.0, self.height)))
return CGSize(width: floor(self.width * scale), height: floor(self.height * scale))
}
public func aspectFittedWithOverflow(_ size: CGSize, leeway: CGFloat) -> CGSize {
func aspectFittedWithOverflow(_ size: CGSize, leeway: CGFloat) -> CGSize {
let scale = min(size.width / max(1.0, self.width), size.height / max(1.0, self.height))
var result = CGSize(width: floor(self.width * scale), height: floor(self.height * scale))
if result.width < size.width && result.width > size.width - leeway {
@ -242,22 +242,22 @@ public extension CGSize {
return result
}
public func fittedToWidthOrSmaller(_ width: CGFloat) -> CGSize {
func fittedToWidthOrSmaller(_ width: CGFloat) -> CGSize {
let scale = min(1.0, width / max(1.0, self.width))
return CGSize(width: floor(self.width * scale), height: floor(self.height * scale))
}
public func multipliedByScreenScale() -> CGSize {
func multipliedByScreenScale() -> CGSize {
let scale = UIScreenScale
return CGSize(width: self.width * scale, height: self.height * scale)
}
public func dividedByScreenScale() -> CGSize {
func dividedByScreenScale() -> CGSize {
let scale = UIScreenScale
return CGSize(width: self.width / scale, height: self.height / scale)
}
public var integralFloor: CGSize {
var integralFloor: CGSize {
return CGSize(width: floor(self.width), height: floor(self.height))
}
}
@ -272,7 +272,7 @@ public extension UIImage {
self.draw(at: CGPoint())
let result = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
if !UIEdgeInsetsEqualToEdgeInsets(self.capInsets, UIEdgeInsets()) {
if self.capInsets != UIEdgeInsets() {
return result.resizableImage(withCapInsets: self.capInsets, resizingMode: self.resizingMode)
}
return result
@ -350,7 +350,7 @@ private func makeLayerSubtreeSnapshot(layer: CALayer) -> CALayer? {
}
public extension UIView {
public func snapshotContentTree(unhide: Bool = false, keepTransform: Bool = false) -> UIView? {
func snapshotContentTree(unhide: Bool = false, keepTransform: Bool = false) -> UIView? {
let wasHidden = self.isHidden
if unhide && wasHidden {
self.isHidden = false
@ -370,7 +370,7 @@ public extension UIView {
}
public extension CALayer {
public func snapshotContentTree(unhide: Bool = false) -> CALayer? {
func snapshotContentTree(unhide: Bool = false) -> CALayer? {
let wasHidden = self.isHidden
if unhide && wasHidden {
self.isHidden = false
@ -390,29 +390,29 @@ public extension CALayer {
}
public extension CGRect {
public var topLeft: CGPoint {
var topLeft: CGPoint {
return self.origin
}
public var topRight: CGPoint {
var topRight: CGPoint {
return CGPoint(x: self.maxX, y: self.minY)
}
public var bottomLeft: CGPoint {
var bottomLeft: CGPoint {
return CGPoint(x: self.minX, y: self.maxY)
}
public var bottomRight: CGPoint {
var bottomRight: CGPoint {
return CGPoint(x: self.maxX, y: self.maxY)
}
public var center: CGPoint {
var center: CGPoint {
return CGPoint(x: self.midX, y: self.midY)
}
}
public extension CGPoint {
public func offsetBy(dx: CGFloat, dy: CGFloat) -> CGPoint {
func offsetBy(dx: CGFloat, dy: CGFloat) -> CGPoint {
return CGPoint(x: self.x + dx, y: self.y + dy)
}
}

View file

@ -65,14 +65,14 @@ private final class ViewControllerPeekContentNode: ASDisplayNode, PeekController
if !self.hasValidLayout {
self.hasValidLayout = true
self.controller.view.frame = CGRect(origin: CGPoint(), size: size)
self.controller.containerLayoutUpdated(ContainerViewLayout(size: size, metrics: LayoutMetrics(), intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, standardInputHeight: 216.0, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
self.controller.containerLayoutUpdated(ContainerViewLayout(size: size, metrics: LayoutMetrics(), deviceMetrics: .unknown(screenSize: size, statusBarHeight: 20.0, onScreenNavigationHeight: nil), intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
self.controller.setIgnoreAppearanceMethodInvocations(true)
self.view.addSubview(self.controller.view)
self.controller.setIgnoreAppearanceMethodInvocations(false)
self.controller.viewWillAppear(false)
self.controller.viewDidAppear(false)
} else {
self.controller.containerLayoutUpdated(ContainerViewLayout(size: size, metrics: LayoutMetrics(), intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, standardInputHeight: 216.0, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: transition)
self.controller.containerLayoutUpdated(ContainerViewLayout(size: size, metrics: LayoutMetrics(), deviceMetrics: .unknown(screenSize: size, statusBarHeight: 20.0, onScreenNavigationHeight: nil), intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: transition)
}
return size

View file

@ -94,7 +94,7 @@ private func inputHeightOffsetForLayout(_ layout: WindowLayout) -> CGFloat {
return 0.0
}
private func containedLayoutForWindowLayout(_ layout: WindowLayout, hasOnScreenNavigation: Bool) -> ContainerViewLayout {
private func containedLayoutForWindowLayout(_ layout: WindowLayout, deviceMetrics: DeviceMetrics) -> ContainerViewLayout {
let resolvedStatusBarHeight: CGFloat?
if let statusBarHeight = layout.statusBarHeight {
if layout.forceInCallStatusBarText != nil {
@ -111,25 +111,13 @@ private func containedLayoutForWindowLayout(_ layout: WindowLayout, hasOnScreenN
updatedInputHeight = inputHeight - inputHeightOffsetForLayout(layout)
}
let isLandscape = layout.size.width > layout.size.height
var resolvedSafeInsets = layout.safeInsets
var standardInputHeight: CGFloat = 216.0
var predictiveHeight: CGFloat = 42.0
if let metrics = DeviceMetrics.forScreenSize(layout.size, hintHasOnScreenNavigation: hasOnScreenNavigation) {
let isLandscape = layout.size.width > layout.size.height
let safeAreaInsets = metrics.safeAreaInsets(inLandscape: isLandscape)
if !safeAreaInsets.left.isZero {
resolvedSafeInsets.left = safeAreaInsets.left
resolvedSafeInsets.right = safeAreaInsets.right
}
standardInputHeight = metrics.standardInputHeight(inLandscape: isLandscape)
predictiveHeight = metrics.predictiveInputHeight(inLandscape: isLandscape)
if layout.safeInsets.left.isZero {
resolvedSafeInsets = deviceMetrics.safeInsets(inLandscape: isLandscape)
}
standardInputHeight += predictiveHeight
return ContainerViewLayout(size: layout.size, metrics: layout.metrics, intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: layout.onScreenNavigationHeight ?? 00, right: 0.0), safeInsets: resolvedSafeInsets, statusBarHeight: resolvedStatusBarHeight, inputHeight: updatedInputHeight, standardInputHeight: standardInputHeight, inputHeightIsInteractivellyChanging: layout.upperKeyboardInputPositionBound != nil && layout.upperKeyboardInputPositionBound != layout.size.height && layout.inputHeight != nil, inVoiceOver: layout.inVoiceOver)
return ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: deviceMetrics, intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: layout.onScreenNavigationHeight ?? 0.0, right: 0.0), safeInsets: resolvedSafeInsets, statusBarHeight: resolvedStatusBarHeight, inputHeight: updatedInputHeight, inputHeightIsInteractivellyChanging: layout.upperKeyboardInputPositionBound != nil && layout.upperKeyboardInputPositionBound != layout.size.height && layout.inputHeight != nil, inVoiceOver: layout.inVoiceOver)
}
private func encodeText(_ string: String, _ key: Int) -> String {
@ -247,11 +235,11 @@ public final class WindowHostView {
self.updatePreferNavigationUIHidden = updatePreferNavigationUIHidden
}
var hasOnScreenNavigation: Bool {
fileprivate var onScreenNavigationHeight: CGFloat? {
if #available(iOSApplicationExtension 11.0, *) {
return !self.eventView.safeAreaInsets.bottom.isZero
return !self.eventView.safeAreaInsets.bottom.isLessThanOrEqualTo(0.0) ? nil : self.eventView.safeAreaInsets.bottom
} else {
return false
return nil
}
}
}
@ -279,10 +267,6 @@ private func layoutMetricsForScreenSize(_ size: CGSize) -> LayoutMetrics {
}
}
private func safeInsetsForScreenSize(_ size: CGSize, hasOnScreenNavigation: Bool) -> UIEdgeInsets {
return DeviceMetrics.forScreenSize(size, hintHasOnScreenNavigation: hasOnScreenNavigation)?.safeAreaInsets(inLandscape: size.width > size.height) ?? UIEdgeInsets.zero
}
public final class WindowKeyboardGestureRecognizerDelegate: NSObject, UIGestureRecognizerDelegate {
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
@ -296,6 +280,8 @@ public final class WindowKeyboardGestureRecognizerDelegate: NSObject, UIGestureR
public class Window1 {
public let hostView: WindowHostView
private let deviceMetrics: DeviceMetrics
private let statusBarHost: StatusBarHost?
private let statusBarManager: StatusBarManager?
private let keyboardManager: KeyboardManager?
@ -356,7 +342,8 @@ public class Window1 {
self.volumeControlStatusBarNode.isHidden = true
let boundsSize = self.hostView.eventView.bounds.size
let deviceMetrics = DeviceMetrics.forScreenSize(boundsSize, hintHasOnScreenNavigation: self.hostView.hasOnScreenNavigation)
self.deviceMetrics = DeviceMetrics(screenSize: UIScreen.main.bounds.size, statusBarHeight: statusBarHost?.statusBarFrame.height ?? 20.0, onScreenNavigationHeight: self.hostView.onScreenNavigationHeight)
self.statusBarHost = statusBarHost
let statusBarHeight: CGFloat
@ -365,14 +352,16 @@ public class Window1 {
self.statusBarManager = StatusBarManager(host: statusBarHost, volumeControlStatusBar: self.volumeControlStatusBar, volumeControlStatusBarNode: self.volumeControlStatusBarNode)
self.keyboardManager = KeyboardManager(host: statusBarHost)
} else {
statusBarHeight = deviceMetrics?.statusBarHeight ?? 20.0
statusBarHeight = self.deviceMetrics.statusBarHeight
self.statusBarManager = nil
self.keyboardManager = nil
}
let onScreenNavigationHeight = deviceMetrics?.onScreenNavigationHeight(inLandscape: boundsSize.width > boundsSize.height)
let isLandscape = boundsSize.width > boundsSize.height
let safeInsets = self.deviceMetrics.safeInsets(inLandscape: isLandscape)
let onScreenNavigationHeight = self.deviceMetrics.onScreenNavigationHeight(inLandscape: isLandscape)
self.windowLayout = WindowLayout(size: boundsSize, metrics: layoutMetricsForScreenSize(boundsSize), statusBarHeight: statusBarHeight, forceInCallStatusBarText: self.forceInCallStatusBarText, inputHeight: 0.0, safeInsets: safeInsetsForScreenSize(boundsSize, hasOnScreenNavigation: self.hostView.hasOnScreenNavigation), onScreenNavigationHeight: onScreenNavigationHeight, upperKeyboardInputPositionBound: nil, inVoiceOver: UIAccessibility.isVoiceOverRunning)
self.windowLayout = WindowLayout(size: boundsSize, metrics: layoutMetricsForScreenSize(boundsSize), statusBarHeight: statusBarHeight, forceInCallStatusBarText: self.forceInCallStatusBarText, inputHeight: 0.0, safeInsets: safeInsets, onScreenNavigationHeight: onScreenNavigationHeight, upperKeyboardInputPositionBound: nil, inVoiceOver: UIAccessibility.isVoiceOverRunning)
self.updatingLayout = UpdatingLayout(layout: self.windowLayout, transition: .immediate)
self.presentationContext = PresentationContext()
self.overlayPresentationContext = GlobalOverlayPresentationContext(statusBarHost: statusBarHost, parentView: self.hostView.aboveStatusBarView)
@ -446,8 +435,8 @@ public class Window1 {
self.presentationContext.view = self.hostView.containerView
self.presentationContext.volumeControlStatusBarNodeView = self.volumeControlStatusBarNode.view
self.presentationContext.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout, hasOnScreenNavigation: self.hostView.hasOnScreenNavigation), transition: .immediate)
self.overlayPresentationContext.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout, hasOnScreenNavigation: self.hostView.hasOnScreenNavigation), transition: .immediate)
self.presentationContext.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout, deviceMetrics: self.deviceMetrics), transition: .immediate)
self.overlayPresentationContext.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout, deviceMetrics: self.deviceMetrics), transition: .immediate)
self.statusBarChangeObserver = NotificationCenter.default.addObserver(forName: UIApplication.willChangeStatusBarFrameNotification, object: nil, queue: OperationQueue.main, using: { [weak self] notification in
if let strongSelf = self {
@ -681,7 +670,7 @@ public class Window1 {
} else {
transition = .immediate
}
self.updateLayout { $0.update(size: value, metrics: layoutMetricsForScreenSize(value), safeInsets: safeInsetsForScreenSize(value, hasOnScreenNavigation: self.hostView.hasOnScreenNavigation), forceInCallStatusBarText: self.forceInCallStatusBarText, transition: transition, overrideTransition: true) }
self.updateLayout { $0.update(size: value, metrics: layoutMetricsForScreenSize(value), safeInsets: self.deviceMetrics.safeInsets(inLandscape: value.width > value.height), forceInCallStatusBarText: self.forceInCallStatusBarText, transition: transition, overrideTransition: true) }
}
private var _rootController: ContainableController?
@ -697,7 +686,7 @@ public class Window1 {
if let rootController = self._rootController {
if !self.windowLayout.size.width.isZero && !self.windowLayout.size.height.isZero {
rootController.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout, hasOnScreenNavigation: self.hostView.hasOnScreenNavigation), transition: .immediate)
rootController.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout, deviceMetrics: self.deviceMetrics), transition: .immediate)
}
self.hostView.containerView.insertSubview(rootController.view, at: 0)
@ -718,7 +707,7 @@ public class Window1 {
}
self._topLevelOverlayControllers = value
let layout = containedLayoutForWindowLayout(self.windowLayout, hasOnScreenNavigation: self.hostView.hasOnScreenNavigation)
let layout = containedLayoutForWindowLayout(self.windowLayout, deviceMetrics: self.deviceMetrics)
for controller in self._topLevelOverlayControllers {
controller.containerLayoutUpdated(layout, transition: .immediate)
@ -940,16 +929,15 @@ public class Window1 {
self.isFirstLayout = false
let boundsSize = updatingLayout.layout.size
let deviceMetrics = DeviceMetrics.forScreenSize(boundsSize, hintHasOnScreenNavigation: self.hostView.hasOnScreenNavigation)
let isLandscape = boundsSize.width > boundsSize.height
var statusBarHeight: CGFloat?
if let statusBarHost = self.statusBarHost {
statusBarHeight = statusBarHost.statusBarFrame.size.height
} else {
statusBarHeight = deviceMetrics?.statusBarHeight ?? 20.0
statusBarHeight = self.deviceMetrics.statusBarHeight
}
let statusBarWasHidden = self.statusBarHidden
if statusBarHiddenInLandscape && updatingLayout.layout.size.width > updatingLayout.layout.size.height {
if statusBarHiddenInLandscape && isLandscape {
statusBarHeight = nil
self.statusBarHidden = true
} else {
@ -961,11 +949,9 @@ public class Window1 {
}
let previousInputOffset = inputHeightOffsetForLayout(self.windowLayout)
let onScreenNavigationHeight = deviceMetrics?.onScreenNavigationHeight(inLandscape: boundsSize.width > boundsSize.height)
self.windowLayout = WindowLayout(size: updatingLayout.layout.size, metrics: layoutMetricsForScreenSize(updatingLayout.layout.size), statusBarHeight: statusBarHeight, forceInCallStatusBarText: updatingLayout.layout.forceInCallStatusBarText, inputHeight: updatingLayout.layout.inputHeight, safeInsets: updatingLayout.layout.safeInsets, onScreenNavigationHeight: self.deviceMetrics.onScreenNavigationHeight(inLandscape: isLandscape), upperKeyboardInputPositionBound: updatingLayout.layout.upperKeyboardInputPositionBound, inVoiceOver: updatingLayout.layout.inVoiceOver)
self.windowLayout = WindowLayout(size: updatingLayout.layout.size, metrics: layoutMetricsForScreenSize(updatingLayout.layout.size), statusBarHeight: statusBarHeight, forceInCallStatusBarText: updatingLayout.layout.forceInCallStatusBarText, inputHeight: updatingLayout.layout.inputHeight, safeInsets: updatingLayout.layout.safeInsets, onScreenNavigationHeight: onScreenNavigationHeight, upperKeyboardInputPositionBound: updatingLayout.layout.upperKeyboardInputPositionBound, inVoiceOver: updatingLayout.layout.inVoiceOver)
let childLayout = containedLayoutForWindowLayout(self.windowLayout, hasOnScreenNavigation: self.hostView.hasOnScreenNavigation)
let childLayout = containedLayoutForWindowLayout(self.windowLayout, deviceMetrics: self.deviceMetrics)
let childLayoutUpdated = self.updatedContainerLayout != childLayout
self.updatedContainerLayout = childLayout

View file

@ -40,6 +40,12 @@ public extension UnicodeScalar {
var isZeroWidthJoiner: Bool {
return self.value == 8205
}
var isVariationSelector: Bool {
return self.value == 0xfe0f
}
static var VariationSelector = UnicodeScalar(0xfe0f)!
}
public extension String {
@ -63,24 +69,24 @@ public extension String {
guard !self.isEmpty else {
return false
}
var nextShouldBeFE0F = false
var nextShouldBeVariationSelector = false
for scalar in self.unicodeScalars {
if nextShouldBeFE0F {
if scalar.value == 0xfe0f {
nextShouldBeFE0F = false
if nextShouldBeVariationSelector {
if scalar.isVariationSelector {
nextShouldBeVariationSelector = false
continue
} else {
return false
}
}
if !scalar.isEmoji && scalar.maybeEmoji {
nextShouldBeFE0F = true
nextShouldBeVariationSelector = true
}
else if !scalar.isEmoji && !scalar.isZeroWidthJoiner {
return false
}
}
return !nextShouldBeFE0F
return !nextShouldBeVariationSelector
}
var emojis: [String] {
@ -106,22 +112,22 @@ public extension String {
var normalizedEmoji: String {
var string = ""
var nextShouldBeFE0F = false
var nextShouldBeVariationSelector = false
for scalar in self.unicodeScalars {
if nextShouldBeFE0F {
if scalar.value != 0xfe0f {
string.unicodeScalars.append("\u{fe0f}")
if nextShouldBeVariationSelector {
if !scalar.isVariationSelector{
string.unicodeScalars.append(UnicodeScalar.VariationSelector)
}
nextShouldBeFE0F = false
nextShouldBeVariationSelector = false
}
string.unicodeScalars.append(scalar)
if !scalar.isEmoji && scalar.maybeEmoji {
nextShouldBeFE0F = true
nextShouldBeVariationSelector = true
}
}
if nextShouldBeFE0F {
string.unicodeScalars.append("\u{fe0f}")
if nextShouldBeVariationSelector {
string.unicodeScalars.append(UnicodeScalar.VariationSelector)
}
return string

View file

@ -414,7 +414,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
func setMessage(_ message: Message) {
self.currentMessage = message
self.actionButton.isHidden = message.containsSecretMedia
self.actionButton.isHidden = message.containsSecretMedia || Namespaces.Message.allScheduled.contains(message.id.namespace)
let canDelete: Bool
if let peer = message.peers[message.id.peerId] {
@ -805,7 +805,9 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
}
if actions.options.contains(.deleteLocally) {
var localOptionText = strongSelf.strings.Conversation_DeleteMessagesForMe
if strongSelf.context.account.peerId == peerId {
if let messageId = messages.first?.id, Namespaces.Message.allScheduled.contains(messageId.namespace) {
localOptionText = messages.count > 1 ? strongSelf.strings.ScheduledMessages_DeleteMany : strongSelf.strings.ScheduledMessages_Delete
} else if strongSelf.context.account.peerId == peerId {
localOptionText = strongSelf.strings.Conversation_Moderate_Delete
}
items.append(ActionSheetButtonItem(title: localOptionText, color: .destructive, action: { [weak actionSheet] in

View file

@ -956,7 +956,7 @@ public class GalleryController: ViewController {
self.galleryNode.setControlsHidden(true, animated: false)
if let centralItemNode = self.galleryNode.pager.centralItemNode(), let itemSize = centralItemNode.contentSize() {
self.preferredContentSize = itemSize.aspectFitted(self.view.bounds.size)
self.containerLayoutUpdated(ContainerViewLayout(size: self.preferredContentSize, metrics: LayoutMetrics(), intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, standardInputHeight: 216.0, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
self.containerLayoutUpdated(ContainerViewLayout(size: self.preferredContentSize, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
}
}
}

View file

@ -70,7 +70,6 @@ open class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGesture
let completion = { [weak self] in
if interfaceAnimationCompleted && contentAnimationCompleted {
if let dismiss = self?.dismiss {
self?.scrollView.isScrollEnabled = true
dismiss()
}
}
@ -123,6 +122,10 @@ open class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGesture
var previousIndex: Int?
self.pager.centralItemIndexOffsetUpdated = { [weak self] itemsIndexAndProgress in
if let strongSelf = self {
if abs(strongSelf.scrollView.contentOffset.y - strongSelf.scrollView.contentSize.height / 3.0) > 0.1 {
strongSelf.scrollView.setContentOffset(CGPoint(x: 0.0, y: strongSelf.scrollView.contentSize.height / 3.0), animated: true)
}
var node: GalleryThumbnailContainerNode?
var thumbnailContainerVisible = false
if let layout = strongSelf.containerLayout?.1, layout.size.width < layout.size.height {
@ -402,7 +405,6 @@ open class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGesture
let completion = { [weak self] in
if interfaceAnimationCompleted && contentAnimationCompleted {
if let dismiss = self?.dismiss {
self?.scrollView.isScrollEnabled = true
dismiss()
}
}

View file

@ -344,6 +344,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
self.dismissOnOrientationChange = item.landscape
var disablePictureInPicture = false
var disablePlayerControls = false
var isAnimated = false
if let content = item.content as? NativeVideoContent {
@ -396,6 +397,10 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
self.requiresDownload = true
var mediaFileStatus: Signal<MediaResourceStatus?, NoError> = .single(nil)
if let contentInfo = item.contentInfo, case let .message(message) = contentInfo {
if Namespaces.Message.allScheduled.contains(message.id.namespace) {
disablePictureInPicture = true
}
var file: TelegramMediaFile?
var isWebpage = false
for m in message.media {
@ -525,7 +530,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
self.zoomableContent = (videoSize, videoNode)
if !isAnimated && !disablePlayerControls {
if !isAnimated && !disablePlayerControls && !disablePictureInPicture {
let rightBarButtonItem = UIBarButtonItem(image: pictureInPictureButtonImage, style: .plain, target: self, action: #selector(self.pictureInPictureButtonPressed))
self._rightBarButtonItem.set(.single(rightBarButtonItem))
}

View file

@ -106,7 +106,7 @@ final class HashtagSearchControllerNode: ASDisplayNode {
insets.top += toolbarHeight - 4.0
let chatSize = CGSize(width: layout.size.width, height: layout.size.height)
transition.updateFrame(node: chatController.displayNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: chatSize))
chatController.containerLayoutUpdated(ContainerViewLayout(size: chatSize, metrics: layout.metrics, intrinsicInsets: UIEdgeInsets(top: insets.top, left: 0.0, bottom: layout.intrinsicInsets.bottom, right: 0.0), safeInsets: layout.safeInsets, statusBarHeight: nil, inputHeight: nil, standardInputHeight: 216.0, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
chatController.containerLayoutUpdated(ContainerViewLayout(size: chatSize, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(top: insets.top, left: 0.0, bottom: layout.intrinsicInsets.bottom, right: 0.0), safeInsets: layout.safeInsets, statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
if chatController.displayNode.supernode == nil {
chatController.viewWillAppear(false)

View file

@ -407,7 +407,7 @@ final class InstantPageSlideshowNode: ASDisplayNode, InstantPageNode {
super.layout()
self.pagerNode.frame = self.bounds
self.pagerNode.containerLayoutUpdated(ContainerViewLayout(size: self.bounds.size, metrics: LayoutMetrics(), intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, standardInputHeight: 216.0, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
self.pagerNode.containerLayoutUpdated(ContainerViewLayout(size: self.bounds.size, metrics: LayoutMetrics(), deviceMetrics: .unknown(screenSize: CGSize(), statusBarHeight: 0.0, onScreenNavigationHeight: nil), intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
self.pageControlNode.layer.transform = CATransform3DIdentity
self.pageControlNode.frame = CGRect(origin: CGPoint(x: 0.0, y: self.bounds.size.height - 20.0), size: CGSize(width: self.bounds.size.width, height: 20.0))

View file

@ -542,16 +542,16 @@ open class ItemListController<Entry: ItemListNodeEntry>: ViewController, KeyShor
}
public func previewingController(from sourceView: UIView, for location: CGPoint) -> (UIViewController, CGRect)? {
guard let layout = self.validLayout else {
guard let layout = self.validLayout, case .phone = layout.deviceMetrics.type else {
return nil
}
let boundsSize = self.view.bounds.size
let contentSize: CGSize
if let metrics = DeviceMetrics.forScreenSize(layout.size) {
contentSize = metrics.previewingContentSize(inLandscape: boundsSize.width > boundsSize.height)
} else {
if case .unknown = layout.deviceMetrics {
contentSize = boundsSize
} else {
contentSize = layout.deviceMetrics.previewingContentSize(inLandscape: boundsSize.width > boundsSize.height)
}
var selectedNode: ItemListItemNode?
@ -567,7 +567,7 @@ open class ItemListController<Entry: ItemListNodeEntry>: ViewController, KeyShor
if let controller = self.previewItemWithTag?(tag) {
if let controller = controller as? ContainableController {
controller.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, standardInputHeight: 216.0, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
controller.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
}
return (controller, sourceRect)
} else {

View file

@ -1359,7 +1359,6 @@ const NSInteger TGMessageImageViewOverlayParticlesCount = 40;
- (void)setCompletedAnimated:(bool)animated;
{
__weak TGMessageImageViewOverlayView *weakSelf = self;
[_contentLayer setCompletedAnimated:animated];
[_progressLayer setNone];
_blurredBackgroundLayer.hidden = _blurless;

View file

@ -56,7 +56,6 @@
galleryController.model = model;
__weak TGModernGalleryController *weakGalleryController = galleryController;
__weak TGMediaPickerGalleryModel *weakModel = model;
[model.interfaceView updateSelectionInterface:1 counterVisible:false animated:false];
model.interfaceView.thumbnailSignalForItem = ^SSignal *(id item)

View file

@ -60,7 +60,6 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16;
CVPixelBufferRef _previousPixelBuffer;
int32_t _repeatingCount;
NSMutableData *_audioBuffer;
int16_t _micLevelPeak;
int _micLevelPeakCount;

View file

@ -14,7 +14,6 @@
#import "TGModernConversationInputMicButton.h"
static const CGFloat TGVideoScrubberMinimumTrimDuration = 1.0f;
static const CGFloat TGVideoScrubberZoomActivationInterval = 0.25f;
static const CGFloat TGVideoScrubberTrimRectEpsilon = 3.0f;
typedef enum

View file

@ -137,10 +137,7 @@ public final class PasscodeEntryController: ViewController {
case .none:
succeed = true
case let .numericalPassword(code, _, _):
succeed = passcode == code
if !succeed {
succeed = convertToArabicNumeralString(passcode) == code
}
succeed = passcode == normalizeArabicNumeralString(code, type: .western)
case let .plaintextPassword(code, _, _):
succeed = passcode == code
}

View file

@ -220,7 +220,7 @@ final class PasscodeEntryKeyboardNode: ASDisplayNode {
if let subnodes = self.subnodes {
for i in 0 ..< subnodes.count {
let subnode = subnodes[i]
var delay: Double = 0.0
var delay: Double = 0.001
if i / 3 == 1 {
delay = 0.05
}

View file

@ -14,98 +14,96 @@ struct PasscodeKeyboardLayout {
let biometricsOffset: CGFloat
let deleteOffset: CGFloat
fileprivate init(layout: ContainerViewLayout, metrics: DeviceMetrics?) {
if let metrics = metrics {
switch metrics {
case .iPhone4:
self.buttonSize = 75.0
self.horizontalSecond = 95.0
self.horizontalThird = 190.0
self.verticalSecond = 88.0
self.verticalThird = 176.0
self.verticalFourth = 264.0
self.size = CGSize(width: 265.0, height: 339.0)
self.topOffset = 122.0
self.biometricsOffset = 0.0
self.deleteOffset = 45.0
case .iPhone5:
self.buttonSize = 75.0
self.horizontalSecond = 95.0
self.horizontalThird = 190.0
self.verticalSecond = 88.0
self.verticalThird = 176.0
self.verticalFourth = 264.0
self.size = CGSize(width: 265.0, height: 339.0)
self.topOffset = 155.0
self.biometricsOffset = 23.0
self.deleteOffset = 20.0
case .iPhone6:
self.buttonSize = 75.0
self.horizontalSecond = 103.0
self.horizontalThird = 206.0
self.verticalSecond = 90.0
self.verticalThird = 180.0
self.verticalFourth = 270.0
self.size = CGSize(width: 281.0, height: 348.0)
self.topOffset = 221.0
self.biometricsOffset = 30.0
self.deleteOffset = 20.0
case .iPhone6Plus:
self.buttonSize = 85.0
self.horizontalSecond = 115.0
self.horizontalThird = 230.0
self.verticalSecond = 100.0
self.verticalThird = 200.0
self.verticalFourth = 300.0
self.size = CGSize(width: 315.0, height: 385.0)
self.topOffset = 226.0
self.biometricsOffset = 30.0
self.deleteOffset = 20.0
case .iPhoneX:
self.buttonSize = 75.0
self.horizontalSecond = 103.0
self.horizontalThird = 206.0
self.verticalSecond = 91.0
self.verticalThird = 182.0
self.verticalFourth = 273.0
self.size = CGSize(width: 281.0, height: 348.0)
self.topOffset = 294.0
self.biometricsOffset = 30.0
self.deleteOffset = 20.0
case .iPhoneXSMax:
self.buttonSize = 85.0
self.horizontalSecond = 115.0
self.horizontalThird = 230.0
self.verticalSecond = 100.0
self.verticalThird = 200.0
self.verticalFourth = 300.0
self.size = CGSize(width: 315.0, height: 385.0)
self.topOffset = 329.0
self.biometricsOffset = 30.0
self.deleteOffset = 20.0
case .iPad, .iPadPro10Inch, .iPadPro11Inch, .iPadPro, .iPadPro3rdGen:
self.buttonSize = 81.0
self.horizontalSecond = 106.0
self.horizontalThird = 212.0
self.verticalSecond = 101.0
self.verticalThird = 202.0
self.verticalFourth = 303.0
self.size = CGSize(width: 293.0, height: 384.0)
self.topOffset = 120.0 + (layout.size.height - self.size.height - 120.0) / 2.0
self.biometricsOffset = 30.0
self.deleteOffset = 80.0
}
} else {
self.buttonSize = 75.0
self.horizontalSecond = 95.0
self.horizontalThird = 190.0
self.verticalSecond = 88.0
self.verticalThird = 176.0
self.verticalFourth = 264.0
self.size = CGSize(width: 265.0, height: 339.0)
self.topOffset = 0.0
self.biometricsOffset = 30.0
self.deleteOffset = 20.0
fileprivate init(layout: ContainerViewLayout) {
switch layout.deviceMetrics {
case .iPhone4:
self.buttonSize = 75.0
self.horizontalSecond = 95.0
self.horizontalThird = 190.0
self.verticalSecond = 88.0
self.verticalThird = 176.0
self.verticalFourth = 264.0
self.size = CGSize(width: 265.0, height: 339.0)
self.topOffset = 122.0
self.biometricsOffset = 0.0
self.deleteOffset = 45.0
case .iPhone5:
self.buttonSize = 75.0
self.horizontalSecond = 95.0
self.horizontalThird = 190.0
self.verticalSecond = 88.0
self.verticalThird = 176.0
self.verticalFourth = 264.0
self.size = CGSize(width: 265.0, height: 339.0)
self.topOffset = 155.0
self.biometricsOffset = 23.0
self.deleteOffset = 20.0
case .iPhone6:
self.buttonSize = 75.0
self.horizontalSecond = 103.0
self.horizontalThird = 206.0
self.verticalSecond = 90.0
self.verticalThird = 180.0
self.verticalFourth = 270.0
self.size = CGSize(width: 281.0, height: 348.0)
self.topOffset = 221.0
self.biometricsOffset = 30.0
self.deleteOffset = 20.0
case .iPhone6Plus:
self.buttonSize = 85.0
self.horizontalSecond = 115.0
self.horizontalThird = 230.0
self.verticalSecond = 100.0
self.verticalThird = 200.0
self.verticalFourth = 300.0
self.size = CGSize(width: 315.0, height: 385.0)
self.topOffset = 226.0
self.biometricsOffset = 30.0
self.deleteOffset = 20.0
case .iPhoneX:
self.buttonSize = 75.0
self.horizontalSecond = 103.0
self.horizontalThird = 206.0
self.verticalSecond = 91.0
self.verticalThird = 182.0
self.verticalFourth = 273.0
self.size = CGSize(width: 281.0, height: 348.0)
self.topOffset = 294.0
self.biometricsOffset = 30.0
self.deleteOffset = 20.0
case .iPhoneXSMax:
self.buttonSize = 85.0
self.horizontalSecond = 115.0
self.horizontalThird = 230.0
self.verticalSecond = 100.0
self.verticalThird = 200.0
self.verticalFourth = 300.0
self.size = CGSize(width: 315.0, height: 385.0)
self.topOffset = 329.0
self.biometricsOffset = 30.0
self.deleteOffset = 20.0
case .iPad, .iPadPro10Inch, .iPadPro11Inch, .iPadPro, .iPadPro3rdGen:
self.buttonSize = 81.0
self.horizontalSecond = 106.0
self.horizontalThird = 212.0
self.verticalSecond = 101.0
self.verticalThird = 202.0
self.verticalFourth = 303.0
self.size = CGSize(width: 293.0, height: 384.0)
self.topOffset = 120.0 + (layout.size.height - self.size.height - 120.0) / 2.0
self.biometricsOffset = 30.0
self.deleteOffset = 80.0
case .unknown:
self.buttonSize = 75.0
self.horizontalSecond = 95.0
self.horizontalThird = 190.0
self.verticalSecond = 88.0
self.verticalThird = 176.0
self.verticalFourth = 264.0
self.size = CGSize(width: 265.0, height: 339.0)
self.topOffset = 0.0
self.biometricsOffset = 30.0
self.deleteOffset = 20.0
}
}
}
@ -120,51 +118,46 @@ struct PasscodeLayout {
init(layout: ContainerViewLayout) {
self.layout = layout
let metrics = DeviceMetrics.forScreenSize(layout.size)
self.keyboard = PasscodeKeyboardLayout(layout: layout, metrics: metrics)
if let metrics = metrics {
switch metrics {
case .iPhone4:
self.titleOffset = 30.0
self.subtitleOffset = -13.0
self.inputFieldOffset = 70.0
case .iPhone5:
self.titleOffset = 50.0
self.subtitleOffset = -7.0
self.inputFieldOffset = 90.0
case .iPhone6:
self.titleOffset = 100.0
self.subtitleOffset = -3.0
self.inputFieldOffset = 144.0
case .iPhone6Plus:
self.titleOffset = 112.0
self.subtitleOffset = -6.0
self.inputFieldOffset = 156.0
case .iPhoneX:
self.titleOffset = 162.0
self.subtitleOffset = 0.0
self.inputFieldOffset = 206.0
case .iPhoneXSMax:
self.titleOffset = 180.0
self.subtitleOffset = 0.0
self.inputFieldOffset = 226.0
case .iPad, .iPadPro10Inch, .iPadPro11Inch, .iPadPro, .iPadPro3rdGen:
self.titleOffset = self.keyboard.topOffset - 120.0
self.subtitleOffset = -2.0
self.inputFieldOffset = self.keyboard.topOffset - 76.0
}
} else {
self.titleOffset = 100.0
self.subtitleOffset = 0.0
self.inputFieldOffset = 140.0
self.keyboard = PasscodeKeyboardLayout(layout: layout)
switch layout.deviceMetrics {
case .iPhone4:
self.titleOffset = 30.0
self.subtitleOffset = -13.0
self.inputFieldOffset = 70.0
case .iPhone5:
self.titleOffset = 50.0
self.subtitleOffset = -7.0
self.inputFieldOffset = 90.0
case .iPhone6:
self.titleOffset = 100.0
self.subtitleOffset = -3.0
self.inputFieldOffset = 144.0
case .iPhone6Plus:
self.titleOffset = 112.0
self.subtitleOffset = -6.0
self.inputFieldOffset = 156.0
case .iPhoneX:
self.titleOffset = 162.0
self.subtitleOffset = 0.0
self.inputFieldOffset = 206.0
case .iPhoneXSMax:
self.titleOffset = 180.0
self.subtitleOffset = 0.0
self.inputFieldOffset = 226.0
case .iPad, .iPadPro10Inch, .iPadPro11Inch, .iPadPro, .iPadPro3rdGen:
self.titleOffset = self.keyboard.topOffset - 120.0
self.subtitleOffset = -2.0
self.inputFieldOffset = self.keyboard.topOffset - 76.0
case .unknown:
self.titleOffset = 100.0
self.subtitleOffset = 0.0
self.inputFieldOffset = 140.0
}
}
init(layout: ContainerViewLayout, titleOffset: CGFloat, subtitleOffset: CGFloat, inputFieldOffset: CGFloat) {
self.layout = layout
let metrics = DeviceMetrics.forScreenSize(layout.size)
self.keyboard = PasscodeKeyboardLayout(layout: layout, metrics: metrics)
self.keyboard = PasscodeKeyboardLayout(layout: layout)
self.titleOffset = titleOffset
self.subtitleOffset = subtitleOffset
self.inputFieldOffset = inputFieldOffset

View file

@ -408,7 +408,7 @@ public class AvatarGalleryController: ViewController {
self.galleryNode.setControlsHidden(true, animated: false)
if let centralItemNode = self.galleryNode.pager.centralItemNode(), let itemSize = centralItemNode.contentSize() {
self.preferredContentSize = itemSize.aspectFitted(self.view.bounds.size)
self.containerLayoutUpdated(ContainerViewLayout(size: self.preferredContentSize, metrics: LayoutMetrics(), intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, standardInputHeight: 216.0, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
self.containerLayoutUpdated(ContainerViewLayout(size: self.preferredContentSize, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
centralItemNode.activateAsInitial()
}
}

View file

@ -96,7 +96,7 @@ public final class SearchDisplayController {
self.containerLayout = (layout, navigationBarFrame.maxY)
transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(), size: layout.size))
self.contentNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: LayoutMetrics(), intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: nil, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), navigationBarHeight: navigationBarFrame.maxY, transition: transition)
self.contentNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: nil, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), navigationBarHeight: navigationBarFrame.maxY, transition: transition)
}
public func activate(insertSubnode: (ASDisplayNode, Bool) -> Void, placeholder: SearchBarPlaceholderNode) {
@ -107,7 +107,7 @@ public final class SearchDisplayController {
insertSubnode(self.contentNode, false)
self.contentNode.frame = CGRect(origin: CGPoint(), size: layout.size)
self.contentNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: LayoutMetrics(), intrinsicInsets: UIEdgeInsets(), safeInsets: layout.safeInsets, statusBarHeight: nil, inputHeight: nil, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), navigationBarHeight: navigationBarHeight, transition: .immediate)
self.contentNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: layout.safeInsets, statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), navigationBarHeight: navigationBarHeight, transition: .immediate)
let initialTextBackgroundFrame = placeholder.convert(placeholder.backgroundNode.frame, to: nil)

View file

@ -424,10 +424,7 @@ public func passcodeOptionsAccessController(context: AccountContext, animateIn:
case .none:
succeed = true
case let .numericalPassword(code, _, _):
succeed = passcode == code
if !succeed {
succeed = convertToArabicNumeralString(passcode) == code
}
succeed = passcode == normalizeArabicNumeralString(code, type: .western)
case let .plaintextPassword(code, _, _):
succeed = passcode == code
}

View file

@ -704,11 +704,9 @@ public func settingsController(context: AccountContext, accountManager: AccountM
updating = $0.updatingAvatar != nil
return $0
}
if updating {
return
}
let _ = (contextValue.get()
|> deliverOnMainQueue
|> take(1)).start(next: { context in

View file

@ -9,6 +9,7 @@ import AvatarNode
import AccountContext
import LocalizedPeerData
private let animationDurationFactor: Double = 1.0
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 16.0)!
private protocol AbstractSwitchAccountItemNode {
@ -216,6 +217,8 @@ final class TabBarAccountSwitchControllerNode: ViewControllerTracingNode {
private let cancel: () -> Void
private let effectView: UIVisualEffectView
private var propertyAnimator: AnyObject?
private var displayLinkAnimator: DisplayLinkAnimator?
private let dimNode: ASDisplayNode
private let contentContainerNode: ASDisplayNode
@ -284,41 +287,44 @@ final class TabBarAccountSwitchControllerNode: ViewControllerTracingNode {
self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
}
deinit {
if let propertyAnimator = self.propertyAnimator {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
let propertyAnimator = propertyAnimator as? UIViewPropertyAnimator
propertyAnimator?.stopAnimation(true)
}
}
}
func animateIn() {
UIView.animate(withDuration: 0.3, animations: {
if #available(iOS 9.0, *) {
if self.presentationData.theme.rootController.keyboardColor == .dark {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.effectView.effect = UIBlurEffect(style: .regular)
if self.effectView.subviews.count == 2 {
self.effectView.subviews[1].isHidden = true
}
} else {
self.effectView.effect = UIBlurEffect(style: .dark)
}
} else {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.effectView.effect = UIBlurEffect(style: .regular)
} else {
self.effectView.effect = UIBlurEffect(style: .light)
}
}
} else {
self.effectView.alpha = 1.0
self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
if #available(iOS 10.0, *) {
if let propertyAnimator = self.propertyAnimator {
let propertyAnimator = propertyAnimator as? UIViewPropertyAnimator
propertyAnimator?.stopAnimation(true)
}
}, completion: { [weak self] _ in
guard let strongSelf = self else {
return
self.propertyAnimator = UIViewPropertyAnimator(duration: 0.2 * animationDurationFactor, curve: .easeInOut, animations: { [weak self] in
self?.effectView.effect = makeCustomZoomBlurEffect()
})
}
if let _ = self.propertyAnimator {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.displayLinkAnimator = DisplayLinkAnimator(duration: 0.2 * animationDurationFactor, from: 0.0, to: 1.0, update: { [weak self] value in
(self?.propertyAnimator as? UIViewPropertyAnimator)?.fractionComplete = value
}, completion: {
})
}
if strongSelf.presentationData.theme.rootController.keyboardColor == .dark {
if strongSelf.effectView.subviews.count == 2 {
strongSelf.effectView.subviews[1].isHidden = true
}
}
})
self.effectView.subviews[1].layer.removeAnimation(forKey: "backgroundColor")
self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
self.contentContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.08)
} else {
UIView.animate(withDuration: 0.2 * animationDurationFactor, animations: {
self.effectView.effect = makeCustomZoomBlurEffect()
}, completion: { _ in
})
}
self.contentContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
if let _ = self.validLayout, let sourceNode = self.sourceNodes.first {
let sourceFrame = sourceNode.view.convert(sourceNode.bounds, to: self.view)
self.contentContainerNode.layer.animateFrame(from: sourceFrame, to: self.contentContainerNode.frame, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
@ -354,23 +360,52 @@ final class TabBarAccountSwitchControllerNode: ViewControllerTracingNode {
}
}
UIView.animate(withDuration: 0.3, animations: {
if #available(iOS 9.0, *) {
self.effectView.effect = nil
} else {
self.effectView.alpha = 0.0
if #available(iOS 10.0, *) {
if let propertyAnimator = self.propertyAnimator {
let propertyAnimator = propertyAnimator as? UIViewPropertyAnimator
propertyAnimator?.stopAnimation(true)
}
}, completion: { [weak self] _ in
if let strongSelf = self {
for sourceNode in strongSelf.sourceNodes {
sourceNode.alpha = 1.0
self.propertyAnimator = UIViewPropertyAnimator(duration: 0.2, curve: .easeInOut, animations: { [weak self] in
self?.effectView.effect = nil
})
}
if let _ = self.propertyAnimator {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.displayLinkAnimator = DisplayLinkAnimator(duration: 0.2 * animationDurationFactor, from: 0.0, to: 0.999, update: { [weak self] value in
(self?.propertyAnimator as? UIViewPropertyAnimator)?.fractionComplete = value
}, completion: { [weak self] in
if let strongSelf = self {
for sourceNode in strongSelf.sourceNodes {
sourceNode.alpha = 1.0
}
}
completedEffect = true
intermediateCompletion()
})
}
self.effectView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.05 * animationDurationFactor, delay: 0.15, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false)
} else {
UIView.animate(withDuration: 0.21 * animationDurationFactor, animations: {
if #available(iOS 9.0, *) {
self.effectView.effect = nil
} else {
self.effectView.alpha = 0.0
}
}
completedEffect = true
intermediateCompletion()
})
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
}, completion: { [weak self] _ in
if let strongSelf = self {
for sourceNode in strongSelf.sourceNodes {
sourceNode.alpha = 1.0
}
}
completedEffect = true
intermediateCompletion()
})
}
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
self.contentContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.12, removeOnCompletion: false, completion: { _ in
})
if let _ = self.validLayout, let sourceNode = self.sourceNodes.first {

View file

@ -26,6 +26,7 @@ private final class EditThemeControllerArguments {
private enum EditThemeEntryTag: ItemListItemTag {
case title
case slug
func isEqual(to other: ItemListItemTag) -> Bool {
if let other = other as? EditThemeEntryTag, self == other {
@ -46,7 +47,7 @@ private enum EditThemeControllerEntry: ItemListNodeEntry {
case slug(PresentationTheme, PresentationStrings, String, String, Bool)
case slugInfo(PresentationTheme, String)
case chatPreviewHeader(PresentationTheme, String)
case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder)
case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, [ChatPreviewMessageItem])
case uploadTheme(PresentationTheme, String)
case uploadInfo(PresentationTheme, String)
@ -104,8 +105,8 @@ private enum EditThemeControllerEntry: ItemListNodeEntry {
} else {
return false
}
case let .chatPreview(lhsTheme, lhsComponentTheme, lhsWallpaper, lhsFontSize, lhsStrings, lhsTimeFormat, lhsNameOrder):
if case let .chatPreview(rhsTheme, rhsComponentTheme, rhsWallpaper, rhsFontSize, rhsStrings, rhsTimeFormat, rhsNameOrder) = rhs, lhsComponentTheme === rhsComponentTheme, lhsTheme === rhsTheme, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsStrings === rhsStrings, lhsTimeFormat == rhsTimeFormat, lhsNameOrder == rhsNameOrder {
case let .chatPreview(lhsTheme, lhsComponentTheme, lhsWallpaper, lhsFontSize, lhsStrings, lhsTimeFormat, lhsNameOrder, lhsItems):
if case let .chatPreview(rhsTheme, rhsComponentTheme, rhsWallpaper, rhsFontSize, rhsStrings, rhsTimeFormat, rhsNameOrder, rhsItems) = rhs, lhsComponentTheme === rhsComponentTheme, lhsTheme === rhsTheme, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsStrings === rhsStrings, lhsTimeFormat == rhsTimeFormat, lhsNameOrder == rhsNameOrder, lhsItems == rhsItems {
return true
} else {
return false
@ -142,7 +143,7 @@ private enum EditThemeControllerEntry: ItemListNodeEntry {
})
case let .slug(theme, strings, title, text, enabled):
return ItemListSingleLineInputItem(theme: theme, strings: strings, title: NSAttributedString(string: "t.me/addtheme/", textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: title, type: .username, clearType: .onFocus, enabled: enabled, sectionId: self.section, textUpdated: { value in
return ItemListSingleLineInputItem(theme: theme, strings: strings, title: NSAttributedString(string: "t.me/addtheme/", textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: title, type: .username, clearType: .onFocus, enabled: enabled, tag: EditThemeEntryTag.slug, sectionId: self.section, textUpdated: { value in
arguments.updateState { current in
var state = current
state.slug = value
@ -155,8 +156,8 @@ private enum EditThemeControllerEntry: ItemListNodeEntry {
return ItemListTextItem(theme: theme, text: .markdown(text), sectionId: self.section)
case let .chatPreviewHeader(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .chatPreview(theme, componentTheme, wallpaper, fontSize, strings, dateTimeFormat, nameDisplayOrder):
return ThemeSettingsChatPreviewItem(context: arguments.context, theme: theme, componentTheme: componentTheme, strings: strings, sectionId: self.section, fontSize: fontSize, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder)
case let .chatPreview(theme, componentTheme, wallpaper, fontSize, strings, dateTimeFormat, nameDisplayOrder, items):
return ThemeSettingsChatPreviewItem(context: arguments.context, theme: theme, componentTheme: componentTheme, strings: strings, sectionId: self.section, fontSize: fontSize, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, messageItems: items)
case let .uploadTheme(theme, text):
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
arguments.openFile()
@ -189,29 +190,51 @@ private func editThemeControllerEntries(presentationData: PresentationData, stat
}
entries.append(.title(presentationData.theme, presentationData.strings, presentationData.strings.EditTheme_Title, state.title, isCreate))
if case .edit = state.mode {
entries.append(.slug(presentationData.theme, presentationData.strings, presentationData.strings.EditTheme_ShortLink, state.slug, true))
entries.append(.slugInfo(presentationData.theme, presentationData.strings.EditTheme_ShortLinkInfo))
}
entries.append(.chatPreviewHeader(presentationData.theme, presentationData.strings.EditTheme_Preview.uppercased()))
entries.append(.chatPreview(presentationData.theme, previewTheme, previewTheme.chat.defaultWallpaper, presentationData.fontSize, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder))
let infoText: String
let uploadText: String
let uploadInfo: String
let previewIncomingReplyName: String
let previewIncomingReplyText: String
let previewIncomingText: String
let previewOutgoingText: String
switch state.mode {
case .create:
infoText = presentationData.strings.EditTheme_Create_TopInfo
uploadText = presentationData.strings.EditTheme_UploadNewTheme
uploadInfo = presentationData.strings.EditTheme_UploadNewInfo
uploadInfo = presentationData.strings.EditTheme_Create_BottomInfo
previewIncomingReplyName = presentationData.strings.EditTheme_Create_Preview_IncomingReplyName
previewIncomingReplyText = presentationData.strings.EditTheme_Create_Preview_IncomingReplyText
previewIncomingText = presentationData.strings.EditTheme_Create_Preview_IncomingText
previewOutgoingText = presentationData.strings.EditTheme_Create_Preview_OutgoingText
case let .edit(theme):
if let _ = theme.theme.file {
infoText = presentationData.strings.EditTheme_Edit_TopInfo
uploadText = presentationData.strings.EditTheme_UploadEditedTheme
uploadInfo = presentationData.strings.EditTheme_UploadEditedInfo
uploadInfo = presentationData.strings.EditTheme_Edit_BottomInfo
previewIncomingReplyName = presentationData.strings.EditTheme_Expand_Preview_IncomingReplyName
previewIncomingReplyText = presentationData.strings.EditTheme_Expand_Preview_IncomingReplyText
previewIncomingText = presentationData.strings.EditTheme_Expand_Preview_IncomingText
previewOutgoingText = presentationData.strings.EditTheme_Expand_Preview_OutgoingText
} else {
infoText = presentationData.strings.EditTheme_Expand_TopInfo
uploadText = presentationData.strings.EditTheme_UploadNewTheme
uploadInfo = presentationData.strings.EditTheme_UploadNewInfo
uploadInfo = presentationData.strings.EditTheme_Expand_BottomInfo
previewIncomingReplyName = presentationData.strings.EditTheme_Edit_Preview_IncomingReplyName
previewIncomingReplyText = presentationData.strings.EditTheme_Edit_Preview_IncomingReplyText
previewIncomingText = presentationData.strings.EditTheme_Edit_Preview_IncomingText
previewOutgoingText = presentationData.strings.EditTheme_Edit_Preview_OutgoingText
}
}
if case .edit = state.mode {
entries.append(.slug(presentationData.theme, presentationData.strings, presentationData.strings.EditTheme_ShortLink, state.slug, true))
}
entries.append(.slugInfo(presentationData.theme, infoText))
entries.append(.chatPreviewHeader(presentationData.theme, presentationData.strings.EditTheme_Preview.uppercased()))
entries.append(.chatPreview(presentationData.theme, previewTheme, previewTheme.chat.defaultWallpaper, presentationData.fontSize, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, [ChatPreviewMessageItem(outgoing: false, reply: (previewIncomingReplyName, previewIncomingReplyText), text: previewIncomingText), ChatPreviewMessageItem(outgoing: true, reply: nil, text: previewOutgoingText)]))
entries.append(.uploadTheme(presentationData.theme, uploadText))
entries.append(.uploadInfo(presentationData.theme, uploadInfo))
@ -225,20 +248,20 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
case .create:
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
initialState = EditThemeControllerState(mode: mode, title: "", slug: "", updatedTheme: nil, updating: false)
previewThemePromise.set(.single(presentationData.theme.withUpdated(name: "", author: nil, defaultWallpaper: presentationData.chatWallpaper)))
previewThemePromise.set(.single(presentationData.theme.withUpdated(name: "", defaultWallpaper: presentationData.chatWallpaper)))
case let .edit(info):
if let file = info.theme.file, let path = context.sharedContext.accountManager.mediaBox.completedResourcePath(file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path)), let theme = makePresentationTheme(data: data, resolvedWallpaper: info.resolvedWallpaper) {
if case let .file(file) = theme.chat.defaultWallpaper, file.id == 0 {
previewThemePromise.set(cachedWallpaper(account: context.account, slug: file.slug)
|> map ({ wallpaper -> PresentationTheme in
if let wallpaper = wallpaper {
return theme.withUpdated(name: nil, author: nil, defaultWallpaper: wallpaper.wallpaper)
return theme.withUpdated(name: nil, defaultWallpaper: wallpaper.wallpaper)
} else {
return theme.withUpdated(name: nil, author: nil, defaultWallpaper: .color(Int32(bitPattern: theme.chatList.backgroundColor.rgb)))
return theme.withUpdated(name: nil, defaultWallpaper: .color(Int32(bitPattern: theme.chatList.backgroundColor.rgb)))
}
}))
} else {
previewThemePromise.set(.single(theme.withUpdated(name: nil, author: nil, defaultWallpaper: info.resolvedWallpaper)))
previewThemePromise.set(.single(theme.withUpdated(name: nil, defaultWallpaper: info.resolvedWallpaper)))
}
} else {
previewThemePromise.set(.single(context.sharedContext.currentPresentationData.with { $0 }.theme))
@ -254,6 +277,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
var presentControllerImpl: ((ViewController, Any?) -> Void)?
var dismissImpl: (() -> Void)?
var dismissInputImpl: (() -> Void)?
var errorImpl: ((EditThemeEntryTag) -> Void)?
let arguments = EditThemeControllerArguments(context: context, updateState: { f in
updateState(f)
@ -281,7 +305,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
}
}
|> deliverOnMainQueue).start(next: { wallpaper in
let updatedTheme = theme.withUpdated(name: nil, author: nil, defaultWallpaper: wallpaper)
let updatedTheme = theme.withUpdated(name: nil, defaultWallpaper: wallpaper)
updateState { current in
var state = current
previewThemePromise.set(.single(updatedTheme))
@ -329,6 +353,16 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
}
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: isComplete, action: {
if state.title.count > 128 {
errorImpl?(.title)
return
}
if case .create = mode {
} else if state.slug.count < 5 || state.slug.count > 64 {
errorImpl?(.slug)
return
}
dismissInputImpl?()
arguments.updateState { current in
var state = current
@ -359,14 +393,14 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
let theme: PresentationTheme?
let hasCustomFile: Bool
if let updatedTheme = state.updatedTheme {
theme = updatedTheme.withUpdated(name: state.title, author: "", defaultWallpaper: nil)
theme = updatedTheme.withUpdated(name: state.title, defaultWallpaper: nil)
hasCustomFile = true
} else {
if case let .edit(info) = mode, let _ = info.theme.file {
theme = nil
hasCustomFile = true
} else {
theme = previewTheme.withUpdated(name: state.title, author: "", defaultWallpaper: nil)
theme = previewTheme.withUpdated(name: state.title, defaultWallpaper: nil)
hasCustomFile = false
}
}
@ -489,6 +523,10 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
state.updating = false
return state
}
if case .slugOccupied = error {
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.EditTheme_ErrorLinkTaken, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
}
})
}
})
@ -523,5 +561,14 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
dismissInputImpl = { [weak controller] in
controller?.view.endEditing(true)
}
let hapticFeedback = HapticFeedback()
errorImpl = { [weak controller] targetTag in
hapticFeedback.error()
controller?.forEachItemNode { itemNode in
if let itemNode = itemNode as? ItemListSingleLineInputItemNode, let tag = itemNode.tag, tag.isEqual(to: targetTag) {
itemNode.animateError()
}
}
}
return controller
}

View file

@ -372,9 +372,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
self.toolbarNode.updateLayout(size: CGSize(width: layout.size.width, height: 49.0), layout: layout, transition: transition)
var bottomInset = toolbarHeight
let metrics = DeviceMetrics.forScreenSize(layout.size)
let standardInputHeight = metrics?.standardInputHeight(inLandscape: false) ?? 216.0
let height = standardInputHeight - bottomInset + 47.0
let standardInputHeight = layout.deviceMetrics.keyboardHeight(inLandscape: false)
let height = max(standardInputHeight, layout.inputHeight ?? 0.0) - bottomInset + 47.0
let colorPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - bottomInset - height), size: CGSize(width: layout.size.width, height: height))
bottomInset += height

View file

@ -34,6 +34,7 @@ public final class ThemePreviewController: ViewController {
private var presentationData: PresentationData
private var presentationDataDisposable: Disposable?
private var disposable: Disposable?
private var applyDisposable = MetaDisposable()
public init(context: AccountContext, previewTheme: PresentationTheme, source: ThemePreviewSource) {
@ -61,19 +62,22 @@ public final class ThemePreviewController: ViewController {
themeName = previewTheme.name.string
}
if let author = previewTheme.author {
let titleView = CounterContollerTitleView(theme: self.previewTheme)
titleView.title = CounterContollerTitle(title: themeName, counter: author)
self.navigationItem.titleView = titleView
} else {
// if
// if let author = previewTheme.author {
// let titleView = CounterContollerTitleView(theme: self.previewTheme)
// titleView.title = CounterContollerTitle(title: themeName, counter: author)
// self.navigationItem.titleView = titleView
// } else {
self.title = themeName
}
// }
self.statusBar.statusBarStyle = self.previewTheme.rootController.statusBarStyle.style
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: self.previewTheme.rootController.navigationBar.accentTextColor), style: .plain, target: self, action: #selector(self.actionPressed))
//self.disposable
self.presentationDataDisposable = (context.sharedContext.presentationData
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
if let strongSelf = self {
@ -88,6 +92,7 @@ public final class ThemePreviewController: ViewController {
deinit {
self.presentationDataDisposable?.dispose()
self.disposable?.dispose()
self.applyDisposable.dispose()
}

View file

@ -10,6 +10,27 @@ import TelegramUIPreferences
import ItemListUI
import AccountContext
struct ChatPreviewMessageItem: Equatable {
static func == (lhs: ChatPreviewMessageItem, rhs: ChatPreviewMessageItem) -> Bool {
if lhs.outgoing != rhs.outgoing {
return false
}
if let lhsReply = lhs.reply, let rhsReply = rhs.reply, lhsReply.0 != rhsReply.0 || lhsReply.1 != rhsReply.1 {
return false
} else if (lhs.reply == nil) != (rhs.reply == nil) {
return false
}
if lhs.text != rhs.text {
return false
}
return true
}
let outgoing: Bool
let reply: (String, String)?
let text: String
}
class ThemeSettingsChatPreviewItem: ListViewItem, ItemListItem {
let context: AccountContext
let theme: PresentationTheme
@ -20,8 +41,9 @@ class ThemeSettingsChatPreviewItem: ListViewItem, ItemListItem {
let wallpaper: TelegramWallpaper
let dateTimeFormat: PresentationDateTimeFormat
let nameDisplayOrder: PresentationPersonNameOrder
let messageItems: [ChatPreviewMessageItem]
init(context: AccountContext, theme: PresentationTheme, componentTheme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, fontSize: PresentationFontSize, wallpaper: TelegramWallpaper, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder) {
init(context: AccountContext, theme: PresentationTheme, componentTheme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, fontSize: PresentationFontSize, wallpaper: TelegramWallpaper, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, messageItems: [ChatPreviewMessageItem]) {
self.context = context
self.theme = theme
self.componentTheme = componentTheme
@ -31,6 +53,7 @@ class ThemeSettingsChatPreviewItem: ListViewItem, ItemListItem {
self.wallpaper = wallpaper
self.dateTimeFormat = dateTimeFormat
self.nameDisplayOrder = nameDisplayOrder
self.messageItems = messageItems
}
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
@ -73,9 +96,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
private let bottomStripeNode: ASDisplayNode
private let containerNode: ASDisplayNode
private var messageNode1: ListViewItemNode?
private var messageNode2: ListViewItemNode?
private var messageNodes: [ListViewItemNode]?
private var item: ThemeSettingsChatPreviewItem?
@ -103,8 +124,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
func asyncLayout() -> (_ item: ThemeSettingsChatPreviewItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
let currentItem = self.item
let currentNode1 = self.messageNode1
let currentNode2 = self.messageNode2
let currentNodes = self.messageNodes
return { item, params, neighbors in
var updatedBackgroundImage: UIImage?
@ -116,65 +136,57 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
let separatorHeight = UIScreenPixel
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: 1)
var peers = SimpleDictionary<PeerId, Peer>()
var messages = SimpleDictionary<MessageId, Message>()
peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: item.strings.Appearance_PreviewReplyAuthor, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
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: [])
let message2 = 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: [])
let message1 = 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.context.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: [])
let item2 = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, message: message2, theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil)
let item1 = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, message: message1, theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil)
var node1: ListViewItemNode?
if let current = currentNode1 {
node1 = current
item1.updateNode(async: { $0() }, node: { return current }, params: params, previousItem: nil, nextItem: nil, animation: .None, completion: { (layout, apply) in
let nodeFrame = CGRect(origin: current.frame.origin, size: CGSize(width: layout.size.width, height: layout.size.height))
current.contentSize = layout.contentSize
current.insets = layout.insets
current.frame = nodeFrame
apply(ListViewItemApply(isOnScreen: true))
})
} else {
item1.nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: true, previousItem: nil, nextItem: nil, completion: { node, apply in
node1 = node
apply().1(ListViewItemApply(isOnScreen: true))
})
let otherPeerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: 2)
var items: [ListViewItem] = []
for messageItem in item.messageItems.reversed() {
var peers = SimpleDictionary<PeerId, Peer>()
var messages = SimpleDictionary<MessageId, Message>()
let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3)
if let (author, text) = messageItem.reply {
peers[peerId] = TelegramUser(id: peerId, accessHash: nil, firstName: author, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
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: text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
}
let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: messageItem.outgoing ? otherPeerId : peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: messageItem.outgoing ? TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) : nil, text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId)] : [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, message: message, theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil))
}
var node2: ListViewItemNode?
if let current = currentNode2 {
node2 = current
item2.updateNode(async: { $0() }, node: { return current }, params: params, previousItem: nil, nextItem: nil, animation: .None, completion: { (layout, apply) in
let nodeFrame = CGRect(origin: current.frame.origin, size: CGSize(width: layout.size.width, height: layout.size.height))
current.contentSize = layout.contentSize
current.insets = layout.insets
current.frame = nodeFrame
apply(ListViewItemApply(isOnScreen: true))
})
var nodes: [ListViewItemNode] = []
if let messageNodes = currentNodes {
nodes = messageNodes
for i in 0 ..< items.count {
let itemNode = messageNodes[i]
items[i].updateNode(async: { $0() }, node: {
return itemNode
}, params: params, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], animation: .None, completion: { (layout, apply) in
let nodeFrame = CGRect(origin: itemNode.frame.origin, size: CGSize(width: layout.size.width, height: layout.size.height))
itemNode.contentSize = layout.contentSize
itemNode.insets = layout.insets
itemNode.frame = nodeFrame
itemNode.isUserInteractionEnabled = false
apply(ListViewItemApply(isOnScreen: true))
})
}
} else {
item2.nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: true, previousItem: nil, nextItem: nil, completion: { node, apply in
node2 = node
apply().1(ListViewItemApply(isOnScreen: true))
})
var messageNodes: [ListViewItemNode] = []
for i in 0 ..< items.count {
var itemNode: ListViewItemNode?
items[i].nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: false, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], completion: { node, apply in
itemNode = node
apply().1(ListViewItemApply(isOnScreen: true))
})
itemNode!.isUserInteractionEnabled = false
messageNodes.append(itemNode!)
}
nodes = messageNodes
}
var contentSize = CGSize(width: params.width, height: 4.0 + 4.0)
if let node1 = node1 {
contentSize.height += node1.frame.size.height
}
if let node2 = node2 {
contentSize.height += node2.frame.size.height
for node in nodes {
contentSize.height += node.frame.size.height
}
insets = itemListNeighborsGroupedInsets(neighbors)
@ -187,23 +199,14 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode {
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize)
strongSelf.messageNodes = nodes
var topOffset: CGFloat = 4.0
if let node1 = node1 {
strongSelf.messageNode1 = node1
if node1.supernode == nil {
strongSelf.containerNode.addSubnode(node1)
for node in nodes {
if node.supernode == nil {
strongSelf.containerNode.addSubnode(node)
}
node1.updateFrame(CGRect(origin: CGPoint(x: 0.0, y: topOffset), size: node1.frame.size), within: layoutSize)
topOffset += node1.frame.size.height
}
if let node2 = node2 {
strongSelf.messageNode2 = node2
if node2.supernode == nil {
strongSelf.containerNode.addSubnode(node2)
}
node2.updateFrame(CGRect(origin: CGPoint(x: 0.0, y: topOffset), size: node2.frame.size), within: layoutSize)
topOffset += node2.frame.size.height
node.updateFrame(CGRect(origin: CGPoint(x: 0.0, y: topOffset), size: node.frame.size), within: layoutSize)
topOffset += node.frame.size.height
}
if let updatedBackgroundImage = updatedBackgroundImage {

View file

@ -94,7 +94,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
case themeListHeader(PresentationTheme, String)
case fontSizeHeader(PresentationTheme, String)
case fontSize(PresentationTheme, PresentationFontSize)
case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder)
case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, [ChatPreviewMessageItem])
case wallpaper(PresentationTheme, String)
case accentColor(PresentationTheme, PresentationThemeReference, String, PresentationThemeAccentColor?)
case autoNightTheme(PresentationTheme, String, String)
@ -156,8 +156,8 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
static func ==(lhs: ThemeSettingsControllerEntry, rhs: ThemeSettingsControllerEntry) -> Bool {
switch lhs {
case let .chatPreview(lhsTheme, lhsComponentTheme, lhsWallpaper, lhsFontSize, lhsStrings, lhsTimeFormat, lhsNameOrder):
if case let .chatPreview(rhsTheme, rhsComponentTheme, rhsWallpaper, rhsFontSize, rhsStrings, rhsTimeFormat, rhsNameOrder) = rhs, lhsComponentTheme === rhsComponentTheme, lhsTheme === rhsTheme, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsStrings === rhsStrings, lhsTimeFormat == rhsTimeFormat, lhsNameOrder == rhsNameOrder {
case let .chatPreview(lhsTheme, lhsComponentTheme, lhsWallpaper, lhsFontSize, lhsStrings, lhsTimeFormat, lhsNameOrder, lhsItems):
if case let .chatPreview(rhsTheme, rhsComponentTheme, rhsWallpaper, rhsFontSize, rhsStrings, rhsTimeFormat, rhsNameOrder, rhsItems) = rhs, lhsComponentTheme === rhsComponentTheme, lhsTheme === rhsTheme, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsStrings === rhsStrings, lhsTimeFormat == rhsTimeFormat, lhsNameOrder == rhsNameOrder, lhsItems == rhsItems {
return true
} else {
return false
@ -255,8 +255,8 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
return ThemeSettingsFontSizeItem(theme: theme, fontSize: fontSize, sectionId: self.section, updated: { value in
arguments.selectFontSize(value)
}, tag: ThemeSettingsEntryTag.fontSize)
case let .chatPreview(theme, componentTheme, wallpaper, fontSize, strings, dateTimeFormat, nameDisplayOrder):
return ThemeSettingsChatPreviewItem(context: arguments.context, theme: theme, componentTheme: componentTheme, strings: strings, sectionId: self.section, fontSize: fontSize, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder)
case let .chatPreview(theme, componentTheme, wallpaper, fontSize, strings, dateTimeFormat, nameDisplayOrder, items):
return ThemeSettingsChatPreviewItem(context: arguments.context, theme: theme, componentTheme: componentTheme, strings: strings, sectionId: self.section, fontSize: fontSize, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, messageItems: items)
case let .wallpaper(theme, text):
return ItemListDisclosureItem(theme: theme, title: text, label: "", sectionId: self.section, style: .blocks, action: {
arguments.openWallpaperSettings()
@ -333,7 +333,7 @@ private func themeSettingsControllerEntries(presentationData: PresentationData,
var entries: [ThemeSettingsControllerEntry] = []
entries.append(.themeListHeader(presentationData.theme, strings.Appearance_ColorTheme.uppercased()))
entries.append(.chatPreview(presentationData.theme, theme, wallpaper, fontSize, presentationData.strings, dateTimeFormat, presentationData.nameDisplayOrder))
entries.append(.chatPreview(presentationData.theme, theme, wallpaper, fontSize, presentationData.strings, dateTimeFormat, presentationData.nameDisplayOrder, [ChatPreviewMessageItem(outgoing: false, reply: (presentationData.strings.Appearance_PreviewReplyAuthor, presentationData.strings.Appearance_PreviewReplyText), text: presentationData.strings.Appearance_PreviewIncomingText), ChatPreviewMessageItem(outgoing: true, reply: nil, text: presentationData.strings.Appearance_PreviewOutgoingText)]))
entries.append(.themeItem(presentationData.theme, presentationData.strings, availableThemes, themeReference, themeSpecificAccentColors, themeSpecificAccentColors[themeReference.index]))
@ -641,7 +641,7 @@ public final class ThemeSettingsCrossfadeController: ViewController {
private let snapshotView: UIView?
public init() {
self.snapshotView = UIScreen.main.snapshotView(afterScreenUpdates: true)
self.snapshotView = UIScreen.main.snapshotView(afterScreenUpdates: false)
super.init(navigationBarPresentationData: nil)

View file

@ -171,9 +171,10 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
func setup(context: AccountContext, theme: PresentationThemeReference, accentColor: UIColor?, currentTheme: PresentationTheme, title: NSAttributedString, bordered: Bool, selected: Bool, action: @escaping () -> Void, longTapAction: @escaping () -> Void) {
if case let .cloud(theme) = theme, theme.theme.file == nil {
if self.currentTheme == nil || currentTheme !== self.currentTheme! {
if self.currentTheme == nil || currentTheme !== self.currentTheme! || accentColor != self.accentColor {
self.imageNode.setSignal(createThemeImage(theme: currentTheme))
self.currentTheme = currentTheme
self.accentColor = accentColor
}
} else {
if theme != self.theme || accentColor != self.accentColor {

View file

@ -681,10 +681,8 @@ public class WallpaperGalleryController: ViewController {
self.toolbarNode!.updateLayout(size: CGSize(width: layout.size.width, height: 49.0), layout: layout, transition: transition)
var bottomInset = layout.intrinsicInsets.bottom + 49.0
let metrics = DeviceMetrics.forScreenSize(layout.size)
let standardInputHeight = metrics?.standardInputHeight(inLandscape: false) ?? 216.0
let height = standardInputHeight - bottomInset + 47.0
let standardInputHeight = layout.deviceMetrics.keyboardHeight(inLandscape: false)
let height = max(standardInputHeight, layout.inputHeight ?? 0.0) - bottomInset + 47.0
if let colorPanelNode = self.colorPanelNode {
var colorPanelFrame = CGRect(x: 0.0, y: layout.size.height, width: layout.size.width, height: height)
if self.colorPanelEnabled {

View file

@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
094DC9C4231B75B400A6FF8F /* TelegramStringFormatting.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 094DC9C3231B75B400A6FF8F /* TelegramStringFormatting.framework */; };
D03E420C230563DB0049C28B /* SinglePhoneInputNode.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E420A230563DB0049C28B /* SinglePhoneInputNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
D03E4217230564570049C28B /* SinglePhoneInputNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E4216230564570049C28B /* SinglePhoneInputNode.swift */; };
D03E421A230564650049C28B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D03E4219230564650049C28B /* Foundation.framework */; };
@ -17,6 +18,7 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
094DC9C3231B75B400A6FF8F /* TelegramStringFormatting.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = TelegramStringFormatting.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D03E4207230563DB0049C28B /* SinglePhoneInputNode.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SinglePhoneInputNode.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D03E420A230563DB0049C28B /* SinglePhoneInputNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SinglePhoneInputNode.h; sourceTree = "<group>"; };
D03E420B230563DB0049C28B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -33,6 +35,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
094DC9C4231B75B400A6FF8F /* TelegramStringFormatting.framework in Frameworks */,
D03E4222230564760049C28B /* TelegramCore.framework in Frameworks */,
D03E4220230564700049C28B /* Display.framework in Frameworks */,
D03E421E2305646D0049C28B /* AsyncDisplayKit.framework in Frameworks */,
@ -74,6 +77,7 @@
D03E4218230564650049C28B /* Frameworks */ = {
isa = PBXGroup;
children = (
094DC9C3231B75B400A6FF8F /* TelegramStringFormatting.framework */,
D03E4221230564760049C28B /* TelegramCore.framework */,
D03E421F230564700049C28B /* Display.framework */,
D03E421D2305646D0049C28B /* AsyncDisplayKit.framework */,

View file

@ -3,6 +3,7 @@ import UIKit
import AsyncDisplayKit
import Display
import TelegramCore
import TelegramStringFormatting
private func removeDuplicatedPlus(_ text: String?) -> String {
var result = ""
@ -34,7 +35,7 @@ private func removePlus(_ text: String?) -> String {
private func cleanPhoneNumber(_ text: String?) -> String {
var cleanNumber = ""
if let text = text {
if let text = text.flatMap({ normalizeArabicNumeralString($0, type: .western) }) {
for c in text {
if c == "+" {
if cleanNumber.isEmpty {
@ -145,7 +146,7 @@ public final class SinglePhoneInputNode: ASDisplayNode, UITextFieldDelegate {
guard let numberField = self.numberField else {
return
}
let inputText = removeDuplicatedPlus(cleanPhoneNumber(cleanPhoneNumber(numberField.textField.text)))
let inputText = removeDuplicatedPlus(cleanPhoneNumber(numberField.textField.text))
self.updateNumber(inputText)
self.numberUpdated?(inputText)
}

View file

@ -169,7 +169,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
})
}
if false, let liveLocationManager = context.liveLocationManager {
if let liveLocationManager = context.liveLocationManager {
switch locationBroadcastPanelSource {
case .none:
self.locationBroadcastMode = nil

View file

@ -917,7 +917,7 @@ extension InstantPageCaption {
}
public extension InstantPageListItem {
public var num: String? {
var num: String? {
switch self {
case let .text(_, num):
return num

View file

@ -6,7 +6,7 @@ import Foundation
#endif
public extension MemoryBuffer {
public func md5Digest() -> Data {
func md5Digest() -> Data {
return CryptoMD5(self.memory, Int32(self.length))
}
}

View file

@ -9,7 +9,7 @@ import Foundation
public extension MemoryBuffer {
public convenience init(_ buffer: Buffer) {
convenience init(_ buffer: Buffer) {
let memory = malloc(Int(buffer.size))!
memcpy(memory, buffer.data, Int(buffer.size))
self.init(memory: memory, capacity: Int(buffer.size), length: Int(buffer.size), freeWhenDone: true)

View file

@ -259,7 +259,7 @@ public indirect enum RichText: PostboxCoding, Equatable {
}
public extension RichText {
public var plainText: String {
var plainText: String {
switch self {
case .empty:
return ""

View file

@ -316,7 +316,7 @@ public enum TelegramChannelPermission {
}
public extension TelegramChannel {
public func hasPermission(_ permission: TelegramChannelPermission) -> Bool {
func hasPermission(_ permission: TelegramChannelPermission) -> Bool {
if self.flags.contains(.isCreator) {
return true
}

View file

@ -210,7 +210,7 @@ public final class TelegramGroup: Peer {
}
public extension TelegramGroup {
public func hasBannedPermission(_ rights: TelegramChatBannedRightsFlags) -> Bool {
func hasBannedPermission(_ rights: TelegramChatBannedRightsFlags) -> Bool {
switch self.role {
case .creator, .admin:
return false

View file

@ -354,7 +354,6 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
return PresentationTheme(
name: .builtin(.night),
author: "Telegram",
referenceTheme: .night,
overallDarkAppearance: true,
baseColor: baseColor,

View file

@ -129,7 +129,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
constructive: PresentationThemeFillForeground(fillColor: constructiveColor, foregroundColor: .white),
accent: PresentationThemeFillForeground(fillColor: UIColor(rgb: 0x007cd6), foregroundColor: .white),
warning: PresentationThemeFillForeground(fillColor: UIColor(rgb: 0xcd7800), foregroundColor: .white),
inactive: PresentationThemeFillForeground(fillColor: UIColor(rgb: 0x26384c), foregroundColor: .white)
inactive: PresentationThemeFillForeground(fillColor: accentColor.withMultiplied(hue: 1.029, saturation: 0.609, brightness: 0.3), foregroundColor: .white)
),
itemCheckColors: PresentationThemeFillStrokeForeground(
fillColor: accentColor,
@ -332,7 +332,6 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
return PresentationTheme(
name: .builtin(.nightAccent),
author: "Telegram",
referenceTheme: .nightAccent,
overallDarkAppearance: true,
baseColor: baseColor,

View file

@ -363,7 +363,6 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
return PresentationTheme(
name: .builtin(day ? .day : .dayClassic),
author: "Telegram",
referenceTheme: day ? .day : .dayClassic,
overallDarkAppearance: false,
baseColor: baseColor,

View file

@ -934,7 +934,6 @@ public enum PresentationThemeName: Equatable {
public final class PresentationTheme: Equatable {
public let name: PresentationThemeName
public let author: String?
public let referenceTheme: PresentationBuiltinThemeReference
public let overallDarkAppearance: Bool
public let baseColor: PresentationThemeBaseColor?
@ -951,9 +950,8 @@ public final class PresentationTheme: Equatable {
public let resourceCache: PresentationsResourceCache = PresentationsResourceCache()
public init(name: PresentationThemeName, author: String?, referenceTheme: PresentationBuiltinThemeReference, overallDarkAppearance: Bool, baseColor: PresentationThemeBaseColor?, intro: PresentationThemeIntro, passcode: PresentationThemePasscode, rootController: PresentationThemeRootController, list: PresentationThemeList, chatList: PresentationThemeChatList, chat: PresentationThemeChat, actionSheet: PresentationThemeActionSheet, contextMenu: PresentationThemeContextMenu, inAppNotification: PresentationThemeInAppNotification, preview: Bool = false) {
public init(name: PresentationThemeName, referenceTheme: PresentationBuiltinThemeReference, overallDarkAppearance: Bool, baseColor: PresentationThemeBaseColor?, intro: PresentationThemeIntro, passcode: PresentationThemePasscode, rootController: PresentationThemeRootController, list: PresentationThemeList, chatList: PresentationThemeChatList, chat: PresentationThemeChat, actionSheet: PresentationThemeActionSheet, contextMenu: PresentationThemeContextMenu, inAppNotification: PresentationThemeInAppNotification, preview: Bool = false) {
self.name = name
self.author = author
self.referenceTheme = referenceTheme
self.overallDarkAppearance = overallDarkAppearance
self.baseColor = baseColor
@ -981,7 +979,7 @@ public final class PresentationTheme: Equatable {
return lhs === rhs
}
public func withUpdated(name: String?, author: String?, defaultWallpaper: TelegramWallpaper?) -> PresentationTheme {
public func withUpdated(name: String?, defaultWallpaper: TelegramWallpaper?) -> PresentationTheme {
var defaultWallpaper = defaultWallpaper
if let wallpaper = defaultWallpaper {
switch wallpaper {
@ -995,6 +993,6 @@ public final class PresentationTheme: Equatable {
break
}
}
return PresentationTheme(name: name.flatMap(PresentationThemeName.custom) ?? .custom(self.name.string), author: author ?? self.author, referenceTheme: self.referenceTheme, overallDarkAppearance: self.overallDarkAppearance, baseColor: nil, intro: self.intro, passcode: self.passcode, rootController: self.rootController, list: self.list, chatList: self.chatList, chat: self.chat.withUpdatedDefaultWallpaper(defaultWallpaper), actionSheet: self.actionSheet, contextMenu: self.contextMenu, inAppNotification: self.inAppNotification)
return PresentationTheme(name: name.flatMap(PresentationThemeName.custom) ?? .custom(self.name.string), referenceTheme: self.referenceTheme, overallDarkAppearance: self.overallDarkAppearance, baseColor: nil, intro: self.intro, passcode: self.passcode, rootController: self.rootController, list: self.list, chatList: self.chatList, chat: self.chat.withUpdatedDefaultWallpaper(defaultWallpaper), actionSheet: self.actionSheet, contextMenu: self.contextMenu, inAppNotification: self.inAppNotification)
}
}

View file

@ -36,7 +36,36 @@ extension TelegramWallpaper: Codable {
if [6,7].contains(value.count), let color = UIColor(hexString: value) {
self = .color(Int32(bitPattern: color.rgb))
} else {
self = .file(id: 0, accessHash: 0, isCreator: false, isDefault: false, isPattern: false, isDark: false, slug: value, file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: []), settings: WallpaperSettings(blur: false, motion: false, color: nil, intensity: nil))
let components = value.components(separatedBy: " ")
var slug: String?
var color: Int32?
var intensity: Int32?
var blur = false
var motion = false
if !components.isEmpty {
slug = components[0]
}
if components.count > 1, !["motion", "blur"].contains(components[1]), [6,7].contains(components[1].count), let value = Int32(components[1]) {
color = value
}
if components.count > 2, !["motion", "blur"].contains(components[2]), [6,7].contains(components[2].count), let value = Int32(components[2]) {
if value >= 0 && value <= 100 {
intensity = value
} else {
intensity = 50
}
}
if components.contains("motion") {
motion = true
}
if components.contains("blur") {
blur = true
}
if let slug = slug {
self = .file(id: 0, accessHash: 0, isCreator: false, isDefault: false, isPattern: color != nil, isDark: false, slug: slug, file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "", size: nil, attributes: []), settings: WallpaperSettings(blur: blur, motion: motion, color: color, intensity: intensity))
} else {
throw PresentationThemeDecodingError.generic
}
}
}
} else {
@ -52,7 +81,23 @@ extension TelegramWallpaper: Codable {
case let .color(value):
try container.encode(String(format: "%06x", value))
case let .file(file):
try container.encode(file.slug)
var components: [String] = []
components.append(file.slug)
if file.isPattern {
if let color = file.settings.color {
components.append(String(format: "%06x", color))
}
if let intensity = file.settings.intensity {
components.append("\(intensity)")
}
if file.settings.motion {
components.append("motion")
}
if file.settings.blur {
components.append("blur")
}
}
try container.encode(components.joined(separator: " "))
default:
break
}
@ -1521,7 +1566,6 @@ extension PresentationBuiltinThemeReference: Codable {
extension PresentationTheme: Codable {
enum CodingKeys: String, CodingKey {
case name
case author
case basedOn
case dark
case intro
@ -1550,7 +1594,6 @@ extension PresentationTheme: Codable {
}
self.init(name: (try? values.decode(PresentationThemeName.self, forKey: .name)) ?? .custom("Untitled"),
author: (try? values.decode(String.self, forKey: .author)) ?? nil,
referenceTheme: referenceTheme,
overallDarkAppearance: (try? values.decode(Bool.self, forKey: .dark)) ?? false,
baseColor: nil,
@ -1569,7 +1612,6 @@ extension PresentationTheme: Codable {
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.name, forKey: .name)
try container.encode(self.author, forKey: .author)
try container.encode(self.referenceTheme, forKey: .basedOn)
try container.encode(self.overallDarkAppearance, forKey: .dark)
try container.encode(self.intro, forKey: .intro)

View file

@ -152,7 +152,7 @@ public struct PresentationResourcesChatList {
context.clear(bounds)
switch state {
case .regular:
context.setFillColor(theme.chatList.backgroundColor.cgColor)
context.setFillColor(theme.chatList.itemBackgroundColor.cgColor)
case .highlighted:
context.setFillColor(theme.chatList.itemHighlightedBackgroundColor.cgColor)
case .pinned:

View file

@ -1,10 +1,21 @@
import Foundation
public func convertToArabicNumeralString(_ string: String) -> String {
public enum ArabicNumeralStringType {
case western
case eastern
}
public func normalizeArabicNumeralString(_ string: String, type: ArabicNumeralStringType) -> String {
var string = string
let arabicNumbers = ["٠": "0", "١": "1", "٢": "2", "٣": "3", "٤": "4", "٥": "5", "٦": "6", "٧": "7", "٨": "8", "٩": "9"]
for (arabic, generic) in arabicNumbers {
string = string.replacingOccurrences(of: generic, with: arabic)
let numerals = ["٠": "0", "١": "1", "٢": "2", "٣": "3", "٤": "4", "٥": "5", "٦": "6", "٧": "7", "٨": "8", "٩": "9"]
for (easternNumeral, westernNumeral) in numerals {
switch type {
case .western:
string = string.replacingOccurrences(of: easternNumeral, with: westernNumeral)
case .eastern:
string = string.replacingOccurrences(of: westernNumeral, with: easternNumeral)
}
}
return string
}

View file

@ -2984,7 +2984,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} else {
var isScheduled = false
for id in messageIds {
if [Namespaces.Message.ScheduledCloud, Namespaces.Message.ScheduledLocal].contains(id.namespace) {
if Namespaces.Message.allScheduled.contains(id.namespace) {
isScheduled = true
break
}
@ -3483,11 +3483,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
let icon: UIImage?
switch DeviceMetrics.forScreenSize(layout.size) {
case .iPhoneX?, .iPhoneXSMax?:
icon = UIImage(bundleImageName: "Chat/Message/VolumeButtonIconX")
default:
icon = UIImage(bundleImageName: "Chat/Message/VolumeButtonIcon")
if layout.deviceMetrics.hasTopNotch {
icon = UIImage(bundleImageName: "Chat/Message/VolumeButtonIconX")
} else {
icon = UIImage(bundleImageName: "Chat/Message/VolumeButtonIcon")
}
if let location = location, let icon = icon {
strongSelf.videoUnmuteTooltipController?.dismiss()
@ -5533,8 +5532,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
for i in (0 ..< attributes.count).reversed() {
if attributes[i] is NotificationInfoMessageAttribute {
attributes.remove(at: i)
}
if let _ = scheduleTime, attributes[i] is OutgoingScheduleInfoMessageAttribute {
} else if let _ = scheduleTime, attributes[i] is OutgoingScheduleInfoMessageAttribute {
attributes.remove(at: i)
}
}
@ -6904,6 +6902,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
func avatarPreviewingController(from sourceView: UIView) -> (UIViewController, CGRect)? {
guard let layout = self.validLayout else {
return nil
}
guard let buttonView = (self.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.avatarNode.view else {
return nil
}
@ -6911,14 +6912,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let galleryController = AvatarGalleryController(context: self.context, peer: peer, remoteEntries: nil, replaceRootController: { controller, ready in
}, synchronousLoad: true)
galleryController.setHintWillBePresentedInPreviewingContext(true)
galleryController.containerLayoutUpdated(ContainerViewLayout(size: CGSize(width: self.view.bounds.size.width, height: self.view.bounds.size.height), metrics: LayoutMetrics(), intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, standardInputHeight: 216.0, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
galleryController.containerLayoutUpdated(ContainerViewLayout(size: CGSize(width: self.view.bounds.size.width, height: self.view.bounds.size.height), metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
return (galleryController, buttonView.convert(buttonView.bounds, to: sourceView))
}
return nil
}
func previewingController(from sourceView: UIView, for location: CGPoint) -> (UIViewController, CGRect)? {
guard let view = self.chatDisplayNode.view.hitTest(location, with: nil), view.isDescendant(of: self.chatDisplayNode.historyNode.view) else {
guard let layout = self.validLayout, case .phone = layout.deviceMetrics.type, let view = self.chatDisplayNode.view.hitTest(location, with: nil), view.isDescendant(of: self.chatDisplayNode.historyNode.view) else {
return nil
}
@ -6952,7 +6953,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
gallery.setHintWillBePresentedInPreviewingContext(true)
let rect = selectedTransitionNode.0.view.convert(selectedTransitionNode.0.bounds, to: sourceView)
let sourceRect = rect.insetBy(dx: -2.0, dy: -2.0)
gallery.containerLayoutUpdated(ContainerViewLayout(size: CGSize(width: self.view.bounds.size.width, height: self.view.bounds.size.height), metrics: LayoutMetrics(), intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, standardInputHeight: 216.0, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
gallery.containerLayoutUpdated(ContainerViewLayout(size: CGSize(width: self.view.bounds.size.width, height: self.view.bounds.size.height), metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
return (gallery, sourceRect)
case .instantPage:
break

View file

@ -41,8 +41,7 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
var contentTypeHint: ChatMessageEntryContentType = .generic
if presentationData.largeEmoji, entry.message.media.isEmpty {
let messageText = entry.message.text
if messageText.count == 1, let _ = associatedData.animatedEmojiStickers[messageText.basicEmoji.0] {
if entry.message.text.count == 1, let _ = associatedData.animatedEmojiStickers[entry.message.text.basicEmoji.0] {
contentTypeHint = .animatedEmoji
} else if messageIsElligibleForLargeEmoji(entry.message) {
contentTypeHint = .largeEmoji

View file

@ -27,7 +27,18 @@ func canEditMessage(context: AccountContext, limitsConfiguration: LimitsConfigur
var unlimitedInterval = false
if message.id.namespace == Namespaces.Message.ScheduledCloud {
hasEditRights = true
if let peer = message.peers[message.id.peerId], let channel = peer as? TelegramChannel {
switch channel.info {
case .broadcast:
if channel.hasPermission(.editAllMessages) {
hasEditRights = true
}
default:
hasEditRights = true
}
} else {
hasEditRights = true
}
} else if message.id.peerId.namespace == Namespaces.Peer.SecretChat || message.id.namespace != Namespaces.Message.Cloud {
hasEditRights = false
} else if let author = message.author, author.id == context.account.peerId {
@ -261,29 +272,9 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
var canPin = false
let canSelect = !isAction
var canDeleteMessage: Bool = false
let message = messages[0]
if let channel = message.peers[message.id.peerId] as? TelegramChannel {
if case .broadcast = channel.info {
if !message.flags.contains(.Incoming) {
canDeleteMessage = channel.hasPermission(.sendMessages)
}
if channel.hasPermission(.deleteAllMessages) {
canDeleteMessage = true
}
} else {
if channel.hasPermission(.deleteAllMessages) || !message.flags.contains(.Incoming) {
canDeleteMessage = true
}
}
} else if message.peers[message.id.peerId] is TelegramSecretChat {
canDeleteMessage = true
} else {
canDeleteMessage = context.account.peerId == message.author?.id
}
if [Namespaces.Message.ScheduledCloud, Namespaces.Message.ScheduledLocal].contains(message.id.namespace) {
if Namespaces.Message.allScheduled.contains(message.id.namespace) {
canReply = false
canPin = false
}

View file

@ -483,7 +483,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
let (_, initialImageWidth, refineLayout) = contentImageLayout(context, presentationData.theme.theme, presentationData.strings, presentationData.dateTimeFormat, message, wallpaper, .full, associatedData.automaticDownloadPeerType, .constrained(CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height)), layoutConstants, contentMode)
initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right
refineContentImageLayout = refineLayout
if case let .file(_, _, isTheme) = wallpaper.content, isTheme {
if case let .file(_, _, isTheme, _) = wallpaper.content, isTheme {
skipStandardStatus = true
}
}

View file

@ -301,7 +301,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
unboundSize = CGSize(width: floor(dimensions.width * 0.5), height: floor(dimensions.height * 0.5))
} else if let wallpaper = media as? WallpaperPreviewMedia {
switch wallpaper.content {
case let .file(file, _, isTheme):
case let .file(file, _, isTheme, isSupported):
if let thumbnail = file.previewRepresentations.first, var dimensions = file.dimensions {
let dimensionsVertical = dimensions.width < dimensions.height
let thumbnailVertical = thumbnail.dimensions.width < thumbnail.dimensions.height
@ -310,7 +310,13 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
}
unboundSize = CGSize(width: floor(dimensions.width * 0.5), height: floor(dimensions.height * 0.5)).fitted(CGSize(width: 240.0, height: 240.0))
} else if isTheme {
unboundSize = CGSize(width: 160.0, height: 240.0).fitted(CGSize(width: 240.0, height: 240.0))
if isSupported {
unboundSize = CGSize(width: 160.0, height: 240.0)
} else if let thumbnail = file.previewRepresentations.first {
unboundSize = CGSize(width: floor(thumbnail.dimensions.width), height: floor(thumbnail.dimensions.height)).fitted(CGSize(width: 240.0, height: 240.0))
} else {
unboundSize = CGSize(width: 54.0, height: 54.0)
}
} else {
unboundSize = CGSize(width: 54.0, height: 54.0)
}
@ -430,7 +436,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
} else {
emptyColor = message.effectivelyIncoming(context.account.peerId) ? theme.chat.message.incoming.mediaPlaceholderColor : theme.chat.message.outgoing.mediaPlaceholderColor
}
if let wallpaper = media as? WallpaperPreviewMedia, case let .file(_, patternColor, _) = wallpaper.content {
if let wallpaper = media as? WallpaperPreviewMedia, case let .file(_, patternColor, _, _) = wallpaper.content {
emptyColor = patternColor ?? UIColor(rgb: 0xd6e2ee, alpha: 0.5)
}
@ -560,7 +566,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
} else if let wallpaper = media as? WallpaperPreviewMedia {
updateImageSignal = { synchronousLoad in
switch wallpaper.content {
case let .file(file, _, isTheme):
case let .file(file, _, isTheme, _):
if isTheme {
return themeImage(account: context.account, accountManager: context.sharedContext.accountManager, fileReference: FileMediaReference.message(message: MessageReference(message), media: file))
} else {
@ -576,7 +582,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
}
}
if case let .file(file, _, _) = wallpaper.content {
if case let .file(file, _, _, _) = wallpaper.content {
updatedFetchControls = FetchControls(fetch: { manual in
if let strongSelf = self {
strongSelf.fetchDisposable.set(messageMediaFileInteractiveFetched(context: context, message: message, file: file, userInitiated: manual).start())
@ -620,7 +626,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
}
} else if let wallpaper = media as? WallpaperPreviewMedia {
switch wallpaper.content {
case let .file(file, _, _):
case let .file(file, _, _, _):
updatedStatusSignal = messageMediaFileStatus(context: context, messageId: message.id, file: file)
|> map { resourceStatus -> (MediaResourceStatus, MediaResourceStatus?) in
return (resourceStatus, nil)

View file

@ -370,7 +370,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
loop: for media in self.message.media {
if let telegramFile = media as? TelegramMediaFile {
if telegramFile.isAnimatedSticker, let size = telegramFile.size, size > 0 && size <= 128 * 1024 {
if telegramFile.isAnimatedSticker, let size = telegramFile.size, size > 0 && size <= 128 * 1024 && !telegramFile.previewRepresentations.isEmpty {
viewClassName = ChatMessageAnimatedStickerItemNode.self
break loop
}

View file

@ -145,7 +145,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
var textLayoutAndApply: (TextNodeLayout, () -> TextNode)?
var isEmoji = false
if item.presentationData.largeEmoji && messageIsElligibleForLargeEmoji(item.message) {
let attributedText = NSAttributedString(string: item.message.text, font: item.presentationData.messageEmojiFont1, textColor: .black)
let attributedText = NSAttributedString(string: item.message.text, font: item.presentationData.messageEmojiFont, textColor: .black)
textLayoutAndApply = textLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: 180.0, height: 90.0), alignment: .natural))
imageSize = CGSize(width: textLayoutAndApply!.0.size.width, height: textLayoutAndApply!.0.size.height)

View file

@ -260,7 +260,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
if let wallpaper = parseWallpaperUrl(webpage.url), case let .slug(_, _, color, intensity) = wallpaper {
patternColor = color?.withAlphaComponent(CGFloat(intensity ?? 50) / 100.0)
}
let media = WallpaperPreviewMedia(content: .file(file, patternColor, false))
let media = WallpaperPreviewMedia(content: .file(file, patternColor, false, false))
mediaAndFlags = (media, [.preferMediaAspectFilled])
if let fileSize = file.size {
badge = dataSizeString(fileSize, decimalSeparator: item.presentationData.dateTimeFormat.decimalSeparator)
@ -297,17 +297,20 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
}
} else if type == "telegram_theme" {
var file: TelegramMediaFile?
var isSupported = false
if let contentFiles = webpage.files {
if let filteredFile = contentFiles.filter({ $0.mimeType == themeMimeType }).first {
isSupported = true
file = filteredFile
} else {
file = contentFiles.first
}
} else if let contentFile = webpage.file {
isSupported = true
file = contentFile
}
if let file = file {
let media = WallpaperPreviewMedia(content: .file(file, nil, true))
let media = WallpaperPreviewMedia(content: .file(file, nil, true, isSupported))
mediaAndFlags = (media, ChatMessageAttachedContentNodeMediaFlags())
}
}

View file

@ -52,9 +52,7 @@ public final class ChatPresentationData {
let isPreview: Bool
let messageFont: UIFont
let messageEmojiFont1: UIFont
let messageEmojiFont2: UIFont
let messageEmojiFont3: UIFont
let messageEmojiFont: UIFont
let messageBoldFont: UIFont
let messageItalicFont: UIFont
let messageBoldItalicFont: UIFont
@ -72,15 +70,13 @@ public final class ChatPresentationData {
self.isPreview = isPreview
let baseFontSize = fontSize.baseDisplaySize
self.messageFont = UIFont.systemFont(ofSize: baseFontSize)
self.messageEmojiFont1 = UIFont.systemFont(ofSize: 53.0)
self.messageEmojiFont2 = UIFont.systemFont(ofSize: 36.0)
self.messageEmojiFont3 = UIFont.systemFont(ofSize: 24.0)
self.messageBoldFont = UIFont.boldSystemFont(ofSize: baseFontSize)
self.messageItalicFont = UIFont.italicSystemFont(ofSize: baseFontSize)
self.messageFont = Font.regular(baseFontSize)
self.messageEmojiFont = Font.regular(53.0)
self.messageBoldFont = Font.bold(baseFontSize)
self.messageItalicFont = Font.italic(baseFontSize)
self.messageBoldItalicFont = Font.semiboldItalic(baseFontSize)
self.messageFixedFont = UIFont(name: "Menlo-Regular", size: baseFontSize - 1.0) ?? UIFont.systemFont(ofSize: baseFontSize)
self.messageBlockQuoteFont = UIFont.systemFont(ofSize: baseFontSize - 1.0)
self.messageFixedFont = Font.monospace(baseFontSize)
self.messageBlockQuoteFont = Font.regular(baseFontSize - 1.0)
self.animatedEmojiScale = animatedEmojiScale
}

View file

@ -155,7 +155,12 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
let makeLabelLayout = TextNode.asyncLayout(self.resultsLabel)
let (labelSize, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: resultsText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - leftInset - rightInset - 50.0, height: 100.0), alignment: .left, cutout: nil, insets: UIEdgeInsets()))
let _ = labelApply()
self.resultsLabel.frame = CGRect(origin: CGPoint(x: leftInset + 16.0, y: floor((panelHeight - labelSize.size.height) / 2.0)), size: labelSize.size)
var resultsOffset: CGFloat = 16.0
if !self.calendarButton.isHidden {
resultsOffset += 48.0
}
self.resultsLabel.frame = CGRect(origin: CGPoint(x: leftInset + resultsOffset, y: floor((panelHeight - labelSize.size.height) / 2.0)), size: labelSize.size)
let indicatorSize = self.activityIndicator.measure(CGSize(width: 22.0, height: 22.0))
self.activityIndicator.frame = CGRect(origin: CGPoint(x: width - rightInset - 41.0, y: floor((panelHeight - indicatorSize.height) / 2.0)), size: indicatorSize)

View file

@ -106,7 +106,7 @@ final class ComposeControllerNode: ASDisplayNode {
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
}
self.contactListNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition)
self.contactListNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition)
self.contactListNode.frame = CGRect(origin: CGPoint(), size: layout.size)
}

View file

@ -127,7 +127,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
var headerInsets = layout.insets(options: [.input])
headerInsets.top += actualNavigationBarHeight
headerInsets.top += strongSelf.tokenListNode.bounds.size.height
searchResultsNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: .immediate)
searchResultsNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: .immediate)
searchResultsNode.frame = CGRect(origin: CGPoint(), size: layout.size)
}
@ -180,11 +180,11 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
insets.top += tokenListHeight
headerInsets.top += tokenListHeight
self.contactListNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition)
self.contactListNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition)
self.contactListNode.frame = CGRect(origin: CGPoint(), size: layout.size)
if let searchResultsNode = self.searchResultsNode {
searchResultsNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition)
searchResultsNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition)
searchResultsNode.frame = CGRect(origin: CGPoint(), size: layout.size)
}
}

View file

@ -100,7 +100,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
}
self.contactListNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition)
self.contactListNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: insets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition)
self.contactListNode.frame = CGRect(origin: CGPoint(), size: layout.size)

View file

@ -200,7 +200,7 @@ private func matchingEmojiEntry(_ emoji: String) -> (UInt8, UInt8, UInt8)? {
}
func messageIsElligibleForLargeEmoji(_ message: Message) -> Bool {
if message.media.isEmpty && !message.text.isEmpty && message.text.containsOnlyEmoji && message.text.emojis.count < 4 {
if !message.text.isEmpty && message.text.containsOnlyEmoji && message.text.emojis.count < 4 {
var messageEntities: [MessageTextEntity]?
for attribute in message.attributes {
if let attribute = attribute as? TextEntitiesMessageAttribute {

View file

@ -243,22 +243,25 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|> mapToSignal { themeInfo -> Signal<(Data, TelegramTheme), GetThemeError> in
return Signal<(Data, TelegramTheme), GetThemeError> { subscriber in
let disposables = DisposableSet()
let mediaBox = context.sharedContext.accountManager.mediaBox
let resource = themeInfo.file?.resource
if let resource = resource {
disposables.add(fetchedMediaResource(mediaBox: mediaBox, reference: .standalone(resource: resource)).start())
disposables.add(fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: .standalone(resource: resource)).start())
let maybeFetched = mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: false)
|> take(1)
let maybeFetched = context.sharedContext.accountManager.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: false)
|> mapToSignal { maybeData -> Signal<Data?, NoError> in
if maybeData.complete {
let loadedData = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: [])
return .single(loadedData)
} else {
return mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: false)
return context.account.postbox.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: false)
|> map { next -> Data? in
return next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: [])
if next.size > 0, let data = try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []) {
context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data)
return data
} else {
return nil
}
}
}
}

View file

@ -41,7 +41,7 @@ public final class OverlayMediaControllerImpl: ViewController, OverlayMediaContr
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
let updatedLayout = ContainerViewLayout(size: layout.size, metrics: layout.metrics, intrinsicInsets: UIEdgeInsets(top: (layout.statusBarHeight ?? 0.0) + 44.0, left: layout.intrinsicInsets.left, bottom: layout.intrinsicInsets.bottom, right: layout.intrinsicInsets.right), safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver)
let updatedLayout = ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(top: (layout.statusBarHeight ?? 0.0) + 44.0, left: layout.intrinsicInsets.left, bottom: layout.intrinsicInsets.bottom, right: layout.intrinsicInsets.right), safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver)
self.controllerNode.containerLayoutUpdated(updatedLayout, transition: transition)
}
}

View file

@ -209,7 +209,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
let contactsInsets = insets
contactListNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, intrinsicInsets: contactsInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition)
contactListNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: contactsInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), headerInsets: headerInsets, transition: transition)
}
if let searchDisplayController = self.searchDisplayController {

View file

@ -4,7 +4,7 @@ import Postbox
import TelegramCore
enum WallpaperPreviewMediaContent: Equatable {
case file(TelegramMediaFile, UIColor?, Bool)
case file(TelegramMediaFile, UIColor?, Bool, Bool)
case color(UIColor)
}

View file

@ -116,6 +116,8 @@ final class WebpagePreviewAccessoryPanelNode: AccessoryPanelNode {
} else {
text = stringForMediaKind(mediaKind, strings: self.strings).0
}
} else if let files = content.files, content.type == "telegram_theme" {
text = strings.Message_Theme
} else if let _ = content.image {
text = stringForMediaKind(.image, strings: self.strings).0
}

View file

@ -131,7 +131,6 @@ private final class TextSelectionGetureRecognizer: UIGestureRecognizer, UIGestur
self.currentLocation = currentLocation
if let (knob, initialKnobPosition, initialGesturePosition) = self.movingKnob, let currentLocation = currentLocation {
self.moveKnob?(knob, CGPoint(x: initialKnobPosition.x + currentLocation.x - initialGesturePosition.x, y: initialKnobPosition.y + currentLocation.y - initialGesturePosition.y))
}
}

View file

@ -944,19 +944,23 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the
wallpaperSignal = cachedWallpaper(account: account, slug: file.slug)
|> mapToSignal { wallpaper in
if let wallpaper = wallpaper, case let .file(file) = wallpaper.wallpaper {
var convertedRepresentations: [ImageRepresentationWithReference] = []
convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: CGSize(width: 100.0, height: 100.0), resource: file.file.resource), reference: .media(media: .standalone(media: file.file), resource: file.file.resource)))
return wallpaperDatas(account: account, accountManager: accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false)
|> mapToSignal { _, fullSizeData, complete -> Signal<(UIColor, UIColor, UIColor, UIImage?), NoError> in
guard complete, let fullSizeData = fullSizeData else {
return .complete()
}
accountManager.mediaBox.storeResourceData(file.file.resource.id, data: fullSizeData)
if file.isPattern {
if let image = UIImage(data: fullSizeData) {
return .single((backgroundColor, incomingColor, outgoingColor, image))
} else {
return .complete()
} else {
var convertedRepresentations: [ImageRepresentationWithReference] = []
convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: CGSize(width: 100.0, height: 100.0), resource: file.file.resource), reference: .media(media: .standalone(media: file.file), resource: file.file.resource)))
return wallpaperDatas(account: account, accountManager: accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false)
|> mapToSignal { _, fullSizeData, complete -> Signal<(UIColor, UIColor, UIColor, UIImage?), NoError> in
guard complete, let fullSizeData = fullSizeData else {
return .complete()
}
accountManager.mediaBox.storeResourceData(file.file.resource.id, data: fullSizeData)
if let image = UIImage(data: fullSizeData) {
return .single((backgroundColor, incomingColor, outgoingColor, image))
} else {
return .complete()
}
}
}
} else {