mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Various UI fixes
This commit is contained in:
parent
e4a472f246
commit
7d2e62fcce
85 changed files with 4488 additions and 4289 deletions
|
|
@ -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] = []
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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.";
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -1359,7 +1359,6 @@ const NSInteger TGMessageImageViewOverlayParticlesCount = 40;
|
|||
|
||||
- (void)setCompletedAnimated:(bool)animated;
|
||||
{
|
||||
__weak TGMessageImageViewOverlayView *weakSelf = self;
|
||||
[_contentLayer setCompletedAnimated:animated];
|
||||
[_progressLayer setNone];
|
||||
_blurredBackgroundLayer.hidden = _blurless;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -60,7 +60,6 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16;
|
|||
CVPixelBufferRef _previousPixelBuffer;
|
||||
int32_t _repeatingCount;
|
||||
|
||||
NSMutableData *_audioBuffer;
|
||||
int16_t _micLevelPeak;
|
||||
int _micLevelPeakCount;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 */,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -917,7 +917,7 @@ extension InstantPageCaption {
|
|||
}
|
||||
|
||||
public extension InstantPageListItem {
|
||||
public var num: String? {
|
||||
var num: String? {
|
||||
switch self {
|
||||
case let .text(_, num):
|
||||
return num
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import Foundation
|
|||
#endif
|
||||
|
||||
public extension MemoryBuffer {
|
||||
public func md5Digest() -> Data {
|
||||
func md5Digest() -> Data {
|
||||
return CryptoMD5(self.memory, Int32(self.length))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -354,7 +354,6 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
|||
|
||||
return PresentationTheme(
|
||||
name: .builtin(.night),
|
||||
author: "Telegram",
|
||||
referenceTheme: .night,
|
||||
overallDarkAppearance: true,
|
||||
baseColor: baseColor,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue