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

This commit is contained in:
overtake
2019-09-07 15:17:48 +03:00
165 changed files with 11392 additions and 4842 deletions
-1
View File
@@ -247,7 +247,6 @@ build_verbose: check_env
kill_xcode:
killall Xcode || true
killall Simulator || true
clean: kill_xcode
sh clean.sh
+21 -10
View File
@@ -63,9 +63,20 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
return self;
}
- (void)completeWithContent:(UNNotificationContent * _Nonnull)content {
/*NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"org.telegram.Telegram-iOS.background"];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURLSessionDownloadTask *task = [session downloadTaskWithURL:[NSURL URLWithString:@"https://telegram.org"]];
[task resume];*/
if (_contentHandler) {
_contentHandler(content);
}
}
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
if (_rootPath == nil) {
contentHandler(request.content);
[self completeWithContent:request.content];
return;
}
@@ -289,8 +300,8 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
_bestAttemptContent.attachments = @[attachment];
}
}
if (_contentHandler && _bestAttemptContent != nil) {
_contentHandler(_bestAttemptContent);
if (_bestAttemptContent != nil) {
[self completeWithContent:_bestAttemptContent];
}
} else {
BuildConfig *buildConfig = [[BuildConfig alloc] initWithBaseAppBundleId:_baseAppBundleId];
@@ -322,21 +333,21 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
}
}
if (strongSelf->_contentHandler && strongSelf->_bestAttemptContent != nil) {
strongSelf->_contentHandler(strongSelf->_bestAttemptContent);
if (strongSelf->_bestAttemptContent != nil) {
[strongSelf completeWithContent:strongSelf->_bestAttemptContent];
}
}
});
});
}
} else {
if (_contentHandler && _bestAttemptContent != nil) {
_contentHandler(_bestAttemptContent);
if (_bestAttemptContent != nil) {
[self completeWithContent:_bestAttemptContent];
}
}
} else {
if (_contentHandler && _bestAttemptContent != nil) {
_contentHandler(_bestAttemptContent);
if (_bestAttemptContent != nil) {
[self completeWithContent:_bestAttemptContent];
}
}
}
@@ -349,7 +360,7 @@ static int64_t makePeerId(int32_t namespace, int32_t value) {
if (_contentHandler) {
if(_bestAttemptContent) {
_contentHandler(_bestAttemptContent);
[self completeWithContent:_bestAttemptContent];
_bestAttemptContent = nil;
}
_contentHandler = nil;
+1 -1
View File
@@ -3,7 +3,7 @@
@implementation Serialization
- (NSUInteger)currentLayer {
return 105;
return 106;
}
- (id _Nullable)parseMessage:(NSData * _Nullable)data {
+3
View File
@@ -277,6 +277,9 @@
<FileRef
location = "group:submodules/RadialStatusNode/RadialStatusNode_Xcode.xcodeproj">
</FileRef>
<FileRef
location = "group:submodules/SegmentedControlNode/SegmentedControlNode_Xcode.xcodeproj">
</FileRef>
<FileRef
location = "group:submodules/LiveLocationTimerNode/LiveLocationTimerNode_Xcode.xcodeproj">
</FileRef>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0900"
version = "1.3">
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
@@ -41,15 +41,6 @@
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">
@@ -72,6 +63,17 @@
</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"
@@ -104,6 +106,8 @@
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "DebugHockeyapp"
+24
View File
@@ -4725,3 +4725,27 @@ Any member of this group will be able to see messages in the channel.";
"Theme.UsersCount_0" = "%@ people are using this theme";
"Conversation.SendMessageErrorTooMuchScheduled" = "Sorry, you can not schedule more than 100 messages.";
"ChatList.Context.MarkAllAsRead" = "Mark All as Read";
"ChatList.Context.HideArchive" = "Hide Above the List";
"ChatList.Context.UnhideArchive" = "Pin in the list";
"ChatList.Context.RemoveFromRecents" = "Clear from Recents";
"ChatList.Context.AddToContacts" = "Add to Contacts";
"ChatList.Context.MarkAsRead" = "Mark as Read";
"ChatList.Context.MarkAsUnread" = "Mark as Unread";
"ChatList.Context.Archive" = "Archive";
"ChatList.Context.Unarchive" = "Unarchive";
"ChatList.Context.Pin" = "Pin";
"ChatList.Context.Unpin" = "Unpin";
"ChatList.Context.Mute" = "Mute";
"ChatList.Context.Unmute" = "Unmute";
"ChatList.Context.JoinChannel" = "Join Channel";
"ChatList.Context.Delete" = "Delete";
"ContactList.Context.SendMessage" = "Send Message";
"ContactList.Context.StartSecretChat" = "Start Secret Chat";
"ContactList.Context.Call" = "Cal";
"Theme.Context.Apply" = "Apply";
"Settings.Context.Logout" = "Logout";
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -706,6 +706,20 @@
ReferencedContainer = "container:submodules/ProgressNavigationButtonNode/ProgressNavigationButtonNode.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E66DC04EB84A82AD00000000"
BuildableName = "libSegmentedControlNode.a"
BlueprintName = "SegmentedControlNode"
ReferencedContainer = "container:submodules/SegmentedControlNode/SegmentedControlNode.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
@@ -1014,6 +1028,48 @@
ReferencedContainer = "container:submodules/ChatTitleActivityNode/ChatTitleActivityNode.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E66DC04EE09F1DE500000000"
BuildableName = "libReactionSelectionNode.a"
BlueprintName = "ReactionSelectionNode"
ReferencedContainer = "container:submodules/ReactionSelectionNode/ReactionSelectionNode.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E66DC04E5B33A96100000000"
BuildableName = "libTextSelectionNode.a"
BlueprintName = "TextSelectionNode"
ReferencedContainer = "container:submodules/TextSelectionNode/TextSelectionNode.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E66DC04E5F32B06300000000"
BuildableName = "libContextUI.a"
BlueprintName = "ContextUI"
ReferencedContainer = "container:submodules/ContextUI/ContextUI.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
@@ -1056,6 +1112,20 @@
ReferencedContainer = "container:submodules/ContactsPeerItem/ContactsPeerItem.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E66DC04EEAAB097E00000000"
BuildableName = "libOverlayStatusController.a"
BlueprintName = "OverlayStatusController"
ReferencedContainer = "container:submodules/OverlayStatusController/OverlayStatusController.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
@@ -1210,20 +1280,6 @@
ReferencedContainer = "container:submodules/LanguageSuggestionUI/LanguageSuggestionUI.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E66DC04EEAAB097E00000000"
BuildableName = "libOverlayStatusController.a"
BlueprintName = "OverlayStatusController"
ReferencedContainer = "container:submodules/OverlayStatusController/OverlayStatusController.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
@@ -1294,48 +1350,6 @@
ReferencedContainer = "container:submodules/ComposePollUI/ComposePollUI.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E66DC04EE09F1DE500000000"
BuildableName = "libReactionSelectionNode.a"
BlueprintName = "ReactionSelectionNode"
ReferencedContainer = "container:submodules/ReactionSelectionNode/ReactionSelectionNode.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E66DC04E5B33A96100000000"
BuildableName = "libTextSelectionNode.a"
BlueprintName = "TextSelectionNode"
ReferencedContainer = "container:submodules/TextSelectionNode/TextSelectionNode.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E66DC04E5F32B06300000000"
BuildableName = "libContextUI.a"
BlueprintName = "ContextUI"
ReferencedContainer = "container:submodules/ContextUI/ContextUI.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
@@ -432,6 +432,7 @@ public protocol SharedAccountContext: class {
func resolveUrl(account: Account, url: String) -> Signal<ResolvedUrl, NoError>
func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void)
func openAddContact(context: AccountContext, firstName: String, lastName: String, phoneNumber: String, label: String, present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, completed: @escaping () -> Void)
func openAddPersonContact(context: AccountContext, peerId: PeerId, present: @escaping (ViewController, Any?) -> Void)
func presentContactsWarningSuppression(context: AccountContext, present: (ViewController, Any?) -> Void)
func navigateToCurrentCall()
@@ -703,6 +703,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E293594DCC000000000</key>
<dict>
<key>isa</key>
@@ -930,6 +943,7 @@
<string>1DD70E29DB6520C800000000</string>
<string>1DD70E29CBE117ED00000000</string>
<string>1DD70E29EA9CBB5B00000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E293594DCC000000000</string>
<string>1DD70E29D65BA68200000000</string>
<string>1DD70E2925BBFEEE00000000</string>
@@ -1379,6 +1393,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -1488,6 +1509,7 @@
<string>E7A30F0497B4D6D800000000</string>
<string>E7A30F04C37F741500000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
<string>E7A30F045A26607D00000000</string>
<string>E7A30F04A889192100000000</string>
@@ -422,6 +422,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E293594DCC000000000</key>
<dict>
<key>isa</key>
@@ -631,6 +644,7 @@
<string>1DD70E290F1A3C6400000000</string>
<string>1DD70E29DB6520C800000000</string>
<string>1DD70E29CBE117ED00000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E293594DCC000000000</string>
<string>1DD70E29D65BA68200000000</string>
<string>1DD70E2925BBFEEE00000000</string>
@@ -935,6 +949,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -1002,6 +1023,7 @@
<string>E7A30F04C37F741500000000</string>
<string>E7A30F04BF0846EE00000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
<string>E7A30F045A26607D00000000</string>
<string>E7A30F042417E0B200000000</string>
@@ -46,7 +46,7 @@ public final class CallListController: ViewController {
self.mode = mode
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.segmentedTitleView = ItemListControllerSegmentedTitleView(segments: [self.presentationData.strings.Calls_All, self.presentationData.strings.Calls_Missed], index: 0, color: self.presentationData.theme.rootController.navigationBar.accentTextColor)
self.segmentedTitleView = ItemListControllerSegmentedTitleView(theme: self.presentationData.theme, segments: [self.presentationData.strings.Calls_All, self.presentationData.strings.Calls_Missed], selectedIndex: 0)
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
@@ -102,7 +102,7 @@ public final class CallListController: ViewController {
private func updateThemeAndStrings() {
let index = self.segmentedTitleView.index
self.segmentedTitleView.segments = [self.presentationData.strings.Calls_All, self.presentationData.strings.Calls_Missed]
self.segmentedTitleView.color = self.presentationData.theme.rootController.navigationBar.accentTextColor
self.segmentedTitleView.theme = self.presentationData.theme
self.segmentedTitleView.index = index
self.tabBarItem.title = self.presentationData.strings.Calls_TabTitle
+1
View File
@@ -37,6 +37,7 @@ static_library(
"//submodules/ContactListUI:ContactListUI",
"//submodules/PhotoResources:PhotoResources",
"//submodules/AppBundle:AppBundle",
"//submodules/ContextUI:ContextUI",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
@@ -182,6 +182,17 @@
<string>1DD70E29BE4AC65500000000</string>
</array>
</dict>
<key>1DD70E29308FEF0200000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>ChatContextMenus.swift</string>
<key>path</key>
<string>Sources/ChatContextMenus.swift</string>
<key>sourceTree</key>
<string>SOURCE_ROOT</string>
</dict>
<key>1DD70E29577C319700000000</key>
<dict>
<key>isa</key>
@@ -292,6 +303,7 @@
<key>children</key>
<array>
<string>B401C9790025222200000000</string>
<string>1DD70E29308FEF0200000000</string>
<string>1DD70E29577C319700000000</string>
<string>1DD70E2943B2E93900000000</string>
<string>1DD70E2906CA893E00000000</string>
@@ -553,6 +565,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E296C9831F100000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libContextUI.a</string>
<key>path</key>
<string>libContextUI.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29CED1620500000000</key>
<dict>
<key>isa</key>
@@ -865,6 +890,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29AD66967300000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libReactionSelectionNode.a</string>
<key>path</key>
<string>libReactionSelectionNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E291D58331200000000</key>
<dict>
<key>isa</key>
@@ -904,6 +942,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29097DBE9200000000</key>
<dict>
<key>isa</key>
@@ -1125,6 +1176,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29F40A586F00000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libTextSelectionNode.a</string>
<key>path</key>
<string>libTextSelectionNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29B4B05B7A00000000</key>
<dict>
<key>isa</key>
@@ -1213,6 +1277,7 @@
<string>1DD70E2997B4D6D800000000</string>
<string>1DD70E292F91702000000000</string>
<string>1DD70E2931752C6000000000</string>
<string>1DD70E296C9831F100000000</string>
<string>1DD70E29CED1620500000000</string>
<string>1DD70E292417E0B200000000</string>
<string>1DD70E295915423000000000</string>
@@ -1237,9 +1302,11 @@
<string>1DD70E29DB6520C800000000</string>
<string>1DD70E29CBE117ED00000000</string>
<string>1DD70E293E4DE92B00000000</string>
<string>1DD70E29AD66967300000000</string>
<string>1DD70E291D58331200000000</string>
<string>1DD70E29EA9CBB5B00000000</string>
<string>1DD70E291328E99400000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E29097DBE9200000000</string>
<string>1DD70E2981AE180900000000</string>
<string>1DD70E29524F478E00000000</string>
@@ -1257,6 +1324,7 @@
<string>1DD70E292395015100000000</string>
<string>1DD70E29BA06E3A600000000</string>
<string>1DD70E29F0C15F9C00000000</string>
<string>1DD70E29F40A586F00000000</string>
<string>1DD70E29B4B05B7A00000000</string>
<string>1DD70E29CD296A8300000000</string>
<string>1DD70E29AE67341000000000</string>
@@ -1390,6 +1458,13 @@
<key>fileRef</key>
<string>1DD70E29BE4AC65500000000</string>
</dict>
<key>E7A30F04308FEF0200000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29308FEF0200000000</string>
</dict>
<key>E7A30F04577C319700000000</key>
<dict>
<key>isa</key>
@@ -1471,6 +1546,7 @@
<string>E7A30F0428E77D0F00000000</string>
<string>E7A30F04978C105400000000</string>
<string>E7A30F04BE4AC65500000000</string>
<string>E7A30F04308FEF0200000000</string>
<string>E7A30F04577C319700000000</string>
<string>E7A30F0443B2E93900000000</string>
<string>E7A30F0406CA893E00000000</string>
@@ -1720,6 +1796,27 @@
<key>fileRef</key>
<string>1DD70E2997B4D6D800000000</string>
</dict>
<key>E7A30F04AD66967300000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29AD66967300000000</string>
</dict>
<key>E7A30F04F40A586F00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29F40A586F00000000</string>
</dict>
<key>E7A30F046C9831F100000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E296C9831F100000000</string>
</dict>
<key>E7A30F04CBE117ED00000000</key>
<dict>
<key>isa</key>
@@ -1727,6 +1824,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -1762,6 +1866,13 @@
<key>fileRef</key>
<string>1DD70E2931752C6000000000</string>
</dict>
<key>E7A30F04BBAF750C00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29BBAF750C00000000</string>
</dict>
<key>E7A30F041328E99400000000</key>
<dict>
<key>isa</key>
@@ -1867,13 +1978,6 @@
<key>fileRef</key>
<string>1DD70E29E220F5E800000000</string>
</dict>
<key>E7A30F04BBAF750C00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29BBAF750C00000000</string>
</dict>
<key>E7A30F04247D9AC100000000</key>
<dict>
<key>isa</key>
@@ -1956,12 +2060,17 @@
<string>E7A30F047AB043C800000000</string>
<string>E7A30F04B6F47D1F00000000</string>
<string>E7A30F0497B4D6D800000000</string>
<string>E7A30F04AD66967300000000</string>
<string>E7A30F04F40A586F00000000</string>
<string>E7A30F046C9831F100000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
<string>E7A30F045A26607D00000000</string>
<string>E7A30F04D233F68C00000000</string>
<string>E7A30F049A24C4DE00000000</string>
<string>E7A30F0431752C6000000000</string>
<string>E7A30F04BBAF750C00000000</string>
<string>E7A30F041328E99400000000</string>
<string>E7A30F041E16CC6C00000000</string>
<string>E7A30F043E4DE92B00000000</string>
@@ -1977,7 +2086,6 @@
<string>E7A30F042F91702000000000</string>
<string>E7A30F04CED1620500000000</string>
<string>E7A30F04E220F5E800000000</string>
<string>E7A30F04BBAF750C00000000</string>
<string>E7A30F04247D9AC100000000</string>
<string>E7A30F04B4B05B7A00000000</string>
<string>E7A30F04490601EB00000000</string>
@@ -0,0 +1,229 @@
import Foundation
import UIKit
import SwiftSignalKit
import ContextUI
import AccountContext
import Postbox
import TelegramCore
import Display
import TelegramUIPreferences
import OverlayStatusController
import AlertUI
func archiveContextMenuItems(context: AccountContext, groupId: PeerGroupId, chatListController: ChatListControllerImpl?) -> Signal<[ContextMenuItem], NoError> {
let strings = context.sharedContext.currentPresentationData.with({ $0 }).strings
return context.account.postbox.transaction { [weak chatListController] transaction -> [ContextMenuItem] in
var items: [ContextMenuItem] = []
if !transaction.getUnreadChatListPeerIds(groupId: groupId).isEmpty {
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_MarkAllAsRead, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MarkAsRead"), color: theme.contextMenu.primaryColor) }, action: { [weak chatListController] _, f in
let _ = (context.account.postbox.transaction { transaction in
markAllChatsAsReadInteractively(transaction: transaction, viewTracker: context.account.viewTracker, groupId: groupId)
}
|> deliverOnMainQueue).start(completed: {
f(.default)
})
})))
}
let settings = transaction.getPreferencesEntry(key: ApplicationSpecificPreferencesKeys.chatArchiveSettings) as? ChatArchiveSettings ?? ChatArchiveSettings.default
let isPinned = !settings.isHiddenByDefault
items.append(.action(ContextMenuActionItem(text: isPinned ? strings.ChatList_Context_HideArchive : strings.ChatList_Context_UnhideArchive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isPinned ? "Chat/Context Menu/Unpin": "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { [weak chatListController] _, f in
chatListController?.toggleArchivedFolderHiddenByDefault()
f(.default)
})))
return items
}
}
enum ChatContextMenuSource {
case chatList
case search(ChatListSearchContextActionSource)
}
func chatContextMenuItems(context: AccountContext, peerId: PeerId, source: ChatContextMenuSource, chatListController: ChatListControllerImpl?) -> Signal<[ContextMenuItem], NoError> {
let strings = context.sharedContext.currentPresentationData.with({ $0 }).strings
return context.account.postbox.transaction { [weak chatListController] transaction -> [ContextMenuItem] in
var items: [ContextMenuItem] = []
if case let .search(search) = source {
switch search {
case .recentPeers:
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_RemoveFromRecents, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { _, f in
let _ = (removeRecentPeer(account: context.account, peerId: peerId)
|> deliverOnMainQueue).start(completed: {
f(.default)
})
})))
items.append(.separator)
case .recentSearch:
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_RemoveFromRecents, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { _, f in
let _ = (removeRecentlySearchedPeer(postbox: context.account.postbox, peerId: peerId)
|> deliverOnMainQueue).start(completed: {
f(.default)
})
})))
items.append(.separator)
case .search:
break
}
}
let isSavedMessages = peerId == context.account.peerId
let chatPeer = transaction.getPeer(peerId)
var peer: Peer?
if let chatPeer = chatPeer {
if let chatPeer = chatPeer as? TelegramSecretChat {
peer = transaction.getPeer(chatPeer.regularPeerId)
} else {
peer = chatPeer
}
}
if !isSavedMessages, let peer = peer as? TelegramUser {
if !transaction.isPeerContact(peerId: peer.id) {
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_AddToContacts, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor) }, action: { _, f in
context.sharedContext.openAddPersonContact(context: context, peerId: peerId, present: { controller, arguments in
if let chatListController = chatListController {
chatListController.present(controller, in: .window(.root), with: arguments)
}
})
f(.default)
})))
items.append(.separator)
}
}
if case .chatList = source, !isSavedMessages {
if let readState = transaction.getCombinedPeerReadState(peerId), readState.isUnread {
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_MarkAsRead, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MarkAsRead"), color: theme.contextMenu.primaryColor) }, action: { _, f in
let _ = togglePeerUnreadMarkInteractively(postbox: context.account.postbox, viewTracker: context.account.viewTracker, peerId: peerId).start()
f(.default)
})))
} else {
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_MarkAsUnread, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MarkAsUnread"), color: theme.contextMenu.primaryColor) }, action: { _, f in
let _ = togglePeerUnreadMarkInteractively(postbox: context.account.postbox, viewTracker: context.account.viewTracker, peerId: peerId).start()
f(.default)
})))
}
}
let groupAndIndex = transaction.getPeerChatListIndex(peerId)
if let (group, index) = groupAndIndex {
if !isSavedMessages {
let isArchived = group == Namespaces.PeerGroup.archive
items.append(.action(ContextMenuActionItem(text: isArchived ? strings.ChatList_Context_Archive : strings.ChatList_Context_Unarchive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isArchived ? "Chat/Context Menu/Unarchive" : "Chat/Context Menu/Archive"), color: theme.contextMenu.primaryColor) }, action: { _, f in
if isArchived {
let _ = (context.account.postbox.transaction { transaction -> Void in
updatePeerGroupIdInteractively(transaction: transaction, peerId: peerId, groupId: .root)
}
|> deliverOnMainQueue).start(completed: {
f(.default)
})
} else {
if let chatListController = chatListController {
chatListController.archiveChats(peerIds: [peerId])
f(.default)
} else {
let _ = (context.account.postbox.transaction { transaction -> Void in
updatePeerGroupIdInteractively(transaction: transaction, peerId: peerId, groupId: Namespaces.PeerGroup.archive)
}
|> deliverOnMainQueue).start(completed: {
f(.default)
})
}
}
})))
}
let isPinned = index.pinningIndex != nil
items.append(.action(ContextMenuActionItem(text: isPinned ? strings.ChatList_Context_Unpin : strings.ChatList_Context_Pin, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isPinned ? "Chat/Context Menu/Unpin" : "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { _, f in
let _ = (toggleItemPinned(postbox: context.account.postbox, groupId: group, itemId: .peer(peerId))
|> deliverOnMainQueue).start(next: { result in
switch result {
case .done:
break
case let .limitExceeded(maxCount):
break
//strongSelf.presentAlert?(strongSelf.currentState.presentationData.strings.DialogList_PinLimitError("\(maxCount)").0)
}
f(.default)
})
})))
if !isSavedMessages, let notificationSettings = transaction.getPeerNotificationSettings(peerId) as? TelegramPeerNotificationSettings {
var isMuted = false
if case .muted = notificationSettings.muteState {
isMuted = true
}
items.append(.action(ContextMenuActionItem(text: isMuted ? strings.ChatList_Context_Unmute : strings.ChatList_Context_Mute, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor) }, action: { _, f in
let _ = (togglePeerMuted(account: context.account, peerId: peerId)
|> deliverOnMainQueue).start(completed: {
f(.default)
})
})))
}
} else {
if case .search = source {
if let channel = peer as? TelegramChannel {
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_JoinChannel, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor) }, action: { _, f in
var createSignal = joinChannel(account: context.account, peerId: peerId)
var cancelImpl: (() -> Void)?
let progressSignal = Signal<Never, NoError> { subscriber in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: {
cancelImpl?()
}))
chatListController?.present(controller, in: .window(.root))
return ActionDisposable { [weak controller] in
Queue.mainQueue().async() {
controller?.dismiss()
}
}
}
|> runOn(Queue.mainQueue())
|> delay(0.15, queue: Queue.mainQueue())
let progressDisposable = progressSignal.start()
createSignal = createSignal
|> afterDisposed {
Queue.mainQueue().async {
progressDisposable.dispose()
}
}
let joinChannelDisposable = MetaDisposable()
cancelImpl = {
joinChannelDisposable.set(nil)
}
joinChannelDisposable.set((createSignal
|> deliverOnMainQueue).start(next: { _ in
if let navigationController = (chatListController?.navigationController as? NavigationController) {
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId)))
}
}, error: { _ in
if let chatListController = chatListController {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
chatListController.present(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}
}))
f(.default)
})))
}
}
}
if case .chatList = source, groupAndIndex != nil {
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_Delete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { _, f in
if let chatListController = chatListController {
chatListController.deletePeerChat(peerId: peerId)
}
f(.default)
})))
}
return items
}
}
@@ -15,6 +15,7 @@ import TelegramNotices
import SearchUI
import DeleteChatPeerActionSheetItem
import LanguageSuggestionUI
import ContextUI
import AppBundle
public func useSpecialTabBarIcons() -> Bool {
@@ -65,6 +66,27 @@ private func fixListNodeScrolling(_ listNode: ListView, searchNode: NavigationBa
return false
}
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
let controller: ViewController
weak var sourceNode: ASDisplayNode?
init(controller: ViewController, sourceNode: ASDisplayNode?) {
self.controller = controller
self.sourceNode = sourceNode
}
func transitionInfo() -> ContextControllerTakeControllerInfo? {
let sourceNode = self.sourceNode
return ContextControllerTakeControllerInfo(contentAreaInScreenSpace: CGRect(origin: CGPoint(), size: CGSize(width: 10.0, height: 10.0)), sourceNode: { [weak sourceNode] in
if let sourceNode = sourceNode {
return (sourceNode, sourceNode.bounds)
} else {
return nil
}
})
}
}
public class ChatListControllerImpl: TelegramBaseController, ChatListController, UIViewControllerPreviewingDelegate {
private var validLayout: ContainerViewLayout?
@@ -442,240 +464,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
guard let strongSelf = self else {
return
}
let _ = (strongSelf.context.account.postbox.transaction { transaction -> Bool in
var updatedValue = false
updateChatArchiveSettings(transaction: transaction, { settings in
var settings = settings
settings.isHiddenByDefault = !settings.isHiddenByDefault
updatedValue = settings.isHiddenByDefault
return settings
})
return updatedValue
}
|> deliverOnMainQueue).start(next: { value in
guard let strongSelf = self else {
return
}
strongSelf.chatListDisplayNode.chatListNode.updateState { state in
var state = state
if value {
state.archiveShouldBeTemporaryRevealed = false
}
state.peerIdWithRevealedOptions = nil
return state
}
strongSelf.forEachController({ controller in
if let controller = controller as? UndoOverlayController {
controller.dismissWithCommitActionAndReplacementAnimation()
}
return true
})
if value {
strongSelf.present(UndoOverlayController(context: strongSelf.context, content: .hidArchive(title: strongSelf.presentationData.strings.ChatList_UndoArchiveHiddenTitle, text: strongSelf.presentationData.strings.ChatList_UndoArchiveHiddenText, undo: false), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] shouldCommit in
guard let strongSelf = self else {
return
}
if !shouldCommit {
let _ = (strongSelf.context.account.postbox.transaction { transaction -> Bool in
var updatedValue = false
updateChatArchiveSettings(transaction: transaction, { settings in
var settings = settings
settings.isHiddenByDefault = false
updatedValue = settings.isHiddenByDefault
return settings
})
return updatedValue
}).start()
}
}), in: .current)
} else {
strongSelf.present(UndoOverlayController(context: strongSelf.context, content: .revealedArchive(title: strongSelf.presentationData.strings.ChatList_UndoArchiveRevealedTitle, text: strongSelf.presentationData.strings.ChatList_UndoArchiveRevealedText, undo: false), elevatedLayout: false, animateInAsReplacement: true, action: { _ in
}), in: .current)
}
})
strongSelf.toggleArchivedFolderHiddenByDefault()
}
self.chatListDisplayNode.chatListNode.deletePeerChat = { [weak self] peerId in
guard let strongSelf = self else {
return
}
let _ = (strongSelf.context.account.postbox.transaction { transaction -> RenderedPeer? in
guard let peer = transaction.getPeer(peerId) else {
return nil
}
if let associatedPeerId = peer.associatedPeerId {
if let associatedPeer = transaction.getPeer(associatedPeerId) {
return RenderedPeer(peerId: peerId, peers: SimpleDictionary([peer.id: peer, associatedPeer.id: associatedPeer]))
} else {
return nil
}
} else {
return RenderedPeer(peer: peer)
}
}
|> deliverOnMainQueue).start(next: { peer in
guard let strongSelf = self, let peer = peer, let chatPeer = peer.peers[peer.peerId], let mainPeer = peer.chatMainPeer else {
return
}
var canRemoveGlobally = false
let limitsConfiguration = strongSelf.context.currentLimitsConfiguration.with { $0 }
if peer.peerId.namespace == Namespaces.Peer.CloudUser && peer.peerId != strongSelf.context.account.peerId {
if limitsConfiguration.maxMessageRevokeIntervalInPrivateChats == LimitsConfiguration.timeIntervalForever {
canRemoveGlobally = true
}
}
if let user = chatPeer as? TelegramUser, user.botInfo == nil, canRemoveGlobally {
strongSelf.maybeAskForPeerChatRemoval(peer: peer, completion: { _ in }, removed: {})
} else {
let actionSheet = ActionSheetController(presentationTheme: strongSelf.presentationData.theme)
var items: [ActionSheetItem] = []
var canClear = true
var canStop = false
var deleteTitle = strongSelf.presentationData.strings.Common_Delete
if let channel = chatPeer as? TelegramChannel {
if case .broadcast = channel.info {
canClear = false
deleteTitle = strongSelf.presentationData.strings.Channel_LeaveChannel
} else {
deleteTitle = strongSelf.presentationData.strings.Group_LeaveGroup
}
if let addressName = channel.addressName, !addressName.isEmpty {
canClear = false
}
} else if let user = chatPeer as? TelegramUser, user.botInfo != nil {
canStop = !user.flags.contains(.isSupport)
canClear = user.botInfo == nil
deleteTitle = strongSelf.presentationData.strings.ChatList_DeleteChat
} else if let _ = chatPeer as? TelegramSecretChat {
deleteTitle = strongSelf.presentationData.strings.ChatList_DeleteChat
}
var canRemoveGlobally = false
let limitsConfiguration = strongSelf.context.currentLimitsConfiguration.with { $0 }
if chatPeer is TelegramUser && chatPeer.id != strongSelf.context.account.peerId {
if limitsConfiguration.maxMessageRevokeIntervalInPrivateChats == LimitsConfiguration.timeIntervalForever {
canRemoveGlobally = true
}
}
items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .delete, strings: strongSelf.presentationData.strings))
if canClear {
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.DialogList_ClearHistoryConfirmation, color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
guard let strongSelf = self else {
return
}
let beginClear: (InteractiveHistoryClearingType) -> Void = { type in
guard let strongSelf = self else {
return
}
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
var state = state
state.pendingClearHistoryPeerIds.insert(peer.peerId)
return state
})
strongSelf.forEachController({ controller in
if let controller = controller as? UndoOverlayController {
controller.dismissWithCommitActionAndReplacementAnimation()
}
return true
})
strongSelf.present(UndoOverlayController(context: strongSelf.context, content: .removedChat(text: strongSelf.presentationData.strings.Undo_ChatCleared), elevatedLayout: false, animateInAsReplacement: true, action: { shouldCommit in
guard let strongSelf = self else {
return
}
if shouldCommit {
let _ = clearHistoryInteractively(postbox: strongSelf.context.account.postbox, peerId: peerId, type: type).start(completed: {
guard let strongSelf = self else {
return
}
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
var state = state
state.pendingClearHistoryPeerIds.remove(peer.peerId)
return state
})
})
} else {
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
var state = state
state.pendingClearHistoryPeerIds.remove(peer.peerId)
return state
})
}
}), in: .current)
}
if canRemoveGlobally {
let actionSheet = ActionSheetController(presentationTheme: strongSelf.presentationData.theme)
var items: [ActionSheetItem] = []
items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .clearHistory, strings: strongSelf.presentationData.strings))
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak actionSheet] in
beginClear(.forEveryone)
actionSheet?.dismissAnimated()
}))
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in
beginClear(.forLocalPeer)
actionSheet?.dismissAnimated()
}))
actionSheet.setItemGroups([
ActionSheetItemGroup(items: items),
ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
})
])
])
strongSelf.present(actionSheet, in: .window(.root))
} else {
beginClear(.forLocalPeer)
}
}))
}
items.append(ActionSheetButtonItem(title: deleteTitle, color: .destructive, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
guard let strongSelf = self else {
return
}
strongSelf.maybeAskForPeerChatRemoval(peer: peer, completion: { _ in }, removed: {})
}))
if canStop {
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.DialogList_DeleteBotConversationConfirmation, color: .destructive, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
if let strongSelf = self {
strongSelf.maybeAskForPeerChatRemoval(peer: peer, completion: { _ in
}, removed: {
guard let strongSelf = self else {
return
}
let _ = requestUpdatePeerIsBlocked(account: strongSelf.context.account, peerId: peer.peerId, isBlocked: true).start()
})
}
}))
}
actionSheet.setItemGroups([ActionSheetItemGroup(items: items),
ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
})
])
])
strongSelf.present(actionSheet, in: .window(.root))
}
})
strongSelf.deletePeerChat(peerId: peerId)
}
self.chatListDisplayNode.chatListNode.peerSelected = { [weak self] peerId, animated, isAd in
@@ -860,6 +656,36 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
self?.toolbarActionSelected(action: action)
}
self.chatListDisplayNode.chatListNode.activateChatPreview = { [weak self] item, node, gesture in
guard let strongSelf = self else {
gesture?.cancel()
return
}
switch item.content {
case let .groupReference(groupReference):
let chatListController = ChatListControllerImpl(context: strongSelf.context, groupId: groupReference.groupId, controlsHistoryPreload: false, enableDebugActions: false)
let contextController = ContextController(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node)), items: archiveContextMenuItems(context: strongSelf.context, groupId: groupReference.groupId, chatListController: strongSelf), reactionItems: [], gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
case let .peer(peer):
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.peer.peerId), subject: nil, botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
let contextController = ContextController(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peer.peerId, source: .chatList, chatListController: strongSelf), reactionItems: [], gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
}
}
self.chatListDisplayNode.peerContextAction = { [weak self] peer, source, node, gesture in
guard let strongSelf = self else {
gesture?.cancel()
return
}
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
let contextController = ContextController(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, source: .search(source), chatListController: strongSelf), reactionItems: [], gesture: gesture)
strongSelf.presentInGlobalOverlay(contextController)
}
let context = self.context
let peerIdsAndOptions: Signal<(ChatListSelectionOptions, Set<PeerId>)?, NoError> = self.chatListDisplayNode.chatListNode.state
|> map { state -> Set<PeerId>? in
@@ -951,7 +777,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
if !self.didSetup3dTouch && self.traitCollection.forceTouchCapability != .unknown {
self.didSetup3dTouch = true
self.registerForPreviewingNonNative(with: self, sourceView: self.view, theme: PeekControllerTheme(presentationTheme: self.presentationData.theme))
//self.registerForPreviewingNonNative(with: self, sourceView: self.view, theme: PeekControllerTheme(presentationTheme: self.presentationData.theme))
}
}
}
@@ -1069,6 +895,13 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
return true
})
}
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
if !self.didSetup3dTouch {
self.didSetup3dTouch = true
//self.registerForPreviewingNonNative(with: self, sourceView: self.view, theme: PeekControllerTheme(presentationTheme: self.presentationData.theme))
}
}
}
override public func viewWillDisappear(_ animated: Bool) {
@@ -1448,6 +1281,240 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
}
}
func toggleArchivedFolderHiddenByDefault() {
let _ = (self.context.account.postbox.transaction { transaction -> Bool in
var updatedValue = false
updateChatArchiveSettings(transaction: transaction, { settings in
var settings = settings
settings.isHiddenByDefault = !settings.isHiddenByDefault
updatedValue = settings.isHiddenByDefault
return settings
})
return updatedValue
}
|> deliverOnMainQueue).start(next: { [weak self] value in
guard let strongSelf = self else {
return
}
strongSelf.chatListDisplayNode.chatListNode.updateState { state in
var state = state
if value {
state.archiveShouldBeTemporaryRevealed = false
}
state.peerIdWithRevealedOptions = nil
return state
}
strongSelf.forEachController({ controller in
if let controller = controller as? UndoOverlayController {
controller.dismissWithCommitActionAndReplacementAnimation()
}
return true
})
if value {
strongSelf.present(UndoOverlayController(context: strongSelf.context, content: .hidArchive(title: strongSelf.presentationData.strings.ChatList_UndoArchiveHiddenTitle, text: strongSelf.presentationData.strings.ChatList_UndoArchiveHiddenText, undo: false), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] shouldCommit in
guard let strongSelf = self else {
return
}
if !shouldCommit {
let _ = (strongSelf.context.account.postbox.transaction { transaction -> Bool in
var updatedValue = false
updateChatArchiveSettings(transaction: transaction, { settings in
var settings = settings
settings.isHiddenByDefault = false
updatedValue = settings.isHiddenByDefault
return settings
})
return updatedValue
}).start()
}
}), in: .current)
} else {
strongSelf.present(UndoOverlayController(context: strongSelf.context, content: .revealedArchive(title: strongSelf.presentationData.strings.ChatList_UndoArchiveRevealedTitle, text: strongSelf.presentationData.strings.ChatList_UndoArchiveRevealedText, undo: false), elevatedLayout: false, animateInAsReplacement: true, action: { _ in
}), in: .current)
}
})
}
func deletePeerChat(peerId: PeerId) {
let _ = (self.context.account.postbox.transaction { transaction -> RenderedPeer? in
guard let peer = transaction.getPeer(peerId) else {
return nil
}
if let associatedPeerId = peer.associatedPeerId {
if let associatedPeer = transaction.getPeer(associatedPeerId) {
return RenderedPeer(peerId: peerId, peers: SimpleDictionary([peer.id: peer, associatedPeer.id: associatedPeer]))
} else {
return nil
}
} else {
return RenderedPeer(peer: peer)
}
}
|> deliverOnMainQueue).start(next: { [weak self] peer in
guard let strongSelf = self, let peer = peer, let chatPeer = peer.peers[peer.peerId], let mainPeer = peer.chatMainPeer else {
return
}
var canRemoveGlobally = false
let limitsConfiguration = strongSelf.context.currentLimitsConfiguration.with { $0 }
if peer.peerId.namespace == Namespaces.Peer.CloudUser && peer.peerId != strongSelf.context.account.peerId {
if limitsConfiguration.maxMessageRevokeIntervalInPrivateChats == LimitsConfiguration.timeIntervalForever {
canRemoveGlobally = true
}
}
if let user = chatPeer as? TelegramUser, user.botInfo == nil, canRemoveGlobally {
strongSelf.maybeAskForPeerChatRemoval(peer: peer, completion: { _ in }, removed: {})
} else {
let actionSheet = ActionSheetController(presentationTheme: strongSelf.presentationData.theme)
var items: [ActionSheetItem] = []
var canClear = true
var canStop = false
var deleteTitle = strongSelf.presentationData.strings.Common_Delete
if let channel = chatPeer as? TelegramChannel {
if case .broadcast = channel.info {
canClear = false
deleteTitle = strongSelf.presentationData.strings.Channel_LeaveChannel
} else {
deleteTitle = strongSelf.presentationData.strings.Group_LeaveGroup
}
if let addressName = channel.addressName, !addressName.isEmpty {
canClear = false
}
} else if let user = chatPeer as? TelegramUser, user.botInfo != nil {
canStop = !user.flags.contains(.isSupport)
canClear = user.botInfo == nil
deleteTitle = strongSelf.presentationData.strings.ChatList_DeleteChat
} else if let _ = chatPeer as? TelegramSecretChat {
deleteTitle = strongSelf.presentationData.strings.ChatList_DeleteChat
}
var canRemoveGlobally = false
let limitsConfiguration = strongSelf.context.currentLimitsConfiguration.with { $0 }
if chatPeer is TelegramUser && chatPeer.id != strongSelf.context.account.peerId {
if limitsConfiguration.maxMessageRevokeIntervalInPrivateChats == LimitsConfiguration.timeIntervalForever {
canRemoveGlobally = true
}
}
items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .delete, strings: strongSelf.presentationData.strings))
if canClear {
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.DialogList_ClearHistoryConfirmation, color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
guard let strongSelf = self else {
return
}
let beginClear: (InteractiveHistoryClearingType) -> Void = { type in
guard let strongSelf = self else {
return
}
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
var state = state
state.pendingClearHistoryPeerIds.insert(peer.peerId)
return state
})
strongSelf.forEachController({ controller in
if let controller = controller as? UndoOverlayController {
controller.dismissWithCommitActionAndReplacementAnimation()
}
return true
})
strongSelf.present(UndoOverlayController(context: strongSelf.context, content: .removedChat(text: strongSelf.presentationData.strings.Undo_ChatCleared), elevatedLayout: false, animateInAsReplacement: true, action: { shouldCommit in
guard let strongSelf = self else {
return
}
if shouldCommit {
let _ = clearHistoryInteractively(postbox: strongSelf.context.account.postbox, peerId: peerId, type: type).start(completed: {
guard let strongSelf = self else {
return
}
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
var state = state
state.pendingClearHistoryPeerIds.remove(peer.peerId)
return state
})
})
} else {
strongSelf.chatListDisplayNode.chatListNode.updateState({ state in
var state = state
state.pendingClearHistoryPeerIds.remove(peer.peerId)
return state
})
}
}), in: .current)
}
if canRemoveGlobally {
let actionSheet = ActionSheetController(presentationTheme: strongSelf.presentationData.theme)
var items: [ActionSheetItem] = []
items.append(DeleteChatPeerActionSheetItem(context: strongSelf.context, peer: mainPeer, chatPeer: chatPeer, action: .clearHistory, strings: strongSelf.presentationData.strings))
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak actionSheet] in
beginClear(.forEveryone)
actionSheet?.dismissAnimated()
}))
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in
beginClear(.forLocalPeer)
actionSheet?.dismissAnimated()
}))
actionSheet.setItemGroups([
ActionSheetItemGroup(items: items),
ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
})
])
])
strongSelf.present(actionSheet, in: .window(.root))
} else {
beginClear(.forLocalPeer)
}
}))
}
items.append(ActionSheetButtonItem(title: deleteTitle, color: .destructive, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
guard let strongSelf = self else {
return
}
strongSelf.maybeAskForPeerChatRemoval(peer: peer, completion: { _ in }, removed: {})
}))
if canStop {
items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.DialogList_DeleteBotConversationConfirmation, color: .destructive, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
if let strongSelf = self {
strongSelf.maybeAskForPeerChatRemoval(peer: peer, completion: { _ in
}, removed: {
guard let strongSelf = self else {
return
}
let _ = requestUpdatePeerIsBlocked(account: strongSelf.context.account, peerId: peer.peerId, isBlocked: true).start()
})
}
}))
}
actionSheet.setItemGroups([ActionSheetItemGroup(items: items),
ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
})
])
])
strongSelf.present(actionSheet, in: .window(.root))
}
})
}
public func maybeAskForPeerChatRemoval(peer: RenderedPeer, deleteGloballyIfPossible: Bool = false, completion: @escaping (Bool) -> Void, removed: @escaping () -> Void) {
guard let chatPeer = peer.peers[peer.peerId], let mainPeer = peer.chatMainPeer else {
completion(false)
@@ -1502,7 +1569,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
}
}
private func archiveChats(peerIds: [PeerId]) {
func archiveChats(peerIds: [PeerId]) {
guard !peerIds.isEmpty else {
return
}
@@ -11,6 +11,7 @@ import ActivityIndicator
import AccountContext
import SearchBarNode
import SearchUI
import ContextUI
private final class ChatListControllerNodeView: UITracingLayerView, PreviewingHostView {
var previewingDelegate: PreviewingHostViewDelegate? {
@@ -64,6 +65,7 @@ final class ChatListControllerNode: ASDisplayNode {
var requestOpenRecentPeerOptions: ((Peer) -> Void)?
var requestOpenMessageFromSearch: ((Peer, MessageId) -> Void)?
var requestAddContact: ((String) -> Void)?
var peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?
var dismissSelf: (() -> Void)?
var isEmptyUpdated: ((Bool) -> Void)?
@@ -272,7 +274,7 @@ final class ChatListControllerNode: ASDisplayNode {
if let requestAddContact = self?.requestAddContact {
requestAddContact(phoneNumber)
}
}), cancel: { [weak self] in
}, peerContextAction: self.peerContextAction), cancel: { [weak self] in
if let requestDeactivateSearch = self?.requestDeactivateSearch {
requestDeactivateSearch()
}
@@ -13,6 +13,7 @@ import SearchUI
import ContactsPeerItem
import ChatListSearchItemHeader
import ContactListUI
import ContextUI
private enum ChatListRecentEntryStableId: Hashable {
case topPeers
@@ -77,7 +78,7 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
}
}
func item(context: AccountContext, filter: ChatListNodePeersFilter, peerSelected: @escaping (Peer) -> Void, peerLongTapped: @escaping (Peer) -> Void, clearRecentlySearchedPeers: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, deletePeer: @escaping (PeerId) -> Void) -> ListViewItem {
func item(context: AccountContext, filter: ChatListNodePeersFilter, peerSelected: @escaping (Peer) -> Void, peerLongTapped: @escaping (Peer) -> Void, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, deletePeer: @escaping (PeerId) -> Void) -> ListViewItem {
switch self {
case let .topPeers(peers, theme, strings):
return ChatListRecentPeersListItem(theme: theme, strings: strings, account: context.account, peers: peers, peerSelected: { peer in
@@ -173,7 +174,13 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
if let chatPeer = peer.peer.peers[peer.peer.peerId] {
peerSelected(chatPeer)
}
}, setPeerIdWithRevealedOptions: setPeerIdWithRevealedOptions, deletePeer: deletePeer)
}, setPeerIdWithRevealedOptions: setPeerIdWithRevealedOptions, deletePeer: deletePeer, contextAction: peerContextAction.flatMap { peerContextAction in
return { node, gesture in
if let chatPeer = peer.peer.peers[peer.peer.peerId] {
peerContextAction(chatPeer, .recentSearch, node, gesture)
}
}
})
}
}
}
@@ -316,7 +323,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
}
}
public func item(context: AccountContext, enableHeaders: Bool, filter: ChatListNodePeersFilter, interaction: ChatListNodeInteraction) -> ListViewItem {
public func item(context: AccountContext, enableHeaders: Bool, filter: ChatListNodePeersFilter, interaction: ChatListNodeInteraction, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?) -> ListViewItem {
switch self {
case let .localPeer(peer, associatedPeer, unreadBadge, _, theme, strings, nameSortOrder, nameDisplayOrder):
let primaryPeer: Peer
@@ -372,6 +379,14 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
return ContactsPeerItem(theme: theme, strings: strings, sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: context.account, peerMode: .generalSearch, peer: .peer(peer: primaryPeer, chatPeer: chatPeer), status: .none, badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in
interaction.peerSelected(peer)
}, contextAction: peerContextAction.flatMap { peerContextAction in
return { node, gesture in
if let chatPeer = chatPeer {
peerContextAction(chatPeer, .search, node, gesture)
} else {
gesture?.cancel()
}
}
})
case let .globalPeer(peer, unreadBadge, _, theme, strings, nameSortOrder, nameDisplayOrder):
var enabled = true
@@ -416,6 +431,10 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
return ContactsPeerItem(theme: theme, strings: strings, sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: context.account, peerMode: .generalSearch, peer: .peer(peer: peer.peer, chatPeer: peer.peer), status: .addressName(suffixString), badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in
interaction.peerSelected(peer.peer)
}, contextAction: peerContextAction.flatMap { peerContextAction in
return { node, gesture in
peerContextAction(peer.peer, .search, node, gesture)
}
})
case let .message(message, peer, readState, presentationData):
return ChatListItem(presentationData: presentationData, context: context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: message.index), content: .peer(message: message, peer: peer, combinedReadState: readState, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: true), editing: false, hasActiveRevealControls: false, selected: false, header: enableHeaders ? ChatListSearchItemHeader(type: .messages, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) : nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
@@ -447,22 +466,22 @@ public struct ChatListSearchContainerTransition {
}
}
private func chatListSearchContainerPreparedRecentTransition(from fromEntries: [ChatListRecentEntry], to toEntries: [ChatListRecentEntry], context: AccountContext, filter: ChatListNodePeersFilter, peerSelected: @escaping (Peer) -> Void, peerLongTapped: @escaping (Peer) -> Void, clearRecentlySearchedPeers: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, deletePeer: @escaping (PeerId) -> Void) -> ChatListSearchContainerRecentTransition {
private func chatListSearchContainerPreparedRecentTransition(from fromEntries: [ChatListRecentEntry], to toEntries: [ChatListRecentEntry], context: AccountContext, filter: ChatListNodePeersFilter, peerSelected: @escaping (Peer) -> Void, peerLongTapped: @escaping (Peer) -> Void, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, deletePeer: @escaping (PeerId) -> Void) -> ChatListSearchContainerRecentTransition {
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, filter: filter, peerSelected: peerSelected, peerLongTapped: peerLongTapped, clearRecentlySearchedPeers: clearRecentlySearchedPeers, setPeerIdWithRevealedOptions: setPeerIdWithRevealedOptions, deletePeer: deletePeer), directionHint: nil) }
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, filter: filter, peerSelected: peerSelected, peerLongTapped: peerLongTapped, clearRecentlySearchedPeers: clearRecentlySearchedPeers, setPeerIdWithRevealedOptions: setPeerIdWithRevealedOptions, deletePeer: deletePeer), directionHint: nil) }
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, filter: filter, peerSelected: peerSelected, peerLongTapped: peerLongTapped, peerContextAction: peerContextAction, clearRecentlySearchedPeers: clearRecentlySearchedPeers, setPeerIdWithRevealedOptions: setPeerIdWithRevealedOptions, deletePeer: deletePeer), directionHint: nil) }
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, filter: filter, peerSelected: peerSelected, peerLongTapped: peerLongTapped, peerContextAction: peerContextAction, clearRecentlySearchedPeers: clearRecentlySearchedPeers, setPeerIdWithRevealedOptions: setPeerIdWithRevealedOptions, deletePeer: deletePeer), directionHint: nil) }
return ChatListSearchContainerRecentTransition(deletions: deletions, insertions: insertions, updates: updates)
}
public func chatListSearchContainerPreparedTransition(from fromEntries: [ChatListSearchEntry], to toEntries: [ChatListSearchEntry], displayingResults: Bool, context: AccountContext, enableHeaders: Bool, filter: ChatListNodePeersFilter, interaction: ChatListNodeInteraction) -> ChatListSearchContainerTransition {
public func chatListSearchContainerPreparedTransition(from fromEntries: [ChatListSearchEntry], to toEntries: [ChatListSearchEntry], displayingResults: Bool, context: AccountContext, enableHeaders: Bool, filter: ChatListNodePeersFilter, interaction: ChatListNodeInteraction, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?) -> ChatListSearchContainerTransition {
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, enableHeaders: enableHeaders, filter: filter, interaction: interaction), directionHint: nil) }
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, enableHeaders: enableHeaders, filter: filter, interaction: interaction), directionHint: nil) }
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, enableHeaders: enableHeaders, filter: filter, interaction: interaction, peerContextAction: peerContextAction), directionHint: nil) }
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, enableHeaders: enableHeaders, filter: filter, interaction: interaction, peerContextAction: peerContextAction), directionHint: nil) }
return ChatListSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, displayingResults: displayingResults)
}
@@ -517,6 +536,12 @@ private struct ChatListSearchMessagesContext {
let loadMoreIndex: MessageIndex?
}
public enum ChatListSearchContextActionSource {
case recentPeers
case recentSearch
case search
}
public final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
private let context: AccountContext
@@ -547,7 +572,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
private let filter: ChatListNodePeersFilter
public init(context: AccountContext, filter: ChatListNodePeersFilter, groupId: PeerGroupId, openPeer: @escaping (Peer, Bool) -> Void, openRecentPeerOptions: @escaping (Peer) -> Void, openMessage: @escaping (Peer, MessageId) -> Void, addContact: ((String) -> Void)?) {
public init(context: AccountContext, filter: ChatListNodePeersFilter, groupId: PeerGroupId, openPeer: @escaping (Peer, Bool) -> Void, openRecentPeerOptions: @escaping (Peer) -> Void, openMessage: @escaping (Peer, MessageId) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((Peer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?) {
self.context = context
self.filter = filter
self.dimNode = ASDisplayNode()
@@ -847,6 +872,19 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
}, updatePeerGrouping: { _, _ in
}, togglePeerMarkedUnread: { _, _ in
}, toggleArchivedFolderHiddenByDefault: {
}, activateChatPreview: { item, node, gesture in
guard let peerContextAction = peerContextAction else {
gesture?.cancel()
return
}
switch item.content {
case let .peer(peer):
if let peer = peer.peer.peer {
peerContextAction(peer, .search, node, gesture)
}
case .groupReference:
gesture?.cancel()
}
})
let previousRecentItems = Atomic<[ChatListRecentEntry]?>(value: nil)
@@ -933,7 +971,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
self?.recentListNode.clearHighlightAnimated(true)
}, peerLongTapped: { peer in
openRecentPeerOptions(peer)
}, clearRecentlySearchedPeers: {
}, peerContextAction: peerContextAction,
clearRecentlySearchedPeers: {
self?.clearRecentSearch()
}, setPeerIdWithRevealedOptions: { peerId, fromPeerId in
interaction.setPeerIdWithRevealedOptions(peerId, fromPeerId)
@@ -954,7 +993,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
let previousEntries = previousSearchItems.swap(entriesAndFlags?.0)
let firstTime = previousEntries == nil
let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entriesAndFlags?.0 ?? [], displayingResults: entriesAndFlags?.0 != nil, context: context, enableHeaders: true, filter: filter, interaction: interaction)
let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entriesAndFlags?.0 ?? [], displayingResults: entriesAndFlags?.0 != nil, context: context, enableHeaders: true, filter: filter, interaction: interaction, peerContextAction: peerContextAction)
strongSelf.enqueueTransition(transition, firstTime: firstTime)
}
}))
@@ -15,6 +15,7 @@ import LocalizedPeerData
import PeerPresenceStatusManager
import PhotoResources
import ChatListSearchItemNode
import ContextUI
public enum ChatListItemContent {
case peer(message: Message?, peer: RenderedPeer, combinedReadState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, embeddedState: PeerChatListEmbeddedInterfaceState?, inputActivities: [(Peer, PeerInputActivity)]?, isAd: Bool, ignoreUnreadBadge: Bool)
@@ -294,6 +295,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
private let backgroundNode: ASDisplayNode
private let highlightedBackgroundNode: ASDisplayNode
let contextContainer: ContextControllerSourceNode
let avatarNode: AvatarNode
let titleNode: TextNode
let authorNode: TextNode
@@ -410,6 +413,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
self.highlightedBackgroundNode = ASDisplayNode()
self.highlightedBackgroundNode.isLayerBacked = true
self.contextContainer = ContextControllerSourceNode()
self.titleNode = TextNode()
self.titleNode.isUserInteractionEnabled = false
self.titleNode.displaysAsynchronously = true
@@ -457,19 +462,22 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
self.addSubnode(self.backgroundNode)
self.addSubnode(self.separatorNode)
self.addSubnode(self.avatarNode)
self.addSubnode(self.onlineNode)
self.addSubnode(self.titleNode)
self.addSubnode(self.authorNode)
self.addSubnode(self.textNode)
self.addSubnode(self.contentImageNode)
self.addSubnode(self.dateNode)
self.addSubnode(self.statusNode)
self.addSubnode(self.pinnedIconNode)
self.addSubnode(self.badgeNode)
self.addSubnode(self.mentionBadgeNode)
self.addSubnode(self.mutedIconNode)
self.addSubnode(self.contextContainer)
self.contextContainer.addSubnode(self.avatarNode)
self.contextContainer.addSubnode(self.onlineNode)
self.contextContainer.addSubnode(self.titleNode)
self.contextContainer.addSubnode(self.authorNode)
self.contextContainer.addSubnode(self.textNode)
self.contextContainer.addSubnode(self.contentImageNode)
self.contextContainer.addSubnode(self.dateNode)
self.contextContainer.addSubnode(self.statusNode)
self.contextContainer.addSubnode(self.pinnedIconNode)
self.contextContainer.addSubnode(self.badgeNode)
self.contextContainer.addSubnode(self.mentionBadgeNode)
self.contextContainer.addSubnode(self.mutedIconNode)
self.peerPresenceManager = PeerPresenceStatusManager(update: { [weak self] in
if let strongSelf = self, let layoutParams = strongSelf.layoutParams {
@@ -477,6 +485,13 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let _ = apply(false, false)
}
})
self.contextContainer.activated = { [weak self] gesture in
guard let strongSelf = self, let item = strongSelf.item else {
return
}
item.interaction.activateChatPreview(item, strongSelf, gesture)
}
}
func setupItem(item: ChatListItem, synchronousLoads: Bool) {
@@ -1110,6 +1125,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
strongSelf.layoutParams = (item, first, last, firstWithHeader, nextIsPinned, params, countersSize)
strongSelf.contentImageMedia = contentImageMedia
strongSelf.contextContainer.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
var dimensions: CGSize?
if let contentImageMedia = contentImageMedia as? TelegramMediaImage {
dimensions = largestRepresentationForPhoto(contentImageMedia)?.dimensions
@@ -1293,7 +1310,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
iconNode.isLayerBacked = true
iconNode.displaysAsynchronously = false
iconNode.displayWithoutProcessing = true
strongSelf.addSubnode(iconNode)
strongSelf.contextContainer.addSubnode(iconNode)
strongSelf.secretIconNode = iconNode
}
iconNode.image = currentSecretIconImage
@@ -1315,7 +1332,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
iconNode.isLayerBacked = true
iconNode.displaysAsynchronously = false
iconNode.displayWithoutProcessing = true
strongSelf.addSubnode(iconNode)
strongSelf.contextContainer.addSubnode(iconNode)
strongSelf.credibilityIconNode = iconNode
}
iconNode.image = currentCredibilityIconImage
@@ -1352,7 +1369,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var animateInputActivitiesFrame = false
if let inputActivities = inputActivities, !inputActivities.isEmpty {
if strongSelf.inputActivitiesNode.supernode == nil {
strongSelf.addSubnode(strongSelf.inputActivitiesNode)
strongSelf.contextContainer.addSubnode(strongSelf.inputActivitiesNode)
} else {
animateInputActivitiesFrame = true
}
@@ -10,6 +10,7 @@ import TelegramUIPreferences
import AccountContext
import TelegramNotices
import ContactsPeerItem
import ContextUI
public enum ChatListNodeMode {
case chatList
@@ -54,10 +55,11 @@ public final class ChatListNodeInteraction {
let updatePeerGrouping: (PeerId, Bool) -> Void
let togglePeerMarkedUnread: (PeerId, Bool) -> Void
let toggleArchivedFolderHiddenByDefault: () -> Void
let activateChatPreview: (ChatListItem, ASDisplayNode, ContextGesture?) -> Void
var highlightedChatLocation: ChatListHighlightedLocation?
public init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (PeerId) -> Void, messageSelected: @escaping (Peer, Message, Bool) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void) {
public init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (PeerId) -> Void, messageSelected: @escaping (Peer, Message, Bool) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void) {
self.activateSearch = activateSearch
self.peerSelected = peerSelected
self.togglePeerSelected = togglePeerSelected
@@ -71,6 +73,7 @@ public final class ChatListNodeInteraction {
self.updatePeerGrouping = updatePeerGrouping
self.togglePeerMarkedUnread = togglePeerMarkedUnread
self.toggleArchivedFolderHiddenByDefault = toggleArchivedFolderHiddenByDefault
self.activateChatPreview = activateChatPreview
}
}
@@ -313,6 +316,7 @@ public final class ChatListNode: ListView {
public var updatePeerGrouping: ((PeerId, Bool) -> Void)?
public var presentAlert: ((String) -> Void)?
public var toggleArchivedFolderHiddenByDefault: (() -> Void)?
public var activateChatPreview: ((ChatListItem, ASDisplayNode, ContextGesture?) -> Void)?
private var theme: PresentationTheme
@@ -469,6 +473,15 @@ public final class ChatListNode: ListView {
})
}, toggleArchivedFolderHiddenByDefault: { [weak self] in
self?.toggleArchivedFolderHiddenByDefault?()
}, activateChatPreview: { [weak self] item, node, gesture in
guard let strongSelf = self else {
return
}
if let activateChatPreview = strongSelf.activateChatPreview {
activateChatPreview(item, node, gesture)
} else {
gesture?.cancel()
}
})
let viewProcessingQueue = self.viewProcessingQueue
@@ -348,6 +348,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E293594DCC000000000</key>
<dict>
<key>isa</key>
@@ -529,6 +542,7 @@
<string>1DD70E290F1A3C6400000000</string>
<string>1DD70E29DB6520C800000000</string>
<string>1DD70E29CBE117ED00000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E293594DCC000000000</string>
<string>1DD70E29D65BA68200000000</string>
<string>1DD70E2925BBFEEE00000000</string>
@@ -792,6 +806,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -837,6 +858,7 @@
<string>E7A30F04C37F741500000000</string>
<string>E7A30F04BF0846EE00000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
<string>E7A30F045A26607D00000000</string>
</array>
+1
View File
@@ -25,6 +25,7 @@ static_library(
"//submodules/AlertUI:AlertUI",
"//submodules/ShareController:ShareController",
"//submodules/AppBundle:AppBundle",
"//submodules/OverlayStatusController:OverlayStatusController",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
@@ -102,6 +102,17 @@
<key>sourceTree</key>
<string>SOURCE_ROOT</string>
</dict>
<key>1DD70E2925DE918A00000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>ContactContextMenus.swift</string>
<key>path</key>
<string>Sources/ContactContextMenus.swift</string>
<key>sourceTree</key>
<string>SOURCE_ROOT</string>
</dict>
<key>1DD70E29CD71432C00000000</key>
<dict>
<key>isa</key>
@@ -212,6 +223,7 @@
<key>children</key>
<array>
<string>1DD70E29C95D7ED900000000</string>
<string>1DD70E2925DE918A00000000</string>
<string>1DD70E29CD71432C00000000</string>
<string>1DD70E29C8C4BFFB00000000</string>
<string>1DD70E29FD9D368500000000</string>
@@ -380,6 +392,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E296C9831F100000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libContextUI.a</string>
<key>path</key>
<string>libContextUI.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E292417E0B200000000</key>
<dict>
<key>isa</key>
@@ -523,6 +548,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29BBAF750C00000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libOverlayStatusController.a</string>
<key>path</key>
<string>libOverlayStatusController.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29928D142900000000</key>
<dict>
<key>isa</key>
@@ -614,6 +652,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29AD66967300000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libReactionSelectionNode.a</string>
<key>path</key>
<string>libReactionSelectionNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E291D58331200000000</key>
<dict>
<key>isa</key>
@@ -653,6 +704,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29097DBE9200000000</key>
<dict>
<key>isa</key>
@@ -861,6 +925,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29F40A586F00000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libTextSelectionNode.a</string>
<key>path</key>
<string>libTextSelectionNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29CD296A8300000000</key>
<dict>
<key>isa</key>
@@ -921,6 +998,7 @@
<string>1DD70E297AB043C800000000</string>
<string>1DD70E2997B4D6D800000000</string>
<string>1DD70E2931752C6000000000</string>
<string>1DD70E296C9831F100000000</string>
<string>1DD70E292417E0B200000000</string>
<string>1DD70E295915423000000000</string>
<string>1DD70E29FF334B1F00000000</string>
@@ -932,6 +1010,7 @@
<string>1DD70E29CE34063500000000</string>
<string>1DD70E2936DE2CF900000000</string>
<string>1DD70E29C37F741500000000</string>
<string>1DD70E29BBAF750C00000000</string>
<string>1DD70E29928D142900000000</string>
<string>1DD70E29D233F68C00000000</string>
<string>1DD70E291631B91900000000</string>
@@ -939,9 +1018,11 @@
<string>1DD70E29DB6520C800000000</string>
<string>1DD70E29CBE117ED00000000</string>
<string>1DD70E293E4DE92B00000000</string>
<string>1DD70E29AD66967300000000</string>
<string>1DD70E291D58331200000000</string>
<string>1DD70E29EA9CBB5B00000000</string>
<string>1DD70E291328E99400000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E29097DBE9200000000</string>
<string>1DD70E2981AE180900000000</string>
<string>1DD70E29524F478E00000000</string>
@@ -958,6 +1039,7 @@
<string>1DD70E292395015100000000</string>
<string>1DD70E29BA06E3A600000000</string>
<string>1DD70E29F0C15F9C00000000</string>
<string>1DD70E29F40A586F00000000</string>
<string>1DD70E29CD296A8300000000</string>
<string>1DD70E2951398CF200000000</string>
<string>1DD70E29597BAFBB00000000</string>
@@ -1012,6 +1094,13 @@
<key>fileRef</key>
<string>1DD70E29C95D7ED900000000</string>
</dict>
<key>E7A30F0425DE918A00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2925DE918A00000000</string>
</dict>
<key>E7A30F04CD71432C00000000</key>
<dict>
<key>isa</key>
@@ -1082,6 +1171,7 @@
<key>files</key>
<array>
<string>E7A30F04C95D7ED900000000</string>
<string>E7A30F0425DE918A00000000</string>
<string>E7A30F04CD71432C00000000</string>
<string>E7A30F04C8C4BFFB00000000</string>
<string>E7A30F04FD9D368500000000</string>
@@ -1289,6 +1379,27 @@
<key>fileRef</key>
<string>1DD70E2997B4D6D800000000</string>
</dict>
<key>E7A30F04AD66967300000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29AD66967300000000</string>
</dict>
<key>E7A30F04F40A586F00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29F40A586F00000000</string>
</dict>
<key>E7A30F046C9831F100000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E296C9831F100000000</string>
</dict>
<key>E7A30F04C37F741500000000</key>
<dict>
<key>isa</key>
@@ -1303,6 +1414,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -1338,6 +1456,13 @@
<key>fileRef</key>
<string>1DD70E2931752C6000000000</string>
</dict>
<key>E7A30F04BBAF750C00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29BBAF750C00000000</string>
</dict>
<key>E7A30F041328E99400000000</key>
<dict>
<key>isa</key>
@@ -1484,13 +1609,18 @@
<string>E7A30F042420028600000000</string>
<string>E7A30F04A54A195300000000</string>
<string>E7A30F0497B4D6D800000000</string>
<string>E7A30F04AD66967300000000</string>
<string>E7A30F04F40A586F00000000</string>
<string>E7A30F046C9831F100000000</string>
<string>E7A30F04C37F741500000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
<string>E7A30F045A26607D00000000</string>
<string>E7A30F04D233F68C00000000</string>
<string>E7A30F049A24C4DE00000000</string>
<string>E7A30F0431752C6000000000</string>
<string>E7A30F04BBAF750C00000000</string>
<string>E7A30F041328E99400000000</string>
<string>E7A30F041E16CC6C00000000</string>
<string>E7A30F04928D142900000000</string>
@@ -0,0 +1,131 @@
import Foundation
import UIKit
import SwiftSignalKit
import ContextUI
import AccountContext
import Postbox
import TelegramCore
import Display
import AlertUI
import OverlayStatusController
func contactContextMenuItems(context: AccountContext, peerId: PeerId, contactsController: ContactsController?) -> Signal<[ContextMenuItem], NoError> {
let strings = context.sharedContext.currentPresentationData.with({ $0 }).strings
return context.account.postbox.transaction { [weak contactsController] transaction -> [ContextMenuItem] in
var items: [ContextMenuItem] = []
items.append(.action(ContextMenuActionItem(text: strings.ContactList_Context_SendMessage, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Message"), color: theme.contextMenu.primaryColor) }, action: { _, f in
if let contactsController = contactsController, let navigationController = contactsController.navigationController as? NavigationController {
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId)))
}
f(.default)
})))
items.append(.action(ContextMenuActionItem(text: strings.ContactList_Context_StartSecretChat, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Timer"), color: theme.contextMenu.primaryColor) }, action: { _, f in
let _ = (context.account.postbox.transaction { transaction -> PeerId? in
let filteredPeerIds = Array(transaction.getAssociatedPeerIds(peerId)).filter { $0.namespace == Namespaces.Peer.SecretChat }
var activeIndices: [ChatListIndex] = []
for associatedId in filteredPeerIds {
if let state = (transaction.getPeer(associatedId) as? TelegramSecretChat)?.embeddedState {
switch state {
case .active, .handshake:
if let (_, index) = transaction.getPeerChatListIndex(associatedId) {
activeIndices.append(index)
}
default:
break
}
}
}
activeIndices.sort()
if let index = activeIndices.last {
return index.messageIndex.id.peerId
} else {
return nil
}
}
|> deliverOnMainQueue).start(next: { currentPeerId in
if let currentPeerId = currentPeerId {
if let contactsController = contactsController, let navigationController = (contactsController.navigationController as? NavigationController) {
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(currentPeerId)))
}
} else {
var createSignal = createSecretChat(account: context.account, peerId: peerId)
var cancelImpl: (() -> Void)?
let progressSignal = Signal<Never, NoError> { subscriber in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: {
cancelImpl?()
}))
contactsController?.present(controller, in: .window(.root))
return ActionDisposable { [weak controller] in
Queue.mainQueue().async() {
controller?.dismiss()
}
}
}
|> runOn(Queue.mainQueue())
|> delay(0.15, queue: Queue.mainQueue())
let progressDisposable = progressSignal.start()
createSignal = createSignal
|> afterDisposed {
Queue.mainQueue().async {
progressDisposable.dispose()
}
}
let createSecretChatDisposable = MetaDisposable()
cancelImpl = {
createSecretChatDisposable.set(nil)
}
createSecretChatDisposable.set((createSignal
|> deliverOnMainQueue).start(next: { peerId in
if let navigationController = (contactsController?.navigationController as? NavigationController) {
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId)))
}
}, error: { _ in
if let contactsController = contactsController {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
contactsController.present(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}
}))
}
})
f(.default)
})))
var canCall = true
if peerId.namespace == Namespaces.Peer.CloudUser, let cachedUserData = transaction.getPeerCachedData(peerId: peerId) as? CachedUserData, cachedUserData.callsPrivate {
canCall = false
}
if canCall {
items.append(.action(ContextMenuActionItem(text: strings.ContactList_Context_Call, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Call"), color: theme.contextMenu.primaryColor) }, action: { _, f in
if let contactsController = contactsController {
let callResult = context.sharedContext.callManager?.requestCall(account: context.account, peerId: peerId, endCurrentIfAny: false)
if let callResult = callResult, case let .alreadyInProgress(currentPeerId) = callResult {
if currentPeerId == peerId {
context.sharedContext.navigateToCurrentCall()
} else {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let _ = (context.account.postbox.transaction { transaction -> (Peer?, Peer?) in
return (transaction.getPeer(peerId), transaction.getPeer(currentPeerId))
}
|> deliverOnMainQueue).start(next: { [weak contactsController] peer, current in
if let contactsController = contactsController, let peer = peer, let current = current {
contactsController.present(textAlertController(context: context, title: presentationData.strings.Call_CallInProgressTitle, text: presentationData.strings.Call_CallInProgressMessage(current.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {
let _ = context.sharedContext.callManager?.requestCall(account: context.account, peerId: peerId, endCurrentIfAny: true)
})]), in: .window(.root))
}
})
}
}
}
f(.default)
})))
}
return items
}
}
@@ -20,6 +20,7 @@ import ChatListSearchItemHeader
import SearchUI
import TelegramPermissionsUI
import AppBundle
import ContextUI
private let dropDownIcon = { () -> UIImage in
UIGraphicsBeginImageContextWithOptions(CGSize(width: 12.0, height: 12.0), false, 0.0)
@@ -98,15 +99,17 @@ private final class ContactListNodeInteraction {
fileprivate let authorize: () -> Void
fileprivate let suppressWarning: () -> Void
fileprivate let openPeer: (ContactListPeer) -> Void
fileprivate let contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)?
let itemHighlighting = ContactItemHighlighting()
init(activateSearch: @escaping () -> Void, openSortMenu: @escaping () -> Void, authorize: @escaping () -> Void, suppressWarning: @escaping () -> Void, openPeer: @escaping (ContactListPeer) -> Void) {
init(activateSearch: @escaping () -> Void, openSortMenu: @escaping () -> Void, authorize: @escaping () -> Void, suppressWarning: @escaping () -> Void, openPeer: @escaping (ContactListPeer) -> Void, contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)?) {
self.activateSearch = activateSearch
self.openSortMenu = openSortMenu
self.authorize = authorize
self.suppressWarning = suppressWarning
self.openPeer = openPeer
self.contextAction = contextAction
}
}
@@ -175,8 +178,10 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
case let .peer(_, peer, presence, header, selection, theme, strings, dateTimeFormat, nameSortOrder, nameDisplayOrder, enabled):
let status: ContactsPeerItemStatus
let itemPeer: ContactsPeerItemPeer
var isContextActionEnabled = false
switch peer {
case let .peer(peer, isGlobal, participantCount):
isContextActionEnabled = true
if isGlobal, let _ = peer.addressName {
status = .addressName("")
} else {
@@ -200,9 +205,22 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
status = .none
itemPeer = .deviceContact(stableId: id, contact: contact)
}
var itemContextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
if isContextActionEnabled, let contextAction = interaction.contextAction {
itemContextAction = { node, gesture in
switch itemPeer {
case let .peer(peer, _):
if let peer = peer {
contextAction(peer, node, gesture)
}
case .deviceContact:
break
}
}
}
return ContactsPeerItem(theme: theme, strings: strings, sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .peer, peer: itemPeer, status: status, enabled: enabled, selection: selection, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in
interaction.openPeer(peer)
}, itemHighlighting: interaction.itemHighlighting)
}, itemHighlighting: interaction.itemHighlighting, contextAction: itemContextAction)
}
}
@@ -744,6 +762,7 @@ public final class ContactListNode: ASDisplayNode {
public var openPeer: ((ContactListPeer) -> Void)?
public var openPrivacyPolicy: (() -> Void)?
public var suppressPermissionWarning: (() -> Void)?
private let contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)?
private let previousEntries = Atomic<[ContactListNodeEntry]?>(value: nil)
private let disposable = MetaDisposable()
@@ -755,10 +774,11 @@ public final class ContactListNode: ASDisplayNode {
private var authorizationNode: PermissionContentNode
private let displayPermissionPlaceholder: Bool
public init(context: AccountContext, presentation: Signal<ContactListPresentation, NoError>, filters: [ContactListFilter] = [.excludeSelf], selectionState: ContactListNodeGroupSelectionState? = nil, displayPermissionPlaceholder: Bool = true, displaySortOptions: Bool = false) {
public init(context: AccountContext, presentation: Signal<ContactListPresentation, NoError>, filters: [ContactListFilter] = [.excludeSelf], selectionState: ContactListNodeGroupSelectionState? = nil, displayPermissionPlaceholder: Bool = true, displaySortOptions: Bool = false, contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)? = nil) {
self.context = context
self.filters = filters
self.displayPermissionPlaceholder = displayPermissionPlaceholder
self.contextAction = contextAction
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
@@ -826,7 +846,7 @@ public final class ContactListNode: ASDisplayNode {
self?.suppressPermissionWarning?()
}, openPeer: { [weak self] peer in
self?.openPeer?(peer)
})
}, contextAction: contextAction)
self.indexNode.indexSelected = { [weak self] section in
guard let strongSelf = self, let layout = strongSelf.validLayout, let entries = previousEntries.with({ $0 }) else {
@@ -12,6 +12,7 @@ import AccountContext
import SearchBarNode
import SearchUI
import AppBundle
import ContextUI
private final class ContactsControllerNodeView: UITracingLayerView, PreviewingHostView {
var previewingDelegate: PreviewingHostViewDelegate? {
@@ -25,6 +26,27 @@ private final class ContactsControllerNodeView: UITracingLayerView, PreviewingHo
weak var controller: ContactsController?
}
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
let controller: ViewController
weak var sourceNode: ASDisplayNode?
init(controller: ViewController, sourceNode: ASDisplayNode?) {
self.controller = controller
self.sourceNode = sourceNode
}
func transitionInfo() -> ContextControllerTakeControllerInfo? {
let sourceNode = self.sourceNode
return ContextControllerTakeControllerInfo(contentAreaInScreenSpace: CGRect(origin: CGPoint(), size: CGSize(width: 10.0, height: 10.0)), sourceNode: { [weak sourceNode] in
if let sourceNode = sourceNode {
return (sourceNode, sourceNode.bounds)
} else {
return nil
}
})
}
}
final class ContactsControllerNode: ASDisplayNode {
let contactListNode: ContactListNode
@@ -69,7 +91,11 @@ final class ContactsControllerNode: ASDisplayNode {
}
}
self.contactListNode = ContactListNode(context: context, presentation: presentation, displaySortOptions: true)
var contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)?
self.contactListNode = ContactListNode(context: context, presentation: presentation, displaySortOptions: true, contextAction: { peer, node, gesture in
contextAction?(peer, node, gesture)
})
super.init()
@@ -106,6 +132,16 @@ final class ContactsControllerNode: ASDisplayNode {
strongSelf.openInvite?()
}
}
contextAction = { [weak self] peer, node, gesture in
guard let strongSelf = self, let contactsController = strongSelf.controller else {
return
}
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.id), subject: nil, botStart: nil, mode: .standard(previewing: true))
chatController.canReadHistory.set(false)
let contextController = ContextController(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: contactContextMenuItems(context: strongSelf.context, peerId: peer.id, contactsController: contactsController), reactionItems: [], gesture: gesture)
contactsController.presentInGlobalOverlay(contextController)
}
}
deinit {
+1
View File
@@ -21,6 +21,7 @@ static_library(
"//submodules/ItemListUI:ItemListUI",
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
"//submodules/ListSectionHeaderNode:ListSectionHeaderNode",
"//submodules/ContextUI:ContextUI",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
@@ -206,6 +206,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E296C9831F100000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libContextUI.a</string>
<key>path</key>
<string>libContextUI.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E295915423000000000</key>
<dict>
<key>isa</key>
@@ -375,6 +388,32 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29AD66967300000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libReactionSelectionNode.a</string>
<key>path</key>
<string>libReactionSelectionNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E293594DCC000000000</key>
<dict>
<key>isa</key>
@@ -505,6 +544,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29F40A586F00000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libTextSelectionNode.a</string>
<key>path</key>
<string>libTextSelectionNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29CD296A8300000000</key>
<dict>
<key>isa</key>
@@ -546,6 +598,7 @@
<string>1DD70E292420028600000000</string>
<string>1DD70E29A54A195300000000</string>
<string>1DD70E2997B4D6D800000000</string>
<string>1DD70E296C9831F100000000</string>
<string>1DD70E295915423000000000</string>
<string>1DD70E29FF334B1F00000000</string>
<string>1DD70E29D6F14E1000000000</string>
@@ -559,6 +612,8 @@
<string>1DD70E290F1A3C6400000000</string>
<string>1DD70E29DB6520C800000000</string>
<string>1DD70E29CBE117ED00000000</string>
<string>1DD70E29AD66967300000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E293594DCC000000000</string>
<string>1DD70E29D65BA68200000000</string>
<string>1DD70E2925BBFEEE00000000</string>
@@ -569,6 +624,7 @@
<string>1DD70E292395015100000000</string>
<string>1DD70E29BA06E3A600000000</string>
<string>1DD70E29F0C15F9C00000000</string>
<string>1DD70E29F40A586F00000000</string>
<string>1DD70E29CD296A8300000000</string>
<string>1DD70E2951398CF200000000</string>
</array>
@@ -793,6 +849,27 @@
<key>fileRef</key>
<string>1DD70E2997B4D6D800000000</string>
</dict>
<key>E7A30F04AD66967300000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29AD66967300000000</string>
</dict>
<key>E7A30F04F40A586F00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29F40A586F00000000</string>
</dict>
<key>E7A30F046C9831F100000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E296C9831F100000000</string>
</dict>
<key>E7A30F04C37F741500000000</key>
<dict>
<key>isa</key>
@@ -814,6 +891,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -877,9 +961,13 @@
<string>E7A30F042420028600000000</string>
<string>E7A30F04A54A195300000000</string>
<string>E7A30F0497B4D6D800000000</string>
<string>E7A30F04AD66967300000000</string>
<string>E7A30F04F40A586F00000000</string>
<string>E7A30F046C9831F100000000</string>
<string>E7A30F04C37F741500000000</string>
<string>E7A30F04BF0846EE00000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
<string>E7A30F045A26607D00000000</string>
<string>E7A30F04D233F68C00000000</string>
@@ -14,6 +14,7 @@ import TelegramStringFormatting
import AccountContext
import PeerPresenceStatusManager
import ItemListPeerItem
import ContextUI
public final class ContactItemHighlighting {
public var chatLocation: ChatLocation?
@@ -125,6 +126,7 @@ public class ContactsPeerItem: ListViewItem, ListViewItemWithHeader {
let setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)?
let deletePeer: ((PeerId) -> Void)?
let itemHighlighting: ContactItemHighlighting?
let contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
public let selectable: Bool
@@ -132,7 +134,7 @@ public class ContactsPeerItem: ListViewItem, ListViewItemWithHeader {
public let header: ListViewItemHeader?
public init(theme: PresentationTheme, strings: PresentationStrings, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, account: Account, peerMode: ContactsPeerItemPeerMode, peer: ContactsPeerItemPeer, status: ContactsPeerItemStatus, badge: ContactsPeerItemBadge? = nil, enabled: Bool, selection: ContactsPeerItemSelection, editing: ContactsPeerItemEditing, options: [ItemListPeerItemRevealOption] = [], actionIcon: ContactsPeerItemActionIcon = .none, index: PeerNameIndex?, header: ListViewItemHeader?, action: @escaping (ContactsPeerItemPeer) -> Void, setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)? = nil, deletePeer: ((PeerId) -> Void)? = nil, itemHighlighting: ContactItemHighlighting? = nil) {
public init(theme: PresentationTheme, strings: PresentationStrings, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, account: Account, peerMode: ContactsPeerItemPeerMode, peer: ContactsPeerItemPeer, status: ContactsPeerItemStatus, badge: ContactsPeerItemBadge? = nil, enabled: Bool, selection: ContactsPeerItemSelection, editing: ContactsPeerItemEditing, options: [ItemListPeerItemRevealOption] = [], actionIcon: ContactsPeerItemActionIcon = .none, index: PeerNameIndex?, header: ListViewItemHeader?, action: @escaping (ContactsPeerItemPeer) -> Void, setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)? = nil, deletePeer: ((PeerId) -> Void)? = nil, itemHighlighting: ContactItemHighlighting? = nil, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil) {
self.theme = theme
self.strings = strings
self.sortOrder = sortOrder
@@ -153,6 +155,7 @@ public class ContactsPeerItem: ListViewItem, ListViewItemWithHeader {
self.header = header
self.itemHighlighting = itemHighlighting
self.selectable = enabled
self.contextAction = contextAction
if let index = index {
var letter: String = "#"
@@ -283,6 +286,8 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
private let separatorNode: ASDisplayNode
private let highlightedBackgroundNode: ASDisplayNode
private let containerNode: ContextControllerSourceNode
private let avatarNode: AvatarNode
private let titleNode: TextNode
private var verificationIconNode: ASImageNode?
@@ -325,6 +330,8 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
self.highlightedBackgroundNode = ASDisplayNode()
self.highlightedBackgroundNode.isLayerBacked = true
self.containerNode = ContextControllerSourceNode()
self.avatarNode = AvatarNode(font: avatarFont)
self.avatarNode.isLayerBacked = !smartInvertColorsEnabled()
@@ -337,9 +344,10 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
self.addSubnode(self.backgroundNode)
self.addSubnode(self.separatorNode)
self.addSubnode(self.avatarNode)
self.addSubnode(self.titleNode)
self.addSubnode(self.statusNode)
self.addSubnode(self.containerNode)
self.containerNode.addSubnode(self.avatarNode)
self.containerNode.addSubnode(self.titleNode)
self.containerNode.addSubnode(self.statusNode)
self.peerPresenceManager = PeerPresenceStatusManager(update: { [weak self] in
if let strongSelf = self, let layoutParams = strongSelf.layoutParams {
@@ -347,6 +355,14 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
let _ = apply()
}
})
self.containerNode.activated = { [weak self] gesture in
guard let strongSelf = self, let item = strongSelf.item, let contextAction = item.contextAction else {
gesture.cancel()
return
}
contextAction(strongSelf.containerNode, gesture)
}
}
override public func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) {
@@ -639,6 +655,9 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
strongSelf.accessibilityLabel = titleAttributedString?.string
strongSelf.accessibilityValue = statusAttributedString?.string
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: nodeLayout.contentSize)
strongSelf.containerNode.isGestureEnabled = item.contextAction != nil
switch item.peer {
case let .peer(peer, _):
if let peer = peer {
@@ -701,7 +720,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
verificationIconNode.displayWithoutProcessing = true
verificationIconNode.displaysAsynchronously = false
strongSelf.verificationIconNode = verificationIconNode
strongSelf.addSubnode(verificationIconNode)
strongSelf.containerNode.addSubnode(verificationIconNode)
}
if let verificationIconNode = strongSelf.verificationIconNode {
verificationIconNode.image = verificationIconImage
@@ -720,7 +739,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
actionIconNode.displayWithoutProcessing = true
actionIconNode.displaysAsynchronously = false
strongSelf.actionIconNode = actionIconNode
strongSelf.addSubnode(actionIconNode)
strongSelf.containerNode.addSubnode(actionIconNode)
}
if let actionIconNode = strongSelf.actionIconNode {
actionIconNode.image = actionIconImage
@@ -744,7 +763,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
badgeBackgroundNode.isLayerBacked = true
badgeBackgroundNode.displaysAsynchronously = false
badgeBackgroundNode.displayWithoutProcessing = true
strongSelf.addSubnode(badgeBackgroundNode)
strongSelf.containerNode.addSubnode(badgeBackgroundNode)
strongSelf.badgeBackgroundNode = badgeBackgroundNode
badgeTransition = .immediate
}
@@ -758,7 +777,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
let badgeTextNode = badgeTextApply()
if badgeTextNode !== strongSelf.badgeTextNode {
strongSelf.badgeTextNode?.removeFromSupernode()
strongSelf.addSubnode(badgeTextNode)
strongSelf.containerNode.addSubnode(badgeTextNode)
strongSelf.badgeTextNode = badgeTextNode
}
+1
View File
@@ -6,6 +6,7 @@ static_library(
"Sources/**/*.swift",
]),
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
"//submodules/AsyncDisplayKit:AsyncDisplayKit#shared",
"//submodules/Display:Display#shared",
"//submodules/TelegramPresentationData:TelegramPresentationData",
@@ -146,6 +146,28 @@
<key>sourceTree</key>
<string>SOURCE_ROOT</string>
</dict>
<key>1DD70E293607624D00000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>ContextControllerSourceNode.swift</string>
<key>path</key>
<string>Sources/ContextControllerSourceNode.swift</string>
<key>sourceTree</key>
<string>SOURCE_ROOT</string>
</dict>
<key>1DD70E2942A6CFDF00000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>ContextGesture.swift</string>
<key>path</key>
<string>Sources/ContextGesture.swift</string>
<key>sourceTree</key>
<string>SOURCE_ROOT</string>
</dict>
<key>B401C979EAB5339800000000</key>
<dict>
<key>isa</key>
@@ -161,6 +183,8 @@
<string>1DD70E2984D0F63E00000000</string>
<string>1DD70E2972528DAC00000000</string>
<string>1DD70E298B1C7CF000000000</string>
<string>1DD70E293607624D00000000</string>
<string>1DD70E2942A6CFDF00000000</string>
</array>
</dict>
<key>B401C9795F32B06300000000</key>
@@ -448,6 +472,20 @@
<key>fileRef</key>
<string>1DD70E298B1C7CF000000000</string>
</dict>
<key>E7A30F043607624D00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E293607624D00000000</string>
</dict>
<key>E7A30F0442A6CFDF00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2942A6CFDF00000000</string>
</dict>
<key>1870857F0000000000000000</key>
<dict>
<key>isa</key>
@@ -459,6 +497,8 @@
<string>E7A30F0484D0F63E00000000</string>
<string>E7A30F0472528DAC00000000</string>
<string>E7A30F048B1C7CF000000000</string>
<string>E7A30F043607624D00000000</string>
<string>E7A30F0442A6CFDF00000000</string>
</array>
</dict>
<key>E7A30F04FF334B1F00000000</key>
@@ -9,6 +9,19 @@ final class ContextContentContainerNode: ASDisplayNode {
super.init()
}
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
func updateLayout(size: CGSize, scaledSize: CGSize, transition: ContainedViewLayoutTransition) {
guard let contentNode = self.contentNode else {
return
}
switch contentNode {
case .extracted:
break
case let .controller(controller):
transition.updatePosition(node: controller, position: CGPoint(x: scaledSize.width / 2.0, y: scaledSize.height / 2.0))
transition.updateBounds(node: controller, bounds: CGRect(origin: CGPoint(), size: size))
transition.updateTransformScale(node: controller, scale: scaledSize.width / size.width)
controller.updateLayout(size: size, transition: transition)
controller.controller.containerLayoutUpdated(ContainerViewLayout(size: size, metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact), deviceMetrics: .iPhoneX, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: transition)
}
}
}
@@ -2,8 +2,8 @@ import Foundation
import AsyncDisplayKit
import Display
public final class ContextContentContainingNode: ASDisplayNode {
public let contentNode: ContextContentNode
public final class ContextExtractedContentContainingNode: ASDisplayNode {
public let contentNode: ContextExtractedContentNode
public var contentRect: CGRect = CGRect()
public var isExtractedToContextPreview: Bool = false
public var willUpdateIsExtractedToContextPreview: ((Bool) -> Void)?
@@ -15,7 +15,7 @@ public final class ContextContentContainingNode: ASDisplayNode {
public var updateDistractionFreeMode: ((Bool) -> Void)?
public override init() {
self.contentNode = ContextContentNode()
self.contentNode = ContextExtractedContentNode()
super.init()
@@ -23,5 +23,26 @@ public final class ContextContentContainingNode: ASDisplayNode {
}
}
public final class ContextContentNode: ASDisplayNode {
public final class ContextExtractedContentNode: ASDisplayNode {
}
final class ContextControllerContentNode: ASDisplayNode {
let controller: ViewController
init(controller: ViewController) {
self.controller = controller
super.init()
self.addSubnode(controller.displayNode)
}
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
transition.updateFrame(node: self.controller.displayNode, frame: CGRect(origin: CGPoint(), size: size))
}
}
enum ContextContentNode {
case extracted(ContextExtractedContentContainingNode)
case controller(ContextControllerContentNode)
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,52 @@
import Foundation
import AsyncDisplayKit
import Display
public final class ContextControllerSourceNode: ASDisplayNode {
private var contextGesture: ContextGesture?
public var isGestureEnabled: Bool = true {
didSet {
self.contextGesture?.isEnabled = self.isGestureEnabled
}
}
public var activated: ((ContextGesture) -> Void)?
override public func didLoad() {
super.didLoad()
let contextGesture = ContextGesture(target: self, action: nil)
self.contextGesture = contextGesture
self.view.addGestureRecognizer(contextGesture)
contextGesture.activationProgress = { [weak self] progress, update in
guard let strongSelf = self, !strongSelf.bounds.width.isZero else {
return
}
let minScale: CGFloat = (strongSelf.bounds.width - 20.0) / strongSelf.bounds.width
let currentScale = 1.0 * (1.0 - progress) + minScale * progress
switch update {
case .update:
strongSelf.layer.sublayerTransform = CATransform3DMakeScale(currentScale, currentScale, 1.0)
case .begin:
strongSelf.layer.sublayerTransform = CATransform3DMakeScale(currentScale, currentScale, 1.0)
/*let previousProgress: CGFloat = 0.0
let previousScale = 1.0 * (1.0 - previousProgress) + minScale * previousProgress
strongSelf.layer.sublayerTransform = CATransform3DMakeScale(currentScale, currentScale, 1.0)
strongSelf.layer.animate(from: previousScale as NSNumber, to: currentScale as NSNumber, keyPath: "sublayerTransform.scale", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.3, additive: false)*/
case let .ended(previousProgress):
let previousScale = 1.0 * (1.0 - previousProgress) + minScale * previousProgress
strongSelf.layer.sublayerTransform = CATransform3DMakeScale(currentScale, currentScale, 1.0)
strongSelf.layer.animate(from: previousScale as NSNumber, to: currentScale as NSNumber, keyPath: "sublayerTransform.scale", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.3)
}
}
contextGesture.activated = { [weak self] gesture in
if let activated = self?.activated {
activated(gesture)
} else {
gesture.cancel()
}
}
contextGesture.isEnabled = self.isGestureEnabled
}
}
@@ -0,0 +1,170 @@
import Foundation
import UIKit
import AsyncDisplayKit
import Display
public enum ContextGestureTransition {
case begin
case update
case ended(CGFloat)
}
private class TimerTargetWrapper: NSObject {
let f: () -> Void
init(_ f: @escaping () -> Void) {
self.f = f
}
@objc func timerEvent() {
self.f()
}
}
private let beginDelay: Double = 0.1
private func cancelParentGestures(view: UIView) {
if let gestureRecognizers = view.gestureRecognizers {
for recognizer in gestureRecognizers {
recognizer.state = .failed
}
}
if let node = (view as? ListViewBackingView)?.target {
node.cancelSelection()
}
if let superview = view.superview {
cancelParentGestures(view: superview)
}
}
public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDelegate {
private var currentProgress: CGFloat = 0.0
private var delayTimer: Timer?
private var isValidated: Bool = false
public var activationProgress: ((CGFloat, ContextGestureTransition) -> Void)?
public var activated: ((ContextGesture) -> Void)?
public var externalUpdated: ((UIView?, CGPoint) -> Void)?
public var externalEnded: (((UIView?, CGPoint)?) -> Void)?
override public init(target: Any?, action: Selector?) {
super.init(target: target, action: action)
self.delegate = self
}
override public func reset() {
super.reset()
self.currentProgress = 0.0
self.delayTimer?.invalidate()
self.delayTimer = nil
self.isValidated = false
self.externalUpdated = nil
self.externalEnded = nil
}
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if otherGestureRecognizer is UIPanGestureRecognizer {
return false
}
return true
}
override public func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
if self.delayTimer == nil {
let delayTimer = Timer(timeInterval: beginDelay, target: TimerTargetWrapper { [weak self] in
guard let strongSelf = self, let _ = strongSelf.delayTimer else {
return
}
strongSelf.isValidated = true
strongSelf.activationProgress?(strongSelf.currentProgress, .begin)
}, selector: #selector(TimerTargetWrapper.timerEvent), userInfo: nil, repeats: false)
self.delayTimer = delayTimer
RunLoop.main.add(delayTimer, forMode: .common)
}
}
override public func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
if let touch = touches.first {
if #available(iOS 9.0, *) {
let maxForce: CGFloat = min(3.0, touch.maximumPossibleForce)
let progress = touch.force / maxForce
self.currentProgress = progress
if self.isValidated {
self.activationProgress?(progress, .update)
}
if touch.force >= maxForce {
switch self.state {
case .possible:
self.delayTimer?.invalidate()
self.activated?(self)
if let view = self.view?.superview {
cancelParentGestures(view: view)
}
self.state = .began
default:
break
}
}
}
self.externalUpdated?(self.view, touch.location(in: self.view))
}
}
override public func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesEnded(touches, with: event)
if let touch = touches.first {
if !self.currentProgress.isZero, self.isValidated {
if #available(iOS 9.0, *) {
self.activationProgress?(0.0, .ended(self.currentProgress))
}
}
self.externalEnded?((self.view, touch.location(in: self.view)))
}
self.delayTimer?.invalidate()
self.state = .failed
}
override public func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesCancelled(touches, with: event)
if let touch = touches.first, !self.currentProgress.isZero, self.isValidated {
if #available(iOS 9.0, *) {
self.activationProgress?(0.0, .ended(self.currentProgress))
}
}
self.delayTimer?.invalidate()
self.state = .failed
}
public func cancel() {
if !self.currentProgress.isZero, self.isValidated {
self.activationProgress?(0.0, .ended(self.currentProgress))
self.delayTimer?.invalidate()
self.state = .failed
}
}
public func endPressedAppearance() {
if !self.currentProgress.isZero, self.isValidated {
let previousProgress = self.currentProgress
self.currentProgress = 0.0
self.delayTimer?.invalidate()
self.isValidated = false
self.activationProgress?(0.0, .ended(previousProgress))
}
}
}
@@ -151,9 +151,8 @@ public func generateImage(_ size: CGSize, opaque: Bool = false, scale: CGFloat?
guard let provider = CGDataProvider(dataInfo: bytes, data: bytes, size: length, releaseData: { bytes, _, _ in
free(bytes)
})
else {
return nil
}) else {
return nil
}
let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | (opaque ? CGImageAlphaInfo.noneSkipFirst.rawValue : CGImageAlphaInfo.premultipliedFirst.rawValue))
+19 -12
View File
@@ -26,39 +26,39 @@ private final class ListViewBackingLayer: CALayer {
}
}
final class ListViewBackingView: UIView {
weak var target: ListView?
public final class ListViewBackingView: UIView {
public fileprivate(set) weak var target: ListView?
override class var layerClass: AnyClass {
override public class var layerClass: AnyClass {
return ListViewBackingLayer.self
}
override func setNeedsLayout() {
override public func setNeedsLayout() {
}
override func layoutSubviews() {
override public func layoutSubviews() {
}
override func setNeedsDisplay() {
override public func setNeedsDisplay() {
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
override public func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.target?.touchesBegan(touches, with: event)
}
override func touchesCancelled(_ touches: Set<UITouch>?, with event: UIEvent?) {
override public func touchesCancelled(_ touches: Set<UITouch>?, with event: UIEvent?) {
self.target?.touchesCancelled(touches, with: event)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
override public func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
self.target?.touchesMoved(touches, with: event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
override public func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.target?.touchesEnded(touches, with: event)
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if !self.isHidden, let target = self.target {
if target.limitHitTestToNodes, !target.internalHitTest(point, with: event) {
return nil
@@ -70,7 +70,7 @@ final class ListViewBackingView: UIView {
return super.hitTest(point, with: event)
}
override func accessibilityScroll(_ direction: UIAccessibilityScrollDirection) -> Bool {
override public func accessibilityScroll(_ direction: UIAccessibilityScrollDirection) -> Bool {
return self.target?.accessibilityScroll(direction) ?? false
}
}
@@ -3744,6 +3744,13 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
super.touchesMoved(touches, with: event)
}
public func cancelSelection() {
if let selectionTouchLocation = self.selectionTouchLocation {
self.clearHighlightAnimated(true)
self.selectionTouchLocation = nil
}
}
override open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let selectionTouchLocation = self.selectionTouchLocation {
let index = self.itemIndexAtPoint(selectionTouchLocation)
@@ -166,6 +166,8 @@ private final class WindowRootViewController: UIViewController, UIViewController
shouldRegister = true
}
shouldRegister = false
if shouldRegister != self.registeredForPreviewing {
self.registeredForPreviewing = shouldRegister
if shouldRegister {
@@ -18,7 +18,7 @@ private func traceDeceleratingScrollView(_ view: UIView, at point: CGPoint) -> B
public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
private let contentAtPoint: (CGPoint) -> Signal<(ASDisplayNode, PeekControllerContent)?, NoError>?
private let present: (PeekControllerContent, ASDisplayNode) -> PeekController?
private let present: (PeekControllerContent, ASDisplayNode) -> ViewController?
private let updateContent: (PeekControllerContent?) -> Void
private let activateBySingleTap: Bool
@@ -36,7 +36,7 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
private var menuActivation: PeerkControllerMenuActivation?
private weak var presentedController: PeekController?
public init(contentAtPoint: @escaping (CGPoint) -> Signal<(ASDisplayNode, PeekControllerContent)?, NoError>?, present: @escaping (PeekControllerContent, ASDisplayNode) -> PeekController?, updateContent: @escaping (PeekControllerContent?) -> Void = { _ in }, activateBySingleTap: Bool = false) {
public init(contentAtPoint: @escaping (CGPoint) -> Signal<(ASDisplayNode, PeekControllerContent)?, NoError>?, present: @escaping (PeekControllerContent, ASDisplayNode) -> ViewController?, updateContent: @escaping (PeekControllerContent?) -> Void = { _ in }, activateBySingleTap: Bool = false) {
self.contentAtPoint = contentAtPoint
self.present = present
self.updateContent = updateContent
@@ -259,29 +259,35 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
}
} else {
if let presentedController = strongSelf.present(content, sourceNode) {
if forceActivate {
strongSelf.candidateContent = nil
if case .press = content.menuActivation() {
(presentedController.displayNode as? PeekControllerNode)?.activateMenu()
}
} else {
strongSelf.candidateContent = (sourceNode, content)
strongSelf.menuActivation = content.menuActivation()
strongSelf.presentedController = presentedController
strongSelf.state = .began
switch content.menuActivation() {
case .drag:
break
case .press:
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
if presentedController.traitCollection.forceTouchCapability != .available {
if let presentedController = presentedController as? PeekController {
if forceActivate {
strongSelf.candidateContent = nil
if case .press = content.menuActivation() {
(presentedController.displayNode as? PeekControllerNode)?.activateMenu()
}
} else {
strongSelf.candidateContent = (sourceNode, content)
strongSelf.menuActivation = content.menuActivation()
strongSelf.presentedController = presentedController
strongSelf.state = .began
switch content.menuActivation() {
case .drag:
break
case .press:
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
if presentedController.traitCollection.forceTouchCapability != .available {
strongSelf.startPressTimer()
}
} else {
strongSelf.startPressTimer()
}
} else {
strongSelf.startPressTimer()
}
}
}
} else {
if strongSelf.state != .ended {
strongSelf.state = .ended
}
}
}
@@ -220,6 +220,8 @@ open class ViewControllerPresentationArguments {
public var scrollToTopWithTabBar: (() -> Void)?
public var longTapWithTabBar: (() -> Void)?
public var customPresentPreviewingController: ((ViewController, ASDisplayNode) -> ViewController?)?
open func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) {
}
@@ -487,12 +489,14 @@ open class ViewControllerPresentationArguments {
@available(iOSApplicationExtension 9.0, iOS 9.0, *)
open func registerForPreviewing(with delegate: UIViewControllerPreviewingDelegate, sourceView: UIView, theme: PeekControllerTheme, onlyNative: Bool) {
if self.traitCollection.forceTouchCapability == .available {
if false, self.traitCollection.forceTouchCapability == .available {
let _ = super.registerForPreviewing(with: delegate, sourceView: sourceView)
} else if !onlyNative {
if self.previewingContext == nil {
let previewingContext = SimulatedViewControllerPreviewing(theme: theme, delegate: delegate, sourceView: sourceView, node: self.displayNode, present: { [weak self] c, a in
self?.presentInGlobalOverlay(c, with: a)
}, customPresent: { [weak self] c, n in
return self?.customPresentPreviewingController?(c, n)
})
self.previewingContext = previewingContext
}
@@ -501,10 +505,12 @@ open class ViewControllerPresentationArguments {
@available(iOSApplicationExtension 9.0, iOS 9.0, *)
public func registerForPreviewingNonNative(with delegate: UIViewControllerPreviewingDelegate, sourceView: UIView, theme: PeekControllerTheme) {
if self.traitCollection.forceTouchCapability != .available {
if true || self.traitCollection.forceTouchCapability != .available {
if self.previewingContext == nil {
let previewingContext = SimulatedViewControllerPreviewing(theme: theme, delegate: delegate, sourceView: sourceView, node: self.displayNode, present: { [weak self] c, a in
self?.presentInGlobalOverlay(c, with: a)
}, customPresent: { [weak self] c, n in
return self?.customPresentPreviewingController?(c, n)
})
self.previewingContext = previewingContext
}
@@ -5,7 +5,7 @@ import SwiftSignalKit
@available(iOSApplicationExtension 9.0, iOS 9.0, *)
private final class ViewControllerPeekContent: PeekControllerContent {
private let controller: ViewController
let controller: ViewController
private let menu: [PeekControllerMenuItem]
init(controller: ViewController) {
@@ -101,7 +101,7 @@ final class SimulatedViewControllerPreviewing: NSObject, UIViewControllerPreview
var sourceRect: CGRect = CGRect()
init(theme: PeekControllerTheme, delegate: UIViewControllerPreviewingDelegate, sourceView: UIView, node: ASDisplayNode, present: @escaping (ViewController, Any?) -> Void) {
init(theme: PeekControllerTheme, delegate: UIViewControllerPreviewingDelegate, sourceView: UIView, node: ASDisplayNode, present: @escaping (ViewController, Any?) -> Void, customPresent: ((ViewController, ASDisplayNode) -> ViewController?)?) {
self.delegateImpl = delegate
self.sourceView = sourceView
self.node = node
@@ -109,11 +109,16 @@ final class SimulatedViewControllerPreviewing: NSObject, UIViewControllerPreview
self.recognizer = PeekControllerGestureRecognizer(contentAtPoint: { point in
return contentAtPointImpl?(point)
}, present: { content, sourceNode in
let controller = PeekController(theme: theme, content: content, sourceNode: {
return sourceNode
})
present(controller, nil)
return controller
if let content = content as? ViewControllerPeekContent, let controller = customPresent?(content.controller, sourceNode) {
present(controller, nil)
return controller
} else {
let controller = PeekController(theme: theme, content: content, sourceNode: {
return sourceNode
})
present(controller, nil)
return controller
}
})
node.view.addGestureRecognizer(self.recognizer)
+1
View File
@@ -15,6 +15,7 @@ static_library(
"//submodules/AccountContext:AccountContext",
"//submodules/TelegramBaseController:TelegramBaseController",
"//submodules/ChatListUI:ChatListUI",
"//submodules/SegmentedControlNode:SegmentedControlNode",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
@@ -260,6 +260,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E296C9831F100000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libContextUI.a</string>
<key>path</key>
<string>libContextUI.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29CED1620500000000</key>
<dict>
<key>isa</key>
@@ -572,6 +585,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29AD66967300000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libReactionSelectionNode.a</string>
<key>path</key>
<string>libReactionSelectionNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E291D58331200000000</key>
<dict>
<key>isa</key>
@@ -611,6 +637,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29097DBE9200000000</key>
<dict>
<key>isa</key>
@@ -832,6 +871,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29F40A586F00000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libTextSelectionNode.a</string>
<key>path</key>
<string>libTextSelectionNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29B4B05B7A00000000</key>
<dict>
<key>isa</key>
@@ -921,6 +973,7 @@
<string>1DD70E2997B4D6D800000000</string>
<string>1DD70E292F91702000000000</string>
<string>1DD70E2931752C6000000000</string>
<string>1DD70E296C9831F100000000</string>
<string>1DD70E29CED1620500000000</string>
<string>1DD70E292417E0B200000000</string>
<string>1DD70E295915423000000000</string>
@@ -945,9 +998,11 @@
<string>1DD70E29DB6520C800000000</string>
<string>1DD70E29CBE117ED00000000</string>
<string>1DD70E293E4DE92B00000000</string>
<string>1DD70E29AD66967300000000</string>
<string>1DD70E291D58331200000000</string>
<string>1DD70E29EA9CBB5B00000000</string>
<string>1DD70E291328E99400000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E29097DBE9200000000</string>
<string>1DD70E2981AE180900000000</string>
<string>1DD70E29524F478E00000000</string>
@@ -965,6 +1020,7 @@
<string>1DD70E292395015100000000</string>
<string>1DD70E29BA06E3A600000000</string>
<string>1DD70E29F0C15F9C00000000</string>
<string>1DD70E29F40A586F00000000</string>
<string>1DD70E29B4B05B7A00000000</string>
<string>1DD70E29CD296A8300000000</string>
<string>1DD70E29AE67341000000000</string>
@@ -1339,6 +1395,27 @@
<key>fileRef</key>
<string>1DD70E2997B4D6D800000000</string>
</dict>
<key>E7A30F04AD66967300000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29AD66967300000000</string>
</dict>
<key>E7A30F04F40A586F00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29F40A586F00000000</string>
</dict>
<key>E7A30F046C9831F100000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E296C9831F100000000</string>
</dict>
<key>E7A30F04CBE117ED00000000</key>
<dict>
<key>isa</key>
@@ -1346,6 +1423,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -1381,6 +1465,13 @@
<key>fileRef</key>
<string>1DD70E2931752C6000000000</string>
</dict>
<key>E7A30F04BBAF750C00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29BBAF750C00000000</string>
</dict>
<key>E7A30F041328E99400000000</key>
<dict>
<key>isa</key>
@@ -1486,13 +1577,6 @@
<key>fileRef</key>
<string>1DD70E29E220F5E800000000</string>
</dict>
<key>E7A30F04BBAF750C00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29BBAF750C00000000</string>
</dict>
<key>E7A30F04247D9AC100000000</key>
<dict>
<key>isa</key>
@@ -1582,12 +1666,17 @@
<string>E7A30F047AB043C800000000</string>
<string>E7A30F04B6F47D1F00000000</string>
<string>E7A30F0497B4D6D800000000</string>
<string>E7A30F04AD66967300000000</string>
<string>E7A30F04F40A586F00000000</string>
<string>E7A30F046C9831F100000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
<string>E7A30F045A26607D00000000</string>
<string>E7A30F04D233F68C00000000</string>
<string>E7A30F049A24C4DE00000000</string>
<string>E7A30F0431752C6000000000</string>
<string>E7A30F04BBAF750C00000000</string>
<string>E7A30F041328E99400000000</string>
<string>E7A30F041E16CC6C00000000</string>
<string>E7A30F043E4DE92B00000000</string>
@@ -1603,7 +1692,6 @@
<string>E7A30F042F91702000000000</string>
<string>E7A30F04CED1620500000000</string>
<string>E7A30F04E220F5E800000000</string>
<string>E7A30F04BBAF750C00000000</string>
<string>E7A30F04247D9AC100000000</string>
<string>E7A30F04B4B05B7A00000000</string>
<string>E7A30F04490601EB00000000</string>
@@ -67,6 +67,8 @@ public final class HashtagSearchController: TelegramBaseController {
}, updatePeerGrouping: { _, _ in
}, togglePeerMarkedUnread: { _, _ in
}, toggleArchivedFolderHiddenByDefault: {
}, activateChatPreview: { _, _, gesture in
gesture?.cancel()
})
let previousSearchItems = Atomic<[ChatListSearchEntry]?>(value: nil)
@@ -76,7 +78,7 @@ public final class HashtagSearchController: TelegramBaseController {
let previousEntries = previousSearchItems.swap(entries)
let firstTime = previousEntries == nil
let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entries, displayingResults: true, context: strongSelf.context, enableHeaders: false, filter: [], interaction: interaction)
let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entries, displayingResults: true, context: strongSelf.context, enableHeaders: false, filter: [], interaction: interaction, peerContextAction: nil)
strongSelf.controllerNode.enqueueTransition(transition, firstTime: firstTime)
}
})
@@ -6,11 +6,12 @@ import TelegramCore
import TelegramPresentationData
import AccountContext
import ChatListUI
import SegmentedControlNode
final class HashtagSearchControllerNode: ASDisplayNode {
private let toolbarBackgroundNode: ASDisplayNode
private let toolbarSeparatorNode: ASDisplayNode
private let segmentedControl: UISegmentedControl
private let segmentedControlNode: SegmentedControlNode
let listNode: ListView
var chatController: ChatController?
@@ -35,9 +36,11 @@ final class HashtagSearchControllerNode: ASDisplayNode {
self.toolbarSeparatorNode = ASDisplayNode()
self.toolbarSeparatorNode.backgroundColor = theme.rootController.navigationBar.separatorColor
self.segmentedControl = UISegmentedControl(items: [peer?.displayTitle ?? "", strings.HashtagSearch_AllChats])
self.segmentedControl.tintColor = theme.rootController.navigationBar.accentTextColor
self.segmentedControl.selectedSegmentIndex = 0
let items = [
peer?.displayTitle ?? "",
strings.HashtagSearch_AllChats
]
self.segmentedControlNode = SegmentedControlNode(theme: SegmentedControlTheme(theme: theme), items: items.map { SegmentedControlItem(title: $0) }, selectedIndex: 0)
if let peer = peer {
self.chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(peer.id), subject: nil, botStart: nil, mode: .inline)
@@ -56,7 +59,17 @@ final class HashtagSearchControllerNode: ASDisplayNode {
self.addSubnode(self.listNode)
self.listNode.isHidden = true
self.segmentedControl.addTarget(self, action: #selector(self.indexChanged), for: .valueChanged)
self.segmentedControlNode.selectedIndexChanged = { [weak self] index in
if let strongSelf = self {
if index == 0 {
strongSelf.chatController?.displayNode.isHidden = false
strongSelf.listNode.isHidden = true
} else {
strongSelf.chatController?.displayNode.isHidden = true
strongSelf.listNode.isHidden = false
}
}
}
}
func enqueueTransition(_ transition: ChatListSearchContainerTransition, firstTime: Bool) {
@@ -84,8 +97,7 @@ final class HashtagSearchControllerNode: ASDisplayNode {
if self.chatController != nil && self.toolbarBackgroundNode.supernode == nil {
self.addSubnode(self.toolbarBackgroundNode)
self.addSubnode(self.toolbarSeparatorNode)
self.view.addSubview(self.segmentedControl)
self.addSubnode(self.segmentedControlNode)
}
var insets = layout.insets(options: [.input])
@@ -97,10 +109,8 @@ final class HashtagSearchControllerNode: ASDisplayNode {
transition.updateFrame(node: self.toolbarBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelY), size: CGSize(width: layout.size.width, height: toolbarHeight)))
transition.updateFrame(node: self.toolbarSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelY + toolbarHeight), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
var controlSize = self.segmentedControl.sizeThatFits(layout.size)
controlSize.width = layout.size.width - 14.0 * 2.0
transition.updateFrame(view: self.segmentedControl, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - controlSize.width) / 2.0), y: panelY + floor((toolbarHeight - controlSize.height) / 2.0)), size: controlSize))
let controlSize = self.segmentedControlNode.updateLayout(.stretchToFill(width: layout.size.width - 14.0 * 2.0), transition: transition)
transition.updateFrame(node: self.segmentedControlNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - controlSize.width) / 2.0), y: panelY + floor((toolbarHeight - controlSize.height) / 2.0)), size: controlSize))
if let chatController = self.chatController {
insets.top += toolbarHeight - 4.0
@@ -146,21 +156,11 @@ final class HashtagSearchControllerNode: ASDisplayNode {
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
if !hasValidLayout {
hasValidLayout = true
if !self.hasValidLayout {
self.hasValidLayout = true
while !self.enqueuedTransitions.isEmpty {
self.dequeueTransition()
}
}
}
@objc private func indexChanged() {
if self.segmentedControl.selectedSegmentIndex == 0 {
self.chatController?.displayNode.isHidden = false
self.listNode.isHidden = true
} else {
self.chatController?.displayNode.isHidden = true
self.listNode.isHidden = false
}
}
}
@@ -260,6 +260,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E293594DCC000000000</key>
<dict>
<key>isa</key>
@@ -440,6 +453,7 @@
<string>1DD70E290F1A3C6400000000</string>
<string>1DD70E29DB6520C800000000</string>
<string>1DD70E29CBE117ED00000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E293594DCC000000000</string>
<string>1DD70E29D65BA68200000000</string>
<string>1DD70E2925BBFEEE00000000</string>
@@ -731,6 +745,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -775,6 +796,7 @@
<string>E7A30F04C37F741500000000</string>
<string>E7A30F04BF0846EE00000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
<string>E7A30F045A26607D00000000</string>
</array>
@@ -286,6 +286,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E293594DCC000000000</key>
<dict>
<key>isa</key>
@@ -468,6 +481,7 @@
<string>1DD70E290F1A3C6400000000</string>
<string>1DD70E29DB6520C800000000</string>
<string>1DD70E29CBE117ED00000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E293594DCC000000000</string>
<string>1DD70E29D65BA68200000000</string>
<string>1DD70E2925BBFEEE00000000</string>
@@ -766,6 +780,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -818,6 +839,7 @@
<string>E7A30F0497B4D6D800000000</string>
<string>E7A30F04C37F741500000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
<string>E7A30F045A26607D00000000</string>
<string>E7A30F04D233F68C00000000</string>
@@ -260,6 +260,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E293594DCC000000000</key>
<dict>
<key>isa</key>
@@ -440,6 +453,7 @@
<string>1DD70E290F1A3C6400000000</string>
<string>1DD70E29DB6520C800000000</string>
<string>1DD70E29CBE117ED00000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E293594DCC000000000</string>
<string>1DD70E29D65BA68200000000</string>
<string>1DD70E2925BBFEEE00000000</string>
@@ -731,6 +745,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -775,6 +796,7 @@
<string>E7A30F04C37F741500000000</string>
<string>E7A30F04BF0846EE00000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
<string>E7A30F045A26607D00000000</string>
</array>
+1
View File
@@ -17,6 +17,7 @@ static_library(
"//submodules/PeerPresenceStatusManager:PeerPresenceStatusManager",
"//submodules/ItemListUI:ItemListUI",
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
"//submodules/ContextUI:ContextUI",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
@@ -143,6 +143,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E296C9831F100000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libContextUI.a</string>
<key>path</key>
<string>libContextUI.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E295915423000000000</key>
<dict>
<key>isa</key>
@@ -286,6 +299,32 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29AD66967300000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libReactionSelectionNode.a</string>
<key>path</key>
<string>libReactionSelectionNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E293594DCC000000000</key>
<dict>
<key>isa</key>
@@ -416,6 +455,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29F40A586F00000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libTextSelectionNode.a</string>
<key>path</key>
<string>libTextSelectionNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29CD296A8300000000</key>
<dict>
<key>isa</key>
@@ -457,6 +509,7 @@
<string>1DD70E292420028600000000</string>
<string>1DD70E29A54A195300000000</string>
<string>1DD70E2997B4D6D800000000</string>
<string>1DD70E296C9831F100000000</string>
<string>1DD70E295915423000000000</string>
<string>1DD70E29FF334B1F00000000</string>
<string>1DD70E29D6F14E1000000000</string>
@@ -468,6 +521,8 @@
<string>1DD70E290F1A3C6400000000</string>
<string>1DD70E29DB6520C800000000</string>
<string>1DD70E29CBE117ED00000000</string>
<string>1DD70E29AD66967300000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E293594DCC000000000</string>
<string>1DD70E29D65BA68200000000</string>
<string>1DD70E2925BBFEEE00000000</string>
@@ -478,6 +533,7 @@
<string>1DD70E292395015100000000</string>
<string>1DD70E29BA06E3A600000000</string>
<string>1DD70E29F0C15F9C00000000</string>
<string>1DD70E29F40A586F00000000</string>
<string>1DD70E29CD296A8300000000</string>
<string>1DD70E2951398CF200000000</string>
</array>
@@ -675,6 +731,27 @@
<key>fileRef</key>
<string>1DD70E29A54A195300000000</string>
</dict>
<key>E7A30F04AD66967300000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29AD66967300000000</string>
</dict>
<key>E7A30F04F40A586F00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29F40A586F00000000</string>
</dict>
<key>E7A30F046C9831F100000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E296C9831F100000000</string>
</dict>
<key>E7A30F045915423000000000</key>
<dict>
<key>isa</key>
@@ -766,6 +843,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -805,6 +889,9 @@
<string>E7A30F042420028600000000</string>
<string>E7A30F0484A59C1D00000000</string>
<string>E7A30F04A54A195300000000</string>
<string>E7A30F04AD66967300000000</string>
<string>E7A30F04F40A586F00000000</string>
<string>E7A30F046C9831F100000000</string>
<string>E7A30F045915423000000000</string>
<string>E7A30F04F33FDAC300000000</string>
<string>E7A30F0451398CF200000000</string>
@@ -818,6 +905,7 @@
<string>E7A30F04C37F741500000000</string>
<string>E7A30F04BF0846EE00000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
<string>E7A30F045A26607D00000000</string>
<string>E7A30F04D233F68C00000000</string>
@@ -11,6 +11,7 @@ import ItemListUI
import AvatarNode
import TelegramStringFormatting
import PeerPresenceStatusManager
import ContextUI
public struct ItemListPeerItemEditing: Equatable {
public var editable: Bool
@@ -127,12 +128,13 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem {
let setPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void
let removePeer: (PeerId) -> Void
let toggleUpdated: ((Bool) -> Void)?
let contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
let hasTopStripe: Bool
let hasTopGroupInset: Bool
let noInsets: Bool
public let tag: ItemListItemTag?
public init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, account: Account, peer: Peer, height: ItemListPeerItemHeight = .peerList, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, nameStyle: ItemListPeerItemNameStyle = .distinctBold, presence: PeerPresence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, selectable: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, hasTopStripe: Bool = true, hasTopGroupInset: Bool = true, noInsets: Bool = false, tag: ItemListItemTag? = nil) {
public init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, account: Account, peer: Peer, height: ItemListPeerItemHeight = .peerList, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, nameStyle: ItemListPeerItemNameStyle = .distinctBold, presence: PeerPresence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, selectable: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil, hasTopStripe: Bool = true, hasTopGroupInset: Bool = true, noInsets: Bool = false, tag: ItemListItemTag? = nil) {
self.theme = theme
self.strings = strings
self.dateTimeFormat = dateTimeFormat
@@ -156,6 +158,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem {
self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions
self.removePeer = removePeer
self.toggleUpdated = toggleUpdated
self.contextAction = contextAction
self.hasTopStripe = hasTopStripe
self.hasTopGroupInset = hasTopGroupInset
self.noInsets = noInsets
@@ -221,6 +224,8 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
private let highlightedBackgroundNode: ASDisplayNode
private var disabledOverlayNode: ASDisplayNode?
private let containerNode: ContextControllerSourceNode
fileprivate let avatarNode: AvatarNode
private let titleNode: TextNode
private let labelNode: TextNode
@@ -260,6 +265,8 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
self.bottomStripeNode = ASDisplayNode()
self.bottomStripeNode.isLayerBacked = true
self.containerNode = ContextControllerSourceNode()
self.avatarNode = AvatarNode(font: avatarFont)
self.avatarNode.isLayerBacked = !smartInvertColorsEnabled()
@@ -290,10 +297,11 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
self.isAccessibilityElement = true
self.addSubnode(self.avatarNode)
self.addSubnode(self.titleNode)
self.addSubnode(self.statusNode)
self.addSubnode(self.labelNode)
self.addSubnode(self.containerNode)
self.containerNode.addSubnode(self.avatarNode)
self.containerNode.addSubnode(self.titleNode)
self.containerNode.addSubnode(self.statusNode)
self.containerNode.addSubnode(self.labelNode)
self.peerPresenceManager = PeerPresenceStatusManager(update: { [weak self] in
if let strongSelf = self, let layoutParams = strongSelf.layoutParams {
@@ -301,6 +309,14 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
apply(false, true)
}
})
self.containerNode.activated = { [weak self] gesture in
guard let strongSelf = self, let item = strongSelf.layoutParams?.0, let contextAction = item.contextAction else {
gesture.cancel()
return
}
contextAction(strongSelf.containerNode, gesture)
}
}
public func asyncLayout() -> (_ item: ItemListPeerItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, (Bool, Bool) -> Void) {
@@ -568,6 +584,9 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
if let strongSelf = self {
strongSelf.layoutParams = (item, params, neighbors)
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
strongSelf.containerNode.isGestureEnabled = item.contextAction != nil
strongSelf.accessibilityLabel = titleAttributedString?.string
var combinedValueString = ""
if let statusString = statusAttributedString?.string, !statusString.isEmpty {
@@ -627,7 +646,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
}
}
strongSelf.editableControlNode = editableControlNode
strongSelf.insertSubnode(editableControlNode, aboveSubnode: strongSelf.avatarNode)
strongSelf.addSubnode(editableControlNode)
editableControlNode.frame = editableControlFrame
transition.animatePosition(node: editableControlNode, from: CGPoint(x: -editableControlFrame.size.width / 2.0, y: editableControlFrame.midY))
editableControlNode.alpha = 0.0
@@ -687,11 +706,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
if let currentSwitchNode = currentSwitchNode {
if currentSwitchNode !== strongSelf.switchNode {
strongSelf.switchNode = currentSwitchNode
if let disabledOverlayNode = strongSelf.disabledOverlayNode, disabledOverlayNode.supernode != nil {
strongSelf.insertSubnode(currentSwitchNode, belowSubnode: disabledOverlayNode)
} else {
strongSelf.addSubnode(currentSwitchNode)
}
strongSelf.containerNode.addSubnode(currentSwitchNode)
currentSwitchNode.valueUpdated = { value in
if let strongSelf = self {
strongSelf.toggleUpdated(value)
@@ -710,11 +725,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
if let currentCheckNode = currentCheckNode {
if currentCheckNode !== strongSelf.checkNode {
strongSelf.checkNode = currentCheckNode
if let disabledOverlayNode = strongSelf.disabledOverlayNode, disabledOverlayNode.supernode != nil {
strongSelf.insertSubnode(currentCheckNode, belowSubnode: disabledOverlayNode)
} else {
strongSelf.addSubnode(currentCheckNode)
}
strongSelf.containerNode.addSubnode(currentCheckNode)
}
if let checkImage = checkImage {
currentCheckNode.image = checkImage
@@ -732,7 +743,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
if let updatedLabelArrowNode = updatedLabelArrowNode {
strongSelf.labelArrowNode = updatedLabelArrowNode
strongSelf.addSubnode(updatedLabelArrowNode)
strongSelf.containerNode.addSubnode(updatedLabelArrowNode)
if let image = updatedLabelArrowNode.image {
let labelArrowNodeFrame = CGRect(origin: CGPoint(x: params.width - params.rightInset - rightLabelInset - image.size.width, y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size)
transition.updateFrame(node: updatedLabelArrowNode, frame: labelArrowNodeFrame)
@@ -755,7 +766,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
if let updateBadgeImage = updatedLabelBadgeImage {
if strongSelf.labelBadgeNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.labelBadgeNode, belowSubnode: strongSelf.labelNode)
strongSelf.containerNode.insertSubnode(strongSelf.labelBadgeNode, belowSubnode: strongSelf.labelNode)
}
strongSelf.labelBadgeNode.image = updateBadgeImage
}
@@ -260,6 +260,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E293594DCC000000000</key>
<dict>
<key>isa</key>
@@ -440,6 +453,7 @@
<string>1DD70E290F1A3C6400000000</string>
<string>1DD70E29DB6520C800000000</string>
<string>1DD70E29CBE117ED00000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E293594DCC000000000</string>
<string>1DD70E29D65BA68200000000</string>
<string>1DD70E2925BBFEEE00000000</string>
@@ -731,6 +745,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -775,6 +796,7 @@
<string>E7A30F04C37F741500000000</string>
<string>E7A30F04BF0846EE00000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
<string>E7A30F045A26607D00000000</string>
</array>
+1
View File
@@ -17,6 +17,7 @@ static_library(
"//submodules/SwitchNode:SwitchNode",
"//submodules/AnimationUI:AnimationUI",
"//submodules/CheckNode:CheckNode",
"//submodules/SegmentedControlNode:SegmentedControlNode",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
@@ -247,6 +247,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E293594DCC000000000</key>
<dict>
<key>isa</key>
@@ -426,6 +439,7 @@
<string>1DD70E290F1A3C6400000000</string>
<string>1DD70E29DB6520C800000000</string>
<string>1DD70E29CBE117ED00000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E293594DCC000000000</string>
<string>1DD70E29D65BA68200000000</string>
<string>1DD70E2925BBFEEE00000000</string>
@@ -746,6 +760,17 @@
<key>sourceTree</key>
<string>SOURCE_ROOT</string>
</dict>
<key>1DD70E294080C57A00000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>ItemListMaskAccessory.swift</string>
<key>path</key>
<string>Sources/ItemListMaskAccessory.swift</string>
<key>sourceTree</key>
<string>SOURCE_ROOT</string>
</dict>
<key>1DD70E29613EB66700000000</key>
<dict>
<key>isa</key>
@@ -799,6 +824,7 @@
<string>1DD70E29AD71BDBE00000000</string>
<string>1DD70E29586B50C900000000</string>
<string>1DD70E2939FE48C000000000</string>
<string>1DD70E294080C57A00000000</string>
<string>1DD70E29613EB66700000000</string>
<string>1DD70E2978C1317D00000000</string>
<string>1DD70E2989E0026900000000</string>
@@ -1028,6 +1054,13 @@
<key>fileRef</key>
<string>1DD70E2939FE48C000000000</string>
</dict>
<key>E7A30F044080C57A00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E294080C57A00000000</string>
</dict>
<key>E7A30F04613EB66700000000</key>
<dict>
<key>isa</key>
@@ -1079,6 +1112,7 @@
<string>E7A30F04AD71BDBE00000000</string>
<string>E7A30F04586B50C900000000</string>
<string>E7A30F0439FE48C000000000</string>
<string>E7A30F044080C57A00000000</string>
<string>E7A30F04613EB66700000000</string>
<string>E7A30F0478C1317D00000000</string>
<string>E7A30F0489E0026900000000</string>
@@ -1252,6 +1286,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -1289,6 +1330,7 @@
<string>E7A30F04C37F741500000000</string>
<string>E7A30F04BF0846EE00000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
</array>
<key>name</key>
@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
09CE5B922322985500743FF4 /* ItemListMaskAccessory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CE5B912322985500743FF4 /* ItemListMaskAccessory.swift */; };
D060182122F35C2300796784 /* ProgressNavigationButtonNode.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D060182022F35C2300796784 /* ProgressNavigationButtonNode.framework */; };
D060185322F35E1F00796784 /* ItemListMultilineInputItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D060184922F35E1E00796784 /* ItemListMultilineInputItem.swift */; };
D060185422F35E1F00796784 /* ItemListTextWithLabelItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D060184A22F35E1E00796784 /* ItemListTextWithLabelItem.swift */; };
@@ -50,6 +51,7 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
09CE5B912322985500743FF4 /* ItemListMaskAccessory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemListMaskAccessory.swift; sourceTree = "<group>"; };
D060182022F35C2300796784 /* ProgressNavigationButtonNode.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ProgressNavigationButtonNode.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D060184922F35E1E00796784 /* ItemListMultilineInputItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemListMultilineInputItem.swift; sourceTree = "<group>"; };
D060184A22F35E1E00796784 /* ItemListTextWithLabelItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemListTextWithLabelItem.swift; sourceTree = "<group>"; };
@@ -168,6 +170,7 @@
D0D3286322F3366500D07EE2 /* ItemListControllerSearch.swift */,
D0D3286122F3366500D07EE2 /* ItemListControllerSegmentedTitleView.swift */,
D0D3289922F345C500D07EE2 /* ItemListItem.swift */,
09CE5B912322985500743FF4 /* ItemListMaskAccessory.swift */,
D060186022F35F6B00796784 /* ItemListRevealOptionsNode.swift */,
D06018BA22F3663900796784 /* ItemListEditableDeleteControlNode.swift */,
D06018B822F3663800796784 /* ItemListEditableReorderControlNode.swift */,
@@ -294,6 +297,7 @@
D060185422F35E1F00796784 /* ItemListTextWithLabelItem.swift in Sources */,
D0C9BFB222FE327700FAB518 /* ItemListPlaceholderItem.swift in Sources */,
D0D3286722F3366600D07EE2 /* ItemListController.swift in Sources */,
09CE5B922322985500743FF4 /* ItemListMaskAccessory.swift in Sources */,
D0D3286822F3366600D07EE2 /* ItemListControllerSearch.swift in Sources */,
D060185722F35E1F00796784 /* ItemListSwitchItem.swift in Sources */,
D0D3286622F3366600D07EE2 /* ItemListControllerSegmentedTitleView.swift in Sources */,
@@ -283,7 +283,7 @@ open class ItemListController<Entry: ItemListNodeEntry>: ViewController, KeyShor
if let segmentedTitleView = strongSelf.segmentedTitleView, segmentedTitleView.segments == sections {
segmentedTitleView.index = index
} else {
let segmentedTitleView = ItemListControllerSegmentedTitleView(segments: sections, index: index, color: controllerState.theme.rootController.navigationBar.accentTextColor)
let segmentedTitleView = ItemListControllerSegmentedTitleView(theme: controllerState.theme, segments: sections, selectedIndex: index)
strongSelf.segmentedTitleView = segmentedTitleView
strongSelf.navigationItem.titleView = strongSelf.segmentedTitleView
segmentedTitleView.indexUpdated = { index in
@@ -406,7 +406,7 @@ open class ItemListController<Entry: ItemListNodeEntry>: ViewController, KeyShor
strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: strongSelf.theme), strings: NavigationBarStrings(presentationStrings: strongSelf.strings)))
strongSelf.statusBar.statusBarStyle = strongSelf.theme.rootController.statusBarStyle.style
strongSelf.segmentedTitleView?.color = controllerState.theme.rootController.navigationBar.accentTextColor
strongSelf.segmentedTitleView?.theme = controllerState.theme
var items = strongSelf.navigationItem.rightBarButtonItems ?? []
for i in 0 ..< strongSelf.rightNavigationButtonTitleAndStyle.count {
@@ -162,6 +162,8 @@ open class ItemListControllerNode<Entry: ItemListNodeEntry>: ASDisplayNode, UISc
private let navigationBar: NavigationBar
public let listNode: ListView
private let leftOverlayNode: ASDisplayNode
private let rightOverlayNode: ASDisplayNode
private var emptyStateItem: ItemListControllerEmptyStateItem?
private var emptyStateNode: ItemListControllerEmptyStateItemNode?
@@ -204,6 +206,8 @@ open class ItemListControllerNode<Entry: ItemListNodeEntry>: ASDisplayNode, UISc
self.updateNavigationOffset = updateNavigationOffset
self.listNode = ListView()
self.leftOverlayNode = ASDisplayNode()
self.rightOverlayNode = ASDisplayNode()
super.init()
@@ -351,15 +355,45 @@ open class ItemListControllerNode<Entry: ItemListNodeEntry>: ASDisplayNode, UISc
var insets = layout.insets(options: [.input])
insets.top += navigationBarHeight
insets.left += layout.safeInsets.left
insets.right += layout.safeInsets.right
var addedInsets: UIEdgeInsets?
if case .tablet = layout.deviceMetrics.type, layout.size.width > 460.0 {
let inset = max(20.0, floor((layout.size.width - 674.0) / 2.0))
insets.left += inset
insets.right += inset
addedInsets = UIEdgeInsets(top: 0.0, left: inset, bottom: 0.0, right: inset)
if self.leftOverlayNode.supernode == nil {
self.addSubnode(self.leftOverlayNode)
}
if self.rightOverlayNode.supernode == nil {
self.addSubnode(self.rightOverlayNode)
}
} else {
insets.left += layout.safeInsets.left
insets.right += layout.safeInsets.right
if self.leftOverlayNode.supernode != nil {
self.leftOverlayNode.removeFromSupernode()
}
if self.rightOverlayNode.supernode != nil {
self.rightOverlayNode.removeFromSupernode()
}
}
self.listNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)
self.listNode.position = CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0)
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, duration: duration, curve: listViewCurve), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
self.leftOverlayNode.frame = CGRect(x: 0.0, y: insets.top + 1.0, width: insets.left, height: layout.size.height - insets.top)
self.rightOverlayNode.frame = CGRect(x: layout.size.width - insets.right, y: insets.top + 1.0, width: insets.right, height: layout.size.height - insets.top)
if let emptyStateNode = self.emptyStateNode {
var layout = layout
if let addedInsets = addedInsets {
layout = layout.addedInsets(insets: addedInsets)
}
emptyStateNode.updateLayout(layout: layout, navigationBarHeight: navigationBarHeight, transition: transition)
}
@@ -401,9 +435,13 @@ open class ItemListControllerNode<Entry: ItemListNodeEntry>: ASDisplayNode, UISc
case .plain:
self.backgroundColor = transition.theme.list.plainBackgroundColor
self.listNode.backgroundColor = transition.theme.list.plainBackgroundColor
self.leftOverlayNode.backgroundColor = transition.theme.list.plainBackgroundColor
self.rightOverlayNode.backgroundColor = transition.theme.list.plainBackgroundColor
case .blocks:
self.backgroundColor = transition.theme.list.blocksBackgroundColor
self.listNode.backgroundColor = transition.theme.list.blocksBackgroundColor
self.leftOverlayNode.backgroundColor = transition.theme.list.blocksBackgroundColor
self.rightOverlayNode.backgroundColor = transition.theme.list.blocksBackgroundColor
}
}
}
@@ -416,9 +454,13 @@ open class ItemListControllerNode<Entry: ItemListNodeEntry>: ASDisplayNode, UISc
case .plain:
self.backgroundColor = transition.theme.list.plainBackgroundColor
self.listNode.backgroundColor = transition.theme.list.plainBackgroundColor
self.leftOverlayNode.backgroundColor = transition.theme.list.plainBackgroundColor
self.rightOverlayNode.backgroundColor = transition.theme.list.plainBackgroundColor
case .blocks:
self.backgroundColor = transition.theme.list.blocksBackgroundColor
self.listNode.backgroundColor = transition.theme.list.blocksBackgroundColor
self.leftOverlayNode.backgroundColor = transition.theme.list.blocksBackgroundColor
self.rightOverlayNode.backgroundColor = transition.theme.list.blocksBackgroundColor
}
}
}
@@ -553,10 +595,6 @@ open class ItemListControllerNode<Entry: ItemListNodeEntry>: ASDisplayNode, UISc
if itemTag.isEqual(to: ensureVisibleItemTag) {
if let itemNode = itemNode as? ListViewItemNode {
strongSelf.listNode.ensureItemNodeVisible(itemNode)
/*itemNode.setHighlighted(true, at: CGPoint(), animated: false)
Queue.mainQueue().after(1.0, {
itemNode.setHighlighted(false, at: CGPoint(), animated: true)
})*/
applied = true
}
}
@@ -613,21 +651,13 @@ open class ItemListControllerNode<Entry: ItemListNodeEntry>: ASDisplayNode, UISc
open func scrollViewDidScroll(_ scrollView: UIScrollView) {
let distanceFromEquilibrium = scrollView.contentOffset.y - scrollView.contentSize.height / 3.0
//let transition = 1.0 - min(1.0, max(0.0, abs(distanceFromEquilibrium) / 50.0))
self.updateNavigationOffset(-distanceFromEquilibrium)
/*if let toolbarNode = toolbarNode {
toolbarNode.layer.position = CGPoint(x: toolbarNode.layer.position.x, y: self.bounds.size.height - toolbarNode.bounds.size.height / 2.0 + (1.0 - transition) * toolbarNode.bounds.size.height)
}*/
}
open func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
targetContentOffset.pointee = scrollView.contentOffset
let scrollVelocity = scrollView.panGestureRecognizer.velocity(in: scrollView)
if abs(scrollVelocity.y) > 200.0 {
self.animateOut()
}
@@ -1,50 +1,49 @@
import Foundation
import UIKit
import SegmentedControlNode
import TelegramPresentationData
public final class ItemListControllerSegmentedTitleView: UIView {
private let segmentedControlNode: SegmentedControlNode
public var theme: PresentationTheme {
didSet {
self.segmentedControlNode.updateTheme(SegmentedControlTheme(theme: self.theme))
}
}
public var segments: [String] {
didSet {
if self.segments != oldValue {
self.control.removeAllSegments()
var index = 0
for segment in self.segments {
self.control.insertSegment(withTitle: segment, at: index, animated: false)
index += 1
}
self.segmentedControlNode.items = self.segments.map { SegmentedControlItem(title: $0) }
self.setNeedsLayout()
}
}
}
public var index: Int {
didSet {
self.control.selectedSegmentIndex = self.index
get {
return self.segmentedControlNode.selectedIndex
}
set {
self.segmentedControlNode.selectedIndex = newValue
}
}
private let control: UISegmentedControl
public var indexUpdated: ((Int) -> Void)?
public var color: UIColor {
didSet {
self.control.tintColor = self.color
}
}
public init(segments: [String], index: Int, color: UIColor) {
public init(theme: PresentationTheme, segments: [String], selectedIndex: Int) {
self.theme = theme
self.segments = segments
self.index = index
self.color = color
self.control = UISegmentedControl(items: segments)
self.control.selectedSegmentIndex = index
self.control.tintColor = color
self.segmentedControlNode = SegmentedControlNode(theme: SegmentedControlTheme(theme: theme), items: segments.map { SegmentedControlItem(title: $0) }, selectedIndex: selectedIndex)
super.init(frame: CGRect())
self.addSubview(self.control)
self.control.addTarget(self, action: #selector(indexChanged), for: .valueChanged)
self.segmentedControlNode.selectedIndexChanged = { [weak self] index in
self?.indexUpdated?(index)
}
self.addSubnode(self.segmentedControlNode)
}
required public init?(coder aDecoder: NSCoder) {
@@ -55,13 +54,7 @@ public final class ItemListControllerSegmentedTitleView: UIView {
super.layoutSubviews()
let size = self.bounds.size
var controlSize = self.control.sizeThatFits(size)
controlSize.width = min(size.width, max(160.0, controlSize.width))
self.control.frame = CGRect(origin: CGPoint(x: floor((size.width - controlSize.width) / 2.0), y: floor((size.height - controlSize.height) / 2.0)), size: controlSize)
}
@objc private func indexChanged() {
self.indexUpdated?(self.control.selectedSegmentIndex)
let controlSize = self.segmentedControlNode.updateLayout(.sizeToFit(maximumWidth: size.width, minimumWidth: 160.0), transition: .immediate)
self.segmentedControlNode.frame = CGRect(origin: CGPoint(x: floor((size.width - controlSize.width) / 2.0), y: floor((size.height - controlSize.height) / 2.0)), size: controlSize)
}
}
@@ -14,6 +14,8 @@ public protocol ItemListItem {
}
public extension ItemListItem {
//let accessoryItem: ListViewAccessoryItem?
var isAlwaysPlain: Bool {
return false
}
@@ -0,0 +1,38 @@
import Foundation
import UIKit
import Display
final class ItemListMaskAccessoryItem: ListViewAccessoryItem {
private let sectionId: Int32
init(sectionId: Int32) {
self.sectionId = sectionId
}
func isEqualToItem(_ other: ListViewAccessoryItem) -> Bool {
if case let other as ItemListMaskAccessoryItem = other {
return self.sectionId == other.sectionId
}
return false
}
func node(synchronous: Bool) -> ListViewAccessoryItemNode {
let node = ItemListMaskAccessoryItemItemNode()
node.frame = CGRect(origin: CGPoint(), size: CGSize(width: 38.0, height: 38.0))
return node
}
}
final class ItemListMaskAccessoryItemItemNode: ListViewAccessoryItemNode {
let node: ASDisplayNode
override init() {
self.node = ASDisplayNode()
self.node.backgroundColor = .red
super.init()
self.addSubnode(self.node)
}
}
@@ -92,6 +92,7 @@ public class ItemListActionItemNode: ListViewItemNode, ItemListItemNode {
private let topStripeNode: ASDisplayNode
private let bottomStripeNode: ASDisplayNode
private let highlightedBackgroundNode: ASDisplayNode
private let maskNode: ASImageNode
private let titleNode: TextNode
@@ -107,7 +108,7 @@ public class ItemListActionItemNode: ListViewItemNode, ItemListItemNode {
self.backgroundNode = ASDisplayNode()
self.backgroundNode.isLayerBacked = true
self.backgroundNode.backgroundColor = .white
self.maskNode = ASImageNode()
self.topStripeNode = ASDisplayNode()
self.topStripeNode.isLayerBacked = true
@@ -216,7 +217,9 @@ public class ItemListActionItemNode: ListViewItemNode, ItemListItemNode {
if strongSelf.bottomStripeNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 0)
}
if strongSelf.maskNode.supernode != nil {
strongSelf.maskNode.removeFromSupernode()
}
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight))
case .blocks:
if strongSelf.backgroundNode.supernode == nil {
@@ -228,11 +231,19 @@ public class ItemListActionItemNode: ListViewItemNode, ItemListItemNode {
if strongSelf.bottomStripeNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
}
if strongSelf.maskNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
}
let hasCorners = params.width > 480
var hasTopCorners = false
var hasBottomCorners = false
switch neighbors.top {
case .sameSection(false):
strongSelf.topStripeNode.isHidden = true
default:
strongSelf.topStripeNode.isHidden = false
hasTopCorners = true
strongSelf.topStripeNode.isHidden = hasCorners
}
let bottomStripeInset: CGFloat
let bottomStripeOffset: CGFloat
@@ -243,8 +254,14 @@ public class ItemListActionItemNode: ListViewItemNode, ItemListItemNode {
default:
bottomStripeInset = 0.0
bottomStripeOffset = 0.0
hasBottomCorners = true
strongSelf.bottomStripeNode.isHidden = hasCorners
}
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: separatorHeight))
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight))
}
@@ -108,6 +108,7 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode {
private let topStripeNode: ASDisplayNode
private let bottomStripeNode: ASDisplayNode
private let highlightedBackgroundNode: ASDisplayNode
private let maskNode: ASImageNode
let iconNode: ASImageNode
let titleNode: TextNode
@@ -137,6 +138,8 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode {
self.backgroundNode.isLayerBacked = true
self.backgroundNode.backgroundColor = .white
self.maskNode = ASImageNode()
self.topStripeNode = ASDisplayNode()
self.topStripeNode.isLayerBacked = true
@@ -357,7 +360,9 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode {
if strongSelf.bottomStripeNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 0)
}
if strongSelf.maskNode.supernode != nil {
strongSelf.maskNode.removeFromSupernode()
}
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight))
case .blocks:
if strongSelf.backgroundNode.supernode == nil {
@@ -369,11 +374,19 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode {
if strongSelf.bottomStripeNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
}
if strongSelf.maskNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
}
let hasCorners = params.width > 480
var hasTopCorners = false
var hasBottomCorners = false
switch neighbors.top {
case .sameSection(false):
strongSelf.topStripeNode.isHidden = true
default:
strongSelf.topStripeNode.isHidden = false
hasTopCorners = true
strongSelf.topStripeNode.isHidden = hasCorners
}
let bottomStripeInset: CGFloat
switch neighbors.bottom {
@@ -381,10 +394,15 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode {
bottomStripeInset = leftInset
default:
bottomStripeInset = 0.0
hasBottomCorners = true
strongSelf.bottomStripeNode.isHidden = hasCorners
}
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: separatorHeight))
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: leftInset, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: separatorHeight))
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight))
}
@@ -114,6 +114,7 @@ public class ItemListSwitchItemNode: ListViewItemNode, ItemListItemNode {
private let topStripeNode: ASDisplayNode
private let bottomStripeNode: ASDisplayNode
private let highlightedBackgroundNode: ASDisplayNode
private let maskNode: ASImageNode
private let titleNode: TextNode
private var switchNode: ASDisplayNode & ItemListSwitchNodeImpl
@@ -133,6 +134,8 @@ public class ItemListSwitchItemNode: ListViewItemNode, ItemListItemNode {
self.backgroundNode.isLayerBacked = true
self.backgroundNode.backgroundColor = .white
self.maskNode = ASImageNode()
self.topStripeNode = ASDisplayNode()
self.topStripeNode.isLayerBacked = true
@@ -299,7 +302,9 @@ public class ItemListSwitchItemNode: ListViewItemNode, ItemListItemNode {
if strongSelf.bottomStripeNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 0)
}
if strongSelf.maskNode.supernode != nil {
strongSelf.maskNode.removeFromSupernode()
}
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight))
case .blocks:
if strongSelf.backgroundNode.supernode == nil {
@@ -311,11 +316,19 @@ public class ItemListSwitchItemNode: ListViewItemNode, ItemListItemNode {
if strongSelf.bottomStripeNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
}
if strongSelf.maskNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
}
let hasCorners = params.width > 480
var hasTopCorners = false
var hasBottomCorners = false
switch neighbors.top {
case .sameSection(false):
strongSelf.topStripeNode.isHidden = true
default:
strongSelf.topStripeNode.isHidden = false
hasTopCorners = true
strongSelf.topStripeNode.isHidden = hasCorners
}
let bottomStripeInset: CGFloat
switch neighbors.bottom {
@@ -323,9 +336,14 @@ public class ItemListSwitchItemNode: ListViewItemNode, ItemListItemNode {
bottomStripeInset = 16.0 + params.leftInset
default:
bottomStripeInset = 0.0
hasBottomCorners = true
strongSelf.bottomStripeNode.isHidden = hasCorners
}
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
}
@@ -60,7 +60,7 @@ public final class ItemListTextEmptyStateItemNode: ItemListControllerEmptyStateI
self.validLayout = (layout, navigationBarHeight)
var insets = layout.insets(options: [.statusBar])
insets.top += navigationBarHeight
let textSize = self.textNode.measure(CGSize(width: layout.size.width - 40.0 - layout.safeInsets.left - layout.safeInsets.right, height: max(1.0, layout.size.height - insets.top - insets.bottom)))
self.textNode.frame = CGRect(origin: CGPoint(x: layout.safeInsets.left + floor((layout.size.width - layout.safeInsets.left - layout.safeInsets.right - textSize.width) / 2.0), y: insets.top + floor((layout.size.height - insets.top - insets.bottom - textSize.height) / 2.0)), size: textSize)
let textSize = self.textNode.measure(CGSize(width: layout.size.width - 40.0 - layout.safeInsets.left - layout.safeInsets.right - layout.intrinsicInsets.left - layout.intrinsicInsets.right, height: max(1.0, layout.size.height - insets.top - insets.bottom)))
self.textNode.frame = CGRect(origin: CGPoint(x: layout.safeInsets.left + layout.intrinsicInsets.left + floor((layout.size.width - layout.safeInsets.left - layout.safeInsets.right - layout.intrinsicInsets.left - layout.intrinsicInsets.right - textSize.width) / 2.0), y: insets.top + floor((layout.size.height - insets.top - insets.bottom - textSize.height) / 2.0)), size: textSize)
}
}
@@ -143,6 +143,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E296C9831F100000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libContextUI.a</string>
<key>path</key>
<string>libContextUI.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E295915423000000000</key>
<dict>
<key>isa</key>
@@ -299,6 +312,32 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29AD66967300000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libReactionSelectionNode.a</string>
<key>path</key>
<string>libReactionSelectionNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E293594DCC000000000</key>
<dict>
<key>isa</key>
@@ -429,6 +468,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29F40A586F00000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libTextSelectionNode.a</string>
<key>path</key>
<string>libTextSelectionNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29CD296A8300000000</key>
<dict>
<key>isa</key>
@@ -470,6 +522,7 @@
<string>1DD70E292420028600000000</string>
<string>1DD70E29A54A195300000000</string>
<string>1DD70E2997B4D6D800000000</string>
<string>1DD70E296C9831F100000000</string>
<string>1DD70E295915423000000000</string>
<string>1DD70E29FF334B1F00000000</string>
<string>1DD70E29D6F14E1000000000</string>
@@ -482,6 +535,8 @@
<string>1DD70E290F1A3C6400000000</string>
<string>1DD70E29DB6520C800000000</string>
<string>1DD70E29CBE117ED00000000</string>
<string>1DD70E29AD66967300000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E293594DCC000000000</string>
<string>1DD70E29D65BA68200000000</string>
<string>1DD70E2925BBFEEE00000000</string>
@@ -492,6 +547,7 @@
<string>1DD70E292395015100000000</string>
<string>1DD70E29BA06E3A600000000</string>
<string>1DD70E29F0C15F9C00000000</string>
<string>1DD70E29F40A586F00000000</string>
<string>1DD70E29CD296A8300000000</string>
<string>1DD70E2951398CF200000000</string>
</array>
@@ -772,6 +828,27 @@
<key>fileRef</key>
<string>1DD70E29A54A195300000000</string>
</dict>
<key>E7A30F04AD66967300000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29AD66967300000000</string>
</dict>
<key>E7A30F04F40A586F00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29F40A586F00000000</string>
</dict>
<key>E7A30F046C9831F100000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E296C9831F100000000</string>
</dict>
<key>E7A30F0497B4D6D800000000</key>
<dict>
<key>isa</key>
@@ -800,6 +877,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -855,10 +939,14 @@
<string>E7A30F043594DCC000000000</string>
<string>E7A30F042420028600000000</string>
<string>E7A30F04A54A195300000000</string>
<string>E7A30F04AD66967300000000</string>
<string>E7A30F04F40A586F00000000</string>
<string>E7A30F046C9831F100000000</string>
<string>E7A30F0497B4D6D800000000</string>
<string>E7A30F04C37F741500000000</string>
<string>E7A30F04BF0846EE00000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
<string>E7A30F045A26607D00000000</string>
<string>E7A30F04D233F68C00000000</string>
@@ -260,6 +260,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E293594DCC000000000</key>
<dict>
<key>isa</key>
@@ -440,6 +453,7 @@
<string>1DD70E290F1A3C6400000000</string>
<string>1DD70E29DB6520C800000000</string>
<string>1DD70E29CBE117ED00000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E293594DCC000000000</string>
<string>1DD70E29D65BA68200000000</string>
<string>1DD70E2925BBFEEE00000000</string>
@@ -731,6 +745,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -775,6 +796,7 @@
<string>E7A30F04C37F741500000000</string>
<string>E7A30F04BF0846EE00000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
<string>E7A30F045A26607D00000000</string>
</array>
@@ -10,7 +10,7 @@
@implementation OggOpusReader
- (instancetype _Nullable)init:(NSString *)path {
- (instancetype _Nullable)initWithPath:(NSString *)path {
self = [super init];
if (self != nil) {
int error = OPUS_OK;
@@ -572,6 +572,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29097DBE9200000000</key>
<dict>
<key>isa</key>
@@ -867,6 +880,7 @@
<string>1DD70E291D58331200000000</string>
<string>1DD70E29CF967D4300000000</string>
<string>1DD70E29EA9CBB5B00000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E29097DBE9200000000</string>
<string>1DD70E2981AE180900000000</string>
<string>1DD70E293594DCC000000000</string>
@@ -2163,6 +2177,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -2250,6 +2271,7 @@
<string>E7A30F04BBAF750C00000000</string>
<string>E7A30F04F523B9DE00000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
<string>E7A30F045A26607D00000000</string>
<string>E7A30F049856EB2400000000</string>
@@ -273,6 +273,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E293594DCC000000000</key>
<dict>
<key>isa</key>
@@ -454,6 +467,7 @@
<string>1DD70E290F1A3C6400000000</string>
<string>1DD70E29DB6520C800000000</string>
<string>1DD70E29CBE117ED00000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E293594DCC000000000</string>
<string>1DD70E29D65BA68200000000</string>
<string>1DD70E2925BBFEEE00000000</string>
@@ -812,6 +826,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -857,6 +878,7 @@
<string>E7A30F0497B4D6D800000000</string>
<string>E7A30F04C37F741500000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
<string>E7A30F045A26607D00000000</string>
</array>
@@ -832,6 +832,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29097DBE9200000000</key>
<dict>
<key>isa</key>
@@ -1264,6 +1277,7 @@
<string>1DD70E29CF967D4300000000</string>
<string>1DD70E29EA9CBB5B00000000</string>
<string>1DD70E291328E99400000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E29097DBE9200000000</string>
<string>1DD70E2981AE180900000000</string>
<string>1DD70E29135DFEDE00000000</string>
@@ -2379,6 +2393,27 @@
<key>fileRef</key>
<string>1DD70E2997B4D6D800000000</string>
</dict>
<key>E7A30F04AD66967300000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29AD66967300000000</string>
</dict>
<key>E7A30F04F40A586F00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29F40A586F00000000</string>
</dict>
<key>E7A30F046C9831F100000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E296C9831F100000000</string>
</dict>
<key>E7A30F04C37F741500000000</key>
<dict>
<key>isa</key>
@@ -2393,6 +2428,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -2428,6 +2470,13 @@
<key>fileRef</key>
<string>1DD70E2931752C6000000000</string>
</dict>
<key>E7A30F04BBAF750C00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29BBAF750C00000000</string>
</dict>
<key>E7A30F041328E99400000000</key>
<dict>
<key>isa</key>
@@ -2547,27 +2596,6 @@
<key>fileRef</key>
<string>1DD70E292F91702000000000</string>
</dict>
<key>E7A30F04AD66967300000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29AD66967300000000</string>
</dict>
<key>E7A30F04F40A586F00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29F40A586F00000000</string>
</dict>
<key>E7A30F046C9831F100000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E296C9831F100000000</string>
</dict>
<key>E7A30F04A59C927400000000</key>
<dict>
<key>isa</key>
@@ -2715,13 +2743,6 @@
<key>fileRef</key>
<string>1DD70E2937C1167400000000</string>
</dict>
<key>E7A30F04BBAF750C00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29BBAF750C00000000</string>
</dict>
<key>E7A30F049856EB2400000000</key>
<dict>
<key>isa</key>
@@ -2819,13 +2840,18 @@
<string>E7A30F042420028600000000</string>
<string>E7A30F04A54A195300000000</string>
<string>E7A30F0497B4D6D800000000</string>
<string>E7A30F04AD66967300000000</string>
<string>E7A30F04F40A586F00000000</string>
<string>E7A30F046C9831F100000000</string>
<string>E7A30F04C37F741500000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
<string>E7A30F045A26607D00000000</string>
<string>E7A30F04D233F68C00000000</string>
<string>E7A30F049A24C4DE00000000</string>
<string>E7A30F0431752C6000000000</string>
<string>E7A30F04BBAF750C00000000</string>
<string>E7A30F041328E99400000000</string>
<string>E7A30F041E16CC6C00000000</string>
<string>E7A30F04928D142900000000</string>
@@ -2843,9 +2869,6 @@
<string>E7A30F04524F478E00000000</string>
<string>E7A30F04DF758A8500000000</string>
<string>E7A30F042F91702000000000</string>
<string>E7A30F04AD66967300000000</string>
<string>E7A30F04F40A586F00000000</string>
<string>E7A30F046C9831F100000000</string>
<string>E7A30F04A59C927400000000</string>
<string>E7A30F04AE190FC000000000</string>
<string>E7A30F04247D9AC100000000</string>
@@ -2867,7 +2890,6 @@
<string>E7A30F04A3994F2C00000000</string>
<string>E7A30F048ED1FB1300000000</string>
<string>E7A30F0437C1167400000000</string>
<string>E7A30F04BBAF750C00000000</string>
<string>E7A30F049856EB2400000000</string>
<string>E7A30F04C7165FF900000000</string>
<string>E7A30F04135DFEDE00000000</string>
@@ -94,7 +94,7 @@ public func presentPeerReportOptions(context: AccountContext, parent: ViewContro
f(.dismissWithoutContent)
})))
}
contextController.setItems(items)
contextController.setItems(.single(items))
} else {
contextController?.dismiss()
parent.view.endEditing(true)
@@ -766,6 +766,30 @@ private func getUserPeer(postbox: Postbox, peerId: PeerId) -> Signal<(Peer?, Cac
}
}
public func openAddPersonContactImpl(context: AccountContext, peerId: PeerId, present: @escaping (ViewController, Any?) -> Void) {
let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId)
|> deliverOnMainQueue).start(next: { peer, cachedData in
guard let user = peer as? TelegramUser, let contactData = DeviceContactExtendedData(peer: user) else {
return
}
var shareViaException = false
if let cachedData = cachedData as? CachedUserData, let peerStatusSettings = cachedData.peerStatusSettings {
shareViaException = peerStatusSettings.contains(.addExceptionWhenAddingContact)
}
present(deviceContactInfoController(context: context, subject: .create(peer: user, contactData: contactData, isSharing: true, shareViaException: shareViaException, completion: { peer, stableId, contactData in
if let peer = peer as? TelegramUser {
if let phone = peer.phone, !phone.isEmpty {
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
present(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .genericSuccess(presentationData.strings.AddContact_StatusSuccess(peer.compactDisplayTitle).0, true)), nil)
}
}), completed: nil, cancelled: nil), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
})
}
public func userInfoController(context: AccountContext, peerId: PeerId, mode: PeerInfoControllerMode = .generic) -> ViewController {
let statePromise = ValuePromise(UserInfoState(), ignoreRepeated: true)
let stateValue = Atomic(value: UserInfoState())
@@ -892,26 +916,8 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Pe
}, openChat: {
openChatImpl?()
}, addContact: {
let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId)
|> deliverOnMainQueue).start(next: { peer, cachedData in
guard let user = peer as? TelegramUser, let contactData = DeviceContactExtendedData(peer: user) else {
return
}
var shareViaException = false
if let cachedData = cachedData as? CachedUserData, let peerStatusSettings = cachedData.peerStatusSettings {
shareViaException = peerStatusSettings.contains(.addExceptionWhenAddingContact)
}
presentControllerImpl?(deviceContactInfoController(context: context, subject: .create(peer: user, contactData: contactData, isSharing: true, shareViaException: shareViaException, completion: { peer, stableId, contactData in
if let peer = peer as? TelegramUser {
if let phone = peer.phone, !phone.isEmpty {
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .genericSuccess(presentationData.strings.AddContact_StatusSuccess(peer.compactDisplayTitle).0, true)), nil)
}
}), completed: nil, cancelled: nil), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
openAddPersonContactImpl(context: context, peerId: peerId, present: { c, a in
presentControllerImpl?(c, a)
})
}, shareContact: {
shareContactImpl?()
@@ -156,6 +156,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E296C9831F100000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libContextUI.a</string>
<key>path</key>
<string>libContextUI.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E292417E0B200000000</key>
<dict>
<key>isa</key>
@@ -377,6 +390,32 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29AD66967300000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libReactionSelectionNode.a</string>
<key>path</key>
<string>libReactionSelectionNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29524F478E00000000</key>
<dict>
<key>isa</key>
@@ -546,6 +585,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29F40A586F00000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libTextSelectionNode.a</string>
<key>path</key>
<string>libTextSelectionNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29CD296A8300000000</key>
<dict>
<key>isa</key>
@@ -588,6 +640,7 @@
<string>1DD70E292420028600000000</string>
<string>1DD70E29A54A195300000000</string>
<string>1DD70E2997B4D6D800000000</string>
<string>1DD70E296C9831F100000000</string>
<string>1DD70E292417E0B200000000</string>
<string>1DD70E295915423000000000</string>
<string>1DD70E29FF334B1F00000000</string>
@@ -605,6 +658,8 @@
<string>1DD70E290F1A3C6400000000</string>
<string>1DD70E29DB6520C800000000</string>
<string>1DD70E29CBE117ED00000000</string>
<string>1DD70E29AD66967300000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E29524F478E00000000</string>
<string>1DD70E293594DCC000000000</string>
<string>1DD70E29D65BA68200000000</string>
@@ -618,6 +673,7 @@
<string>1DD70E292395015100000000</string>
<string>1DD70E29BA06E3A600000000</string>
<string>1DD70E29F0C15F9C00000000</string>
<string>1DD70E29F40A586F00000000</string>
<string>1DD70E29CD296A8300000000</string>
<string>1DD70E2951398CF200000000</string>
</array>
@@ -933,6 +989,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -961,6 +1024,27 @@
<key>fileRef</key>
<string>1DD70E29A54A195300000000</string>
</dict>
<key>E7A30F04AD66967300000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29AD66967300000000</string>
</dict>
<key>E7A30F04F40A586F00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29F40A586F00000000</string>
</dict>
<key>E7A30F046C9831F100000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E296C9831F100000000</string>
</dict>
<key>E7A30F04D233F68C00000000</key>
<dict>
<key>isa</key>
@@ -1049,10 +1133,14 @@
<string>E7A30F04C37F741500000000</string>
<string>E7A30F04BF0846EE00000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
<string>E7A30F045A26607D00000000</string>
<string>E7A30F0405A95CB400000000</string>
<string>E7A30F04A54A195300000000</string>
<string>E7A30F04AD66967300000000</string>
<string>E7A30F04F40A586F00000000</string>
<string>E7A30F046C9831F100000000</string>
<string>E7A30F04D233F68C00000000</string>
<string>E7A30F049A24C4DE00000000</string>
<string>E7A30F04BBAF750C00000000</string>
@@ -34,7 +34,7 @@ private func generateBackgroundImage(foreground: UIColor, diameter: CGFloat, sha
private func generateBackgroundShadowImage(shadow: UIColor, diameter: CGFloat, shadowBlur: CGFloat) -> UIImage? {
return generateImage(CGSize(width: diameter * 2.0 + shadowBlur * 2.0, height: diameter + shadowBlur * 2.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(UIColor.white.cgColor)
context.setFillColor(shadow.cgColor)
context.setShadow(offset: CGSize(), blur: shadowBlur, color: shadow.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
@@ -61,7 +61,7 @@ private func generateBubbleImage(foreground: UIColor, diameter: CGFloat, shadowB
private func generateBubbleShadowImage(shadow: UIColor, diameter: CGFloat, shadowBlur: CGFloat) -> UIImage? {
return generateImage(CGSize(width: diameter + shadowBlur * 2.0, height: diameter + shadowBlur * 2.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(UIColor.white.cgColor)
context.setFillColor(shadow.cgColor)
context.setShadow(offset: CGSize(), blur: shadowBlur, color: shadow.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowBlur, y: shadowBlur), size: CGSize(width: diameter, height: diameter)))
context.setShadow(offset: CGSize(), blur: 1.0, color: shadow.cgColor)
+17
View File
@@ -0,0 +1,17 @@
load("//Config:buck_rule_macros.bzl", "static_library")
static_library(
name = "SegmentedControlNode",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/AsyncDisplayKit:AsyncDisplayKit#shared",
"//submodules/Display:Display#shared",
"//submodules/TelegramPresentationData:TelegramPresentationData",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
"$SDKROOT/System/Library/Frameworks/UIKit.framework",
],
)
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
</plist>
@@ -0,0 +1,511 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>archiveVersion</key>
<string>1</string>
<key>classes</key>
<dict>
</dict>
<key>objectVersion</key>
<string>46</string>
<key>objects</key>
<dict>
<key>1DD70E299315E66800000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>SegmentedControlNode-Debug.xcconfig</string>
<key>path</key>
<string>../../buck-out/gen/submodules/SegmentedControlNode/SegmentedControlNode-Debug.xcconfig</string>
<key>sourceTree</key>
<string>SOURCE_ROOT</string>
<key>explicitFileType</key>
<string>text.xcconfig</string>
</dict>
<key>1DD70E290A5883D200000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>SegmentedControlNode-Profile.xcconfig</string>
<key>path</key>
<string>../../buck-out/gen/submodules/SegmentedControlNode/SegmentedControlNode-Profile.xcconfig</string>
<key>sourceTree</key>
<string>SOURCE_ROOT</string>
<key>explicitFileType</key>
<string>text.xcconfig</string>
</dict>
<key>1DD70E297DEF2F3400000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>SegmentedControlNode-Release.xcconfig</string>
<key>path</key>
<string>../../buck-out/gen/submodules/SegmentedControlNode/SegmentedControlNode-Release.xcconfig</string>
<key>sourceTree</key>
<string>SOURCE_ROOT</string>
<key>explicitFileType</key>
<string>text.xcconfig</string>
</dict>
<key>B401C9792F7F325000000000</key>
<dict>
<key>isa</key>
<string>PBXGroup</string>
<key>name</key>
<string>Buck (Do Not Modify)</string>
<key>sourceTree</key>
<string><![CDATA[<group>]]></string>
<key>children</key>
<array>
<string>1DD70E299315E66800000000</string>
<string>1DD70E290A5883D200000000</string>
<string>1DD70E297DEF2F3400000000</string>
</array>
</dict>
<key>B401C979B781F65D00000000</key>
<dict>
<key>isa</key>
<string>PBXGroup</string>
<key>name</key>
<string>Configurations</string>
<key>sourceTree</key>
<string><![CDATA[<group>]]></string>
<key>children</key>
<array>
<string>B401C9792F7F325000000000</string>
</array>
</dict>
<key>1DD70E29FF334B1F00000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libDisplay.dylib</string>
<key>path</key>
<string>libDisplay.dylib</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>compiled.mach-o.dylib</string>
</dict>
<key>1DD70E2936DE2CF900000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libMediaResources.a</string>
<key>path</key>
<string>libMediaResources.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29DB6520C800000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libPostbox.dylib</string>
<key>path</key>
<string>libPostbox.dylib</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>compiled.mach-o.dylib</string>
</dict>
<key>1DD70E29D65BA68200000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSwiftSignalKit.dylib</string>
<key>path</key>
<string>libSwiftSignalKit.dylib</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>compiled.mach-o.dylib</string>
</dict>
<key>1DD70E29119CDA0700000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libTelegramCore.dylib</string>
<key>path</key>
<string>libTelegramCore.dylib</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>compiled.mach-o.dylib</string>
</dict>
<key>1DD70E2984A59C1D00000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libTelegramPresentationData.a</string>
<key>path</key>
<string>libTelegramPresentationData.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E292395015100000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libTelegramUIPreferences.a</string>
<key>path</key>
<string>libTelegramUIPreferences.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>B401C97968022A5500000000</key>
<dict>
<key>isa</key>
<string>PBXGroup</string>
<key>name</key>
<string>Frameworks</string>
<key>sourceTree</key>
<string><![CDATA[<group>]]></string>
<key>children</key>
<array>
<string>1DD70E29FF334B1F00000000</string>
<string>1DD70E2936DE2CF900000000</string>
<string>1DD70E29DB6520C800000000</string>
<string>1DD70E29D65BA68200000000</string>
<string>1DD70E29119CDA0700000000</string>
<string>1DD70E2984A59C1D00000000</string>
<string>1DD70E292395015100000000</string>
</array>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>B401C979C806358400000000</key>
<dict>
<key>isa</key>
<string>PBXGroup</string>
<key>name</key>
<string>Products</string>
<key>sourceTree</key>
<string><![CDATA[<group>]]></string>
<key>children</key>
<array>
<string>1DD70E2957B5522500000000</string>
</array>
</dict>
<key>1DD70E29001F47FB00000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>BUCK</string>
<key>path</key>
<string>BUCK</string>
<key>sourceTree</key>
<string>SOURCE_ROOT</string>
<key>explicitFileType</key>
<string>text.script.python</string>
</dict>
<key>1DD70E29D782379200000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>SegmentedControlNode.swift</string>
<key>path</key>
<string>Sources/SegmentedControlNode.swift</string>
<key>sourceTree</key>
<string>SOURCE_ROOT</string>
</dict>
<key>B401C979EAB5339800000000</key>
<dict>
<key>isa</key>
<string>PBXGroup</string>
<key>name</key>
<string>Sources</string>
<key>sourceTree</key>
<string><![CDATA[<group>]]></string>
<key>children</key>
<array>
<string>1DD70E29D782379200000000</string>
</array>
</dict>
<key>B401C979B84A82AD00000000</key>
<dict>
<key>isa</key>
<string>PBXGroup</string>
<key>name</key>
<string>SegmentedControlNode</string>
<key>sourceTree</key>
<string><![CDATA[<group>]]></string>
<key>children</key>
<array>
<string>1DD70E29001F47FB00000000</string>
<string>B401C979EAB5339800000000</string>
</array>
</dict>
<key>B401C979EFB6AC4600000000</key>
<dict>
<key>isa</key>
<string>PBXGroup</string>
<key>name</key>
<string>mainGroup</string>
<key>sourceTree</key>
<string><![CDATA[<group>]]></string>
<key>children</key>
<array>
<string>B401C979B781F65D00000000</string>
<string>B401C97968022A5500000000</string>
<string>B401C979C806358400000000</string>
<string>B401C979B84A82AD00000000</string>
</array>
</dict>
<key>E7A30F04D782379200000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29D782379200000000</string>
</dict>
<key>1870857F0000000000000000</key>
<dict>
<key>isa</key>
<string>PBXSourcesBuildPhase</string>
<key>files</key>
<array>
<string>E7A30F04D782379200000000</string>
</array>
</dict>
<key>E7A30F04FF334B1F00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29FF334B1F00000000</string>
</dict>
<key>E7A30F04DB6520C800000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29DB6520C800000000</string>
</dict>
<key>E7A30F04D65BA68200000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29D65BA68200000000</string>
</dict>
<key>E7A30F04119CDA0700000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29119CDA0700000000</string>
</dict>
<key>E7A30F0436DE2CF900000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2936DE2CF900000000</string>
</dict>
<key>E7A30F042395015100000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E292395015100000000</string>
</dict>
<key>E7A30F0484A59C1D00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2984A59C1D00000000</string>
</dict>
<key>FAF5FAC90000000000000000</key>
<dict>
<key>isa</key>
<string>PBXCopyFilesBuildPhase</string>
<key>files</key>
<array>
<string>E7A30F04FF334B1F00000000</string>
<string>E7A30F04DB6520C800000000</string>
<string>E7A30F04D65BA68200000000</string>
<string>E7A30F04119CDA0700000000</string>
<string>E7A30F0436DE2CF900000000</string>
<string>E7A30F042395015100000000</string>
<string>E7A30F0484A59C1D00000000</string>
</array>
<key>name</key>
<string>Fake Swift Dependencies (Copy Files Phase)</string>
<key>runOnlyForDeploymentPostprocessing</key>
<integer>1</integer>
<key>dstSubfolderSpec</key>
<integer>16</integer>
<key>dstPath</key>
<string></string>
</dict>
<key>4952437303EDA63300000000</key>
<dict>
<key>isa</key>
<string>XCBuildConfiguration</string>
<key>name</key>
<string>Debug</string>
<key>buildSettings</key>
<dict>
</dict>
<key>baseConfigurationReference</key>
<string>1DD70E299315E66800000000</string>
</dict>
<key>4952437350C7218900000000</key>
<dict>
<key>isa</key>
<string>XCBuildConfiguration</string>
<key>name</key>
<string>Profile</string>
<key>buildSettings</key>
<dict>
</dict>
<key>baseConfigurationReference</key>
<string>1DD70E290A5883D200000000</string>
</dict>
<key>49524373A439BFE700000000</key>
<dict>
<key>isa</key>
<string>XCBuildConfiguration</string>
<key>name</key>
<string>Release</string>
<key>buildSettings</key>
<dict>
</dict>
<key>baseConfigurationReference</key>
<string>1DD70E297DEF2F3400000000</string>
</dict>
<key>218C37090000000000000000</key>
<dict>
<key>isa</key>
<string>XCConfigurationList</string>
<key>buildConfigurations</key>
<array>
<string>4952437303EDA63300000000</string>
<string>4952437350C7218900000000</string>
<string>49524373A439BFE700000000</string>
</array>
<key>defaultConfigurationIsVisible</key>
<false/>
</dict>
<key>E66DC04EB84A82AD00000000</key>
<dict>
<key>isa</key>
<string>PBXNativeTarget</string>
<key>name</key>
<string>SegmentedControlNode</string>
<key>productName</key>
<string>SegmentedControlNode</string>
<key>productReference</key>
<string>1DD70E2957B5522500000000</string>
<key>productType</key>
<string>com.apple.product-type.library.static</string>
<key>dependencies</key>
<array>
</array>
<key>buildPhases</key>
<array>
<string>1870857F0000000000000000</string>
<string>FAF5FAC90000000000000000</string>
</array>
<key>buildConfigurationList</key>
<string>218C37090000000000000000</string>
</dict>
<key>4952437303EDA63300000001</key>
<dict>
<key>isa</key>
<string>XCBuildConfiguration</string>
<key>name</key>
<string>Debug</string>
<key>buildSettings</key>
<dict>
</dict>
</dict>
<key>4952437350C7218900000001</key>
<dict>
<key>isa</key>
<string>XCBuildConfiguration</string>
<key>name</key>
<string>Profile</string>
<key>buildSettings</key>
<dict>
</dict>
</dict>
<key>49524373A439BFE700000001</key>
<dict>
<key>isa</key>
<string>XCBuildConfiguration</string>
<key>name</key>
<string>Release</string>
<key>buildSettings</key>
<dict>
</dict>
</dict>
<key>218C37090000000000000001</key>
<dict>
<key>isa</key>
<string>XCConfigurationList</string>
<key>buildConfigurations</key>
<array>
<string>4952437303EDA63300000001</string>
<string>4952437350C7218900000001</string>
<string>49524373A439BFE700000001</string>
</array>
<key>defaultConfigurationIsVisible</key>
<false/>
</dict>
<key>96C84793B84A82AD00000000</key>
<dict>
<key>isa</key>
<string>PBXProject</string>
<key>mainGroup</key>
<string>B401C979EFB6AC4600000000</string>
<key>targets</key>
<array>
<string>E66DC04EB84A82AD00000000</string>
</array>
<key>buildConfigurationList</key>
<string>218C37090000000000000001</string>
<key>compatibilityVersion</key>
<string>Xcode 3.2</string>
<key>attributes</key>
<dict>
<key>LastUpgradeCheck</key>
<string>9999</string>
</dict>
</dict>
</dict>
<key>rootObject</key>
<string>96C84793B84A82AD00000000</string>
</dict>
</plist>
@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><Scheme LastUpgradeVersion="9999" version="1.7"><BuildAction buildImplicitDependencies="YES" parallelizeBuildables="YES"><BuildActionEntries><BuildActionEntry buildForAnalyzing="YES" buildForArchiving="YES" buildForProfiling="YES" buildForRunning="YES" buildForTesting="YES"><BuildableReference BlueprintIdentifier="E66DC04EB84A82AD00000000" BlueprintName="SegmentedControlNode" BuildableIdentifier="primary" BuildableName="libSegmentedControlNode.a" ReferencedContainer="container:SegmentedControlNode.xcodeproj"/></BuildActionEntry></BuildActionEntries></BuildAction><TestAction buildConfiguration="Debug" shouldUseLaunchSchemeArgsEnv="YES"><Testables/></TestAction><LaunchAction buildConfiguration="Debug"/><ProfileAction buildConfiguration="Release"/><AnalyzeAction buildConfiguration="Debug"/><ArchiveAction buildConfiguration="Release" revealArchiveInOrganizer="YES"/></Scheme>
@@ -0,0 +1,563 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
09CE5B9023225AC200743FF4 /* TelegramPresentationData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09CE5B8F23225AC200743FF4 /* TelegramPresentationData.framework */; };
D060188A22F3604000796784 /* SegmentedControlNode.h in Headers */ = {isa = PBXBuildFile; fileRef = D060188822F3604000796784 /* SegmentedControlNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
D060189522F3615800796784 /* SegmentedControlNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D060189422F3615800796784 /* SegmentedControlNode.swift */; };
D060189822F3616300796784 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D060189722F3616300796784 /* Foundation.framework */; };
D060189A22F3616600796784 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D060189922F3616600796784 /* UIKit.framework */; };
D060189C22F3616A00796784 /* AsyncDisplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D060189B22F3616A00796784 /* AsyncDisplayKit.framework */; };
D060189E22F3616E00796784 /* LegacyComponents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D060189D22F3616E00796784 /* LegacyComponents.framework */; };
D0A0B53922F370DF00628AF3 /* Display.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0A0B53822F370DF00628AF3 /* Display.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
09CE5B8F23225AC200743FF4 /* TelegramPresentationData.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = TelegramPresentationData.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D060188522F3604000796784 /* SegmentedControlNode.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SegmentedControlNode.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D060188822F3604000796784 /* SegmentedControlNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SegmentedControlNode.h; sourceTree = "<group>"; };
D060188922F3604000796784 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D060189422F3615800796784 /* SegmentedControlNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SegmentedControlNode.swift; sourceTree = "<group>"; };
D060189722F3616300796784 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
D060189922F3616600796784 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
D060189B22F3616A00796784 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AsyncDisplayKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D060189D22F3616E00796784 /* LegacyComponents.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = LegacyComponents.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D0A0B53822F370DF00628AF3 /* Display.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Display.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
D060188222F3604000796784 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
09CE5B9023225AC200743FF4 /* TelegramPresentationData.framework in Frameworks */,
D0A0B53922F370DF00628AF3 /* Display.framework in Frameworks */,
D060189E22F3616E00796784 /* LegacyComponents.framework in Frameworks */,
D060189C22F3616A00796784 /* AsyncDisplayKit.framework in Frameworks */,
D060189A22F3616600796784 /* UIKit.framework in Frameworks */,
D060189822F3616300796784 /* Foundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
D060187B22F3604000796784 = {
isa = PBXGroup;
children = (
D060188922F3604000796784 /* Info.plist */,
D060188722F3604000796784 /* Sources */,
D060188622F3604000796784 /* Products */,
D060189622F3616300796784 /* Frameworks */,
);
sourceTree = "<group>";
};
D060188622F3604000796784 /* Products */ = {
isa = PBXGroup;
children = (
D060188522F3604000796784 /* SegmentedControlNode.framework */,
);
name = Products;
sourceTree = "<group>";
};
D060188722F3604000796784 /* Sources */ = {
isa = PBXGroup;
children = (
D060189422F3615800796784 /* SegmentedControlNode.swift */,
D060188822F3604000796784 /* SegmentedControlNode.h */,
);
path = Sources;
sourceTree = "<group>";
};
D060189622F3616300796784 /* Frameworks */ = {
isa = PBXGroup;
children = (
09CE5B8F23225AC200743FF4 /* TelegramPresentationData.framework */,
D0A0B53822F370DF00628AF3 /* Display.framework */,
D060189D22F3616E00796784 /* LegacyComponents.framework */,
D060189B22F3616A00796784 /* AsyncDisplayKit.framework */,
D060189922F3616600796784 /* UIKit.framework */,
D060189722F3616300796784 /* Foundation.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
D060188022F3604000796784 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
D060188A22F3604000796784 /* SegmentedControlNode.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
D060188422F3604000796784 /* SegmentedControlNode */ = {
isa = PBXNativeTarget;
buildConfigurationList = D060188D22F3604000796784 /* Build configuration list for PBXNativeTarget "SegmentedControlNode" */;
buildPhases = (
D060188022F3604000796784 /* Headers */,
D060188122F3604000796784 /* Sources */,
D060188222F3604000796784 /* Frameworks */,
D060188322F3604000796784 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = SegmentedControlNode;
productName = SegmentedControlNode;
productReference = D060188522F3604000796784 /* SegmentedControlNode.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
D060187C22F3604000796784 /* Project object */ = {
isa = PBXProject;
attributes = {
DefaultBuildSystemTypeForWorkspace = Latest;
LastUpgradeCheck = 1010;
ORGANIZATIONNAME = "Telegram Messenger LLP";
TargetAttributes = {
D060188422F3604000796784 = {
CreatedOnToolsVersion = 10.1;
LastSwiftMigration = 1010;
};
};
};
buildConfigurationList = D060187F22F3604000796784 /* Build configuration list for PBXProject "SegmentedControlNode_Xcode" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = D060187B22F3604000796784;
productRefGroup = D060188622F3604000796784 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
D060188422F3604000796784 /* SegmentedControlNode */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
D060188322F3604000796784 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
D060188122F3604000796784 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D060189522F3615800796784 /* SegmentedControlNode.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
D060188B22F3604000796784 /* DebugAppStoreLLC */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = DebugAppStoreLLC;
};
D060188C22F3604000796784 /* ReleaseAppStoreLLC */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = ReleaseAppStoreLLC;
};
D060188E22F3604000796784 /* DebugAppStoreLLC */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Manual;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MACH_O_TYPE = staticlib;
PRODUCT_BUNDLE_IDENTIFIER = org.telegram.SegmentedControlNode;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = DebugAppStoreLLC;
};
D060188F22F3604000796784 /* ReleaseAppStoreLLC */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Manual;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MACH_O_TYPE = staticlib;
PRODUCT_BUNDLE_IDENTIFIER = org.telegram.SegmentedControlNode;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = ReleaseAppStoreLLC;
};
D060189022F360BF00796784 /* DebugHockeyapp */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = DebugHockeyapp;
};
D060189122F360BF00796784 /* DebugHockeyapp */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Manual;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MACH_O_TYPE = staticlib;
PRODUCT_BUNDLE_IDENTIFIER = org.telegram.SegmentedControlNode;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = DebugHockeyapp;
};
D060189222F360CA00796784 /* ReleaseHockeyappInternal */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = ReleaseHockeyappInternal;
};
D060189322F360CA00796784 /* ReleaseHockeyappInternal */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Manual;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MACH_O_TYPE = staticlib;
PRODUCT_BUNDLE_IDENTIFIER = org.telegram.SegmentedControlNode;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = ReleaseHockeyappInternal;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
D060187F22F3604000796784 /* Build configuration list for PBXProject "SegmentedControlNode_Xcode" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D060188B22F3604000796784 /* DebugAppStoreLLC */,
D060189022F360BF00796784 /* DebugHockeyapp */,
D060188C22F3604000796784 /* ReleaseAppStoreLLC */,
D060189222F360CA00796784 /* ReleaseHockeyappInternal */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = ReleaseAppStoreLLC;
};
D060188D22F3604000796784 /* Build configuration list for PBXNativeTarget "SegmentedControlNode" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D060188E22F3604000796784 /* DebugAppStoreLLC */,
D060189122F360BF00796784 /* DebugHockeyapp */,
D060188F22F3604000796784 /* ReleaseAppStoreLLC */,
D060189322F360CA00796784 /* ReleaseHockeyappInternal */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = ReleaseAppStoreLLC;
};
/* End XCConfigurationList section */
};
rootObject = D060187C22F3604000796784 /* Project object */;
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
@@ -0,0 +1,11 @@
#import <UIKit/UIKit.h>
//! Project version number for SegmentedControlNode.
FOUNDATION_EXPORT double SegmentedControlNodeVersionNumber;
//! Project version string for SegmentedControlNode.
FOUNDATION_EXPORT const unsigned char SegmentedControlNodeVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <SegmentedControlNode/PublicHeader.h>
@@ -0,0 +1,381 @@
import Foundation
import Display
import UIKit
import AsyncDisplayKit
import TelegramPresentationData
private let textFont = Font.regular(13.0)
private let selectedTextFont = Font.bold(13.0)
public enum SegmentedControlLayout {
case stretchToFill(width: CGFloat)
case sizeToFit(maximumWidth: CGFloat, minimumWidth: CGFloat)
}
public final class SegmentedControlTheme: Equatable {
public let backgroundColor: UIColor
public let foregroundColor: UIColor
public let shadowColor: UIColor
public let textColor: UIColor
public let dividerColor: UIColor
public init(backgroundColor: UIColor, foregroundColor: UIColor, shadowColor: UIColor, textColor: UIColor, dividerColor: UIColor) {
self.backgroundColor = backgroundColor
self.foregroundColor = foregroundColor
self.shadowColor = shadowColor
self.textColor = textColor
self.dividerColor = dividerColor
}
public static func ==(lhs: SegmentedControlTheme, rhs: SegmentedControlTheme) -> Bool {
if lhs.backgroundColor != rhs.backgroundColor {
return false
}
if lhs.foregroundColor != rhs.foregroundColor {
return false
}
if lhs.shadowColor != rhs.shadowColor {
return false
}
if lhs.textColor != rhs.textColor {
return false
}
if lhs.dividerColor != rhs.dividerColor {
return false
}
return true
}
}
public extension SegmentedControlTheme {
convenience init(theme: PresentationTheme) {
self.init(backgroundColor: theme.rootController.navigationSearchBar.inputFillColor, foregroundColor: theme.rootController.navigationBar.backgroundColor, shadowColor: .black, textColor: theme.rootController.navigationBar.primaryTextColor, dividerColor: theme.list.freeInputField.strokeColor)
}
}
private func generateSelectionImage(theme: SegmentedControlTheme) -> UIImage? {
return generateImage(CGSize(width: 20.0, height: 20.0), rotatedContext: { size, context in
let bounds = CGRect(origin: CGPoint(), size: size)
context.clear(bounds)
if theme.shadowColor != .clear {
context.setShadow(offset: CGSize(width: 0.0, height: -3.0), blur: 6.0, color: theme.shadowColor.withAlphaComponent(0.12).cgColor)
}
context.setFillColor(theme.foregroundColor.cgColor)
context.fillEllipse(in: CGRect(x: 2.0, y: 2.0, width: 16.0, height: 16.0))
})?.stretchableImage(withLeftCapWidth: 10, topCapHeight: 10)
}
public struct SegmentedControlItem: Equatable {
public let title: String
public init(title: String) {
self.title = title
}
}
public final class SegmentedControlNode: ASDisplayNode, UIGestureRecognizerDelegate {
private var theme: SegmentedControlTheme
private var _items: [SegmentedControlItem]
private var _selectedIndex: Int = 0
private var validLayout: SegmentedControlLayout?
private let selectionNode: ASImageNode
private var itemNodes: [HighlightTrackingButtonNode]
private var dividerNodes: [ASDisplayNode]
private var gestureRecognizer: UIPanGestureRecognizer?
private var gestureSelectedIndex: Int?
public var items: [SegmentedControlItem] {
get {
return self._items
}
set {
let previousItems = self._items
self._items = newValue
guard previousItems != newValue else {
return
}
self.itemNodes.forEach { $0.removeFromSupernode() }
self.itemNodes = self._items.map { item in
let itemNode = HighlightTrackingButtonNode()
itemNode.setTitle(item.title, with: textFont, with: self.theme.textColor, for: .normal)
itemNode.setTitle(item.title, with: selectedTextFont, with: self.theme.textColor, for: .selected)
itemNode.setTitle(item.title, with: selectedTextFont, with: self.theme.textColor, for: [.selected, .highlighted])
return itemNode
}
self.setupButtons()
self.itemNodes.forEach(self.addSubnode(_:))
let dividersCount = self._items.count > 2 ? self._items.count - 1 : 0
if self.dividerNodes.count != dividersCount {
self.dividerNodes.forEach { $0.removeFromSupernode() }
self.dividerNodes = (0 ..< dividersCount).map { _ in ASDisplayNode() }
}
if let layout = self.validLayout {
let _ = self.updateLayout(layout, transition: .immediate)
}
}
}
public var selectedIndex: Int {
get {
return self._selectedIndex
}
set {
guard newValue != self._selectedIndex else {
return
}
self._selectedIndex = newValue
if let layout = self.validLayout {
let _ = self.updateLayout(layout, transition: .immediate)
}
}
}
public var selectedIndexChanged: (Int) -> Void = { _ in }
public init(theme: SegmentedControlTheme, items: [SegmentedControlItem], selectedIndex: Int) {
self.theme = theme
self._items = items
self._selectedIndex = selectedIndex
self.selectionNode = ASImageNode()
self.selectionNode.displaysAsynchronously = false
self.selectionNode.displayWithoutProcessing = true
self.itemNodes = items.map { item in
let itemNode = HighlightTrackingButtonNode()
itemNode.setTitle(item.title, with: textFont, with: theme.textColor, for: .normal)
itemNode.setTitle(item.title, with: selectedTextFont, with: theme.textColor, for: .selected)
itemNode.setTitle(item.title, with: selectedTextFont, with: theme.textColor, for: [.selected, .highlighted])
return itemNode
}
let dividersCount = items.count > 2 ? items.count - 1 : 0
self.dividerNodes = (0 ..< dividersCount).map { _ in
let node = ASDisplayNode()
node.backgroundColor = theme.dividerColor
return node
}
super.init()
self.clipsToBounds = true
self.cornerRadius = 9.0
self.addSubnode(self.selectionNode)
self.itemNodes.forEach(self.addSubnode(_:))
self.setupButtons()
self.dividerNodes.forEach(self.addSubnode(_:))
self.backgroundColor = self.theme.backgroundColor
self.selectionNode.image = generateSelectionImage(theme: self.theme)
}
override public func didLoad() {
super.didLoad()
self.view.disablesInteractiveTransitionGestureRecognizer = true
let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
gestureRecognizer.delegate = self
self.view.addGestureRecognizer(gestureRecognizer)
self.gestureRecognizer = gestureRecognizer
}
private func setupButtons() {
for i in 0 ..< self.itemNodes.count {
let itemNode = self.itemNodes[i]
itemNode.addTarget(self, action: #selector(self.buttonPressed(_:)), forControlEvents: .touchUpInside)
itemNode.highligthedChanged = { [weak self, weak itemNode] highlighted in
if let strongSelf = self, let itemNode = itemNode {
let transition = ContainedViewLayoutTransition.animated(duration: 0.25, curve: .easeInOut)
if strongSelf.selectedIndex == i {
if let gestureRecognizer = strongSelf.gestureRecognizer, case .began = gestureRecognizer.state {
} else {
strongSelf.updateButtonsHighlights(highlightedIndex: highlighted ? i : nil, gestureSelectedIndex: strongSelf.gestureSelectedIndex)
}
} else if highlighted {
transition.updateAlpha(node: itemNode, alpha: 0.4)
}
if !highlighted {
transition.updateAlpha(node: itemNode, alpha: 1.0)
}
}
}
}
}
private func updateButtonsHighlights(highlightedIndex: Int?, gestureSelectedIndex: Int?) {
let transition = ContainedViewLayoutTransition.animated(duration: 0.25, curve: .easeInOut)
if highlightedIndex == nil && gestureSelectedIndex == nil {
transition.updateTransformScale(node: self.selectionNode, scale: 1.0)
} else {
transition.updateTransformScale(node: self.selectionNode, scale: 0.92)
}
for i in 0 ..< self.itemNodes.count {
let itemNode = self.itemNodes[i]
if i == highlightedIndex || i == gestureSelectedIndex {
transition.updateTransformScale(node: itemNode, scale: 0.92)
} else {
transition.updateTransformScale(node: itemNode, scale: 1.0)
}
}
}
private func updateButtonsHighlights() {
let transition = ContainedViewLayoutTransition.animated(duration: 0.25, curve: .easeInOut)
if let gestureSelectedIndex = self.gestureSelectedIndex {
for i in 0 ..< self.itemNodes.count {
let itemNode = self.itemNodes[i]
transition.updateTransformScale(node: itemNode, scale: i == gestureSelectedIndex ? 0.92 : 1.0)
}
} else {
for itemNode in self.itemNodes {
transition.updateTransformScale(node: itemNode, scale: 1.0)
}
}
}
public func updateTheme(_ theme: SegmentedControlTheme) {
guard theme != self.theme else {
return
}
self.theme = theme
self.backgroundColor = self.theme.backgroundColor
self.selectionNode.image = generateSelectionImage(theme: self.theme)
for itemNode in self.itemNodes {
if let title = itemNode.attributedTitle(for: .normal)?.string {
itemNode.setTitle(title, with: textFont, with: self.theme.textColor, for: .normal)
itemNode.setTitle(title, with: selectedTextFont, with: self.theme.textColor, for: .selected)
itemNode.setTitle(title, with: selectedTextFont, with: self.theme.textColor, for: [.selected, .highlighted])
}
}
for dividerNode in self.dividerNodes {
dividerNode.backgroundColor = theme.dividerColor
}
}
public func updateLayout(_ layout: SegmentedControlLayout, transition: ContainedViewLayoutTransition) -> CGSize {
self.validLayout = layout
let calculatedWidth: CGFloat = 0.0
let width: CGFloat
switch layout {
case let .stretchToFill(targetWidth):
width = targetWidth
case let .sizeToFit(maximumWidth, minimumWidth):
width = max(minimumWidth, min(maximumWidth, calculatedWidth))
}
let size = CGSize(width: width, height: 32.0)
if !self.itemNodes.isEmpty {
let itemSize = CGSize(width: floorToScreenPixels(size.width / CGFloat(self.itemNodes.count)), height: size.height)
let selectedIndex: Int
if let gestureSelectedIndex = self.gestureSelectedIndex {
selectedIndex = gestureSelectedIndex
} else {
selectedIndex = self.selectedIndex
}
transition.updateBounds(node: self.selectionNode, bounds: CGRect(origin: CGPoint(), size: itemSize))
transition.updatePosition(node: self.selectionNode, position: CGPoint(x: itemSize.width / 2.0 + itemSize.width * CGFloat(selectedIndex), y: size.height / 2.0))
for i in 0 ..< self.itemNodes.count {
let itemNode = self.itemNodes[i]
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: itemSize.width * CGFloat(i), y: (size.height - itemSize.height) / 2.0), size: itemSize))
let isSelected = selectedIndex == i
if itemNode.isSelected != isSelected {
if case .animated = transition {
UIView.transition(with: itemNode.view, duration: 0.2, options: .transitionCrossDissolve, animations: {
itemNode.isSelected = isSelected
}, completion: nil)
} else {
itemNode.isSelected = isSelected
}
}
}
}
if !self.dividerNodes.isEmpty {
let dividerSize = CGSize(width: 1.0, height: 16.0)
let delta: CGFloat = size.width / CGFloat(self.dividerNodes.count + 1)
for i in 0 ..< self.dividerNodes.count {
let dividerNode = self.dividerNodes[i]
transition.updateFrame(node: dividerNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels(delta * CGFloat(i + 1) - dividerSize.width / 2.0), y: (size.height - dividerSize.height) / 2.0), size: dividerSize))
let dividerAlpha: CGFloat
if (self.selectedIndex - 1 ... self.selectedIndex).contains(i) {
dividerAlpha = 0.0
} else {
dividerAlpha = 1.0
}
transition.updateAlpha(node: dividerNode, alpha: dividerAlpha)
}
}
return size
}
@objc private func buttonPressed(_ button: HighlightTrackingButtonNode) {
guard let index = self.itemNodes.firstIndex(of: button) else {
return
}
self._selectedIndex = index
self.selectedIndexChanged(index)
if let layout = self.validLayout {
let _ = self.updateLayout(layout, transition: .animated(duration: 0.2, curve: .slide))
}
}
public override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
let location = gestureRecognizer.location(in: self.view)
return self.selectionNode.frame.contains(location)
}
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
let location = recognizer.location(in: self.view)
switch recognizer.state {
case .changed:
if !self.selectionNode.frame.contains(location) {
let point = CGPoint(x: max(0.0, min(self.bounds.width, location.x)), y: 1.0)
for i in 0 ..< self.itemNodes.count {
let itemNode = self.itemNodes[i]
if itemNode.frame.contains(point) {
if i != self.gestureSelectedIndex {
self.gestureSelectedIndex = i
self.updateButtonsHighlights(highlightedIndex: nil, gestureSelectedIndex: i)
if let layout = self.validLayout {
let _ = self.updateLayout(layout, transition: .animated(duration: 0.35, curve: .slide))
}
}
break
}
}
}
case .ended:
if let gestureSelectedIndex = self.gestureSelectedIndex {
if gestureSelectedIndex != self.selectedIndex {
self._selectedIndex = gestureSelectedIndex
self.selectedIndexChanged(self._selectedIndex)
}
self.gestureSelectedIndex = nil
self.updateButtonsHighlights(highlightedIndex: nil, gestureSelectedIndex: nil)
}
default:
break
}
}
}
+1
View File
@@ -75,6 +75,7 @@ static_library(
"//submodules/GridMessageSelectionNode:GridMessageSelectionNode",
"//submodules/InstantPageCache:InstantPageCache",
"//submodules/AppBundle:AppBundle",
"//submodules/ContextUI:ContextUI",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
@@ -286,6 +286,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E296C9831F100000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libContextUI.a</string>
<key>path</key>
<string>libContextUI.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2915F6A49D00000000</key>
<dict>
<key>isa</key>
@@ -1001,6 +1014,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29AD66967300000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libReactionSelectionNode.a</string>
<key>path</key>
<string>libReactionSelectionNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E291D58331200000000</key>
<dict>
<key>isa</key>
@@ -1053,6 +1079,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E2957B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libSegmentedControlNode.a</string>
<key>path</key>
<string>libSegmentedControlNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29097DBE9200000000</key>
<dict>
<key>isa</key>
@@ -1339,6 +1378,19 @@
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29F40A586F00000000</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>name</key>
<string>libTextSelectionNode.a</string>
<key>path</key>
<string>libTextSelectionNode.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
<key>explicitFileType</key>
<string>archive.ar</string>
</dict>
<key>1DD70E29B4B05B7A00000000</key>
<dict>
<key>isa</key>
@@ -1482,6 +1534,7 @@
<string>1DD70E2997B4D6D800000000</string>
<string>1DD70E292F91702000000000</string>
<string>1DD70E2931752C6000000000</string>
<string>1DD70E296C9831F100000000</string>
<string>1DD70E2915F6A49D00000000</string>
<string>1DD70E29016F454200000000</string>
<string>1DD70E2979E58A0000000000</string>
@@ -1537,10 +1590,12 @@
<string>1DD70E29CBE117ED00000000</string>
<string>1DD70E29AA32BBC600000000</string>
<string>1DD70E293E4DE92B00000000</string>
<string>1DD70E29AD66967300000000</string>
<string>1DD70E291D58331200000000</string>
<string>1DD70E29CF967D4300000000</string>
<string>1DD70E29EA9CBB5B00000000</string>
<string>1DD70E291328E99400000000</string>
<string>1DD70E2957B5522500000000</string>
<string>1DD70E29097DBE9200000000</string>
<string>1DD70E2981AE180900000000</string>
<string>1DD70E29524F478E00000000</string>
@@ -1563,6 +1618,7 @@
<string>1DD70E29F18DE1D900000000</string>
<string>1DD70E29BA06E3A600000000</string>
<string>1DD70E29F0C15F9C00000000</string>
<string>1DD70E29F40A586F00000000</string>
<string>1DD70E29B4B05B7A00000000</string>
<string>1DD70E29CD296A8300000000</string>
<string>1DD70E29AE67341000000000</string>
@@ -4117,6 +4173,13 @@
<key>fileRef</key>
<string>1DD70E29CBE117ED00000000</string>
</dict>
<key>E7A30F0457B5522500000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E2957B5522500000000</string>
</dict>
<key>E7A30F0425BBFEEE00000000</key>
<dict>
<key>isa</key>
@@ -4222,6 +4285,27 @@
<key>fileRef</key>
<string>1DD70E29B6F47D1F00000000</string>
</dict>
<key>E7A30F04AD66967300000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29AD66967300000000</string>
</dict>
<key>E7A30F04F40A586F00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29F40A586F00000000</string>
</dict>
<key>E7A30F046C9831F100000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E296C9831F100000000</string>
</dict>
<key>E7A30F04D233F68C00000000</key>
<dict>
<key>isa</key>
@@ -4243,6 +4327,13 @@
<key>fileRef</key>
<string>1DD70E2931752C6000000000</string>
</dict>
<key>E7A30F04BBAF750C00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29BBAF750C00000000</string>
</dict>
<key>E7A30F041328E99400000000</key>
<dict>
<key>isa</key>
@@ -4327,13 +4418,6 @@
<key>fileRef</key>
<string>1DD70E29E220F5E800000000</string>
</dict>
<key>E7A30F04BBAF750C00000000</key>
<dict>
<key>isa</key>
<string>PBXBuildFile</string>
<key>fileRef</key>
<string>1DD70E29BBAF750C00000000</string>
</dict>
<key>E7A30F04247D9AC100000000</key>
<dict>
<key>isa</key>
@@ -4703,6 +4787,7 @@
<string>E7A30F0497B4D6D800000000</string>
<string>E7A30F04C37F741500000000</string>
<string>E7A30F04CBE117ED00000000</string>
<string>E7A30F0457B5522500000000</string>
<string>E7A30F0425BBFEEE00000000</string>
<string>E7A30F045A26607D00000000</string>
<string>E7A30F042417E0B200000000</string>
@@ -4718,9 +4803,13 @@
<string>E7A30F0471ED41B100000000</string>
<string>E7A30F047AB043C800000000</string>
<string>E7A30F04B6F47D1F00000000</string>
<string>E7A30F04AD66967300000000</string>
<string>E7A30F04F40A586F00000000</string>
<string>E7A30F046C9831F100000000</string>
<string>E7A30F04D233F68C00000000</string>
<string>E7A30F049A24C4DE00000000</string>
<string>E7A30F0431752C6000000000</string>
<string>E7A30F04BBAF750C00000000</string>
<string>E7A30F041328E99400000000</string>
<string>E7A30F041E16CC6C00000000</string>
<string>E7A30F043E4DE92B00000000</string>
@@ -4733,7 +4822,6 @@
<string>E7A30F042F91702000000000</string>
<string>E7A30F04CED1620500000000</string>
<string>E7A30F04E220F5E800000000</string>
<string>E7A30F04BBAF750C00000000</string>
<string>E7A30F04247D9AC100000000</string>
<string>E7A30F04B4B05B7A00000000</string>
<string>E7A30F04490601EB00000000</string>
@@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
091EABA5231DAC7500A0EC14 /* ThemeNameGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 091EABA4231DAC7500A0EC14 /* ThemeNameGenerator.swift */; };
09B4A9B823102B7A005C2E08 /* EditThemeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B4A9B723102B7A005C2E08 /* EditThemeController.swift */; };
09CE5B8E2322154400743FF4 /* SegmentedControlNode.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09CE5B8D2322154400743FF4 /* SegmentedControlNode.framework */; };
D03E465223075D930049C28B /* SettingsUI.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E465023075D930049C28B /* SettingsUI.h */; settings = {ATTRIBUTES = (Public, ); }; };
D03E466823075E660049C28B /* TabBarAccountSwitchControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E465C23075E630049C28B /* TabBarAccountSwitchControllerNode.swift */; };
D03E466923075E660049C28B /* LogoutOptionsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E465D23075E630049C28B /* LogoutOptionsController.swift */; };
@@ -194,6 +195,7 @@
/* Begin PBXFileReference section */
091EABA4231DAC7500A0EC14 /* ThemeNameGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeNameGenerator.swift; sourceTree = "<group>"; };
09B4A9B723102B7A005C2E08 /* EditThemeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditThemeController.swift; sourceTree = "<group>"; };
09CE5B8D2322154400743FF4 /* SegmentedControlNode.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SegmentedControlNode.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D03E464D23075D930049C28B /* SettingsUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SettingsUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D03E465023075D930049C28B /* SettingsUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SettingsUI.h; sourceTree = "<group>"; };
D03E465123075D930049C28B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -384,6 +386,7 @@
buildActionMask = 2147483647;
files = (
D0EFF26A2319825B00CF5164 /* AppBundle.framework in Frameworks */,
09CE5B8E2322154400743FF4 /* SegmentedControlNode.framework in Frameworks */,
D03E493A2308678D0049C28B /* InstantPageCache.framework in Frameworks */,
D03E490E2308661A0049C28B /* GridMessageSelectionNode.framework in Frameworks */,
D03E48E22308649C0049C28B /* CounterContollerTitleView.framework in Frameworks */,
@@ -688,6 +691,7 @@
isa = PBXGroup;
children = (
D0EFF2692319825B00CF5164 /* AppBundle.framework */,
09CE5B8D2322154400743FF4 /* SegmentedControlNode.framework */,
D03E49392308678D0049C28B /* InstantPageCache.framework */,
D03E490D2308661A0049C28B /* GridMessageSelectionNode.framework */,
D03E48E12308649C0049C28B /* CounterContollerTitleView.framework */,
File diff suppressed because it is too large Load Diff
@@ -50,7 +50,7 @@ func openEditSettings(context: AccountContext, accountsAndPeers: Signal<((Accoun
}
openEditingDisposable.set((signal
|> deliverOnMainQueue).start(next: { peer, cachedData, canAddAccounts in
pushController(editSettingsController(context: context, currentName: .personName(firstName: peer.firstName ?? "", lastName: peer.lastName ?? ""), currentBioText: cachedData.about ?? "", accountManager: context.sharedContext.accountManager, canAddAccounts: canAddAccounts, focusOnItemTag: focusOnItemTag))
pushController(editSettingsController(context: context, currentName: .personName(firstName: peer.firstName ?? "", lastName: peer.lastName ?? " "), currentBioText: cachedData.about ?? "", accountManager: context.sharedContext.accountManager, canAddAccounts: canAddAccounts, focusOnItemTag: focusOnItemTag))
}))
return openEditingDisposable
}
@@ -85,15 +85,15 @@ final class RecentSessionsEmptyStateItemNode: ItemListControllerEmptyStateItemNo
let imageSize = self.imageNode.image?.size ?? CGSize()
let imageHeight = layout.size.width < layout.size.height ? imageSize.height + imageSpacing : 0.0
let titleSize = self.titleNode.measure(CGSize(width: layout.size.width - 50.0, height: max(1.0, layout.size.height - insets.top - insets.bottom)))
let textSize = self.textNode.measure(CGSize(width: layout.size.width - 50.0, height: max(1.0, layout.size.height - insets.top - insets.bottom)))
let titleSize = self.titleNode.measure(CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - layout.intrinsicInsets.left - layout.intrinsicInsets.right - 50.0, height: max(1.0, layout.size.height - insets.top - insets.bottom)))
let textSize = self.textNode.measure(CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - layout.intrinsicInsets.left - layout.intrinsicInsets.right - 50.0, height: max(1.0, layout.size.height - insets.top - insets.bottom)))
let totalHeight = imageHeight + titleSize.height + textSpacing + textSize.height
let topOffset = insets.top + floor((layout.size.height - insets.top - insets.bottom - totalHeight) / 2.0)
transition.updateAlpha(node: self.imageNode, alpha: imageHeight > 0.0 ? 1.0 : 0.0)
transition.updateFrame(node: self.imageNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - imageSize.width) / 2.0), y: topOffset), size: imageSize))
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: topOffset + imageHeight), size: titleSize))
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - textSize.width) / 2.0), y: self.titleNode.frame.maxY + textSpacing), size: textSize))
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width - layout.safeInsets.left - layout.safeInsets.right - layout.intrinsicInsets.left - layout.intrinsicInsets.right) / 2.0), y: topOffset + imageHeight), size: titleSize))
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - textSize.width - layout.safeInsets.left - layout.safeInsets.right - layout.intrinsicInsets.left - layout.intrinsicInsets.right) / 2.0), y: self.titleNode.frame.maxY + textSpacing), size: textSize))
}
}
@@ -33,11 +33,33 @@ import WebSearchUI
import PeerAvatarGalleryUI
import MapResourceToAvatarSizes
import AppBundle
import ContextUI
private let maximumNumberOfAccounts = 3
private let avatarFont = UIFont(name: ".SFCompactRounded-Semibold", size: 13.0)!
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
let controller: ViewController
weak var sourceNode: ASDisplayNode?
init(controller: ViewController, sourceNode: ASDisplayNode?) {
self.controller = controller
self.sourceNode = sourceNode
}
func transitionInfo() -> ContextControllerTakeControllerInfo? {
let sourceNode = self.sourceNode
return ContextControllerTakeControllerInfo(contentAreaInScreenSpace: CGRect(origin: CGPoint(), size: CGSize(width: 10.0, height: 10.0)), sourceNode: { [weak sourceNode] in
if let sourceNode = sourceNode {
return (sourceNode, sourceNode.bounds)
} else {
return nil
}
})
}
}
private enum SettingsEntryTag: Equatable, ItemListItemTag {
case account(AccountRecordId)
@@ -80,6 +102,7 @@ private struct SettingsItemArguments {
let removeAccount: (AccountRecordId) -> Void
let keepPhone: () -> Void
let openPhoneNumberChange: () -> Void
let accountContextAction: (AccountRecordId, ASDisplayNode, ContextGesture?) -> Void
}
private enum SettingsSection: Int32 {
@@ -409,6 +432,8 @@ private enum SettingsEntry: ItemListNodeEntry {
arguments.setAccountIdWithRevealedOptions(lhsAccountId, rhsAccountId)
}, removePeer: { _ in
arguments.removeAccount(account.id)
}, contextAction: { node, gesture in
arguments.accountContextAction(account.id, node, gesture)
}, tag: SettingsEntryTag.account(account.id))
case let .addAccount(theme, text):
return ItemListPeerActionItem(theme: theme, icon: PresentationResourcesItemList.plusIconImage(theme), title: text, alwaysPlain: false, sectionId: self.section, height: .generic, editing: false, action: {
@@ -627,6 +652,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM
var pushControllerImpl: ((ViewController) -> Void)?
var presentControllerImpl: ((ViewController, Any?) -> Void)?
var presentInGlobalOverlayImpl: ((ViewController, Any?) -> Void)?
var dismissInputImpl: (() -> Void)?
var setDisplayNavigationBarImpl: ((Bool) -> Void)?
var getNavigationControllerImpl: (() -> NavigationController?)?
@@ -695,6 +721,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM
return cachedFaqInstantPage(context: context)
}
var removeAccountImpl: ((AccountRecordId) -> Void)?
var switchToAccountImpl: ((AccountRecordId) -> Void)?
let displayPhoneNumberConfirmation = ValuePromise<Bool>(false)
@@ -873,27 +900,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM
return state
}
}, removeAccount: { id in
let _ = (contextValue.get()
|> deliverOnMainQueue
|> take(1)).start(next: { context in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let controller = ActionSheetController(presentationTheme: presentationData.theme)
let dismissAction: () -> Void = { [weak controller] in
controller?.dismissAnimated()
}
var items: [ActionSheetItem] = []
items.append(ActionSheetTextItem(title: presentationData.strings.Settings_LogoutConfirmationText.trimmingCharacters(in: .whitespacesAndNewlines)))
items.append(ActionSheetButtonItem(title: presentationData.strings.Settings_Logout, color: .destructive, action: {
dismissAction()
let _ = logoutFromAccount(id: id, accountManager: context.sharedContext.accountManager, alreadyLoggedOutRemotely: false).start()
}))
controller.setItemGroups([
ActionSheetItemGroup(items: items),
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
])
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
})
removeAccountImpl?(id)
}, keepPhone: {
displayPhoneNumberConfirmation.set(false)
}, openPhoneNumberChange: {
@@ -907,6 +914,37 @@ public func settingsController(context: AccountContext, accountManager: AccountM
pushControllerImpl?(ChangePhoneNumberIntroController(context: context, phoneNumber: formatPhoneNumber(phoneNumber)))
})
})
}, accountContextAction: { id, node, gesture in
var selectedAccount: Account?
let _ = (accountsAndPeers.get()
|> take(1)
|> deliverOnMainQueue).start(next: { accountsAndPeers in
for (account, _, _) in accountsAndPeers.1 {
if account.id == id {
selectedAccount = account
break
}
}
})
var sharedContext: SharedAccountContext?
let _ = (contextValue.get()
|> deliverOnMainQueue
|> take(1)).start(next: { context in
sharedContext = context.sharedContext
})
if let selectedAccount = selectedAccount, let sharedContext = sharedContext {
let accountContext = sharedContext.makeTempAccountContext(account: selectedAccount)
let chatListController = accountContext.sharedContext.makeChatListController(context: accountContext, groupId: .root, controlsHistoryPreload: false, hideNetworkActivityStatus: true, enableDebugActions: enableDebugActions)
let presentationData = accountContext.sharedContext.currentPresentationData.with { $0 }
let contextController = ContextController(account: accountContext.account, theme: presentationData.theme, strings: presentationData.strings, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node)), items: accountContextMenuItems(context: accountContext, logout: {
removeAccountImpl?(id)
}), reactionItems: [], gesture: gesture)
presentInGlobalOverlayImpl?(contextController, nil)
} else {
gesture?.cancel()
}
})
changeProfilePhotoImpl = {
@@ -1269,6 +1307,9 @@ public func settingsController(context: AccountContext, accountManager: AccountM
presentControllerImpl = { [weak controller] value, arguments in
controller?.present(value, in: .window(.root), with: arguments ?? ViewControllerPresentationArguments(presentationAnimation: .modalSheet), blockInteraction: true)
}
presentInGlobalOverlayImpl = { [weak controller] value, arguments in
controller?.presentInGlobalOverlay(value, with: arguments)
}
dismissInputImpl = { [weak controller] in
controller?.view.window?.endEditing(true)
}
@@ -1357,6 +1398,29 @@ public func settingsController(context: AccountContext, accountManager: AccountM
}
})
}
removeAccountImpl = { id in
let _ = (contextValue.get()
|> deliverOnMainQueue
|> take(1)).start(next: { context in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let controller = ActionSheetController(presentationTheme: presentationData.theme)
let dismissAction: () -> Void = { [weak controller] in
controller?.dismissAnimated()
}
var items: [ActionSheetItem] = []
items.append(ActionSheetTextItem(title: presentationData.strings.Settings_LogoutConfirmationText.trimmingCharacters(in: .whitespacesAndNewlines)))
items.append(ActionSheetButtonItem(title: presentationData.strings.Settings_Logout, color: .destructive, action: {
dismissAction()
let _ = logoutFromAccount(id: id, accountManager: context.sharedContext.accountManager, alreadyLoggedOutRemotely: false).start()
}))
controller.setItemGroups([
ActionSheetItemGroup(items: items),
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
])
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
})
}
switchToAccountImpl = { id in
let _ = (contextValue.get()
|> take(1)
@@ -1459,3 +1523,28 @@ public func settingsController(context: AccountContext, accountManager: AccountM
return controller
}
private func accountContextMenuItems(context: AccountContext, logout: @escaping () -> Void) -> Signal<[ContextMenuItem], NoError> {
let strings = context.sharedContext.currentPresentationData.with({ $0 }).strings
return context.account.postbox.transaction { transaction -> [ContextMenuItem] in
var items: [ContextMenuItem] = []
if !transaction.getUnreadChatListPeerIds(groupId: .root).isEmpty {
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_MarkAllAsRead, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MarkAsRead"), color: theme.contextMenu.primaryColor) }, action: { _, f in
let _ = (context.account.postbox.transaction { transaction in
markAllChatsAsReadInteractively(transaction: transaction, viewTracker: context.account.viewTracker, groupId: .root)
}
|> deliverOnMainQueue).start(completed: {
f(.default)
})
})))
}
items.append(.action(ContextMenuActionItem(text: strings.Settings_Context_Logout, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Logout"), color: theme.contextMenu.destructiveColor) }, action: { _, f in
logout()
f(.default)
})))
return items
}
}
@@ -219,7 +219,9 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) {
var items: [ChatListItem] = []
let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _ in }, togglePeerSelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {})
let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _ in }, togglePeerSelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, activateChatPreview: { _, _, gesture in
gesture?.cancel()
})
let chatListPresentationData = ChatListPresentationData(theme: self.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
let peers = SimpleDictionary<PeerId, Peer>()
@@ -231,7 +231,7 @@ private enum ThemeAutoNightSettingsControllerEntry: ItemListNodeEntry {
return ThemeSettingsThemeItem(context: arguments.context, theme: theme, strings: strings, sectionId: self.section, themes: themes, themeSpecificAccentColors: themeSpecificAccentColors, currentTheme: currentTheme, updatedTheme: { theme in
arguments.updateTheme(theme)
}, longTapped: { _ in
})
}, contextAction: nil)
}
}
}
@@ -335,9 +335,10 @@ func generateThemeName(accentColor: UIColor) -> String {
if let color = nearest?.color, let colorName = colors[color]?.capitalized {
if arc4random() % 2 == 0 {
return "\(adjectives[Int(arc4random()) % adjectives.count].capitalized) \(colorName)"
return "\((adjectives.randomElement() ?? "").capitalized) \(colorName)"
} else {
return "\(colorName) \(subjectives[Int(arc4random()) % subjectives.count].capitalized)"
return "\(colorName) \((subjectives.randomElement() ?? "").capitalized)"
}
} else {
return ""
@@ -15,6 +15,7 @@ import OverlayStatusController
import AppBundle
public enum ThemePreviewSource {
case settings(PresentationThemeReference)
case theme(TelegramTheme)
case slug(String, TelegramMediaFile)
case media(AnyMediaReference)
@@ -83,14 +84,21 @@ public final class ThemePreviewController: ViewController {
themeName = previewTheme.name.string
}
var isPreview = false
if case .settings = source {
isPreview = true
}
let titleView = CounterContollerTitleView(theme: self.previewTheme)
titleView.title = CounterContollerTitle(title: themeName, counter: " ")
titleView.title = CounterContollerTitle(title: themeName, counter: isPreview ? "" : " ")
self.navigationItem.titleView = titleView
self.statusBar.statusBarStyle = self.previewTheme.rootController.statusBarStyle.style
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionAction"), color: self.previewTheme.rootController.navigationBar.accentTextColor), style: .plain, target: self, action: #selector(self.actionPressed))
if !isPreview {
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 = (combineLatest(self.theme.get(), self.presentationTheme.get())
|> deliverOnMainQueue).start(next: { [weak self] theme, presentationTheme in
@@ -134,6 +142,10 @@ public final class ThemePreviewController: ViewController {
override public func loadDisplayNode() {
super.loadDisplayNode()
var isPreview = false
if case .settings = self.source {
isPreview = true
}
self.displayNode = ThemePreviewControllerNode(context: self.context, previewTheme: self.previewTheme, dismiss: { [weak self] in
if let strongSelf = self {
strongSelf.dismiss()
@@ -142,7 +154,7 @@ public final class ThemePreviewController: ViewController {
if let strongSelf = self {
strongSelf.apply()
}
})
}, isPreview: isPreview)
self.displayNodeDidLoad()
let previewTheme = self.previewTheme
@@ -166,6 +178,8 @@ public final class ThemePreviewController: ViewController {
let disposable = self.applyDisposable
switch self.source {
case let .settings(reference):
theme = .single(reference)
case .theme, .slug:
theme = combineLatest(self.theme.get() |> take(1), wallpaperPromise.get() |> take(1))
|> mapToSignal { theme, wallpaper -> Signal<PresentationThemeReference?, NoError> in
@@ -334,6 +348,8 @@ public final class ThemePreviewController: ViewController {
let subject: ShareControllerSubject
let preferredAction: ShareControllerPreferredAction
switch self.source {
case let .settings(reference):
return
case let .theme(theme):
subject = .url("https://t.me/addtheme/\(theme.slug)")
preferredAction = .default
@@ -31,6 +31,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
private let context: AccountContext
private var previewTheme: PresentationTheme
private var presentationData: PresentationData
private let isPreview: Bool
public let wallpaperPromise = Promise<TelegramWallpaper>()
@@ -59,9 +60,10 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
private var statusDisposable: Disposable?
private var fetchDisposable = MetaDisposable()
init(context: AccountContext, previewTheme: PresentationTheme, dismiss: @escaping () -> Void, apply: @escaping () -> Void) {
init(context: AccountContext, previewTheme: PresentationTheme, dismiss: @escaping () -> Void, apply: @escaping () -> Void, isPreview: Bool) {
self.context = context
self.previewTheme = previewTheme
self.isPreview = isPreview
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
@@ -83,6 +85,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.chatListBackgroundNode = ASDisplayNode()
self.chatContainerNode = ASDisplayNode()
self.chatContainerNode.clipsToBounds = true
self.instantChatBackgroundNode = WallpaperBackgroundNode()
self.instantChatBackgroundNode.displaysAsynchronously = false
self.instantChatBackgroundNode.image = chatControllerBackgroundImage(theme: previewTheme, wallpaper: previewTheme.chat.defaultWallpaper, mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
@@ -94,7 +97,6 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.remoteChatBackgroundNode.view.contentMode = .scaleAspectFill
self.blurredNode = BlurredImageNode()
self.blurredNode.clipsToBounds = true
self.blurredNode.blurView.contentMode = .scaleAspectFill
self.toolbarNode = WallpaperGalleryToolbarNode(theme: self.previewTheme, strings: self.presentationData.strings)
@@ -131,9 +133,11 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.addSubnode(self.scrollNode)
self.chatListBackgroundNode.addSubnode(self.maskNode)
self.addSubnode(self.pageControlBackgroundNode)
self.addSubnode(self.pageControlNode)
self.addSubnode(self.toolbarNode)
if !isPreview {
self.addSubnode(self.pageControlBackgroundNode)
self.addSubnode(self.pageControlNode)
self.addSubnode(self.toolbarNode)
}
self.scrollNode.addSubnode(self.chatListBackgroundNode)
self.scrollNode.addSubnode(self.chatContainerNode)
@@ -307,7 +311,9 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
private func updateChatsLayout(layout: ContainerViewLayout, topInset: CGFloat, transition: ContainedViewLayoutTransition) {
var items: [ChatListItem] = []
let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _ in }, togglePeerSelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {})
let interaction = ChatListNodeInteraction(activateSearch: {}, peerSelected: { _ in }, togglePeerSelected: { _ in }, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, activateChatPreview: { _, _, gesture in
gesture?.cancel()
})
let chatListPresentationData = ChatListPresentationData(theme: self.previewTheme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
let peers = SimpleDictionary<PeerId, Peer>()
@@ -473,7 +479,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.toolbarNode.updateLayout(size: CGSize(width: layout.size.width, height: 49.0), layout: layout, transition: transition)
self.updateChatsLayout(layout: layout, topInset: navigationBarHeight, transition: transition)
self.updateMessagesLayout(layout: layout, bottomInset: toolbarHeight + 66.0, transition: transition)
self.updateMessagesLayout(layout: layout, bottomInset: self.isPreview ? 0.0 : (toolbarHeight + 66.0), transition: transition)
let pageControlSize = self.pageControlNode.measure(CGSize(width: bounds.width, height: 100.0))
let pageControlFrame = CGRect(origin: CGPoint(x: floor((bounds.width - pageControlSize.width) / 2.0), y: layout.size.height - toolbarHeight - 42.0), size: pageControlSize)
@@ -11,6 +11,28 @@ import AlertUI
import WallpaperResources
import ShareController
import AccountContext
import ContextUI
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
let controller: ViewController
weak var sourceNode: ASDisplayNode?
init(controller: ViewController, sourceNode: ASDisplayNode?) {
self.controller = controller
self.sourceNode = sourceNode
}
func transitionInfo() -> ContextControllerTakeControllerInfo? {
let sourceNode = self.sourceNode
return ContextControllerTakeControllerInfo(contentAreaInScreenSpace: CGRect(origin: CGPoint(), size: CGSize(width: 10.0, height: 10.0)), sourceNode: { [weak sourceNode] in
if let sourceNode = sourceNode {
return (sourceNode, sourceNode.bounds)
} else {
return nil
}
})
}
}
func themeDisplayName(strings: PresentationStrings, reference: PresentationThemeReference) -> String {
let name: String
@@ -47,8 +69,9 @@ private final class ThemeSettingsControllerArguments {
let selectAppIcon: (String) -> Void
let presentThemeMenu: (PresentationThemeReference, Bool) -> Void
let editTheme: (PresentationCloudTheme) -> Void
let contextAction: (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void
init(context: AccountContext, updateTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, PresentationThemeAccentColor?) -> Void, openAutoNightTheme: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void, presentThemeMenu: @escaping (PresentationThemeReference, Bool) -> Void, editTheme: @escaping (PresentationCloudTheme) -> Void) {
init(context: AccountContext, updateTheme: @escaping (PresentationThemeReference) -> Void, selectFontSize: @escaping (PresentationFontSize) -> Void, openWallpaperSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, PresentationThemeAccentColor?) -> Void, openAutoNightTheme: @escaping () -> Void, toggleLargeEmoji: @escaping (Bool) -> Void, disableAnimations: @escaping (Bool) -> Void, selectAppIcon: @escaping (String) -> Void, presentThemeMenu: @escaping (PresentationThemeReference, Bool) -> Void, editTheme: @escaping (PresentationCloudTheme) -> Void, contextAction: @escaping (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void) {
self.context = context
self.updateTheme = updateTheme
self.selectFontSize = selectFontSize
@@ -61,6 +84,7 @@ private final class ThemeSettingsControllerArguments {
self.selectAppIcon = selectAppIcon
self.presentThemeMenu = presentThemeMenu
self.editTheme = editTheme
self.contextAction = contextAction
}
}
@@ -300,7 +324,9 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
arguments.updateTheme(theme)
}
}, longTapped: { theme in
arguments.presentThemeMenu(theme, theme.index == currentTheme.index)
//arguments.presentThemeMenu(theme, theme.index == currentTheme.index)
}, contextAction: { theme, node, gesture in
arguments.contextAction(theme.index == currentTheme.index, theme, node, gesture)
})
case let .iconHeader(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
@@ -368,6 +394,7 @@ private func themeSettingsControllerEntries(presentationData: PresentationData,
public func themeSettingsController(context: AccountContext, focusOnItemTag: ThemeSettingsEntryTag? = nil) -> ViewController {
var pushControllerImpl: ((ViewController) -> Void)?
var presentControllerImpl: ((ViewController, Any?) -> Void)?
var presentInGlobalOverlayImpl: ((ViewController, Any?) -> Void)?
var getNavigationControllerImpl: (() -> NavigationController?)?
var updateThemeImpl: ((PresentationThemeReference) -> Void)?
@@ -500,6 +527,77 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
}
})
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}, contextAction: { isCurrent, reference, node, gesture in
let _ = (context.account.postbox.transaction { transaction in
return makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: reference, accentColor: nil, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: .blue)
}
|> deliverOnMainQueue).start(next: { theme in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let strings = presentationData.strings
let themeController = ThemePreviewController(context: context, previewTheme: theme, source: .settings(reference))
var items: [ContextMenuItem] = []
items.append(.action(ContextMenuActionItem(text: strings.Theme_Context_Apply, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ApplyTheme"), color: theme.contextMenu.primaryColor) }, action: { c, f in
c.dismiss(completion: {
updateThemeImpl?(reference)
})
})))
if case let .cloud(theme) = reference {
if theme.theme.isCreator {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_EditTheme, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { c, f in
let controller = editThemeController(context: context, mode: .edit(theme), navigateToChat: { peerId in
if let navigationController = getNavigationControllerImpl?() {
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId)))
}
})
c.dismiss(completion: {
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
})
})))
}
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_ShareTheme, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor) }, action: { c, f in
c.dismiss(completion: {
let controller = ShareController(context: context, subject: .url("https://t.me/addtheme/\(theme.theme.slug)"), preferredAction: .default)
presentControllerImpl?(controller, nil)
})
})))
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_RemoveTheme, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { c, f in
c.dismiss(completion: {
let actionSheet = ActionSheetController(presentationTheme: presentationData.theme)
var items: [ActionSheetItem] = []
items.append(ActionSheetButtonItem(title: presentationData.strings.Appearance_RemoveThemeConfirmation, color: .destructive, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
let _ = (cloudThemes.get() |> delay(0.5, queue: Queue.mainQueue())
|> take(1)
|> deliverOnMainQueue).start(next: { themes in
if isCurrent, let themeIndex = themes.firstIndex(where: { $0.id == theme.theme.id }) {
let newTheme: PresentationThemeReference
if themeIndex > 0 {
newTheme = .cloud(PresentationCloudTheme(theme: themes[themeIndex - 1], resolvedWallpaper: nil))
} else {
newTheme = .builtin(.nightAccent)
}
updateThemeImpl?(newTheme)
}
let _ = deleteThemeInteractively(account: context.account, accountManager: context.sharedContext.accountManager, theme: theme.theme).start()
})
}))
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
})
])])
presentControllerImpl?(actionSheet, nil)
})
})))
}
let contextController = ContextController(account: context.account, theme: presentationData.theme, strings: presentationData.strings, source: .controller(ContextControllerContentSourceImpl(controller: themeController, sourceNode: node)), items: .single(items), reactionItems: [], gesture: gesture)
presentInGlobalOverlayImpl?(contextController, nil)
})
})
let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings]), cloudThemes.get(), availableAppIcons, currentAppIconName.get())
@@ -548,6 +646,9 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
presentControllerImpl = { [weak controller] c, a in
controller?.present(c, in: .window(.root), with: a, blockInteraction: true)
}
presentInGlobalOverlayImpl = { [weak controller] c, a in
controller?.presentInGlobalOverlay(c, with: a)
}
getNavigationControllerImpl = { [weak controller] in
return controller?.navigationController as? NavigationController
}
@@ -11,6 +11,7 @@ import ItemListUI
import WallpaperResources
import AccountContext
import AppBundle
import ContextUI
private var borderImages: [String: UIImage] = [:]
@@ -88,9 +89,10 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
let currentTheme: PresentationThemeReference
let updatedTheme: (PresentationThemeReference) -> Void
let longTapped: (PresentationThemeReference) -> Void
let contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?
let tag: ItemListItemTag?
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, longTapped: @escaping (PresentationThemeReference) -> Void, tag: ItemListItemTag? = nil) {
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, sectionId: ItemListSectionId, themes: [PresentationThemeReference], themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], currentTheme: PresentationThemeReference, updatedTheme: @escaping (PresentationThemeReference) -> Void, longTapped: @escaping (PresentationThemeReference) -> Void, contextAction: ((PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void)?, tag: ItemListItemTag? = nil) {
self.context = context
self.theme = theme
self.strings = strings
@@ -99,6 +101,7 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
self.currentTheme = currentTheme
self.updatedTheme = updatedTheme
self.longTapped = longTapped
self.contextAction = contextAction
self.tag = tag
self.sectionId = sectionId
}
@@ -138,11 +141,13 @@ class ThemeSettingsThemeItem: ListViewItem, ItemListItem {
}
private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
private let containerNode: ContextControllerSourceNode
private let imageNode: TransformImageNode
private let overlayNode: ASImageNode
private let textNode: ASTextNode
private var action: (() -> Void)?
private var longTapAction: (() -> Void)?
private var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
private var theme: PresentationThemeReference?
private var currentTheme: PresentationTheme?
@@ -151,6 +156,8 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
private var selected: Bool?
override init() {
self.containerNode = ContextControllerSourceNode()
self.imageNode = TransformImageNode()
self.imageNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 98.0, height: 62.0))
self.imageNode.isLayerBacked = true
@@ -165,12 +172,21 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
super.init()
self.addSubnode(self.imageNode)
self.addSubnode(self.overlayNode)
self.addSubnode(self.textNode)
self.addSubnode(self.containerNode)
self.containerNode.addSubnode(self.imageNode)
self.containerNode.addSubnode(self.overlayNode)
self.containerNode.addSubnode(self.textNode)
self.containerNode.activated = { [weak self] gesture in
guard let strongSelf = self else {
gesture.cancel()
return
}
strongSelf.contextAction?(strongSelf.containerNode, gesture)
}
}
func setup(context: AccountContext, theme: PresentationThemeReference, accentColor: UIColor?, currentTheme: PresentationTheme, title: NSAttributedString, bordered: Bool, selected: Bool, action: @escaping () -> Void, longTapAction: @escaping () -> Void) {
func setup(context: AccountContext, theme: PresentationThemeReference, accentColor: UIColor?, currentTheme: PresentationTheme, title: NSAttributedString, bordered: Bool, selected: Bool, action: @escaping () -> Void, longTapAction: @escaping () -> Void, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?) {
let updatedTheme = self.currentTheme == nil || currentTheme !== self.currentTheme!
if case let .cloud(theme) = theme, theme.theme.file == nil {
if updatedTheme || accentColor != self.accentColor {
@@ -198,6 +214,8 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
self.longTapAction = {
longTapAction()
}
self.contextAction = contextAction
self.containerNode.isGestureEnabled = !selected
}
override func didLoad() {
@@ -234,6 +252,8 @@ private final class ThemeSettingsThemeItemIconNode : ASDisplayNode {
let bounds = self.bounds
self.containerNode.frame = CGRect(origin: CGPoint(), size: bounds.size)
let imageSize = CGSize(width: 98.0, height: 62.0)
self.imageNode.frame = CGRect(origin: CGPoint(x: 10.0, y: 14.0), size: imageSize)
let makeLayout = self.imageNode.asyncLayout()
@@ -381,6 +401,11 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
}
}, longTapAction: {
item.longTapped(theme)
}, contextAction: item.contextAction.flatMap {
contextAction in
return { node, gesture in
contextAction(theme, node, gesture)
}
})
imageNode.frame = CGRect(origin: CGPoint(x: nodeOffset, y: 0.0), size: nodeSize)
+14 -1
View File
@@ -5,6 +5,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[570911930] = { return $0.readInt64() }
dict[571523412] = { return $0.readDouble() }
dict[-1255641564] = { return parseString($0) }
dict[-475111160] = { return Api.MessageReactionsList.parse_messageReactionsList($0) }
dict[-1240849242] = { return Api.messages.StickerSet.parse_stickerSet($0) }
dict[-457104426] = { return Api.InputGeoPoint.parse_inputGeoPointEmpty($0) }
dict[-206066487] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) }
@@ -240,6 +241,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[967122427] = { return Api.Update.parse_updateNewScheduledMessage($0) }
dict[-1870238482] = { return Api.Update.parse_updateDeleteScheduledMessages($0) }
dict[-2112423005] = { return Api.Update.parse_updateTheme($0) }
dict[357013699] = { return Api.Update.parse_updateMessageReactions($0) }
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) }
dict[367766557] = { return Api.ChannelParticipant.parse_channelParticipant($0) }
@@ -281,6 +283,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1012306921] = { return Api.InputTheme.parse_inputTheme($0) }
dict[-175567375] = { return Api.InputTheme.parse_inputThemeSlug($0) }
dict[1158290442] = { return Api.messages.FoundGifs.parse_foundGifs($0) }
dict[-1199954735] = { return Api.MessageReactions.parse_messageReactions($0) }
dict[-1132476723] = { return Api.FileLocation.parse_fileLocationToBeDeprecated($0) }
dict[-716006138] = { return Api.Poll.parse_poll($0) }
dict[423314455] = { return Api.InputNotifyPeer.parse_inputNotifyUsers($0) }
@@ -558,7 +561,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1820043071] = { return Api.User.parse_user($0) }
dict[-2082087340] = { return Api.Message.parse_messageEmpty($0) }
dict[-1642487306] = { return Api.Message.parse_messageService($0) }
dict[1160515173] = { return Api.Message.parse_message($0) }
dict[-1752573244] = { return Api.Message.parse_message($0) }
dict[186120336] = { return Api.messages.RecentStickers.parse_recentStickersNotModified($0) }
dict[586395571] = { return Api.messages.RecentStickers.parse_recentStickers($0) }
dict[-182231723] = { return Api.InputFileLocation.parse_inputEncryptedFileLocation($0) }
@@ -612,6 +615,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1625153079] = { return Api.InputWebFileLocation.parse_inputWebFileGeoPointLocation($0) }
dict[-332168592] = { return Api.MessageFwdHeader.parse_messageFwdHeader($0) }
dict[398898678] = { return Api.help.Support.parse_support($0) }
dict[1873957073] = { return Api.ReactionCount.parse_reactionCount($0) }
dict[1474492012] = { return Api.MessagesFilter.parse_inputMessagesFilterEmpty($0) }
dict[-1777752804] = { return Api.MessagesFilter.parse_inputMessagesFilterPhotos($0) }
dict[-1614803355] = { return Api.MessagesFilter.parse_inputMessagesFilterVideo($0) }
@@ -638,6 +642,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-290921362] = { return Api.upload.CdnFile.parse_cdnFileReuploadNeeded($0) }
dict[-1449145777] = { return Api.upload.CdnFile.parse_cdnFile($0) }
dict[415997816] = { return Api.help.InviteText.parse_inviteText($0) }
dict[-764945220] = { return Api.MessageUserReaction.parse_messageUserReaction($0) }
dict[-1937807902] = { return Api.BotInlineMessage.parse_botInlineMessageText($0) }
dict[982505656] = { return Api.BotInlineMessage.parse_botInlineMessageMediaGeo($0) }
dict[1984755728] = { return Api.BotInlineMessage.parse_botInlineMessageMediaAuto($0) }
@@ -837,6 +842,8 @@ public struct Api {
public static func serializeObject(_ object: Any, buffer: Buffer, boxed: Swift.Bool) {
switch object {
case let _1 as Api.MessageReactionsList:
_1.serialize(buffer, boxed)
case let _1 as Api.messages.StickerSet:
_1.serialize(buffer, boxed)
case let _1 as Api.InputGeoPoint:
@@ -969,6 +976,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.messages.FoundGifs:
_1.serialize(buffer, boxed)
case let _1 as Api.MessageReactions:
_1.serialize(buffer, boxed)
case let _1 as Api.FileLocation:
_1.serialize(buffer, boxed)
case let _1 as Api.Poll:
@@ -1257,6 +1266,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.help.Support:
_1.serialize(buffer, boxed)
case let _1 as Api.ReactionCount:
_1.serialize(buffer, boxed)
case let _1 as Api.MessagesFilter:
_1.serialize(buffer, boxed)
case let _1 as Api.messages.Dialogs:
@@ -1267,6 +1278,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.help.InviteText:
_1.serialize(buffer, boxed)
case let _1 as Api.MessageUserReaction:
_1.serialize(buffer, boxed)
case let _1 as Api.BotInlineMessage:
_1.serialize(buffer, boxed)
case let _1 as Api.InputPeerNotifySettings:

Some files were not shown because too many files have changed in this diff Show More